Michael Zazon

Architecture & design · Book notes

If agents write the tests, is Feathers' legacy code still legacy?

Feathers defined legacy code as code without tests, and agents that can write those tests cheaply move the hard part of modernization to knowing which pinned behavior is a bug.

In 2004, Michael Feathers wrote a book called Working Effectively with Legacy Code which makes the following argument in one line: “To me, legacy code is simply code without tests.” The method in the book follows from that definition: find a seam (a place where you can change the program’s behavior without changing the code in that place), write characterization tests that pin down the current behavior (right or wrong), and only then change the code.

But in early 2026, Anthropic published a post on COBOL modernization that challenges this method. The company claims that the most expensive part of any COBOL migration is always understanding what the old code does. AI can map dependencies, trace execution paths, and generate tests to verify that the migrated code produces the same outputs as the COBOL. This matters because COBOL handles an estimated 95% of US ATM transactions. The post also says something crucial: people still need to decide if those tests are enough.

This question resonates with me because I’ve spent the last ten years working with large financial institutions and Fortune 500 companies, now as a principal cloud architect. Those estates run mainframe batch jobs and old Java code under the newest platforms, but their modernization plans keep stalling because of the exact problem this book names: code without tests can’t be changed safely.

Feathers still keeps the definition. In a 2024 interview on Tech Lead Journal, he also describes legacy code as code we don’t understand. He’s skeptical of tools that promise to write all the tests for you, but makes an exception for characterization tests, since they document the current behavior and can be thrown away after the job is done.

If tests are almost free, what’s still legacy? I think the definition holds, but the scarce part has changed. An agent pinning down a COBOL program’s behavior or an old Java service is way cheaper than getting a team to do it manually. But the agent can’t tell you which of that pinned behavior is a business rule and which is a bug that’s survived for decades in production because nobody dared fix it. That judgment is where the legacy cost now lives.

Five steps from seam to parallel run, marking where agents are cheap and humans expensive Find a seam Pin current behavior agent: cheap now Decide what is intended human: expensive Change the code Prove it in a parallel run old and new, live inputs

I’m saying agent-generated characterization tests are the cheapest on-ramp to modernization that financial services has ever had. They give a team the safety net to start changing code well before they can afford a full parallel run. Getting started is where such programs have always stalled.

But it’s only the on-ramp, and it doesn’t prove the new system. Pinned tests only cover the inputs someone thought to try. When you cut over a core system, you run the old and the new side by side on live inputs and switch over in small pieces. Anthropic’s post recommends the same.

The book’s question comes back: if cheap tests mean legacy is code nobody can explain, the hard part of modernization is the person deciding the pinned behavior is the requirement. Who signs off that the tests mean the right thing?