The image shows five wooden blocks with symbols representing interpersonal skills. A hand deliberately highlights the cube with the red heart and the inscription "EMPATHY". The warm, calm atmosphere emphasises empathy as a technical skill in software development.

In most if not all legacy systems there is a treasure of domain knowledge that is worth to be dug out. That's why I agree a hundred percent with “treating legacy with respect”! These systems have become so old for a reason and that reason is that they did their job well (or at least well enough…).

The above is a comment by Henning Schwentner on a statement by Michael Plöd:

Always treat the legacy with respect. No one got up 20 years ago to build a bad system. To drive change, you need empathy first.

These two statements, shared on LinkedIn not long ago by two respected voices in software architecture, struck a chord deeper than a simple professional observation. They articulated a truth that is often forgotten when navigating complex codebases and architectural challenges: technical debt is not malicious. It is not the result of malice, laziness, incompetence, or carelessness. Instead, it is the accumulated weight of constraints, trade-offs, and context that we were not there to witness.

Over the years, I have learned that the most valuable skill in software development is not architectural mastery or technical virtuosity. It is empathy. Specifically, it is the ability to refrain from asking with contempt, “What were these developers thinking?”, and instead ask, “What problem were they trying to solve? What constraints were they working under? Why was their solution the best choice at that time?”

Stories hidden in code

Every line of legacy code is the result of someone's best attempt to solve a real problem using the tools, knowledge, and constraints available to them at the time. Consider the context: A developer in 2006 choosing a particular framework because it was state-of-the-art at the time, for example. Or a team working under relentless deadline pressure choosing the quick solution over the perfect one because shipping mattered more that week. Or an architect making a design decision that was entirely reasonable given the business constraints and technical landscape of that era.

When we approach these systems with curiosity rather than contempt, their stories are revealed. We discover organisational priorities that shifted mid-project. We uncover technologies that seemed promising but did not work out. We find architectural decisions that made perfect sense before the business pivoted in a new direction. We see the marks of people who cared deeply about solving problems, just as we do.

Yet, in far too many code reviews and team discussions, I hear a different narrative: dismissal disguised as critique. Developers scoff at legacy systems without considering that dismissing the code is, fundamentally, dismissing the people who wrote it. They blame previous developers without understanding the constraints they faced.

The problem is not just one of tone. This blame-oriented approach is actively counterproductive. When we start from a place of judgement, we miss the crucial information needed to meaningfully improve the system. We miss the opportunity to extract the domain knowledge embedded in the codebase, which may be irreplaceable and incredibly valuable.

Empathy as a technical skill

For too long, the software industry has treated empathy “just” as a soft skill: something nice to have, but not central to technical excellence. However, this is a mistake born from a fundamental misunderstanding of what empathy actually is.

Empathy is not just a feeling. It is a technical skill that can be learned and improved through deliberate practice. Andrea Goulet defines it in Empathy-Driven Development as “proactive perspective-taking and problem-solving”. This definition transforms empathy from something abstract and intangible into something concrete and actionable. Before making a decision, take a moment to consider who will be affected by it and what they might need from you.

When we apply this approach to legacy code, we are conducting an investigation rather than making a criticism. We ask: “What trade-offs did this person make?”, “What constraints did they navigate?”, “What knowledge do they have that I do not?” Asking these questions shifts us from a posture of superiority to a posture of learning.

This is not sentimentality. It is pragmatism. The systems we inherit have been running in production for years or even decades precisely because they fulfil their intended purpose well (enough). They have accumulated understanding of business rules, edge cases, and organisational nuances, that exists in the code and in the collective memory of the teams that built and maintained them. When we approach legacy systems with respect rather than scorn, we can access this knowledge. We can then extract and learn from this knowledge to build better systems going forward.

Understanding technical debt

Technical debt does not accumulate simply because previous developers were incompetent or indifferent. It builds up due to the real-world constraints that software teams face daily.

Consider the decision tree a developer encounters: Should they ship the feature on time by taking a shortcut, or build it properly but miss the deadline — and perhaps miss the market window that renders the feature valuable? These are not purely technical decisions. They are human decisions made in organisational contexts. They involve competing priorities, budget constraints, time pressure, and the knowledge gaps that inevitably exist in any team.

A developer who chooses the pragmatic solution is not just writing code that they will be proud of. They are solving a business problem within real constraints. Years later, when the business context has changed, that decision may appear foolish. But at the time, it was often the right decision — or at least the only decision they could make.

