Skip to content
DevMeme
3847 of 7435
Why reading legacy code feels messier than rewriting from scratch
CodeQuality Post #4190, on Feb 9, 2022 in TG

Why reading legacy code feels messier than rewriting from scratch

Why is this CodeQuality meme funny?

Level 1: Cleaning Up a Messy Playroom

Imagine you walk into a playroom where someone else has been playing all day. There are toys scattered everywhere, blocks built into half-finished towers, and puzzle pieces mixed together on the floor. It looks like a giant mess! Now, you’re told to take care of this playroom. Cleaning up someone else’s mess is tricky: you have to figure out where everything goes, what each half-built lego structure was supposed to be, and why there’s a random stack of books in the corner. You didn’t make this mess, so understanding it takes time. You might think, “It’d be so much easier to shove all this aside and start a brand new game from scratch.” Starting fresh in an empty room is fun – you know exactly where you put things and what you want to build. Cleaning up (or finishing) someone else’s project? Not so fun, because you keep discovering odd little things that you have to make sense of. That’s exactly how a programmer feels when reading old code. Reading code is like cleaning up or figuring out a messy playroom left by someone else, while writing new code is like making your own new game. It’s no surprise which one feels easier! The meme is pointing out that even though cleaning the playroom (reading the code) is harder and less fun than starting a new game (writing it all new), it’s often better to tidy up what’s there. After all, that messy playroom might have all the toys you need already – you just have to learn how they’re arranged. So, the next time you see a jumble of toys (or a jumble of code), remember: organizing what’s there is harder than dumping it out and starting over, but you’ll learn a lot by cleaning up that mess.

Level 2: Refactor vs. Rewrite

Let’s break down what this meme is talking about in simpler terms. In programming, legacy code means any old codebase that’s already in use – usually written by other people (or by you, but so long ago that you hardly remember it). It’s the code running a working system, possibly from years back. Now, often when a new developer opens up a legacy system, they find it confusing or messy. Maybe the code isn’t commented, the naming is weird, or it’s using outdated frameworks. This can hurt CodeReadability – a fancy term for how easy it is for a human to read and understand the code. When code is not readable, working with it is slow and frustrating. CodeMaintainability is closely related: it’s about how easy it is to fix, update, or add to the code. If reading the code is hard, maintaining it (which involves reading and then modifying) is even harder. So, a newbie might conclude “This is bad code!” and think the solution is to start over fresh. That’s the instinct to rewrite from scratch.

Rewriting means throwing away the old code completely and writing a brand new program to replace it. It feels attractive because you’re not constrained by any of the weird decisions in the old code. You can use the latest language or framework, apply modern design patterns, and write everything your way. In contrast, refactoring means improving the existing code without changing what it does. When you refactor, you might clean up the structure, rename variables to be clearer, break big functions into smaller ones, or add comments and tests. Importantly, refactoring keeps the program’s behavior the same; you’re just making the code cleaner and easier to understand step by step. The meme’s quote is telling us that programmers often prefer a rewrite because reading and understanding the old code is hard work. It’s harder to read code than to write it, especially if that code wasn’t written with readability in mind.

Now, why is reading someone else’s code so hard? Imagine you open a source file with hundreds of lines you’ve never seen before. There are functions calling other functions, variables with names like temp3 or dataObj, and no comments explaining the intent. You don’t have the original author sitting next to you to explain their thinking. So you have to play detective. You run the program in your head (or with a debugger), line by line, trying to figure out what it’s doing. This mental effort is huge – it’s easy to get lost. On the other hand, when you write code, you start from a blank canvas. You can implement one small piece at a time, and you choose how to structure it. Writing something new feels like solving a problem you understand; reading old code feels like solving a puzzle someone else designed. It’s the difference between making your own plan versus deciphering a mystery plan. No wonder writing can feel easier!

