Module 1: Why Not Rewrite

Modernize in place vs start from scratch

Overview

This module has built a strong case against the rewrite and for incremental. But there's a risk in such an emphatic message: turning it into dogma. "Never rewrite" is as false as "always rewrite" —and a good engineer doesn't swap one simplistic rule for another—. This lesson consolidates the module's thesis with honesty: rewriting is not always bad. There's a region, narrow but real, where starting from scratch is the right option, and knowing how to recognize it is as important as knowing how to avoid the rewrite when it doesn't fit. The difference between an engineer with judgment and one with a slogan is precisely that: the one with the slogan applies the same answer to everything; the one with judgment knows when each one applies.

The operational question, then, isn't "rewrite or modernize?" in the abstract, but "what does this system have that makes rewriting it safe or suicidal?". And that question can be answered with measurable criteria. A system is a safe candidate for a rewrite when it's small (little to reimplement), without users in production (nobody suffers if the cutover fails), well understood (little buried tacit knowledge), not very critical for the business (a failure doesn't cost dearly) and —key— well tested (a net of tests that catches the regressions). When a system meets those conditions, the four failure modes of the previous lessons are deactivated, and rewriting all at once can even be the simplest thing. When it doesn't meet them —and Mercado meets none—, rewriting is the trap the whole module described. This lesson scores several systems on those five axes and locates each one on the map, so the decision stops being ideological and becomes a measurement.

Connection with the module. It's the consolidation lesson: it gathers the four failure modes (lessons 2-4) and the case for incremental (lesson 5) and turns them into a single, measurable criterion —the rewrite_score— that says when each path is the right one. The test-coverage axis connects directly to module 2: the safety net that makes any change safer (rewrite or modernization) is exactly what module 2 builds for Mercado with characterization tests. With this lesson you close the "why"; the mini-project (lesson 8) applies the whole criterion to Mercado, and modules 2 onward teach the "how."

An analogy: repair the old car vs buy a new one

Your car is fifteen years old and something's failing. Do you repair it or buy a new one? The correct answer isn't a fixed rule ("always repair" or "always replace"): it depends on the car and on your situation, and you give it by evaluating a few factors. If it's a well-kept classic, with a complete service history, of which you know every detail, and the failure is bounded —a part to change—, repairing is sensible: you know what you have. But if it's a car whose history you don't know, with cascading problems, that you use every day for work and depend on to eat, and on top of that you have no way to know what else is about to fail, things change: each repair uncovers another, and you can't be left without a car while you fix it.

Notice that the decision depends on concrete factors: how well you know the car (complete history or a black box?), how critical it is to your life (a second car or your only way to work?), how big the problem is (one part or the whole engine?), and whether you can test the result before trusting it. Rewriting software is identical. A weekend script only you use is the classic car you know in detail: rewriting it is low-risk. Mercado's monolith is the car you depend on to eat, with unknown history and cascading failures: here you don't demolish, you repair in place, carefully and with a net. This lesson turns those "concrete factors" into five axes you can score, so the decision doesn't depend on a hunch but on evidence.

Worked example: the rewrite_score of several systems

We're going to score five systems on five axes from 1 to 5. Four axes raise the danger of rewriting (size, users in production, tacit knowledge, criticality); the fifth, test coverage, is the safety net that lowers it. The rewrite_score subtracts the danger from the net: high and positive when rewriting is safe, very negative when it's suicidal:

# Axes (1-5). The first four raise the DANGER of rewriting; the last
# (test coverage) is the safety NET that lowers it.
SYSTEMS = [
    # (name, size, users_in_prod, tacit_knowledge, criticality, test_coverage)
    ("throwaway_prototype", 1, 1, 1, 1, 3),
    ("internal_admin_tool", 2, 2, 2, 2, 4),
    ("greenfield_service",  3, 1, 1, 2, 3),  # not in production yet
    ("well_tested_module",  4, 4, 3, 4, 5),  # large but WITH a safety net
    ("mercado_monolith",    5, 5, 5, 5, 1),  # the case of this guide
]

THRESHOLD = 3  # rewrite_score >= 3 => rewriting is viable


def rewrite_score(size, users, tacit, criticality, coverage):
    danger = size + users + tacit + criticality   # 4..20 (more = more dangerous)
    safety_net = coverage * 4                      # 4..20 (more = safer)
    return safety_net - danger


ranked = sorted(SYSTEMS, key=lambda s: rewrite_score(*s[1:]), reverse=True)

print(f"{'system':<22}{'danger':>8}{'net':>5}{'score':>7}  verdict")
print("-" * 62)
for name, size, users, tacit, crit, cov in ranked:
    danger = size + users + tacit + crit
    score = rewrite_score(size, users, tacit, crit, cov)
    verdict = "rewrite viable" if score >= THRESHOLD else "MODERNIZE IN PLACE"
    print(f"{name:<22}{danger:>8}{cov * 4:>5}{score:>7}  {verdict}")

print("-" * 62)
mercado = next(s for s in SYSTEMS if s[0] == "mercado_monolith")
print(f"\n  Mercado scores {rewrite_score(*mercado[1:])}: the worst on the list, "
      f"in the opposite corner")
print(f"  to where rewriting is safe. Large, in production, full of")
print(f"  tacit knowledge, critical for the business and WITHOUT tests.")
print(f"\n  Note the 'well_tested_module': large and critical, but its coverage 5")
print(f"  (safety net) makes it viable. That net is exactly what")
print(f"  Module 2 builds for Mercado before touching a single line.")

What to expect. When you run the file, the output is exactly this:

system                  danger  net  score  verdict
--------------------------------------------------------------
throwaway_prototype          4   12      8  rewrite viable
internal_admin_tool          8   16      8  rewrite viable
greenfield_service           7   12      5  rewrite viable
well_tested_module          15   20      5  rewrite viable
mercado_monolith            20    4    -16  MODERNIZE IN PLACE
--------------------------------------------------------------

  Mercado scores -16: the worst on the list, in the opposite corner
  to where rewriting is safe. Large, in production, full of
  tacit knowledge, critical for the business and WITHOUT tests.

  Note the 'well_tested_module': large and critical, but its coverage 5
  (safety net) makes it viable. That net is exactly what
  Module 2 builds for Mercado before touching a single line.

Read the table top to bottom, from the safest to the most suicidal.

At the top are the safe candidates for a rewrite. The throwaway_prototype scores 8: minimal danger (4, all in 1) and a modest net (12). It's the weekend prototype —small, without users, that you just wrote (no tacit knowledge, it's fresh in your head), without criticality—. Rewriting it to "do it right" before it has users is perfectly reasonable. The internal_admin_tool also gives 8: a bit larger and with some users, but with good coverage (net 16) that compensates. The greenfield_service gives 5: it's not in production yet, so even if it's medium-sized, there's nobody suffering if the cutover fails —in fact there's no "cutover," because there's nothing alive to replace—.

