Rewriting from scratch: 'hold my beer'
Posted on April 10, 2026 • 12 minutes • 2438 words
Table of contents
- The classic disaster scenario
- Why we underestimate what already exists
- The real cost of hitting the red button
- When a rewrite actually makes sense
- The self-deception of “this time it’ll be different”
- The boring alternative that usually wins
- The virtue of staying and fixing
- Epilogue: living with the urge
- Quick glossary
- Sources and references
Every developer hears that little voice sooner or later.
It shows up on some random afternoon, after wrestling with a chunk of code that looks like it was written out of spite, and it whispers something like: “this thing is a dumpster fire; give me a couple of weeks and I’ll rebuild the whole thing from scratch, clean and proper.” It speaks with the confidence of someone who’s already lost their patience, with a certain contempt for everything that’s been built so far, and with an almost religious faith that this “new me” finally, truly knows how to do things right.
I call that urge the “hold my beer” syndrome.
It’s a very specific cocktail of pride, burnout, and selective amnesia. Pride because, obviously, we “know more now” than when that code was originally written. Burnout because you’ve spent months patching a system that’s smelled like legacy since line one. And amnesia because you conveniently forget all the pain, the countless decisions, trade-offs, and production scares that left their scars in the very code you now despise.
The classic disaster scenario
The story almost always starts the same way. There’s an old, ugly system with way too many years on it. Patches have piled up, emergency fixes, features that were needed yesterday. The logs read like gibberish from another dimension, the database has tables with names no one remembers who designed, and the documentation… well, the documentation is that folder in the repo everyone glances at respectfully but no one dares open.
On one of those particularly bad afternoons, after fighting with an 800-line function, you drop the cursed phrase: “honestly, this whole thing needs to be rewritten.”
And sometimes it sticks.
There’s a famous anecdote that’s become almost a cautionary fable: Netscape’s attempt to rewrite their browser from scratch . They decided to throw out the existing code, start a brand-new project, and in the meantime, the old browser was left unloved, unimproved, and adrift. Microsoft, with Internet Explorer, happily took the gift. While Netscape’s engineers were busy designing the perfect future, the present was bleeding out in market share and users. By the time the new product was “ready,” the world had changed hands.
The moral is pretty brutal: your perfect rewrite can show up late to a party you’re no longer invited to.
Why we underestimate what already exists
That’s far from the only case. It’s a pattern that repeats in startups and big companies alike. A team gets fed up with an unbearable monolith, decides the solution is to build a new one — prettier, cleaner, more modular, this time for real — and promises that in a few months, it’ll replace the old one. Those months turn into a couple of years . The old product keeps generating revenue, keeps changing, keeps accumulating patches… and the new one is always “a couple of sprints away” from being production-ready.
The problem with rewriting from scratch isn’t just technical — it’s deeply human. When you look at old code, you judge it with today’s eyes but yesterday’s memory. You see “bad decisions” where there were actually deadlines, technical constraints, lack of experience, or simply a different context. And above all, you forget that this tangle of code has already survived battles that the new system doesn’t even suspect yet: bugs that were fixed, weird business edge cases, absurd conditions from big clients that ended up embedded in some stray if statement.
On top of that, there’s a neat little mental trick: we’re great at seeing the complexity of the old code, but we brutally underestimate the complexity of the new one . And when you say “it won’t take that long, it doesn’t really do that much,” what usually happens is you’re only counting the visible parts, the pretty ones, the ones that show up in demos. You forget the sordid details: the bizarre reports that only one person in finance uses every March, the Jurassic endpoint that a partner still in integration needs, the CSV export with an absurd format that exists solely to keep a big client happy.
Rewriting from scratch means reimplementing not just the happy path, but all those dark alleys full of weird behavior that the current system has learned the hard way. And many of them aren’t documented anywhere except in the code itself.
The real cost of hitting the red button
That’s why it’s usually an absurdly expensive mistake: you stop or slow down new feature development, you duplicate effort (maintaining the old system while building the new one), you reintroduce tons of bugs that were already fixed in the original system, and with luck, you end up in a similar spot… but two years later and with less morale. In the worst case, you die along the way — financially or from sheer team exhaustion.
It’s not just a matter of development hours. It’s a hemorrhage of energy. A team that spends months or years in “rewrite mode” lives on a constant promise. They work hard, but almost nothing reaches the user until very late. It’s hard to celebrate progress because it always feels internal, invisible. The risk of burnout and cynicism is sky-high.
Meanwhile, the business side looks at the roadmap and watches it stretch like taffy. “What have you been doing the last six months?” “We’re rewriting the core — it’s going to be amazing, I swear.”
Explaining to someone who pays the bills that you’re halfway through building a system that does the same thing as the current one, just in a “prettier” way, has a lot less glamour than it sounds when you say it in a standup.
When a rewrite actually makes sense
None of this means a rewrite is never warranted. There are cases where “burn it down and start over” is practically a matter of survival. A typical one is when the underlying technology has become fossilized. Think of critical systems built on obsolete versions of languages, frameworks, or even operating systems that nobody maintains anymore. Sometimes the cost of patching, reinforcing, and propping something like that up ends up being higher than building a new structure with materials from this century.
Another reasonable case comes up when the original design has been completely outgrown by the current problem. A product that was born as a two-week experiment at an internal hackathon and, seven years later, processes millions of daily transactions is probably stretching an architecture that was never meant for that. In that situation, it does make sense to ask whether you should keep welding pieces onto Frankenstein or design a new body.
But even in those cases, the rewrites that work look less like a revolution and more like a well-planned move. You don’t burn down the house and build another one on an empty lot. You go room by room, moving the furniture carefully, making sure you have water and electricity before sleeping there.
In developer terms: extract modules gradually, encapsulate the old stuff behind clean interfaces, move isolated features to new services while the monolith keeps running, use patterns like the strangler fig instead of going all-or-nothing.
The self-deception of “this time it’ll be different”
The uncomfortable question that almost nobody asks up front is: “Do I want to rewrite because it’s what’s best for the product, or because I’m sick of this code and I just want a shiny new toy?” The second option is dangerously common. The rewrite quietly becomes a kind of “professional redemption project ”: this time I won’t make the same mistakes, this time the code will be beautiful, this time I’ll use all the design patterns I didn’t get to use before.
If you want to know whether you’re fooling yourself, you can do a couple of things that are uncomfortable but necessary.
The first: try to honestly estimate how long you’d need to rewrite everything, counting the ugly parts. Not just the main modules, but everything around them: integrations, data migration, internal scripts, reports, tools that other teams rely on. If the number that comes out of that honest exercise doesn’t make you a little dizzy, you’ve probably underestimated it.
The second: ask yourself what happens to the product in the meantime. Are you going to freeze new features? Are you going to maintain two development tracks in parallel? Who’s going to eat the bugs that surface during migration? What are you going to tell the business when they ask why you haven’t shipped anything meaningful in eight months, besides “I promise we’re building something incredible, you just can’t see it yet”?
There’s another sign of self-deception that others have pointed out well: if you say you need to rewrite because “you can’t work with this code” but at the same time you’re not introducing good practices or incremental refactoring into the current codebase, the problem probably isn’t just the legacy code — it’s how you’re working with it.
The boring alternative that usually wins
Against all of this, incremental refactoring has much worse marketing but significantly better survival stats. There are no epic headlines, no announcement of “we threw out all that garbage and now it’s finally right.” What you get is steady, almost boring improvement: splitting monster functions, extracting modules, improving names, adding tests where before there were only prayers.
It’s less glamorous, but it tends to be far more sustainable. Companies that make a living analyzing codebases report that well-executed refactoring strategies can speed up development and noticeably cut production bugs. Not because there’s any magic involved, but because a system you understand better is a system you can touch without it blowing up in some unexpected way every time.
Instead of one big all-or-nothing bet, you have many small, reversible ones. Each change can be tested, measured, rolled back if needed. You can keep delivering value while you clean house. You can swap out the bricks of the bridge without shutting down traffic.
The virtue of staying and fixing
So when does it actually make sense to hit the big red “start from scratch” button? When the current system is, honestly, a dead end. When you can’t deploy without crossing your fingers, when any change takes weeks because everything is coupled to everything else, when you can’t even run a decent test suite because there isn’t one. When the underlying technology chains you to an insecure, inefficient, or unsupported environment. When the very act of maintaining the code is a constant drain on time that prevents you from moving forward on anything else.
But even then, the healthy approach isn’t “delete the repo and open a new one” — it’s something much humbler: “I’m going to map out an exit path.” A path that can coexist with the old system for a while, that allows migrating in phases, that has clear checkpoints. A path where you decide upfront what minimum functionality the first version of the new system needs to cover before you can start shutting down parts of the old one without killing the business.
Only after thinking that way does it make sense to ask the final question: “if I weren’t exhausted, if this weren’t personal between me and this horrific code, would I still recommend rewriting?” If the honest answer is still yes, go ahead — but with respect. Rewriting isn’t punishing the past; it’s learning from it. And that means listening to what that ugly code is telling you, understanding why it ended up the way it did, and honoring the decisions that were reasonable at the time, even if you look at them today and wince.
Epilogue: living with the urge
The “hold my beer” syndrome isn’t going away. It’s part of human nature, just like throwing out a half-written draft because “I don’t like it anymore” and starting fresh on a blank page. Sometimes it works, but more often it just swaps one hard problem for a different one — more expensive and with fewer guarantees.
The real virtue is, almost always, in resisting the urge and doing something much more boring: staying, understanding the mess, taking it apart piece by piece, and in the meantime, keep delivering value.
It doesn’t sound as glorious as “we rewrote the entire core in six months,” but it tends to have one huge advantage: this story, unlike so many others, actually has a happy ending.
Quick glossary
In case you’ve been in this business for less time than the code you’re about to rewrite.
- Legacy: an inherited system or codebase, written in a different context and era, that’s still in production because nobody has dared (or managed) to replace it. Usually covered in scars from decisions nobody remembers anymore.
- Strangler pattern: an incremental migration pattern where you gradually “strangle” the old system by replacing parts one by one, while the original system keeps running. The name comes from the strangler fig, which grows around a host tree until it replaces it entirely.
- Happy path: the ideal execution flow of a system: no errors, no edge cases, no users doing weird things. The pretty part that shows up in demos. The problem is that reality rarely follows the happy path.
- Incremental refactoring: restructuring code in small, controlled steps without changing its external behavior. The goal is to improve readability, reduce technical debt, and make future changes easier — without throwing anything away.
- Monolith: a software architecture where the entire application lives in a single deployable unit. It’s not bad by definition, but it scales poorly and means any change touches more things than it should.
Sources and references
If you’re going to rewrite this article from scratch, at least check these sources first. You know how that usually ends.
- Things You Should Never Do, Part I (Joel Spolsky) - Joel Spolsky. The classic 2000 article on why rewriting software from scratch is “the single worst strategic mistake.” r/ExperiencedDevs thread discussing it 24 years later: https://www.reddit.com/r/ExperiencedDevs/comments/18vxqqf/24_years_ago_joel_spolsky_joel_on_software_wrote/
- Reescribir desde cero - serch.dev. Analysis of the risks and failure patterns in software rewrites (Spanish).
- La trampa de la reescritura: por qué nunca debes tirar el código y empezar de cero - CrazyImagine. The case against total rewrites vs. incremental refactoring (Spanish).
- Lo difícil (imposible) de rehacer de cero un legacy - Javier Garzás. Analysis of the Netscape case and other failed rewrites in large projects (Spanish).
- Síndrome Ícaro en ingeniería de software - cómo vencerlo - Q2B Studio. On developers’ tendency to over-engineer solutions and believe they can do it better from scratch (Spanish).
- Did another developer ever completely rewrite a project from scratch? - r/AskProgramming. Thread with real-world experiences from teams that undertook total rewrites.