However, the meme (and Joel Spolsky’s advice) is a gentle warning. Just because writing new code feels easier at the start doesn’t mean it’s the best decision. The old code – as messy as it might look – probably works. It has been used in the real world, so it’s handled a lot of corner cases (special situations that aren’t obvious at first). That messiness might include critical fixes and hard-earned lessons. If you toss it out entirely, you risk losing those lessons. For a junior developer or anyone new to a codebase, a smarter approach is often to read the code gradually and try to understand why it was written that way. Ask questions: “What does this function do? Why did they choose this approach?” You might discover reasons the code is structured oddly (maybe a performance constraint or a quirky requirement). With that understanding, you can then refactor bits of it—improve variable names, add comments, reorganize logic—so that it becomes easier to read for the next person (and for your future self). This addresses the root problem (code readability and maintainability) without the extreme step of a full rewrite. In many cases, after some refactoring and adding of tests, the legacy code doesn’t seem so scary or ugly anymore; it starts to make sense.

In summary, legacy code isn’t automatically “bad” code; it’s just old and established code. The quote in the meme is famous in developer circles because it captures a truth: working with existing code is tough! It’s easy to underestimate that difficulty. When you’re new, you might feel, “I can do better if I start fresh.” But as you gain experience, you learn that incremental improvement is usually safer and more efficient. Refactoring lets you modernize and clean the code a little at a time, so you don’t break everything that currently works. A complete rewrite might sound like a bold fix, but it often means reimplementing years of work – and that’s a project that can spiral out of control. For a relatable example, if you join a team and inherit a decade-old codebase, your senior colleagues will likely advise: “Take time to understand it. Don’t just delete it all.” They know that reading and maintaining code is a core part of programming – one that feels messy and challenging, but gets easier as you familiarize yourself with the system. The meme is basically every experienced developer reminding us: slow down, read that legacy code, learn from it – writing new code is fun, but reading (though harder) is where the real engineering happens.

Level 3: The Big Rewrite Trap

This meme quotes a classic software wisdom from Joel Spolsky (of Joel on Software fame), and every seasoned developer can relate. “They think the old code is a mess. They are probably wrong.” Why would Joel – a veteran developer and tech historian himself – say that so bluntly? Because time and again, young programmers join a project, encounter a gnarly legacy codebase, and immediately proclaim: “Ugh, who wrote this spaghetti? Let’s throw it out and rewrite everything cleaner!” It’s an almost irresistible temptation in software engineering: the greenfield fantasy. Writing new code feels like a fresh start on a blank canvas, whereas reading and untangling old code feels like trudging through a swamp of other people’s choices. But our battle-scarred industry history has shown that a full rewrite is usually a trap – a seductive trap that can sink entire companies.

Joel’s Law (as we might dub it) comes from hard lessons. One famous example he highlighted was Netscape’s big rewrite in the 1990s. Netscape had a dominant web browser, but the codebase was aging and, yes, “messy.” So they decided to throw it all away and re-engineer from scratch. The result? They vanished from the browser market for years as the rewrite dragged on, and Internet Explorer ate their lunch. By the time Netscape’s new code (Mozilla) was ready, it was too late – a classic case of the rewrite trap. Joel’s cautionary tale, “Things You Should Never Do, Part I,” was largely inspired by this fiasco. Lesson learned: that “messy” old code embodied years of bug fixes, domain knowledge, and working features. Tossing it out meant throwing away millions of dollars worth of debugging and hard-earned stability. The new system had to re-discover and re-implement all those edge cases from scratch – an enormously slow and costly endeavor.

So why does old code often feel like a disaster to fresh eyes? A senior engineer knows that a legacy system is like an archaeological site: layers upon layers of decisions made under different constraints. Perhaps multiple developers with different styles worked on it. Perhaps features were rushed under deadline, leaving technical debt (short-term hacks) entangled in the logic. Over years, that code evolved to handle real-world edge cases – weird user inputs, environment quirks, production bugs that had to be squashed quickly at 3 AM. The result might not be pretty; it’s full of odd-looking if-conditions and seemingly redundant checks. To a newcomer skimming through, it reads as “garbage code.” But to an experienced eye, each odd piece often has a rationale (even if the code doesn’t loudly announce it). That extra null check or that one-line workaround you want to rip out? It might be preventing a crash on leap years that happened back in 2016. In other words, what looks like cruft could be carefully battle-tested logic holding up the whole castle.

New Developer: “This code is such a mess, I bet I could rewrite it from scratch and do it right.”
Seasoned Dev (smirking): “Oh, you think it’s a mess? Trust me, this mess survived real users and real edge cases. Rewrite it, and you’ll just be writing a new mess – you just don’t know it yet.”

