A field guide to modernizing legacy software instead of demolishing it
Why Rewriting Software From Scratch Threatens Your Business
“We should just rewrite it”
Rewriting working software from scratch is one of the most expensive and most over-sold decisions a business can make — and the person who recommends it is rarely the person who pays for it. Most “we have to rewrite it” problems are solved better, and far more cheaply, by modernizing what you already have: fix it, test it, and replace only the parts that actually hurt. What follows is why rewrites fail so reliably, why “we’re 90% done” is usually the halfway point, seven examples I watched go wrong, five I did right, and a six-question test to run before you fund one.
Table of contents
- The rewrite pitch
- Five real reasons people rewrite
- Why software rewrites fail
- Field notes: rewrites gone wrong
- Rewrites done right
- Respect for others’ work
- The fashion tax
- Why rewrites stall at 90% done
- The sunk-cost trap
- Rewrite vs refactor: what to do instead
- Which value will it bring?
There is a sentence that has cost more money than almost any bug ever written. It is short, confident, and usually delivered by someone who has been on the project less than a month:
“We should just rewrite it.”
I have heard it for twenty years — from salespeople who never saw the code, managers who can’t read the language it’s in, designers judging a colleague’s work, and engineers who would rather write ten thousand new lines than read existing ones. The movie always ends the same way — budgets balloon, features go missing, the team that understood the system leaves, and the supposed-to-be-shiny replacement limps to a finish line the original had already crossed.
This article is about that sentence — and the question you should ask before anyone touches your code: should you rewrite your software from scratch, or modernize what you already have? It covers why people say it, what the evidence shows, what it costs you, and one quieter thing — respect for work other people do.
A disclosure. I make my living modernizing existing systems without unnecessary rewrites, so I am biased — I profit when you keep your system and money, exactly as the rewrite consultant profits when you don’t. Read me as skeptically as I encourage you to read them.
The pitch you have heard a hundred times
Open your spam folder: your website has “a horrible problem,” is “losing you customers every day,” is “not built on modern technology” — and, what luck, the sender can rebuild it! The diagnosis is grave, the cure is a rewrite, and the surgeon owns the operating room. It doesn’t stop at spam. The pattern repeats inside companies with ritual reliability:
- A new manager arrives and within weeks declares the system “legacy” and due for a rebuild — the easiest flag to plant: big, visible, and always finds ears.
- A vendor barely looks at your working software and says: “we should start the fight and then see the plan.” Is this advice based on your real situation?
- A developer handed an unfamiliar codebase, instead of reading it, declares it unsalvageable. This one has a law behind it we’ll discuss below.
- A designer shown another’s layout, with name withheld, pours out contempt: amateurish, dated, out of trends.
The common thread — the person recommending the rewrite is, surprisingly often, the one who gets paid because of it. I called a similar approach “invent a problem, sell the cure” — manufacture a crisis, sell the remedy, publish the triumph whether or not the thing ships. Many proposers are sincere — but sincerity and incentives differ, and you hold the incentive bill.
The five real reasons people want to rewrite (only one is technical)
After two decades, almost every rewrite proposal sorts into five buckets. Notice how few are about your software.
- It’s billable. The longest engagement a vendor can sell. Maintenance ends, rewrites run for quarters. The incentive to find your system “beyond repair” is structural.
- It’s a flag to plant. “I made the system 40% faster” is a great outcome and a dull story. “I’m leading the platform rebuild” is close to zero in outcome, but a fantastic story.
- It’s easier than reading. Understanding someone else’s code is harder and gives less dopamine than writing your own. A rewrite skips the reading and goes straight to the fun part.
- It’s fashionable. The old language isn’t cool to write on social. The new one is what the conference talks are about. That’s about résumés, not fitness for your problem.
- It’s actually broken. Sometimes — rarely — the platform is genuinely end of life or the architecture truly blocks the next product. The bucket exists. It’s far smaller than the noise, and even then a full rewrite is usually the wrong shape.
Four are about the career, comfort, or cash flow of the proposer. One is about your business — yet the proposal always arrives dressed as the fifth.
Why software rewrites usually fail — “as if source code rusted”
This isn’t only my opinion. The definitive essay is Joel Spolsky’s Things You Should Never Do, Part I (2000). Spolsky — co-founder of Stack Overflow and Trello — watched Netscape rewrite its browser from scratch:
“They did it by making the single worst strategic mistake that any software company can make: They decided to rewrite the code from scratch.”
The numbers are brutal. After version 4.0, Netscape threw away its codebase and started over. Three years passed before the rewrite shipped, and in that silence it couldn’t improve the product customers used while Internet Explorer — free, bundled, continuously improved — ate its market share to almost nothing. AOL bought the company on the way down and the team was soon disbanded.
The rewrite had help — Microsoft bundled IE free with Windows, and courts later found it had illegally maintained its monopoly — but that’s the point. When a competitor is bundling you out of the market, a three-year self-imposed silence is the one gift you can’t afford to give. Netscape wasn’t even first: Borland did it with dBase and Quattro Pro, and Microsoft nearly killed Word with a doomed rewrite called “Pyramid,” saved only because it never stopped shipping the old code.
Spolsky’s explanation of why is worth taping to the wall:
“There’s a subtle reason that programmers always want to throw away the code and start over. The reason is that they think the old code is a mess. And here is the interesting observation: they are probably wrong. The reason that they think the old code is a mess is because of a cardinal, fundamental law of programming: It’s harder to read code than to write it.”
So “unreadable garbage” usually describes not the code but the experience of not having read it yet. And old code knows things new code doesn’t:
“The idea that new code is better than old is patently absurd. Old code has been used. It has been tested. Lots of bugs have been found, and they’ve been fixed. … When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work.”
Every ugly branch is usually a scar — a bug that hit a real customer once and got fixed. A rewrite throws away the scar tissue and the immunity with it. “As if source code rusted.” It doesn’t — software that hasn’t changed isn’t rotting, it’s seasoned.
The second system is the dangerous one
Long before Spolsky, Fred Brooks diagnosed the other half in The Mythical Man-Month (1975) — the book that gave us “adding manpower to a late software project makes it later.” He named the second-system effect:
“This second is the most dangerous system a man ever designs. … The general tendency is to over-design the second system, using all the ideas and frills that were cautiously sidetracked on the first one.”
The first system is lean because its builders were humble. The rewrite bloats because everyone now “knows better.”
Brooks is also the source of the line rewriters quote at you — “Plan to throw one away; you will, anyhow.” — but he meant a cheap, disposable pilot, not bulldozing a profitable production system. He even warned that shipping the throwaway buys time only “at the cost of agony for the user.”
Chesterton’s Fence, for software
Older still is G. K. Chesterton’s fence across a road, from his 1929 book The Thing. The reformer says, “I don’t see the use of this. Let us clear it away.” The wiser answer: “If you don’t see the use of it, I certainly won’t let you clear it away. Go away and think. Then, when you can come back and tell me that you do see the use of it, I may allow you to destroy it.”
Most “pointless legacy code” is a fence. The competent move is to learn why it’s there before tearing it down. Most rewrite proposals demand removing every fence without reading one.
How often do software rewrites fail?
Hard statistics are messy, because whoever publishes them is selling one side — modernization firms quote terrifying failure rates, rewrite shops quote glorious greenfield wins. The numbers are soft. The pattern is not. Big-bang rewrites overrun schedule and budget far more often than incremental modernization, and a sobering share are abandoned partway — the team crawling back to the system they swore was beyond saving. Netscape, Borland, the Word “Pyramid” project aren’t outliers — they’re the genre.
When is a rewrite actually worth it?
Rewrites aren’t always wrong. Herb Caudill’s “Lessons from 6 software rewrite stories” documents wins — Basecamp, VS Code, FreshBooks — but the pattern is the opposite of the usual pitch. They built something new, not the same product. They built it beside the old one rather than on its grave — Basecamp still runs “Basecamp Classic,” promised “until the end of the internet,” and FreshBooks built its replacement in secret, as a fake competitor. And they did it for a product reason, not code-aesthetics.
Get Timofey Bugaevsky’s stories in your inbox
Join Medium for free to get updates from this writer.
Even Michael McDerment, the FreshBooks founder who did rewrite, put the stakes plainly:
“There’s no greater risk for a software company than rewriting. Chances are you won’t even finish the project. It will take longer than you think. It will cost more. When you do it, the customers could like it less. … The number one rule in software is you don’t re-platform your software.”
That’s what a good rewrite decision sounds like — scared of the risk, specific about the reason, driven by the product, not the code. The usual pitch — “your code is bad, your programmers are incompetent, we’ll rebuild it faster and bug-free” — is a sales script.
Field notes: rewrites I watched up close
From my own career, anonymized on purpose, my recollection and opinion, not a claim against anyone.
- The powerful tool that was powerful somewhere else. Twenty years ago, at a company in my country of origin running its accounting on the local standard, American leadership decided to roll out the big US cloud ERP they’d used back home. The integrator arrived with “we should start the fight and then see the plan.” But the tool that was powerful in its home market simply lacked the accounting and regulatory modules operations needed there. Lesson: a tool’s reputation isn’t transferable, and “it’s powerful” is not a requirements document.
- Nine months to rebuild what a few weeks could fix. A company had grown a substantial in-house PHP platform — CMS, CRM, warehouse, partner and dropshipping management, marketing integrations — and another programmer convinced the company owner to rebuild all of it on one off-the-shelf platform. Nine months later it covered only part of the features and was clumsy, while I spent that time fixing the existing system’s performance and usability and made it so good the client chose to stay on it. Lesson: while the rewrite chases parity, a well-tended incumbent keeps pulling ahead.
- The rewrite that was really about office politics. At one company I built a methodology and tools and led several products on top of them. A colleague for whom my role was a personal irritant about status invested in office wars instead of collaboration, and when I went to the next adventure, shut my work down in favor of an outside solution to do something different, and then quit himself. I heard a testimonial from ex-colleagues like “He was a bad kitty.” Lesson: many “let’s replace it” calls are about ego, not merits — find out which one you’re funding.
- The rewrite that quietly dropped the safety net. I’d made a JavaScript-heavy frontend several times faster, covered it with tests, and streamlined its build and deploy. New people in a growth phase rewrote it over months, with no measurable improvement and — the part that still makes me wince — they did not rewrite the tests. Lesson: rewrites abandon the unglamorous assets first — tests, rarely used features, edge cases — which are exactly what kept you safe.
- The shortcut that became permanent. A project once written in Ruby on Rails was rewritten in PHP. To save time it skipped a normalized database and stuffed fields into JSON blobs — saving time the way skipping oil changes does. Verifications vanished, debugging got harder, functionality went missing, and the data needed migrating before we could move further. Lesson: the corners a rewrite cuts harden into its permanent foundation, and someone — probably you — pays interest forever.
- When the person deciding can’t read the system. I had Laravel projects running so much faster after optimization the client, instead of scaling, started thinking about shrinking its infrastructure bill. Then a manager who was a JavaScript programmer and couldn’t read PHP convinced the stakeholders a rewrite was needed, and the dream of a “two-week rewrite with AI” took four months, delivered partial functionality, and cost the company its PHP team. Lesson: when the engineering manager can’t read the system, “rewrite it” masks a deeper problem — a gap in understanding what you actually have.
- The story continues. It always does — names change, the buzzword changes, the timeline and rewriter stay optimistic and the reality stays inexorable. Yours is being written right now by someone three weeks into a project who has already concluded it should be thrown away.
The rewrites I’m proud of
I rewrite often. The difference is scope and understanding — I replace the part that’s actually the problem, measure before and after, and don’t rewrite what I can’t explain.
- The hot path, in the right language. A PHP image-processing service was doing CPU-bound work PHP was never built for. I rewrote that one component as a Rust extension (details in “How to make PHP 2000 times faster”) — the bottleneck, not the app — and it ran hundreds of times faster, keeping everything else untouched.
- A framework upgrade that made the app smaller. I took a Vue 2 codebase to Vue 3, replaced rarely-used libraries with short purpose-built code, and moved the build to Vite — minimal code changes, package sizes are several times smaller, pages load several times faster.
- One correct algorithm instead of many copies. A content-access permission system had become piles of inconsistent rules from copy-pasted checks each tweaked later “where found”. I consolidated them into one set-based approach — multi-row queries instead of per-row loops, N+1s and inconsistencies eliminated, the right indexes added — and the bugs disappeared because the logic finally had one home.
- The prototype you plan to throw away. The one place Brooks’s “plan to throw one away” truly applies: when an AI-generated proof of concept confirms an idea, I don’t ship the PoC — I design and build the real thing deliberately, understanding every part. AI is superb for validating an idea fast, helps in implementation a lot, and dangerous as a shortcut around understanding.
- Many marketing sites, one codebase. A single CSS file per skin changed the color scheme and layout, so several marketing sites ran on the same code while each got a distinct look for minimal effort. Site names, logos, and highlighted products were differentiated in the database, so content managers could run each brand without touching code. Giving an existing system a brand-new face is a rewrite without rewriting.
Through all five I knew exactly what I was replacing, why, and what “better” would measure. That’s the line between modernization and demolition.
The part nobody puts on the invoice: respect
Show one designer another’s layout, with no name attached, and the contempt pours out — amateur, dated, “would never convert.” Show one developer another’s code and it’s a mess by someone who didn’t know what they were doing, fit only to start over. Reveal the work was their own from two years ago, and the verdict softens to “well, given the constraints at the time…” — the honest version.
Every system was built by people solving real problems with real deadlines and information you don’t have when you sneer at the result.
Spolsky’s law cuts past code, too: judging someone else’s work as incompetent is usually proof you haven’t read it well, not proof it’s bad. Design is more subjective than a schema, but contempt on sight is as unreliable in a Figma file as in a pull request.
There’s a blind spot specific to design. Whoever insists a page must be rebuilt — because it’s ugly, dated, or “would never convert” — usually doesn’t grasp that the site itself doesn’t sell.
A site is one part of a machine — the product sells, the offer sells, the quality of the traffic sells, the trust you’ve earned sells, the follow-up sells. A beautiful layout bolted onto a weak offer changes none of that, so “we need to redo the site” is usually a design-shaped answer to a business-shaped problem.
This isn’t an argument against criticism — strong systems get torn into by the people who maintain them. It’s against declaring others’ work worthless to sell its replacement — an opening line, not an assessment, and one that ends with an invoice.
The fashion tax: rewriting because a language fell out of trends
A whole flavor of rewrite replaces a working system for no reason beyond an unfashionable language. Laravel — and PHP generally — is a superb tool for web applications, and it spends much of its life bullied by pricier specialists in trendier stacks promising a rewrite that’s faster and bug-free. Swap in Rails, Django, .NET, Java, or a five-year-old Node app and the script is identical. The target is rarely the language — it’s whatever the newcomer didn’t write.
“Bug-free” is easy to dismiss. New code isn’t bug-free, it’s bug-unknown — the old system’s bugs were found and fixed over years of real use, while the rewrite’s all wait inside for your customers to find for free.
“Faster” is usually true of the benchmark and false of your business — the bottleneck is the database, the infrastructure, and the team’s discipline, not the language. I’ve made PHP apps run tens of times faster without changing the language at all.
Fashion doesn’t even reliably point “newer is better.” In 2023, David Heinemeier Hansson — creator of Ruby on Rails — removed TypeScript from Turbo, writing it “pollutes the code with type gymnastics that add ever so little joy to my development experience, and quite frequently considerable grief. Things that should be easy become hard, and things that are hard become any."
TypeScript has real benefits and the backlash was legitimate — which is the point. Even at the bleeding edge, serious engineers disagree and choose tools for fit, not fashion. Anyone claiming the industry has unanimously moved on from your stack is selling something, and “your stack is obsolete, so we’ll have to rebuild it” is that sale’s favorite opening line.
Tools should be chosen for the task, the team, and the context — the thesis of “How to Choose a Tech Stack in 2026”. Replacing a working system because its language is unfashionable isn’t modernization. It’s cost and risk dressed up as progress.
Why rewrites stall at “90% done” — and who pays to finish them
There’s a predictable second act, and it costs you twice. The rewrite is sold as a finish line. What arrives months later is a demo. The happy path works and the screens look modern, but the last slice never closes — login works while the rate limiter doesn’t. The invoice renders, but the unusual tax rule, the partial refund, and the customer whose data has been malformed since 2019 all fall over.
The replacement does the easy eighty to ninety percent and almost none of the hard part — and on a mature system, the hard part is the job.
This has a name. The Ninety-Ninety Rule, attributed to Tom Cargill of Bell Labs and popularized in Jon Bentley’s 1985 Communications of the ACM column: “The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.”
Project managers call it the 90-Percent-Done Syndrome — work sits at “90% complete” for months because the invisible remainder — integration, edge cases, data migration, performance under load, parity with everything the old system quietly handled — is most of the real effort.
Why do capable teams believe “ninety percent” when it’s nearer half? Two biases. The planning fallacy (Kahneman and Tversky, popularized in Kahneman’s Thinking, Fast and Slow): we underestimate our own tasks even after being burned, picturing the smooth path and forgetting the swarm of small disasters.
And Dunning–Kruger (Kruger and Dunning’s 1999 paper, “Unskilled and Unaware of It”): the less you understand a domain, the more you overestimate your competence in it — a portrait of the newcomer who calls a codebase trivial after a week of not reading it.
Add incentives and it turns specific. A rewrite is most fun to build at the start, so the demoable low-hanging fruit gets picked first. The hard remainder — the migration nobody scoped, the rule the original team spent three years getting right — is where the people who called your system “garbage” disengage to find the next greenfield.
I’m often called in “just to finish the last bit,” and I usually decline — it’s rarely a last bit, and whoever’s asking isn’t ready to pay for the real work it takes. Recall field note 6: the manager declared the PHP unreadable yet never spent the days it would take to learn a little of it, then, racing a deadline, shipped something the engineers building it privately judged worse than what they’d condemned.
The proof is in plain sight — every freelance marketplace is full of “finish my project,” and “fix my app,” out to bid for a fraction of what the glamorous start was billed at. And AI is only adding to the pile. So you risk paying twice — for the demo, then for the engineering that turns a demo into a product — and the second invoice is larger. When someone says the rewrite is “almost done,” read it as a warning, not a milestone. “Ninety percent done” is usually the halfway mark, and the easy half.
The trap inside the trap: sunk cost
You fund the finish line. Months in, it’s over budget, behind schedule, and not close to matching the old system — and now comes the most dangerous argument of all: “We’ve already invested so much — we can’t stop now.” That’s the sunk-cost fallacy, the engine that turns a bad three-month decision into a catastrophic two-year one.
The money spent is gone either way. The only real question is whether the next dollar is better spent finishing the rewrite or returning to the system you should never have abandoned. But from the inside it feels like loyalty, and the people billing you understand that perfectly — a rewrite too far along to cancel is the most stable revenue stream in consulting.
None of this needs villains, only incentives. You were told your application was bad and your team wasn’t good enough. You believed it, and now you’re chasing a finish line that recedes as you approach it, held in place by everything you’ve already spent. Was this what was promised?
Rewrite vs. refactor: what to do instead if you own the thing
You don’t have to choose between “rewrite everything” and “touch nothing” — that false binary is the rewriter’s favorite frame. Here’s the test I’d put to anyone proposing a rewrite. Good engineers pass it in good faith — the ones selling you something won’t. Make them put the answers in writing:
- Diagnose before you prescribe — with numbers. A doctor who recommends surgery before examining you isn’t a doctor. “It’s old / legacy / not modern” is a vibe. “We can’t add multi-currency without a six-week change to one tangled module” is a diagnosis.
- Redefine “legacy.” Michael Feathers (Working Effectively with Legacy Code) defines legacy not as old code but as code without tests — usually a problem you solve in place by wrapping it in tests, not a reason to throw away years of knowledge.
- Strangle, don’t bulldoze. Martin Fowler’s Strangler Fig pattern grows the new system beside the old and migrates function by function, so something always ships and you can stop anytime with value banked — carrying the tests across as you go.
- Spend innovation tokens deliberately. Dan McKinley’s “Choose Boring Technology” gives you a small budget of “innovation tokens”. Spend them where they buy real advantage, not re-implementing solved problems in a trendier language.
- Set the stop-loss now. Before a line is written, agree on a checkpoint — say 30% of the timeline — and the cost at which you walk back, so “we’ve spent too much to stop” can’t be the answer later.
- Follow the money. Ask who gets paid, promoted, or a better résumé if the answer is “rewrite,” and whether that’s the person recommending it. Then get a second opinion from someone whose business is improving systems, not replacing them.
The first five points protect your software. The sixth protects you.
Which value will it bring?
A rewrite is the only renovation that begins by demolishing a house people still live in, on the promise that the replacement will be nicer, roughly on time, and somehow free of the problems that took years to find in the original.
Sometimes — rarely, with a real product reason and a team that builds the new thing beside the old — it’s the right call. Far more often it’s a threat dressed as a plan: to your budget, your timeline, the team that understands your business, and the quiet, valuable work others did before someone arrived to call it garbage.
The people who built your system weren’t fools, and code that’s been quietly making you money isn’t rusting. The most senior thing a technologist can do isn’t to bulldoze — it’s to walk in, respect what’s there, read the fences before removing them, and make the thing measurably better without discarding what it learned.
That’s harder than “let’s just rewrite it,” and it’s the difference between a professional and a pitch. So when the email says your site has a horrible problem, or the new arrival announces the platform must be rebuilt, pause and ask the only question that matters: Which value will it bring?
About the author: Timofey Bugaevsky is a senior full-stack engineer with 20+ years across PHP, JavaScript, Rust, Python, .NET, Elixir, and other languages, specializing in making existing systems modern, fast, and feature-rich rather than throwing them away. Preferred tools: Laravel + Livewire, Phoenix + LiveView, and Rust. See his work at zkinteractive.com.
Support writers. All our non-profit’s offerings here.
Click here to subscribe to the IT Chronicles newsletter.
Click for Wordsmith, Mystery Writing, Write Like Stephen King, more









