← All posts

August 10, 2026

Why Your AI Assistant Rewrites Code You Already Have

You ask for a helper to format prices. You get a clean, well-tested formatMoney(). It works. It also sits fourteen files away from the formatCurrency() you wrote in March, which does the same thing, handles two currencies yours doesn't, and is already used in nine places.

Nobody did anything wrong. The assistant wrote good code. That is what makes this failure mode so durable: the output passes review, because the problem isn't in the code that got written. It's in the question that never got asked.

The assistant probably did look. Looking couldn't return a "no"

Ask a person to add a currency helper and they don't start typing. They search. Maybe they grep for "currency", maybe they remember writing something like it, maybe they ask someone. The writing starts after the looking.

An assistant can search too — most will grep the repository before writing. The trouble is what a text search can tell you when it finds nothing. A hit is proof of presence. A miss is not proof of absence: it means the word you guessed didn't appear. Search currency and you match a CSS class, a test fixture, and a comment; search formatMoney because that's the name in your head, and formatCurrency never surfaces at all.

So the assistant does the reasonable thing with what it has: it searches, it reads what looks relevant, it doesn't find formatCurrency, and it treats that as the helper not existing.

That conclusion is wrong, and nothing in the result said so. The assistant didn't skip the check. It ran a check that cannot distinguish "not there" from "not spelled the way I guessed" — and then had to act on the answer anyway.

Reading more files doesn't fix it

The obvious response is to give it more context. Bigger window, more files, whole directories.

This helps right up until it doesn't, and it fails in a way that's easy to miss. Sampling more of a codebase makes you more likely to find any particular thing, but "more likely" isn't the property you need here. You need to know whether formatCurrency exists — and reading 40% of a repository and not finding it tells you almost nothing. The absence of evidence is barely evidence of absence when you've only looked at part of the picture.

And an assistant that read the right file and one that read forty wrong ones both say "I'll write that for you." You can't tell from the output which of them checked.

Here's the thing worth noticing: "does a function called formatCurrency exist in this repository" has an exact answer. It isn't a judgement call or a similarity ranking. There is a fact of the matter, and it's cheap to retrieve — if something has indexed your symbols by name.

That's a different operation from text search. Grep for "currency" and you get comments, string literals, test fixtures, a variable named currencyCode, and the CSS class .currency-badge. You get everything that contains the word. What you want is everything that is the definition — and then, separately, everything that calls it.

Once symbols are indexed by name, the sequence changes:

  1. Does this already exist? — a lookup, exact answer
  2. If yes, read just that function — not the file it lives in
  3. If you're changing it, list what calls it first

Each step has a real answer. None of them require guessing which files to open.

The asymmetry

The check costs a fraction of a second. The duplicate costs an unbounded amount of time, arriving later:

You now have two currency formatters. They drift. One gets a bug fix in June, the other doesn't. Six months on, a new engineer picks the wrong one and ships a rounding error to production. Somebody spends an afternoon working out which is canonical, and another afternoon migrating the nine call sites.

None of that shows up in the pull request that added formatMoney(). It shows up quarters later, as "this codebase is hard to work in," which is the sound duplicated concepts make when there are enough of them.

What to do about it

If you're working with an AI assistant on a codebase of any size, the highest-leverage habit is making "does this already exist?" a step that actually runs. Not a reminder in your prompt — prompts asking for diligence produce the appearance of diligence. A real lookup, against a real index.

Some of this you can do by hand: search before you accept new code, ask what already handles this, treat a confident "I'll write that" on a mature codebase as a prompt to check. That works, and it depends on you remembering every time.

The alternative is giving the assistant the ability to check for itself — so that searching before writing is what it does, rather than something you have to ask for. That's what cix does: it indexes your project's symbols, routes, and schema, and exposes them to Claude Code, Codex, and Gemini CLI as tools they can query mid-task. The assistant looks things up because looking things up is now possible.

Either way, the principle is the same one that applies to people: the writing should start after the looking. The difference is what a person does when the looking turns up nothing — they go and ask someone. An assistant has to act on the empty result, and writing the helper is the reasonable thing to do with it.