The humor (and pain) in this meme comes from how relatable this scenario is. Virtually every developer has felt that frustration: reading someone else’s convoluted code and daydreaming about hitting the reset button. But experienced developers have also felt the sting of reality when that dream fails. Rewriting an entire system is risky and rarely as swift as you’d hope. Why? Because software isn’t messy by pure accident; it’s messy because the world is messy. Business requirements shift, odd bugs surface, users do unexpected things – robust code accounts for all that messiness. When you start over, you initially have a clean slate, yes, but also an empty slate devoid of all those hard-won insights. The perceived ease of writing new code is partly an illusion: you’re only seeing the start of the curve. Imagine a legacy codebase as an overgrown jungle with all the creatures (features) already living in it. Starting from scratch is like planting a new garden – at first everything is green and orderly with just a few seeds (basic features). But given time, that garden too will grow into a jungle of complexity if it lives in the same environment. Greenfield projects (new rewrites) feel exciting and straightforward in the early stages – fewer constraints, no weird legacy patterns. Meanwhile, reading a mature brownfield codebase (existing system) forces you to confront all the complexity upfront. It’s like comparing a neat, empty field to a dense forest. Sure, the field looks cleaner now, but if your goal is a rich forest ecosystem, you either grow one (slowly) or you clear-cut the old forest and plant anew – losing all the old growth in the process.

From a CodeQuality and CodeMaintainability standpoint, the meme’s quote is a plea for refactoring over rewriting. It’s usually better to gradually improve that “messy” old code – prune the jungle – rather than burn it down entirely. Why? Because incremental refactoring lets you keep the system running and preserve its essential knowledge while tidying pieces one at a time. In contrast, a rewrite is a high-stakes all-or-nothing gamble. There’s a saying among engineers: “Don’t rewrite code from scratch, refactor it.”* Continuous refactoring addresses TechnicalDebt without throwing away the value the legacy system provides. Indeed, the dilemma of rewrite vs. refactor is an ongoing struggle in software teams. The meme nails the reason behind that struggle: reading and truly understanding a hairy legacy codebase is daunting, so starting fresh feels like an easy escape. But jumping to write it all anew, while emotionally satisfying at first, often leads you right back to a mess – just a different one, with the added pain that you spent months reinventing features the users already had. In short, the old code feels messy to read because reading is hard, not necessarily because the code was written by incompetents. As Joel succinctly put it, the difficulty is inherent, and recognizing that saves you from the costly mistake of a naive rewrite.

Level 4: Code Comprehension Conundrum

At a theoretical level, reading code is essentially solving an inverse problem of writing code. When you write code, you’re encoding specific solutions in a step-by-step way guided by your intent. But when you read legacy code, you have to decode that intent after the fact, reconstructing the developer’s mental model from the raw source. In computer science terms, it’s like attempting to reverse a function: finding input and intention from the output (the code) itself. This inversion is inherently harder. There’s an analogy to program analysis: determining what an arbitrary program does (or if it halts) can be computationally complex or even undecidable in the general case – a nod to the infamous Halting Problem. While everyday code comprehension isn’t literally undecidable, it sure feels NP-hard when you’re staring at a tangle of legacy logic with zero comments.

From an information theory perspective, a codebase is a highly compressed form of knowledge about a problem domain – full of optimizations, edge-case handling, and years of accumulated fixes. The act of reading code is like decompressing that knowledge. If the original developers didn’t include enough redundancy (like clear naming and documentation), the “compression” is lossy: vital context got tossed out. Now you, the reader, must infer missing context and rebuild the big picture in your head. This hefty cognitive load is why parsing someone else’s large program can explode in mental complexity. The number of possible paths and interactions in code grows combinatorially with size. You have to juggle function call chains, state changes, and exception conditions simultaneously – human working memory gets overtaxed quickly. Little wonder that Joel Spolsky elevated this to a “fundamental law of programming” in his famous quote: “It’s harder to read code than to write it.” He wasn’t just being folksy – it’s a recognition of a genuine asymmetry in complexity.

