esc
Anthology / Yagnipedia / Grief

Grief

The Five Stages of Inheriting a Legacy Codebase
Phenomenon · First observed 1969 (Elisabeth Kübler-Ross); in software, every Monday since · Severity: Existential (but temporary — the Squirrel recovers in 48 hours)

Grief is the natural emotional response to loss, first formally modelled by Elisabeth Kübler-Ross in 1969 as a five-stage process (Denial, Anger, Bargaining, Depression, Acceptance), and subsequently discovered to apply with unsettling precision to software development — where loss is not occasional but structural, not exceptional but scheduled, and not something that happens to you but something that ships on the quarterly roadmap.

In software, grief is the background process that never terminates. Developers grieve codebases, languages, tools, projects, pull requests, and — most quietly — the code they wrote themselves and must now delete because it is no longer needed. The grief is real. The stages are predictable. The only variable is velocity: The Lizard completes all five stages during a single compile step; The Caffeinated Squirrel grieves with theatrical intensity and recovers in forty-eight hours; riclib is still processing Stage 2 about something Commodore did in 1994.

“It’s not the code you lose. It’s the version of yourself that wrote it.”
— A developer, mass-deleting a module that took three months to write, in eleven seconds

The Five Stages of Inheriting a Legacy Codebase

The Kübler-Ross model, when applied to the moment a developer first opens a codebase they did not write and must now maintain, maps with a fidelity that would have alarmed Kübler-Ross herself.

Stage 1: Denial

The developer opens the repository. The developer reads the first file. The developer closes the file. The developer opens it again, because surely they misread it.

“This can’t be the architecture.”

The denial is not about code quality. The denial is about the gap between what the developer imagined the system looked like — clean, modular, tested, documented — and what the system actually looks like, which is four thousand lines of procedural logic in a file called utils.go, a database schema that appears to have been designed by a committee that never met simultaneously, and a deployment script that begins with # DO NOT MODIFY — NOBODY KNOWS WHY THIS WORKS.

Denial typically lasts between three hours and two days, depending on how many files the developer can avoid opening.

Stage 2: Anger

“WHO WROTE THIS.”

The git blame stage. The developer traces each architectural decision to its author, seeking not understanding but accountability. The authors have, universally, left the company. This does not reduce the anger. This increases it, because the authors are now beyond the reach of code review comments.

The anger manifests as Slack messages that begin with “Can someone explain…” and end with a question mark that is doing the work of an accusation. The developer does not want an explanation. The developer wants a confession.

“The git blame revealed seventeen contributors over four years. Twelve had left. Three denied involvement. The remaining two blamed the twelve who left.”
The Caffeinated Squirrel, investigating a service that worked perfectly but offended aesthetically

Stage 3: Bargaining

“Maybe if I just refactor this one module…”

The developer has accepted that the codebase is what it is, but believes — with the specific optimism of someone who has not yet attempted it — that surgical intervention is possible. One module. One clean boundary. One well-tested package extracted from the mass of working, ugly, incomprehensible code.

The bargain is always the same: “I will not rewrite the whole thing. I will just fix this one part.” The bargain is always broken, because the one part depends on three other parts, which depend on seven other parts, which depend on utils.go, which depends on everything and is depended upon by everything else.

The bargain escalates. “Maybe if I refactor these three modules.” “Maybe if I introduce a new interface layer.” “Maybe if I just —” and at this point the developer has written a twelve-page proposal for a complete rewrite and is deep in The Second System Effect, which is where bargaining goes to become depression.

Stage 4: Depression

“It’s all like this.”

The developer has opened every file. The developer has read the tests (there are four, and one is commented out). The developer has examined the deployment pipeline (it is a shell script that SSHes into production). The developer has checked the documentation (it is a README from 2019 that describes a system three rewrites ago).

It is all like this. Not just this module. Not just this service. All of it. The entire codebase. The entire company. Possibly the entire industry.

The depression stage is characterised by long silences in standups, a sudden interest in job listings, and the consumption of unreasonable quantities of coffee. The developer stares at the code and the code stares back and neither of them blinks because neither of them has the energy.

Stage 5: Acceptance

“I am now the person who maintains this.”

The developer stops fighting. The developer stops blaming. The developer opens utils.go, adds a function to the bottom, and does not refactor the four thousand lines above it. The developer writes a test — one test, the fifth test, which is one more than existed before, which is progress. The developer deploys using the shell script and does not ask why it works.

Acceptance is not peace. Acceptance is the absence of resistance. The developer has become the codebase. The codebase has become the developer. In six months, a new developer will join, open the repository, and begin at Stage 1.

The cycle is eternal. The headcount changes. The grief does not.

The Death of a Beloved Tool

There is a grief that is not about code but about the tools and languages that shaped a developer’s identity and then, through no fault of their own, became irrelevant.

The Amiga is the canonical example. A machine that was right about everything — pre-emptive multitasking, custom DMA hardware, the Copper’s three-instruction elegance — killed not by a better machine but by worse management. The developers who loved it did not stop loving it when it died. They carried the grief forward, into every technology choice they made afterwards, choosing tools that were right over tools that were popular, because they had learned that popular does not mean correct, and they were never going to make that mistake again, even though it was never their mistake to begin with.

