Nobody told me it was impossible
By Konrad — CEO
Przeczytaj po polskuPaying off technical debt has never been as cheap as it is today!
Let's start with numbers. GitClear analyzed 623 million code changes: refactoring — modernizing code without changing its business effect — fell from 21% of changed lines (2022) to 3.8% (2026). Changes to code older than a year — down 74%. Duplication — an all-time record. We have stopped fixing old code, and it has never been cheaper to do than it is today.
In my entire career I have spent exactly two years at a "corporation". I came in as a developer — not even a senior one — and almost from day one I was dropped into a Java codebase surrounded by a certain informal consensus across the whole team. A lasting one, never officially written down anywhere: you don't touch it.
Spring (an application framework, popular back when "people were throwing rocks at dinosaurs") was used there the way Spring should never be used. Somewhere inside, some component was setting references on a static object — hand-rolled dependency injection into global, mutable state, inside a framework that exists precisely so you don't do that — and closing a stream was the exception. A holy mess of messes.
Nobody did much about it, apart from complaining. People copied the pattern they found — and moved on. Code often erodes because copying something that was once properly screwed up is still the fastest route to shipping, and nobody at a big corp has "stop and think" in their job description.
So I fixed that code into something usable. Not out of courage. Out of ignorance — nobody told me it was impossible, and I was too "fresh", straight from a pretty sensible run at some very cool "startups", to know that the expected reaction was to look, shrug, and add another layer of spaghetti.

There was no AI. There was an IDE that, with a bit of luck, would suggest the correct method name, and there was a great deal of reading — reading for comprehension.
The Revolution
The whole job was mostly understanding — and that is not my personal quirk, that is the rule. Xia and colleagues measured it in IEEE TSE: developers spend about 58% of their time understanding code and about 5% editing it.
And understanding is exactly what the current "generation" of LLMs is brilliant at.
IBM's NOSI project measured a 79% drop in the time needed to understand an application. CodeConcise from Thoughtworks cut the reverse engineering of 1,500 modules (~10,000 lines each) from six weeks to just two per module. AgentModernize extracts 91.2% of the business rules from legacy code. Morgan Stanley used DevGen.AI on nine million lines of code and had it produce specifications in English, not code.

Bun's co-founder, now at Anthropic, used Claude Code to move the solution from Zig to Rust — a million lines in under two weeks, for roughly $165,000 in tokens. Zalando migrated a component library with over 90% accuracy for under $40 per repository. Allegro — the best-documented Polish case — took a Koin-to-Hilt module migration from about a week down to under two days.
And one more thing got cheap — for me the most important one, because I used to do it by hand: the whole apparatus for checking that we haven't broken anything. Tests pinning down the system's current behavior (bugs included — yes, deliberately), comparing the old and new versions' outputs on the same data, a metrics dashboard with history. A solid setup like that used to be a month of work. Today you put it together in hours, sometimes less — and tailored to the specific project and technology at that. American Express did not hand-write the tests for its COBOL migration — it generated them with six different algorithms at once, and whether old and new behaved the same was decided by a plain, deterministic comparison of outputs.
One "But"
The heart of the problem is not understanding itself. It is faithful reimplementation.
The same AgentModernize that extracts 91.2% of the rules achieves just 9.4–19.4% behavioral equivalence when rebuilding what it understood. And this does not blow up suddenly and loudly — the code compiles nicely, it just does something slightly different than the old version. My favorite example from that paper: the new version compiled without complaint, "quietly dropping, however, a suspended-account exemption that had been in production for fifteen years". We all know that such "exemptions" are more rule than exception in code that grows over years, often built by different teams and different people.
"But that's what tests are for" — well, not quite. Eq@DFuzz showed that roughly one in five refactorings broken this way passes the existing tests (emphasis on: existing). Coverage only says a line was executed — not that it did what it should. Don't count on the models' honesty either — in AWS's Articulate but Wrong study, the model approved 31.7% of the cases where it had broken something itself.
Keep in mind that all these studies are "old" — counting time in AI years. The COBOL pessimism was measured on GPT-4 and GPT-4o, and the newer whole-repository tests were also run on models a few generations back. Since around GPT-4o, "ages" of progress have passed — we have Fable 5 and GPT-5.6! Those results are the worst it can possibly get, because they were measured on models from another era.
And that is the whole "but" — a list of things you need to plan well and verify.
7 Steps to Safe Code Modernization with AI
To be clear: this is not a methodology — there is nothing here to certify. These are elements that prove themselves on real, live projects. At its core it is the same work we did by hand twenty years ago, except today most of it is done by machines and can be delivered orders of magnitude faster.
-
Start with triage, that is, sizing up the problem: an inventory of everything running in production, plus a security analysis — AI tools are excellent at both these days. Then a decision on each item: tolerate, migrate, or delete. The cheapest debt to pay off is deleting something nobody uses anymore (which you also need to measure and demonstrate).
-
Next, agents dig through the repository and write up a specification. People who know the business read a few dozen pages, not forty thousand lines of diff. Even if the project ended right here, something remains: knowledge of the system stops residing in a single head.
-
From the specification we build the tooling — the real product of the whole effort. Tests pinning the system's current behavior (bugs included), tests for the system's specific usage scenarios, later run against both versions, output comparison on the same data, and a metrics dashboard with history. Some of the tests we "keep hidden from the agents".
-
Before we move anything, we clean up. Dead code goes out, configuration is externalized, hidden globals come out into the daylight.
-
Only now the changes — automation first, then AI. Whatever can be described as a rule is done by a rule (codemods, OpenRewrite recipes), with the model merely picking and parameterizing the recipes. Slack measured it: codemod alone 45%, LLM alone 40–60%, the combination — 80%. Agents get the rest: small pieces, one change at a time, tests and behavior comparison after each one. Airbnb reworked the tests of 3,500 components this way in 6 weeks (doing it by hand was originally estimated at 1.5 years).
-
Before rollout, a separate agent — ideally on a different model — gets a single job: prove that the new version behaves differently from the old one.
-
And all of it stays for good. The supporting tools move into regular CI, agents do weekly clean-ups (Spotify has clocked up 2.5 million such PRs), and review is tiered by risk (at Zalando a bot approves about a third of the PRs on its own).

So, both things at once: fixing old code has never been cheaper — and it has never been done less. A neat little paradox.
The belief that old systems cannot be modernized was always wrong. Now it is also wrong to believe it is expensive.
— Konrad
PS. There are no prompt details here, and nothing about which model to use — because that is a commodity today, a tool like the plain editor once was.