Computer science research has long acknowledged this challenge. There’s an entire field of program comprehension studying how developers understand code, and why it’s so taxing. Techniques like code analysis tools, or practices like literate programming, were devised to bridge the gap by making code more self-explanatory. Literate programming, introduced by Donald Knuth, literally intermixes prose and source code to make programs readable as explanatory essays, highlighting that code is meant for human readers as much as for machines. All these underscore the same conundrum: maintaining code clarity is hard because reading is the bottleneck. When that clarity isn’t there (which is often in legacy systems), deciphering the old codebase can demand more mental effort than writing a brand-new implementation from scratch. It’s not that the computer is being difficult – it’s that our human brains find reverse-engineering of complex logic far more demanding than forward engineering it.

Description

The meme has a white background with a large block of black text at the top that reads: “There’s a reason that programmers always want to throw away old code and start over: they think the old code is a mess. 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.” Beneath the quote is a blurred image of a person’s face (identity not discernible), overlaid on a light-gray panel. In the bottom-left corner, a small white caption says “joelonsoftware,” referencing Joel Spolsky’s famous blog post about rewriting code, and a tiny share icon appears at the bottom-right. The visual emphasizes the cognitive burden of deciphering unfamiliar codebases versus the seductive ease of starting anew, highlighting themes of legacy systems, refactoring decisions, and the ongoing struggle for code quality in software engineering

Comments

22
Anonymous ★ Top Pick Reading the 200 KLOC monolith feels like archaeology with grep and hope, but rewriting it just guarantees future interns inherit two monoliths - and Joel Spolsky’s ghost at every stand-up
  1. Anonymous ★ Top Pick

    Reading the 200 KLOC monolith feels like archaeology with grep and hope, but rewriting it just guarantees future interns inherit two monoliths - and Joel Spolsky’s ghost at every stand-up

  2. Anonymous

    After 20 years in the industry, I've learned that 'legacy code' is just code written by someone who understood the business requirements better than you do, but documented them worse than you would have

  3. Anonymous

    Every rewrite starts with 'the old code is unreadable' and ends with new code that's unreadable plus the old bugs, reimplemented from scratch

  4. Anonymous

    Ah yes, the classic Joel Spolsky wisdom hitting different after you've been the architect of your third 'temporary' system that's now running critical infrastructure for 8 years. Every senior engineer eventually learns this the hard way: that 'spaghetti code' you inherited isn't actually worse than what you'd write - you just don't have the 10,000 hours of context about why that seemingly insane conditional exists. Spoiler: it's handling the edge case from the client who threatened to leave in 2015, and yes, they're still your biggest customer

  5. Anonymous

    Joel's law decoded: You're a genius writing it, an idiot reading it - both the same dev, months apart

  6. Anonymous

    "Let's rewrite it" is dev-speak for "reading is hard; let's delete the undocumented invariants and let prod teach them back to us."

  7. Anonymous

    “Let’s rewrite it” is architect-speak for “let’s delete a decade of encoded edge cases and call it velocity.”

  8. @flex_ape 4y

    Where the meme

  9. @karim_mahyari 4y

    The meme's in your commits

  10. @niklashh 4y

    disagee

  11. @QutePoet 4y

    Just make much comments in code so everyone can understand your code. It's good to comment at least what a block of code do. And it's superb to comment almost every line of code. That's what I do. I also mark ifs and cycles start and finish with comments. Code must look like a book, not like a puzzle.

    1. @captain_gaga 4y

      Overcommenting is as bad, as no comments at all imo.

    2. Deleted Account 4y

      ideally, code should not have comments and be self-explanatory

      1. Deleted Account 4y

        Bruh...

        1. @RiedleroD 4y

          the code is said to be genius, not necessarily good

    3. @sashakity 4y

      >it's superb to comment almost every line of code jesus christ

    4. @saniel42 4y

      Oh yes, famous Function SquareRoot Comment: returns square root

      1. @picolino 4y

        Oh, seems like you kind of forget to comment parameters of that function

  12. @ozalexo 4y

    My internal monkey says that code is hard to read only if this code was written by another monkeys.

  13. @cptnBoku 4y

    If it was hard to write it should be hard to read

  14. @digital_insanity 4y

    Those who have ever wanted to rewrite complex projects or their parts never ACTUALLY had experience rewriting tons of legacy code

  15. Deleted Account 4y

    And comments can lie

Use J and K for navigation