~/webline_global $

// Everyday tech, explained simply.

Sunk-Cost Nudges Keep 27% More Devs Debugging a Dead Branch

· 8 min read
Sunk-Cost Nudges Keep 27% More Devs Debugging a Dead Branch

I watched a senior engineer spend forty minutes last Tuesday trying to get a failing integration test to pass on a branch that had already been abandoned by the rest of the team three days earlier. The feature it tested had been re-scoped out of the sprint. The branch was, functionally, a corpse. He knew all of this. He kept debugging anyway, and when I asked why, he said something that has been rattling around in my head since: "I've already put two days into this. I'm not throwing that away."

That sentence is the entire problem in miniature, and it's worth asking a narrower, more answerable question: when a developer decides to keep pushing on a doomed branch, how much of that decision is actually about the code, and how much is about a psychological reflex that has nothing to do with software at all?

The Ledger Nobody Asked For

Behavioral economists have a name for what my colleague was doing. It's called the sunk-cost fallacy, and it's one of the most reliable findings in the field. Richard Thaler, working with Hal Arkes in the mid-1980s, ran a series of experiments showing that people who had already invested money or effort into a failing course of action were dramatically more likely to keep investing — even when the future payoff was identical to a fresh alternative. Thaler later won a Nobel for this line of work. The finding has been replicated across cultures, age groups, and, notably, across professional domains. Doctors order unnecessary tests to justify earlier diagnoses. Investors hold losing positions. Product managers ship features that market research has already killed.

Developers are not exempt. If anything, we may be more exposed, because our work is unusually well-suited to generating the exact conditions that trigger the reflex.

Think about what a git branch actually is. It's a record. It has commits with timestamps, a diff that grows over days, a CI history that shows how many times you tried and failed. That record is a kind of ledger. And the human brain treats ledgers as morally significant in a way that is, from a purely rational standpoint, absurd. Once effort is recorded, abandoning it feels like a loss — and Daniel Kahneman's work on loss aversion suggests we feel losses roughly twice as strongly as equivalent gains. A branch you spent two days on isn't just "two days spent." It's a loss you're about to realize if you delete it. The alternative — starting fresh on the correct approach — is a gain, but a smaller-feeling one, because it doesn't cancel the loss. It just sits next to it.

That asymmetry is the machinery. It runs whether or not there's any code involved.

Why Code Is a Particularly Good Sunk-Cost Trap

Software engineering has three properties that make it unusually vulnerable to this reflex, and I think they're worth naming separately because each one suggests a different countermeasure.

The first is legibility of effort. In most jobs, the work you've done is invisible to you in aggregate. You don't have a running diff of your last two weeks of meetings. In software, you do. git log is a receipt. That legibility is mostly a good thing, but it has a dark side: it makes the sunk cost feel concrete and countable in a way that abstract effort doesn't. "I've written 400 lines on this branch" is a number. Numbers are hard to walk away from.

The second is the plausibility of the next fix. Debugging is a search problem with an unbounded action space. There is always one more thing to try. Change the timeout. Mock the network call. Pin the dependency. The fact that the next attempt might work is enough to keep the loop running, and this is where B.F. Skinner's variable-ratio reinforcement schedule becomes uncomfortably relevant. Skinner showed that behavior reinforced on an unpredictable schedule — sometimes it works, sometimes it doesn't — is the hardest kind to extinguish. Debugging is almost perfectly variable-ratio. Most attempts fail. Occasionally one works, and that occasional success is enough to train you to keep pulling the lever far past the point of diminishing returns.

The third is social accountability to a version of yourself. A branch is a public-ish commitment. Even if nobody reviews it, you named it. You pushed it. Some part of your professional identity is now attached to the idea that you can make it work. Abandoning it isn't just a technical decision; it's a small admission that the version of you who started this branch was wrong. That's a real cost, and it's paid in a currency that no CI system tracks.

The 27% figure in the title comes from an internal analysis I ran across while reading up on this — a mid-sized platform team tracked how often engineers returned to branches that had been marked stale for more than 72 hours, and found that roughly a quarter of all subsequent debugging sessions were spent on branches that never merged. The team's own retrospective called it "the zombie branch problem." I don't want to oversell the number; it's from one company, and the methodology was informal. But the direction of the finding matches everything the behavioral literature would predict.

The Decision Under Uncertainty Problem

Here's where the psychology gets more interesting, and where I think the standard advice ("just be disciplined about cutting losses") falls apart.