Delphi. Object Pascal in a visual IDE that could produce native Windows applications faster than anything else on the market. Developers built careers on it. Companies built products on it. And then the industry shifted — to the web, to Java, to .NET — and Delphi didn’t shift with it, or shifted too slowly, or shifted in the wrong direction, and the developers who loved it watched as their expertise became a line on a CV that recruiters didn’t recognise.

The grief of a dying tool is not like the grief of a dying codebase. A codebase you can rewrite. A tool you can only watch. You watch as the conference talks stop. You watch as the job listings thin. You watch as the Stack Overflow questions go unanswered. You watch as someone asks “Is X still maintained?” and the silence that follows is the answer.

"The Amiga didn’t die. Commodore’s management killed it. There’s a difference between dying and being killed, and the people who loved the machine have never confused the two."
The Lizard, who grieved once, completely, and moved on — but whose monitor still runs warm

The Rejected Pull Request

A pull request is not code. A pull request is a developer’s public assertion that they have understood a problem, devised a solution, and implemented it correctly. A rejected pull request is therefore not a technical event but an existential one.

The grief follows the stages with painful fidelity:

  1. Denial. “They must have misread my implementation.”
  2. Anger. “The reviewer clearly doesn’t understand the problem.”
  3. Bargaining. “What if I address comments 1 through 7 but push back on 8?”
  4. Depression. Reading the forty-seven review comments, each one reasonable, each one correct, each one a small paper cut to the developer’s self-image.
  5. Acceptance. Closing the PR. Opening a new branch. Starting again. Not because the reviewer was right — they were — but because the developer has finally separated their identity from their diff.

The Squirrel’s pull requests are legendary for their ambition and their rejection rate. The Squirrel does not submit incremental changes. The Squirrel submits architectural visions, complete with new abstractions, renamed interfaces, and a cover letter that reads like a manifesto. The reviewer’s response is always the same: “Could you break this into smaller PRs?” The Squirrel’s grief is loud, public, and resolved by the next morning, when the Squirrel has already conceived of an even more ambitious PR.

The Lizard’s pull requests are three lines long and always merged.

The Cancelled Project

Six months. The developer spent six months on it. The architecture was clean. The tests were green. The deployment pipeline was automated. The documentation was current. The developer had, for once in their career, done everything right.

The project was cancelled on a Tuesday. Not because it failed — it hadn’t launched yet. Not because it was behind schedule — it was on time. Not because the code was bad — the code was the best the developer had ever written. It was cancelled because the business priorities shifted. Because a reorganisation happened. Because someone in a meeting room the developer has never entered made a decision based on a spreadsheet the developer has never seen.

The six months of work becomes a private repository. The private repository becomes a line on a CV. The line on a CV becomes a story told at the pub: “I once built this thing…”

The grief of a cancelled project is unique because the thing being grieved never existed in the world. It existed only in the developer’s IDE, in the test suite, in the staging environment. The public never saw it. The users never used it. The only person who knows it was good is the person who built it, and that person’s testimony is inadmissible because they are biased and they know it.

“The Squirrel grieves cancelled projects for exactly forty-eight hours, then proposes something bigger. The Lizard grieves cancelled projects by not starting projects that will be cancelled. This is either wisdom or cowardice, and the Lizard does not find the distinction interesting.”
riclib, on the difference between processing grief and avoiding it

The Quiet Grief of Deleting Good Code

The loudest grief gets the attention — the cancelled project, the rejected PR, the deprecated language. But the quietest grief is the most common: deleting code you are proud of because it is no longer needed.

Not bad code. Not hacky code. Not code that should never have been written. Good code. Code that solved a real problem elegantly. Code that had tests. Code that you showed a colleague and they said “that’s nice.” Code that worked.

And now the feature has changed, or the requirement has shifted, or the system has been redesigned, and the code — your code, the good code — is no longer needed. It does not belong. It is not broken. It is irrelevant, which is worse than broken, because broken code gets fixed and irrelevant code gets deleted.

The developer selects the file. The developer presses delete. The developer does not look at the diff. Looking at the diff is how you end up keeping code that should be deleted, and keeping code that should be deleted is how utils.go gets to four thousand lines.

The code lives on in the git history, technically. The developer knows this. The developer also knows that code in the git history is code that no one will ever read again, which is the same as code that does not exist, which is the same as having never written it, which is the grief.

“I wrote a parser. It was sixty-three lines. It handled every edge case. It had twelve tests. It was beautiful. Then we switched to a library that did the same thing in one function call. I deleted the parser. I did not delete the tests. I could not delete the tests. The tests were all that proved the parser had ever existed.”
— A developer, name withheld, because admitting you grieve deleted code is not something you do publicly in this industry

The Saudade Differential

Saudade — the Portuguese word for a longing for something absent — is grief’s cultured cousin. Grief is the acute response. Saudade is what grief becomes when it ages, mellows, and starts wearing a nice scarf.

The Amiga developer does not grieve the Amiga. The Amiga developer feels saudade for the Amiga. The distinction matters. Grief implies the possibility of resolution — you grieve, you process, you move on. Saudade implies no resolution and no desire for one. The longing is the thing. The longing is permanent. The longing is what makes you choose SQLite over a twelve-node Postgres cluster thirty years after the Copper taught you that three instructions are enough.

Measured Characteristics

See Also