The Ghosts in Your Software

What Happens When Nobody Understands the Code?
Every software system carries decisions made by people who may no longer be around. Some remain valuable. Others become expensive mysteries. The real danger is not always the age of the technology, but how little we understand about it.
In Scotland, Samhainn traditionally marks the transition from autumn into winter, a time associated with stories of spirits, disguises and the boundary between the familiar and the unknown. It is a fitting metaphor for something we encounter surprisingly often in software development: systems that continue to operate long after the people who built them have moved on.
These applications may be processing payments, managing customer records or supporting essential public services. They appear to work, sometimes remarkably well, yet nobody is entirely sure why certain decisions were made, what might break if a particular component changes, or whether an obscure integration is still needed.
The ghosts in these systems are not necessarily bugs. They are the missing explanations, forgotten assumptions and undocumented dependencies that quietly influence every decision about what happens next.
Every System Has a History
Software is rarely built in a single, uninterrupted process. It evolves through changing business requirements, shifting budgets, new regulations and the arrival and departure of developers. What began as a straightforward application gradually becomes a record of decisions made under different circumstances.
A database field might exist because a former customer required it. An unusual validation rule may reflect legislation that has since changed. A complicated integration might have been the only practical solution available when the platform was first developed.
Without that context, perfectly reasonable engineering decisions can appear arbitrary. The temptation is to remove whatever looks outdated, particularly when a modern framework offers a cleaner way to achieve the same result. Yet code that seems unnecessary may still be protecting an important business rule.
This is why understanding an existing system requires more than reading its source code. It requires understanding the organisation, its processes and the decisions that shaped the software over time.
When Knowledge Leaves the Building
Most organisations recognise the importance of documenting their business processes, but the same discipline does not always extend to the software supporting them.
Developers change roles, external suppliers move on and internal teams are reorganised. Documentation becomes outdated, while important decisions survive only in old messages, support tickets or the memories of people who were involved years earlier.
Eventually, a relatively simple request becomes unexpectedly difficult. A new feature requires investigating several interconnected services. A security update reveals dependencies that cannot easily be upgraded. A change to one workflow produces an unexpected result somewhere else.
The immediate problem may look technical, but the underlying issue is often organisational. Knowledge has been allowed to disappear faster than the system itself has evolved.
This creates a particular kind of technical debt: not necessarily poor-quality code, but uncertainty about how the software works and why it works that way. That uncertainty makes every subsequent change slower, more expensive and more difficult to estimate.
The Cost of Being Afraid to Change Anything
One of the clearest warning signs of an unhealthy software system is when an organisation becomes reluctant to change it.
The application may still perform its core function, but the team avoids updates because nobody can confidently predict their consequences. Small improvements are postponed, integrations remain untouched, and security patches require increasingly elaborate workarounds.
Over time, this caution creates its own risks. The software becomes less adaptable to changing user expectations, operational requirements and security standards. Meanwhile, the gap between what the organisation needs and what its technology can safely support continues to widen.
There is also a commercial consequence. When a development partner cannot establish how a system behaves, even modest changes require additional investigation and contingency. Estimates become less certain because the work involves discovering hidden dependencies as much as implementing new functionality.
As we explored in Why Software Development Quotes Vary So Much, the visible feature is only part of the work being priced. In an unfamiliar legacy system, the effort required to understand existing behaviour can be just as significant as the effort required to change it.
Old Software Is Not Necessarily Bad Software
It is worth challenging a common assumption: an ageing technology stack does not automatically mean an application is badly designed.
Some older systems are exceptionally reliable. Their behaviour is well understood, their requirements have stabilised, and they continue to perform valuable work without unnecessary complexity. Replacing them simply because a newer technology exists can introduce considerable cost and risk without delivering a meaningful improvement for users.
Equally, a relatively new application can accumulate serious technical debt in a remarkably short period. Modern frameworks, cloud infrastructure and AI-assisted development do not guarantee maintainability. A system built quickly without clear architectural decisions, testing or documentation may become difficult to change long before its technology becomes obsolete.
We explored this problem from another angle in When Vibe-Coded Apps Meet Reality, where the apparent speed of software creation can conceal the longer-term work required to make a product dependable.
The distinction that matters is not simply old versus new. It is whether the organisation can understand, maintain, and safely evolve the software it depends upon.
Before Rebuilding, Start Investigating
When an organisation inherits an unfamiliar application, a complete rebuild can appear attractive. Starting again promises cleaner architecture, modern technology and an opportunity to remove years of accumulated complexity.
Sometimes that is the right decision. However, a rebuild also means rediscovering business rules, recreating established workflows, migrating data and testing behaviours that existing users may take for granted. Without a clear understanding of the original system, the new version can reproduce its problems while introducing new ones.
A more measured approach begins with investigation. Reviewing the architecture, dependencies, data structures, and critical workflows helps establish which parts of the platform are healthy, which present genuine risks and where knowledge is missing.
It is also important to speak with the people who use the software. They often understand its real behaviour better than the available documentation, including exceptions and workarounds that have become part of everyday operations.
The outcome might be a targeted refactor, a series of dependency upgrades, improved automated testing or a phased replacement of particular components. A complete rebuild remains an option, but it becomes an informed decision rather than an instinctive reaction to unfamiliar code.
Making Software Easier to Inherit
Every software system will eventually be maintained by somebody who did not build it. That might be a new employee, another agency or the same development team returning to a project after several years.
Good engineering should account for that reality from the beginning. Clear architectural boundaries, meaningful automated tests, consistent coding conventions and accessible documentation all help future developers understand how the system is intended to behave.
Documentation does not need to describe every line of code. It is often more useful to capture the decisions that are difficult to reconstruct: why a particular integration exists, which business rules must remain intact, what assumptions underpin the data model and how the application is deployed and monitored.
Equally, maintainability is not achieved through documentation alone. It depends on teams resisting unnecessary complexity, reviewing changes carefully and treating long-term ownership as part of delivery rather than an activity postponed until after launch.
This thinking is central to how we approach software development at GearedApp. Whether we are taking over an existing platform, planning improvements or building something new, understanding the wider system matters as much as delivering the next feature. The aim is not to eliminate every compromise, but to make those compromises visible, deliberate and manageable.
The Best Time to Understand Your Software Is Before Something Breaks
Organisations often discover how little they know about their software during a crisis. A critical integration fails, a supplier becomes unavailable, or an urgent security issue forces changes to a component nobody has touched in years.
By that point, the organisation is trying to recover knowledge under pressure, with limited time to distinguish genuine risks from harmless complexity.
A more sustainable approach is to treat technical understanding as an ongoing responsibility. Periodic reviews, dependency maintenance, documentation of important decisions and sensible testing all reduce the likelihood that ordinary changes become major projects.
As Samhainn reminds us, the past has a habit of making itself felt in the present. Software is no different. Every system carries the consequences of earlier decisions, but those decisions do not have to remain mysterious.
The goal is not to build software without a history. It is to build and maintain software whose history can still be understood.
If your organisation relies on a platform that has become difficult to maintain, extend or even explain, get in touch with GearedApp. We can help assess what is there, identify the genuine risks and determine whether the right next step is improvement, modernisation or replacement.