Abandoning a branch is a decision under uncertainty. You don't actually know the branch is dead. You believe it is, based on incomplete information. Maybe the failing test is exposing a real bug that will bite you later in a different form. Maybe the approach is salvageable with one more refactor. Maybe your judgment is being clouded by fatigue, and the branch is closer to working than it feels.

Kahneman and Tversky's work on prospect theory is relevant here, but the more useful frame might be the distinction between risk and ambiguity. In a risky situation, you know the probabilities. In an ambiguous one, you don't. Most engineering decisions are ambiguous, not risky. And the research on ambiguity aversion — Ellsberg's famous 1961 thought experiment with urns — shows that people will pay a premium to avoid situations where they don't know the odds, even when the known-odds option is objectively worse.

What does that mean for a developer staring at a dead branch? The "abandon and start fresh" option is ambiguous. You don't know how long the fresh approach will take, or whether it will work either. The "keep debugging" option, by contrast, feels like it has known structure: you have a failing test, you have a hypothesis, you have a next step. That feeling of known structure is an illusion — the branch is just as uncertain as the rewrite — but it's a comfortable illusion, and it wins.

This is why "just cut your losses" doesn't work as advice. It's asking someone to trade a familiar ambiguity for an unfamiliar one, at the exact moment when their accumulated effort is screaming at them not to.

What Actually Changes the Behavior

If the reflex is real and the standard advice is useless, what does work? A few things, in rough order of how reliably I've seen them applied.

Time-boxing with a pre-committed kill criterion. Not "I'll give it another hour," which is a feeling, but "if the test isn't green by 4pm, I close the branch and open a fresh one against main." The key word is pre-committed. Deciding the criterion in advance, before you're emotionally invested in the outcome, sidesteps the sunk-cost reflex because you're not making the decision at the moment of maximum pressure. This is the same logic behind Ulysses contracts in behavioral economics — binding your future self to a rule your present self knows is correct.

Resetting the ledger. This is the one that surprises people. When you abandon a branch, the effort isn't actually wasted — the knowledge you gained is real. But the diff is what your brain is mourning, and the diff is the least valuable artifact of the work. Some teams I've talked to have a practice of writing a short "what we learned" note in the ticket before closing a dead branch, and then deleting the branch entirely. The note is the salvage. The branch is the thing that was keeping the reflex alive. Once the ledger is gone, the pull weakens noticeably.

Making the fresh start cheaper. A lot of sunk-cost behavior is downstream of the fact that starting over is expensive. If your codebase makes it easy to scaffold a new approach against current main — good test fixtures, clear module boundaries, a working local dev environment — the fresh option stops looking so ambiguous. The behavioral problem has a tooling solution. This is one of the more underrated arguments for investing in developer experience: it's not just about speed, it's about reducing the psychological cost of changing your mind.

Naming the pattern out loud. This sounds soft, but it's the one I've seen work most reliably in practice. When a colleague is deep in a dead branch, saying "hey, is this a sunk-cost thing?" is often enough to break the spell. Not because it's a clever observation, but because it externalizes a decision that was happening below the level of conscious reasoning. The reflex doesn't survive being pointed at. It's the same mechanism as the "are we solving the right problem?" question in a design review — sometimes the value is just in forcing the question into the room.

The Forward-Looking Bit

I don't think sunk-cost behavior in engineering is going away, and I don't think it should. The same instinct that keeps a developer grinding on a dead branch is the instinct that keeps them grinding on a hard bug that is worth solving. The reflex is not a bug in human cognition; it's a feature that misfires under specific conditions. The trick isn't to eliminate it — it's to notice when the conditions are wrong.

What I'd watch for over the next few years is tooling that makes the misfire more visible. We already have CI systems that flag stale branches. We have build tools that measure how long a target has been failing. None of them, as far as I know, surface the one signal that would actually help: how much of your recent work has been on branches that never merged. That's a computable number. It's sitting in every git repository in the world. Nobody's putting it on a dashboard, partly because it's a little uncomfortable to look at, and partly because we haven't quite accepted that the problem is psychological rather than technical.

My colleague eventually closed his branch. It took another twenty minutes and a walk to the coffee machine, and when he came back he said the obvious thing: the failing test had been testing a code path that no longer existed. He'd been debugging a ghost. The two days he thought he was protecting had already been spent; the only real question was whether he'd spend a third one on top. He didn't, and the fix on main took about ninety minutes the next morning. That ratio — two days of sunk effort versus ninety minutes of fresh work — is roughly the shape of the problem. The sunk cost doesn't just fail to help. It actively delays the thing that would have worked.