Investigating Interface Architecture in Legacy Systems
When we first learned to write computer programs, the vast majority of us started with a blank screen. Civil engineers call this a “green field” for building: There are no existing structures to interact with, demolish, or renovate.
When we begin to work professionally, careers bifurcate. Some of us continue to build shiny, new systems, still working on green-field projects. Others must learn a different set of skills working on “brown field” projects, replacing, modifying, or interacting with existing systems.
Programmers who haven’t worked with legacy systems often seek to understand how the program is designed; determine its boundaries, inputs, and outputs; and build a mental model of the system. This approach isn’t wrong; it’s just incomplete. They must also consider the system’s interfaces.
Interfaces between systems arise for several reasons. Sometimes they are the result of an initial design choice to isolate or partition functionality. Some interfaces emerge as necessary connections between two systems, one or both of which were never initially designed to exchange data with one another. Some interfaces are quick and dirty “cheats” that take data from a system that is unaware it is sharing. Some interfaces evolve like cell division—when an existing system gets too complex or a necessary change is too big and confusing to implement in a base system, some of the functionality is amputated and grown into a system of its own.
These different interface origins can produce a variety of behavior—some elegant, some kludgy, some terrifying. Some interfaces are well-behaved, well-defined, and well-documented. (Allegedly. It could happen.) As a practical matter, most real interfaces have little or no documentation, and the specification is always suspect.
When trying to understand legacy systems, special care must be taken to explore and understand interfaces. Some are not always obvious, like when an adjacent system may eavesdrop by reading your system’s files or, more pernicious, modify your system’s files; some may have idiosyncratic behavior, resending messages periodically until they are acknowledged or ignoring every other message received; and some may not practice good interface hygiene, by which I mean not clearing data in fields that aren’t expected to be used or not validating incoming data before processing it.
Some interfaces even have timing-dependent behavior that can affect data integrity. I’ve seen interfaces that crashed because a processor upgrade increased throughput so that an interface could receive more than one message per second and the receiving system was using a date/time stamp of YYYYMMDDHHMMSS to uniquely identify the messages.
Legacy systems are challenging puzzles. The detective work it takes to understand them is not usually part of traditional computer science or programming curricula. Particular care must be taken to identify, inventory, and understand the design and behavior of interfaces because they represent a strange twist to the notion most people have of system architecture.