"The Retry Logic" Meant Three Different Things — and No One Noticed for Four Days
Four days. Three engineers. One phrase — "the retry logic" — thrown around in standup like it meant the same thing every time. Same repo, same file, same words. Then someone finally asked the question nobody had thought to ask: "wait, which retry logic?" The room went dead quiet. All three of them had been nodding along in agreement for four straight days while describing three completely different pieces of code.
DEV A: I already fixed the retry logic yesterday, the backoff is exponential now.
DEV B: Wait, I thought we were keeping it linear for the webhook case — that's the pattern.
DEV C: ...the pattern? I've been calling the whole thing dead code since Tuesday, we're not even supposed to hit that path anymore.
DEV A: Hold on. Which retry logic are you all talking about?
[silence]Nobody in that room was lying. Nobody was careless either. Each of them had spent four days talking to an agent that told them a confident, internally consistent story about what that file did — and each story quietly diverged from the other two. The bug wasn't in the code. The code was fine. The bug was in the layer of language sitting on top of the code, and that layer had forked without anyone deciding it should.
Rewind: one sprint, three harnesses, no plan to write about it
This wasn't an experiment I designed. Three engineers were picking up work in the same service, and — mostly because of laptop setup and personal habit, not strategy — one was running Claude Code, one was running Cursor, and one had wired up a custom LangGraph agent for a specific refactor. Nobody chose this as an A/B test. I didn't sit down one morning and think, let's see what happens when three memory systems watch the same codebase. It happened the way team tooling always ends up a patchwork — by accretion. I only started paying attention once that standup transcript above actually occurred.
Same file, three stories
Once I went looking, the divergence was easy to trace. Each harness had broken in its own characteristic way. Cursor's engineer had, a week earlier, written a quick workaround for a webhook retry that was timing out under load — a linear backoff, hacked in under deadline pressure, with a comment that said essentially "temporary, revisit." Cursor's context window kept surfacing that workaround on every subsequent query about retries, because it was the most recent, most-referenced code in the area. By day three the engineer wasn't treating it as a workaround anymore. Repeated back to them enough times, framed as established behavior, it had quietly been promoted to "the pattern." Nobody relabeled it on purpose — it just accreted authority through repetition.
Claude Code's engineer had the opposite problem. Two sprints back, the team had actually deprecated an older retry approach in favor of the exponential backoff — a real decision, made in a real PR, genuinely merged. But Claude Code's memory had captured the deprecated approach as fact earlier in the project's life, before the deprecation, and kept treating it as current gospel across sessions. The tool wasn't wrong about what it remembered. It was wrong about how stale that memory had become, and nothing in its workflow ever forced a revalidation against the present state of the code.
The LangGraph engineer had it worst, in a way that's easy to miss unless you've built one of these yourself: the custom state graph had no naming layer at all. It was optimized for control flow, not for explaining itself in plain language. So when that engineer asked "what's our retry strategy here," the agent had no stable term to reach for — it grabbed whatever words were lying closest in the prompt history and recent diffs, which happened to be a half-digested mix of the other two engineers' language. That engineer wasn't wrong so much as fluent in a dialect nobody else spoke, one stitched together from context clues.
Why the diffs looked clean
Here's the part that made this genuinely hard to catch: code review found nothing, because there was nothing wrong at the code layer. The commits were fine. The diffs were small, sensible, individually correct. Every tool we had — linters, review checklists, CI, a second engineer eyeballing a PR — is built to catch drift in the code. None of it is built to catch drift in the shared vocabulary engineers use to talk about the code, because that vocabulary doesn't live in the repo. It lives in three separate context windows and memory stores, invisible to git, invisible to review, invisible to everything except a standup where someone happens to ask the right question at the right moment. We got lucky that day. Most days, nobody asks, and the drift just compounds quietly underneath everything that looks fine.
The unpaid interpreter
The other thing I only noticed in hindsight: one engineer on the team — not by assignment, just by temperament — had been quietly translating between the other two the whole sprint. She used Cursor herself but had also touched the LangGraph project earlier, and she kept catching mismatches in code review comments and Slack threads, gently reconciling them before they turned into standup collisions. Nobody named this as a role. It wasn't in her sprint plan, wasn't tracked as work anywhere, and I wouldn't have known to look for it if the retry-logic moment hadn't forced my hand. She'd become load-bearing infrastructure for the team's shared understanding, doing real cognitive work that was completely unaccounted for — the kind of role that's invisible right up until the person doing it takes a vacation.
What I actually changed
I didn't try to standardize on one harness. That fight wasn't worth having, and honestly I'm not convinced it would have fixed anything — the same three-fork failure mode can happen with three engineers on identical tooling if their sessions diverge far enough. What I changed was smaller and more mechanical:
- ▹Picked one artifact — a plain markdown glossary living in the repo, not in anyone's agent memory — as the source of truth for ambiguous terms like "the retry logic," "the pattern," "the fallback path."
- ▹Made every harness's memory defer to that file instead of its own accumulated context. If an agent's answer disagreed with the glossary, the glossary won. Full stop, no exceptions for whichever tool sounded more confident.
- ▹Added a five-minute "define your terms" pass before standup — not a full sync, just: if you're about to say a noun that refers to code, name the file and the commit you mean.
- ▹Named the translation work the fourth engineer had been doing for free, made it explicit, and rotated it, so the team's shared understanding didn't depend on one person's unpaid vigilance.
This isn't a tooling problem
It's tempting to read this as "pick the right harness" or "standardize your stack." I'd have believed that too, before this sprint. But the actual failure mode was organizational: each harness's memory silently became a competing narrator of what the code does, and standups turned into undetected translation exercises because shared vocabulary felt like shared understanding. Those are not the same thing, and the gap between them doesn't shrink as you adopt more agentic tooling — it grows, because every additional harness is another narrator with its own memory, its own staleness, its own blind spots, and no shared notion of what a word means. The more of your team's thinking you outsource to tools that don't talk to each other, the more this becomes a management problem, not an engineering one. Somebody has to own the glossary. It might as well be named, and it might as well be you.
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.