The fourth case is the most instructive. The well_tested_module has a high danger —15, almost as much as Mercado: it's large (4), with users (4), some tacit knowledge (3), and critical (4)—. And yet its verdict is "rewrite viable," with score 5. Why? Because of the net column: test coverage 5, which gives a net of 20, the maximum. The safety net alone raised a dangerous system into the viable zone. This case is the lesson within the lesson: what makes a rewrite safe or suicidal isn't just the size or the criticality; it's, above all, whether you have a net that catches the regressions. A large and critical system but well tested can be rewritten with relative safety, because the tests scream when you break something (including the tacit rules of lesson 4).

And at the very bottom, alone, is mercado_monolith with a score of -16 —not a bit worse than the rest, but in another category—. Maximum danger (20: large, with many users, full of tacit knowledge, critical for the business) and minimal net (4: coverage 1, almost no tests). It's the exact combination of everything this module described as a trap: the four failure modes activated at once and with no net to catch them. Mercado isn't "near the threshold"; it's in the opposite corner from where rewriting is safe. For Mercado, the measured verdict is unequivocal: modernize in place.

Deep dive: test coverage is the lever you can move

Of the five axes, four are largely given: the system's size, how many users it has in production, how much tacit knowledge it accumulated, and how critical it is for the business you don't change easily —they're properties of what the system is today—. But the fifth, test coverage, you can move, and the example showed how much it weighs: it was the only thing that separated the well_tested_module (viable) from Mercado (suicidal), even though their danger is almost the same. That's the most useful observation of the lesson for practice.

