Adding automation to a legacy codebase isn't about tools. It's about understanding the structure well enough to introduce change responsibly.
I've been spending time thinking through what it actually takes to add automation into a legacy codebase.
It's one thing to automate something built with testing in mind. It's another when the original design didn't account for it at all.
Also shared on LinkedIn.
Most conversations about legacy automation start in the wrong place:
These aren't bad questions. They're just premature. The more important questions come first.
Where can you introduce seams without changing behavior?
A seam is a place in the code where you can change behavior without modifying the code itself. In legacy systems, seams are often hidden — buried in tight coupling, global state, or direct dependencies that make isolation nearly impossible. Finding them requires reading the system carefully before writing a single test.
How do you improve visibility without overengineering it?
The temptation in legacy automation work is to build a beautiful, comprehensive framework from scratch. Resist it. The goal isn't to build the perfect test suite — it's to add enough visibility that you can change the system with confidence. Start small. Add value incrementally.
What's worth adapting — and what should remain untouched?
Not everything in a legacy system is wrong. A lot of it reflects decisions made under real constraints — performance requirements, team knowledge, infrastructure limitations. The goal isn't to rewrite it. The goal is to understand it well enough to layer in testability without breaking what works.
This is worth saying directly: legacy systems aren't failures. They're systems that survived.
They reflect years of decisions made under different constraints, with different information, by different teams. Treating them as enemies to be defeated usually produces worse outcomes than treating them as systems to be understood.
The engineers who do this work well are the ones who approach legacy code with curiosity instead of contempt.
Coverage numbers are almost meaningless in legacy automation work. What matters is:
The hardest part of legacy automation isn't technical. It's having the patience to understand the system before trying to change it.
That's the kind of engineering problem I enjoy most.