The Things We Build Outlive the Decisions We Remember Making
A system can become important long before anyone notices it has become old.
One day it is a modest script, a spreadsheet with a few sensible-looking formulas, a database table added during a busy week, or a device configuration that will do until the next release. Then people begin relying on it. Reports are built around it. Customers receive alerts from it. Finance reconciles against it. Someone writes a procedure with a screenshot from 2017, and suddenly the temporary arrangement has achieved constitutional status.
Years later, an engineer opens it up and finds a small decision that nobody remembers making. A timeout is set to 30 seconds. A sensor is assumed never to report below zero. A field is limited to 16 characters. A service retries forever because, at the time, that felt safer than dropping a message.
The decision may have been entirely reasonable. That is worth saying clearly. Most legacy code was not written by fools in a darkened room, cackling over a keyboard. It was written by people under constraints: a deadline, incomplete information, a customer waiting, a budget, a component datasheet that was optimistic in the manner of a Labrador promising not to eat the sandwich.
But the thing we built remains, long after the reasons for building it that way have faded.
Old systems are archives of assumptions
Software and hardware do not merely preserve instructions. They preserve beliefs about the world.
Consider temperature monitoring. A system may store readings as whole numbers because the original application only needed broad cold-chain confirmation. Later, someone needs to investigate short excursions around a limit, but the original rounding has quietly discarded the evidence. Or perhaps timestamps are recorded in local time, without an offset or time-zone identifier. Everything looks perfectly civilised until the clocks change, at which point one hour occurs twice and another does not occur at all. Computers, bless them, will follow the instruction exactly. It is people who then have to explain why the audit trail appears to have taken a brief holiday from chronology.
The same applies to device identifiers, alarm rules, access permissions, and network design. An MQTT client configured with a fixed keep-alive interval may have behaved perfectly on one reliable network. Move it behind a mobile connection, a proxy, or a slightly overenthusiastic firewall and the supposedly harmless setting becomes a source of phantom disconnects. A retry loop without backoff can turn one unavailable service into a cheerful distributed denial-of-service attack on the very system it is trying to reach.
None of these are exotic failures. They are ordinary assumptions, stored in durable form.
Maintenance is an act of interpretation
People sometimes describe maintenance as though it is the less glamorous work that happens after the real engineering is done. This is nonsense, though it is persistent nonsense, rather like the idea that printers are improved by shouting at them.
Maintaining an old system requires interpretation. You are not simply changing a line of code or replacing a component. You are trying to establish what a previous decision meant, what conditions made it sensible, who depends on it now, and what might break if you alter it.
Documentation helps, obviously. A short note explaining why a limit exists can save days of archaeology later. But documents age too. A design decision record that says “temporary workaround” is less useful when it does not say temporary until when, or workaround for what.
The most valuable documentation is often the kind that captures uncertainty:
- what the system assumes about its inputs;
- what happens when a dependency is unavailable;
- which values are business rules rather than technical defaults;
- who owns a decision when the system crosses team boundaries;
- what was deliberately left unresolved.
That final point deserves more respect. Engineers are often encouraged to sound certain, even when a system is being built in a fog of changing requirements. A candid note saying, “We do not yet know how this behaves at scale” is not a confession of incompetence. It is a warning label for the future, and future engineers deserve warning labels.
Inheriting responsibility without blaming the past
There is a temptation, when meeting awkward legacy code, to sneer at those who came before. We have all seen the sort of function whose name suggests a minor calculation and whose body contains seventeen special cases, three date formats and what appears to be a private argument with a spreadsheet.
A little private grumbling is human. It may even be medicinal. But blame is usually less useful than curiosity.
Ask what pressure shaped the system. Was it designed for a smaller organisation? Did regulations change? Did an integration partner impose a strange format? Was a manual process quietly automated without anyone reconsidering the underlying policy? Has the business grown around a workaround because changing it would interrupt a process that now feels indispensable?
The engineer maintaining the system inherits consequences, not necessarily fault. That distinction matters. It makes room for compassion towards previous teams while retaining a firm duty to improve what can be improved.
It also applies beyond code. A building, a public service, a product interface, or an accessibility policy can carry forward assumptions about who will use it and how. When I encounter technology that excludes disabled people, I am wary of treating it as an unfortunate edge case. It often reflects a decision, explicit or otherwise, about whose difficulty was considered worth designing around. The decision may be old. Its effect is very much current.
Build with future strangers in mind
We cannot predict every future use, failure, merger, regulation, or strange little requirement that arrives at 4:45 on a Friday. Anyone claiming otherwise is probably selling a framework.
We can, however, leave systems kinder than we found them. Name things clearly. Make failure states visible. Avoid hiding business policy in unexplained constants. Record why a compromise was made. Add sensible observability so that the next person can see queue depth, error rates, calibration status, battery condition, or failed authentication attempts before the customer notices first.
And when a shortcut is necessary, say so. A shortcut honestly labelled is technical debt with a map. An undocumented shortcut is a trapdoor under the carpet.
The things we build will outlast our recollection of choosing a particular field length, retry interval, wiring route, or validation rule. That is not a reason for paralysis. It is simply a reason for humility. Somewhere, eventually, another person will inherit our decisions. The least we can do is leave them a light on, a note in the drawer, and fewer surprises than we found.