This realisation should change our entire approach to technical debt. It is not a moral failing. It is the natural consequence of having to deliver software in the real world, where striving for perfection can prevent progress, and where getting things out there matters.

A path forward

If technical debt is not malicious, then we should not respond with blame. We must respond with understanding.

This shift has profound implications for the way we work. When a developer scoffs at a legacy system and says, “This should never have been done this way,” they are not offering a useful critique. They are signalling that they do not understand the context in which the code was written. More importantly, they are closing the door to the very understanding we need to move forward.

When encountering confusing code, the most valuable question we can ask is not “Why would anyone do this?”, but “What problem was this solving?” This reframing from judgement to curiosity opens up entirely different conversations. It invites and encourages older team members to share their knowledge. It helps newer developers to understand not just what the code does, but also why it exists. It creates space for genuine learning.

The same applies to how we write code today. If we advocate empathy for legacy systems, we must ask ourselves what constraints we are operating under and whether our future selves or successors deserve the same empathy. What compromises are we making? What problem are we solving, and what will someone reading this code need to understand about our decision five years from now?

Building with empathy

Respecting legacy code is about more than just being nice. It forms the basis of the evolution of sustainable software. When we approach systems with empathy, we transform our relationship with the codebase from adversarial to collaborative. Instead of asking “Should we rewrite this?”, we ask “What do we need to understand about this system before we improve it?” We transform a challenging modernisation process into an opportunity for knowledge transfer.

This requires specific practices:

  • Well-crafted commit messages that explain the “why”, not just the “what”
  • Pull request comments that reveal thought processes and constraints
  • Code comments that clarify non-obvious decisions
  • Documentation that captures the behaviour of the current system and the knowledge of those who built it
  • Tests that explain the expected behaviour and the edge cases that matter

In a previous article, I explored some of this artefacts, architecture decision records and technical debt records, for example, further.

These artefacts are not burdensome overhead. They are acts of empathy. They enable you to have a conversation with a future developer, showing them not just what your code does, but also why you built it the way you did.

The same principle applies to how we organise our work and teams. When we treat legacy systems as repositories of knowledge rather than problems to be discarded, we preserve the learning embedded within them. We avoid the immense waste of rewriting code that works perfectly well. We create space for incremental improvement. And we honour the people who created what we have inherited.

The mirror of our future

The most important insight is this: The systems we build today will be tomorrow's legacy. The code we write now will be maintained by developers whom we will never meet. The architectural decisions we make today will either constrain or enable teams five, ten or even fifteen years from now.

Our future selves, and our successors, deserve the same empathy that we are advocating for now. This means writing tests that explain not just what the code does, but also why it is done in a certain way. It means documenting the constraints we operated under and acknowledging that our choices were human decisions made in organisational contexts, never purely technical decisions made in isolation. We must resist the temptation to present our work as the inevitable result of pure logic when, in reality, we are always making trade-offs influenced by business priorities, deadline pressure, available expertise, and countless other human factors.

Above all, it means fostering a culture where empathy informs our approach to code, our mentorship of teammates, and our professional growth as engineers. It transforms “rewrite everything” into “understand, then improve strategically”. It turns adversarial code reviews into collaborative knowledge transfer. It reminds us that we are not merely manipulating machines. We are continuing a dialogue with other people who cared deeply about solving problems, just as we do.

Empathy as technical excellence

Empathy is not the opposite of technical excellence. In fact, it is its foundation. The most technically skilled developers understand not just how systems work, but also why they exist. They recognise that sustainable software is not created through individual virtuosity, but through collective knowledge, clear communication, and respect for previous work.

When we treat legacy code with the respect that Michael Plöd advocates, we are not being sentimental. We are being strategic. We recognise that the domain knowledge embedded in existing systems is invaluable, that the constraints those systems navigated are still relevant and that those who built them can teach us something.

Any team's path forward is not to dismiss or despise the code they inherited. Rather, they should ask better questions. They should approach the codebase with curiosity. They must extract the knowledge it contains. They must understand the constraints that shaped it. With this understanding as a foundation, they can then improve the code incrementally, thoughtfully, and with empathy for those who came before and those who will come after.

This is how we build software that lasts. That is how we honour the people behind the code. It is also how we ensure that the systems we build today will be treated with the same respect and understanding when they become tomorrow's legacy.