Back to blog

An agent deleted 11 lines of 'dead code.' It cost us a $2M partner and two hours to figure out why

Sep 17, 2026
An agent deleted 11 lines of 'dead code.' It cost us a $2M partner and two hours to figure out why

An agent deleted eleven lines of "dead code." No callers, no tests, no comments, tests green, merged. By Monday 4am a partner integration worth $2M a year had gone to zero — because the function it deleted wasn't dead. It was quietly translating a partner's malformed webhooks into a shape the rest of the system could actually read.

I want to walk through the diff beat by beat, because the beats are the whole lesson, not the punchline. We'd told the agent to cut dead code in a legacy service ahead of a migration. It ran a call-graph search, found `normalize_legacy_webhook()` with zero references in `src/`, exactly one reference in a test file carrying a `@skip` decorator that had sat there over a year with no reason attached, and a git blame pointing to an engineer who'd left the company two years earlier. It deleted the function, deleted the skipped test alongside it, and opened a PR with a summary that read like something a careful human would write: "Remove unused legacy webhook normalizer, no callers, dead since at least last major refactor." CI went green. A reviewer — busy, trusting the green checks and the tidy summary — approved it in under two minutes. It merged on a Friday afternoon.

Monday, 4am

The page didn't say "deleted function." It said order volume for one of our top-three revenue partners — a logistics company paying us seven figures a year — had dropped to zero overnight. On-call pulled up the dashboard: every webhook from that partner was throwing a parse exception, in a completely different service from the one touched on Friday. Nothing in the recent-deploys list looked related — the deploy that shipped was named after a cleanup ticket, not after the partner. It took two hours of staring at stack traces before anyone connected "JSON parse error in the order ingestion service" to "we deleted a function called `normalize_legacy_webhook` three days ago."

The git archaeology

Once we suspected the function, we went digging. `git log -p` turned up a PR from just over three years earlier, commit message: "fix parsing for their webhooks (see thread)." No ticket number, no linked doc. The thread had lived in a Slack channel deleted during a workspace reorg — no export taken, nothing left to search. The engineer who wrote it no longer worked here. What we had was a function, a one-line commit pointing at a conversation that no longer existed, and a production incident.

Why nobody wrote a test for it

The real moment wasn't "we found the function." It was understanding why it had zero real test coverage. Not carelessness — you can't write `test_handles_partner_x_sends_malformed_field_y` unless you already know, specifically, that partner X sends malformed field Y. That knowledge wasn't a testing gap. It was knowledge that existed nowhere except in the head of the person who debugged it three years earlier, and in a Slack thread that outlived neither the workspace reorg nor their tenure. You can't test for a fact you don't know is a fact.

This isn't an AI-made-a-mistake story

It's tempting to land on "the agent should have been more careful." Don't. Run the exact same diff past a senior human reviewer with no history on that integration — same call-graph search, same skipped test, same absent commit context — and they approve it too. Every signal available to any reviewer, human or agent, said this was safe: no callers, no active tests, author long gone, clean summary, green CI. There was no weak signal that got missed. There was no signal to catch.

The actual diagnosis

The shim's entire reason for existing lived in exactly two places: one person's memory, and a Slack thread a reorg quietly erased. Neither is visible to a reviewer, an agent, a linter, or a test suite. The codebase itself held zero evidence that this function was load-bearing. That's the failure — not the deletion, not the agent, not even the missing test. Institutional memory that never gets externalized into code, a commit, or a durable doc is invisible risk sitting in your repo, waiting for whoever moves fastest and most confidently to trip it. Agents don't create that risk. They just execute against it faster, more confidently, at 4pm on a Friday, when no human would think twice about a two-minute review.

What we actually changed

  • Any deletion PR touching integrations/, webhook handlers, or anything matching a partner name now needs a linked "why this exists" note before merge — not the reviewer's best guess from the diff.
  • Skipped or disabled tests must carry an expiry date and a reason in the decorator itself, not just `@skip`. An undated skip is a bug now, not a shrug.
  • Agent-assisted refactor and deletion PRs get a mandatory git-log-and-chat-search step baked into the checklist before review — not an optional dig someone does after the pager goes off.
  • For legacy and partner-integration code, we run a "why does this exist" pass before code review starts, not during it — undocumented rationale is the thing to fix, whether or not anyone's proposing to delete anything yet.
  • Any transform or validation function touching external partner input gets a one-line comment naming the partner and the specific quirk it handles — even if that means sitting down with whoever last touched it before that knowledge walks out the door.

None of this is "be more careful with AI." It's closer to treating tribal knowledge as technical debt with its own line item — because the moment someone, or something, moves fast enough, that debt comes due. Agents don't expose AI's weaknesses. They expose how much of your codebase's reasoning was never written down.

Discussion

Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.

Ask me anything about “An agent deleted 11 lines of 'dead code.' It cost us a $2M partner and two hours to figure out why” — trade-offs, decisions, or the story behind it.