Engineering5 min read
Inheriting a legacy .NET backend nobody wants to touch
What to do when a business-critical system runs on old .NET, the people who wrote it are gone, and rewriting is not an option. The order of work that keeps it running.
A client came to us with a backend written in an old version of .NET. It runs real volume, it is the system the business depends on, and it had reached the point where nobody left had the capacity to maintain it. Not broken. Not replaceable. Just quietly becoming a risk.
That combination is more common than the industry likes to admit, and the advice usually offered is the least useful one available: rewrite it.
Rewriting is the expensive answer to the wrong question
The instinct is understandable. The code is unfamiliar, the framework is old, the patterns are not what anyone would choose today. A rewrite promises a clean slate.
What it actually promises is this: for the next twelve to eighteen months you will maintain two systems, one of which does not work yet. Every behaviour the old system has, including the ones nobody documented and nobody remembers deciding, has to be rediscovered and reproduced. The business keeps running on the old one throughout, so it keeps needing changes, so the target keeps moving.
Meanwhile the actual problem was never "this code is old". It was nobody can safely change it. Those are different problems, and only one of them requires a rewrite.
Old code that people can change confidently is not a crisis. It is just old code, and plenty of it will outlive the teams that keep proposing to replace it.
Step one: find someone who knows the stack
This sounds obvious and it is where most of these situations stall. Teams try to have a modern stack team maintain an old .NET system by reading their way into it. It half works, slowly, and every change carries more risk than it should.
We brought in an engineer who actually knows the era of .NET the system was written in. Not as a permanent hire for the client, and not to rewrite anything either. To read it, understand what it does, and be the person who can say whether a given change is safe.
That single move changes the economics. Work that took a week of cautious archaeology takes a day, because someone recognises the patterns instead of deriving them.
Step two: clean up before changing anything
Before touching behaviour, we cleared the ground: dead code paths that nothing called, duplicated logic that had drifted apart, configuration scattered across places it should not have been, and the accumulated commented-out blocks that make every file harder to read than it needs to be.
None of this changes what the system does. That is the point. It shrinks the surface area you have to hold in your head before you start making changes that do matter, and it separates "what does this system do" from "what did somebody try once and leave behind".
Step three: make it observable and testable
This is the step that converts a risky system into a maintainable one.
Automated checks around the behaviour that matters, so a change that breaks something says so in minutes rather than in a support ticket. On a legacy codebase you do not start with full coverage - you start with the paths where being wrong is expensive, and grow outward.
A build and deploy pipeline that runs those checks every time, so nothing reaches production without passing them.
Monitoring on the things that indicate trouble, so problems surface before customers report them.
Once those are in place, the system stops being frightening. Changes become routine even though the code underneath is the same code it was last month. Nothing about the framework version changed. What changed is that you can now tell, quickly and automatically, whether you broke something.
Step four: modernise incrementally, once it is safe to
Only now does moving to current technology become sensible, and it happens piece by piece rather than as an event. New functionality goes on the modern stack. Existing pieces get moved when there is a reason to touch them anyway. The old system keeps serving traffic the whole time, and each step is small enough to reverse.
This is slow. It is worth being honest about that rather than pretending otherwise: it is deliberate, unglamorous work measured in quarters. But at no point is the business betting itself on a migration completing. That is the trade being made, and for a system the company runs on, it is usually the right one.
What this actually buys
Not novelty. Stability.
The system stops depending on people who have left. Changes stop being events. The knowledge lives in tests and pipelines instead of in someone's memory. And the modernisation that everyone wanted happens anyway, just without a period where the business is exposed.
If you are looking at an old system nobody wants to own, the useful questions are not about the framework version. They are: can we change this safely today, would we know if we broke it, and what does an outage cost. Answer those and the plan writes itself.
If this sounds familiar
We take on systems like this - older .NET, legacy PHP, inherited codebases where the original team is gone. The first step is always an assessment rather than a proposal to rebuild: what the system does, what state it is in, what is safe to change, and what it would take to get it under control.
Have a look at our custom development work, or tell us what you are dealing with and we will come back with an honest read on it.