Think of it this way: Mercado scores -16 today, with coverage 1. What would happen if, without changing anything else, you took its coverage from 1 to 5? The danger stays at 20, but the net rises from 4 to 20, and the score goes from -16 to 0 —still not "viable" (still below the threshold of 3), but no longer in disaster territory—. The safety net doesn't turn Mercado into a good candidate for a rewrite (it's still enormous, critical, and full of tacit knowledge), but it does make any change to Mercado —including an incremental modernization— far safer. That's exactly module 2's job: not to enable a rewrite, but because before touching the legacy with any strategy, you have to stretch the net.

This is where the module's pieces fit together. Lesson 4's tacit knowledge —the five hidden rules a blind rewrite broke— is exactly what a net of tests captures and protects. Module 2's characterization tests are the concrete tool: they pin the legacy's current behavior (quirks included) so that any change that alters it jumps out instantly. With that net stretched, modernizing Mercado by slices (lesson 6's strangler) stops being "changing a testless system blind" and becomes "changing a characterized system with a net." The sequence of the rest of the guide isn't arbitrary: first you stretch the net (M2), then you strangle (M3+), precisely because coverage is the lever that lowers the risk of everything else.

flowchart TD
    Q{"How does the system score<br/>on the 5 axes?"}
    Q -->|"high score: small, no users,<br/>understood, not critical, tested"| REW["Rewrite from scratch<br/>may be the right thing<br/>(the narrow region)"]
    Q -->|"low score: large, in prod,<br/>tacit, critical, no tests"| MOD["Modernize in place<br/>(Mercado is here)"]
    MOD --> NET["First: stretch the net<br/>(characterization tests, M2)"]
    NET --> STR["Then: strangle<br/>by slices (M3+)"]

Common mistakes

Turning "don't rewrite" into dogma. What happens: someone absorbs the module's message and applies it as a universal rule —"rewriting is always wrong"— and blocks a rewrite that in their case was the right option (a prototype, a small script, a service without users). Why it happens: slogans are convenient and save thinking. How to spot it: if you reject a rewrite without having scored the system on the five axes, you're applying dogma, not judgment. How to fix it: the correct answer depends on the system, and it's measured. Score the five axes; if the system is small, without users, understood, not very critical, and (ideally) tested, rewriting can be the simplest and most sensible thing. The module doesn't say "never rewrite"; it says "rewriting a system like Mercado —large, alive, critical, tacit, without tests— almost always fails." The condition matters as much as the conclusion.

Ignoring test coverage when deciding. What happens: the decision to rewrite or modernize is made looking only at the size and the criticality ("it's large and important, let's not touch it" or "it's small, let's rewrite it"), without considering whether there's a net of tests. Why it happens: size and criticality are visible; coverage is a datum you have to go looking for. How to spot it: if in the rewrite-vs-modernize discussion nobody mentions "how well tested is it?", the axis that weighs most is missing. How to fix it: test coverage can flip the verdict —the example's well_tested_module was almost as dangerous as Mercado and came out viable only because of its net—. And since it's the only axis you can move, it's also your main lever: before deciding the strategy, ask whether stretching a net changes the equation. For Mercado, stretching it doesn't enable a rewrite, but it is the prerequisite of any safe modernization (module 2).

Scoring the system with optimism instead of honesty. What happens: when evaluating the axes, the team underestimates the tacit knowledge ("no, the system is pretty straightforward") or overestimates the coverage ("we have several tests"), and the score comes out artificially favorable to rewriting. Why it happens: rewriting is tempting, and it's easy to adjust the evaluation to justify what you already want to do. How to spot it: if the tacit-knowledge axis is low but nobody can explain what certain parts of the code do, or if the coverage is high but the tests don't compare against the real behavior (only against the docs), the scoring is optimistic. How to fix it: score with evidence, not with desire. Tacit knowledge is measured by replaying the real behavior (as in lesson 4) and seeing how many hidden rules appear; coverage is measured by whether the tests would catch a real regression, not by how many there are. An honest score for Mercado gives -16; an optimistic one could dress it up, and that dressing-up is exactly what leads to the disastrous rewrite the whole module prevents.

Exercises

Exercise 1 — Score a system. A startup has a recommendations service that a single engineer wrote six months ago, still in beta with 50 test users, with logic the engineer understands well and documented, and with a decent test suite. The service isn't critical (if it fails, the app works the same, just without recommendations). Score the five axes (justify each one), compute the rewrite_score with the example's formula, and give the verdict.

See solution

A reasonable scoring:

  • size = 2: a single recommendations service, written by one person in six months; it's medium-small, not a monolith.
  • users_in_prod = 2: 50 test users in beta; there are some users, but few and warned that it's beta, so the impact of a failure is low.
  • tacit_knowledge = 2: the engineer who wrote it understands it well and documented it, and it's recent (six months); little buried knowledge, though it's never zero.
  • criticality = 1: it's not critical —if it fails, the app works the same, just without recommendations—. A failure doesn't cost dearly.
  • test_coverage = 4: it has a decent test suite; a good safety net, though not perfect.

danger = 2 + 2 + 2 + 1 = 7. safety_net = 4 × 4 = 16. rewrite_score = 16 - 7 = 9. Since 9 ≥ 3, the verdict is rewrite viable.

Interpretation: this service is clearly in the narrow region where rewriting is a legitimate option. It's small, not very critical, well understood, well tested, and with few users who also know it's beta. None of the module's four failure modes has force here: the business doesn't depend on it (it can "stop"), the tacit knowledge is minimal, and the net of tests catches regressions. It's the exact opposite of Mercado. The module's conclusion (don't rewrite) doesn't apply to this system, and applying it out of dogma would be a mistake —exactly the lesson's first common mistake—.

Exercise 2 — The coverage lever. Take the mercado_monolith from the example (danger 20, coverage 1, score -16). Without changing any of the other four axes, compute what score it would have with coverage 3 and with coverage 5. Does any coverage make it "rewrite viable" (score ≥ 3)? What does that tell you about what stretching the net in Mercado is for?

See solution

Mercado's danger is fixed at 20 (large + many users + tacit + critical, all at 5). We only move the coverage:

  • Coverage 1 (current): net = 4, score = 4 - 20 = -16. Suicidal.
  • Coverage 3: net = 12, score = 12 - 20 = -8. Still very negative.
  • Coverage 5: net = 20, score = 20 - 20 = 0. No longer a disaster, but still below the threshold of 3.

No coverage makes Mercado "rewrite viable": not even with the maximum net (coverage 5) does the score reach 3, because its danger of 20 is too high. An enormous system, with a great many users, full of tacit knowledge and critical for the business doesn't become a good candidate for a rewrite just by having tests —it's still enormous, alive, and critical—.

What that tells you about stretching the net in Mercado: the net does not serve to enable a rewrite (it doesn't enable it). It serves something different and more important: lowering the risk of any change to Mercado, including incremental modernization. Going from -16 to 0 doesn't make the rewrite a good idea, but it does turn "touching Mercado blind" into "touching Mercado with a net." That's why module 2 goes before module 3: the net isn't optional nor is it a step toward the rewrite; it's the prerequisite of modernizing safely. Coverage is the only lever you can move, and moving it is the first thing you do —not to rewrite, but to be able to modernize without blowing up—.

Exercise 3 — Defend the nuance. A colleague, after reading the module, declares in a meeting: "I learned that rewriting is always a mistake, so I vote against any rewrite, period." Correct their conclusion without conceding to the opposite extreme ("rewrite whenever you want"). What's the judgment-based formulation?

See solution

The colleague's conclusion is dogma, and dogma —even if it points in the right direction— fails in the narrow-region cases. Correcting it without falling into the opposite extreme:

What the colleague has right: the intuition to distrust rewrites is healthy, because most of the systems people want to rewrite are alive, large, and critical systems —like Mercado—, and for those the rewrite almost always fails due to the module's four failure modes. In the typical case, their vote against would be the correct one.

Where it fails: "it's always a mistake" is false. The module didn't demonstrate that rewriting is bad in the abstract; it demonstrated that rewriting a system with certain properties (large, in production, critical, tacit, without tests) is a trap. A weekend prototype, a small script without users, a well-tested service in beta (like the one in exercise 1) are legitimate candidates for a rewrite, and blocking them out of dogma wastes the right option.

The judgment-based formulation: "rewriting is neither good nor bad in itself; it depends on the system, and it can be measured. Before deciding, let's score the system on five axes —size, users in production, tacit knowledge, criticality, and test coverage—. If it comes out with a high score (small, no users, understood, not critical, tested), rewriting can be the simplest thing. If it comes out with a low score, like Mercado (-16), we modernize in place. The rule isn't 'never rewrite'; it's 'don't rewrite this kind of system, and here's how to know what kind ours is.'" That's the difference between the engineer with a slogan and the engineer with judgment: the second brings a measurement to the meeting, not a catchphrase.

Summary and next step

In this lesson you consolidated the module's thesis without falling into dogma: modernize in place vs start from scratch is a decision that depends on the system and is measured. You saw, with the old car, that the correct answer isn't a fixed rule but an evaluation of concrete factors, and you turned it into the rewrite_score over five axes. Rewriting is viable in a narrow region —small systems, without users, understood, not very critical, well tested—, and Mercado falls in the opposite corner, with a score of -16, because it activates the four failure modes and has no net. And you saw the key lever: test coverage is the only axis you can move, and although for Mercado it doesn't enable a rewrite, it is the prerequisite of any safe modernization —that's why module 2 goes first—.

Before moving on you should be able to: score a system on the five axes and give a measured verdict; explain why test coverage is the lever that weighs most and the only one you move; distinguish the narrow region where rewriting is correct from the majority of cases where it isn't; and correct both the "never rewrite" dogma and its opposite extreme.

With this you close the "why" of the whole module. Lesson 8 is the mini-project: you're going to apply the whole criterion to Mercado end to end —classify its modules by value and risk to choose the first slice, model the cost/value of rewrite vs incremental, and write the migration recommendation that justifies, with the numbers, why incremental wins—. It's the guide's step 0, and the direct bridge to the mechanics that begins in module 2.

Resources

  • Martin Fowler, "Who Needs an Architect?" and his work on modernization — martinfowler.com/architecture. The framework for why hard-to-reverse decisions (like rewriting a live system) deserve an explicit criterion, not a hunch. In English.
  • Michael Feathers, Working Effectively with Legacy Code (Prentice Hall, 2004) — the thesis that the net of tests is what makes any change to the legacy safe; this lesson's coverage axis, developed as a technique in module 2. In English.
  • Sam Newman, Monolith to Microservices (O'Reilly, 2019), ch. 1, "Is Your Migration Going to Work?" — the criteria for deciding when a migration (or rewrite) makes sense and when it doesn't; the direct complement of the rewrite_score. In English.
  • Joel Spolsky, "Things You Should Never Do, Part I" (2000) — joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i. The title is a deliberate absolute, but the argument is nuanced: it applies to large, live systems, not to everything. A good exercise in reading the nuance behind the slogan. In English.