I recently had the opportunity to assist on a project where we’re replacing a highly-customized legacy system. As part of the discovery process, we were looking at the integrations to the other systems, third-party administrators, and so on currently in place. One in particular seemed unnecessarily convoluted – it received a file from one system, validated all of the data, and generated workflows to various roles in the event a particular transaction was deemed invalid. The transaction would stay in the “staging area” until corrected, at which point it would be loaded to the receiving system. I asked how many times these workflows were generated; six or eight times, was the reply. Per week, or per day, I asked? Six or eight times, to date, throughout the life of the system. Not exactly cost-effective, I thought.
One of the things I’ve found over the years is that all designs begin with requirements, but they end with constraints. During a development or implementation project, these constraints might arise from cost and schedule targets, as well as operational and compliance needs. The project team takes various short cuts, or they accept a certain inherent error rate, or take some other action (or inaction) that creates what Ward Cunningham refers to as “technical debt.” Of course, once a system or program goes into production, the maintenance cycle begins, and Manny Lehman’s law, coined in 1980, becomes operative: “As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it.” Maintenance programmers take the already-flawed work of the implementation team, and insert additional flaws in a series of attempts to improve it.
Consequently, trying to reverse-engineer requirements from a body of code is essentially a fool’s errand. The combined load of technical debt, “nice to have” features added by the maintenance team, and vestigial code from obsolete requirements acts to obscure the original requirements, which will have almost certainly changed since the original program was designed. And yet, we ask to see the code, or at least a file containing the output from a program, as though it will give us what we need more accurately and unambiguously than an interview with the subject matter experts. Apparently, we’re going to replace the old with an essentially identical process, using more advanced technology.
There are few systems exchanging data that are completely static. This is especially true with HR, payroll, financial, and other systems which must comply with frequently-changing legislation and standards. Before replacing a buggy whip with an electric horse prod, let’s take the time to understand what’s different, both in terms of the endpoint systems and compliance requirements, before we design any interfaces. Let’s review the “as-is” with the SME’s, and then ask what should be changed. Let’s try to understand operational rhythms, fault tolerance, and how errors will be handled in production. Let’s see what will be different in the data sources and sinks, once one of the endpoints has been replaced. And let’s try not to create a new set of maintenance problems.
There is more to a design than requirements – there are constraints, and compromises, and an operating environment, and a support model. Good designers take all of these into account, and their designs reflect that. And good implementation teams recognize that the last implementation team also faced requirements, constraints, compromises, and operating and support models, but not necessarily the same ones.