Stackness
Sign up
Stop a coding agent writing duplicate helper functions: the four causes, a test for each and the fix

Stop a coding agent writing duplicate helper functions: the four causes, a test for each and the fix

A coding agent writes a duplicate helper for one of two measured reasons: it never looked for the existing one, or it looked and rewrote it anyway. In the RepoReuse study published on 28 September 2026, agents read 83.6% of the relevant repository code on the first task of a chain and 35.4% by the fifth, and the share of chains with a re-implemented function rose from 13.8% to 50.8%. To tell which cause hit you, open the session transcript and look for a search or read of the helper's file before the write. No search means the agent never looked; a read followed by a twin means the second reason, which only a check after the session catches.

The symptom: a second helper next to the one you already have

The duplicate-helper symptom looks like this, in a Reddit thread of 29 September with 49 comments: you ask the agent to format a date, and it writes a new function in the file it is editing, while a formatDate in your utils folder is used in about forty places and carries a timezone fix the new one lacks. Now there are two.

Two measurements put a size on it. In a study of 617 pull requests in one Python repository, agent-written changes scored 0.2867 on a redundancy measure against 0.1532 for human ones. GitClear, which sells code analytics, counted duplicated blocks of five or more lines rising from 40.3 to 73.0 per million changed lines between 2023 and 2026 year to date, a rise it ties to growing AI authorship.

Why do coding agents write a new helper instead of reusing the existing one?

Coding agents write a new helper for one of four reasons, ranked here by how much evidence backs each: they did not search, they searched and rewrote anyway, nothing told them where shared code lives, or the helper dropped out of context. RepoReuse, which ran OpenCode and mini-SWE-agent with four models across 75 five-task chains, measured the first two.

Figure 1 from Do Coding Agents Reuse Existing Code or Reinvent the Wheel? by Ma et al., 2026, CC BY 4.0.

Every run in that study already had an instruction to find and reuse existing modules where it made sense, so a generic reuse rule alone does not stop it.

The agent never searched before writing

The first cause is an exploration gap: the agent works from the file it is in and writes, because, as the thread's author put it, writing is cheaper than searching. In RepoReuse, configurations that viewed 900 or more source lines a turn read 47 to 61% of the functions they should have reused, against 35 to 42% for those that viewed under 700. Commenters in the Reddit thread report that a general "search first" rule fades once the agent is deep in a file, while naming the existing helper in the task works where the general rule fails.

Test: in the transcript (Claude Code keeps it as JSONL under ~/.claude/projects/), look for a grep, glob or read naming the helper or its file before the write that created the twin. None means this cause.

Fix: name the helper in the task when you know it, and ask for a plan that lists the existing functions it will reuse before any code is written.

The agent read the helper and wrote a twin anyway

The second cause is that the agent saw the helper and did not use it. In RepoReuse, every configuration read at least 98.9% of the functions it had written on earlier turns, yet reused only 67.0 to 86.4% of them. In an ablation on OpenCode with Qwen3.7-plus, putting the full earlier source into context left reuse at 29.2%, no better than no memory at 30.0%. The authors conclude that the bottleneck is the agent's disposition, not its access to the code.

Test: the helper's file was read in the transcript, and a twin was still written.

Fix: no instruction in RepoReuse closed this gap, so check the diff after the session (see the jscpd section below).

The instruction file does not say where shared code lives

The third cause is an instruction file that names no shared code. RepoReuse measured the closest thing to a helper list: giving the agent each earlier function's module, name, signature and behaviour raised reuse from 30.0% to 67.8% on OpenCode with Qwen3.7-plus. The same list cut exploration from 27.7 to 13.5 files a turn, and recall of repository functions not on the list fell from 55.5% to 35.1%. The list also told the agent to reuse, and the paper cannot fully separate the two.

Test: does your AGENTS.md or CLAUDE.md say where date, money, retry and API helpers live, and is that list current?

Fix: a short map plus the rule to keep it current, and keep the search rule, because the map makes the agent search less for anything it leaves out:

## Shared code
- Date, money and retry helpers live in src/lib/. API calls go through src/api/client.ts.
- Before writing a new function, search the codebase for an existing one that does the same job. Name what you found, and write a new one only if none fits, saying why.
- When you add a shared helper, add it to this list in the same commit.
- To rename a function, update every caller. Do not leave a wrapper.

The second line borrows the thread author's wording, "search the codebase for an existing one that does the same job". The author builds a browser extension that stores such prompts.

The helper dropped out of context

The fourth cause is context loss: the agent read the helper earlier, then compaction or a new session dropped it. After compaction, Claude Code re-reads the project-root CLAUDE.md but only up to five of the files it read or edited, most recently modified first, and path-scoped rules wait until a matching file is touched again (docs). The compaction post covers what else goes.

Test: a compaction event, or a fresh session, sits between the read and the write.

Fix: keep the map in the root file, which survives compaction, and start long tasks in a new session with the helper named.

Check a session for duplicates with jscpd and a review prompt

A duplicate check after the session catches the twins every rule misses. jscpd v5 compares uncommitted changes against the whole project and marks clones the baseline does not have; in CI, --baseline-from-ref origin/main does the same for a branch (README):

jscpd --changed --fail-on-new-clones .   # exit code 1 when the session added a clone
jscpd --similarity src/                  # function-level matches, catches renamed copies

A new clone prints as Clone found (javascript) [NEW] with both file ranges, then Found 1 clones (1 new). A twin rewritten from scratch can still pass, so add a review prompt in a fresh context: list every function added this session, search for existing functions that do the same thing, and show each match. In Claude Code, /simplify runs four review agents at once, one of them on reuse of existing helpers (commands).

Agents patch the line that threw, not the line that made the bad value

The same pattern produces symptom fixes: given a stack trace, the agent adds a null check or a try/except where the error surfaced, the tests pass, and the bad value is still created upstream. A thread of 27 September proposed rules for this, and a study of SWE-bench Verified found 29.6% of plausible patches behaved differently from the developers' fix, though not all of them were wrong.

The rule that works for commenters is a trace before the edit, plus an exception one of them hit when the rule leaked into review tasks:

## Bugs
- Before editing, trace the bad value to where it is created and name that file and line.
- If the fix is a null check, default or try/except at the line that threw, explain why the cause is not upstream. Validate input from users and APIs at the boundary.
- For review feedback, change only what was pointed out and list anything else you noticed.

OpenAI Codex already ships a root-cause line in its default instructions, and symptom patches are still reported, so the trace has to be a step the agent shows, not a value it is told to hold.

Does telling an agent it is a senior engineer change the answer?

A "you are a senior engineer" line changes tone more than correctness, in most measurements. Across 162 roles and 2,410 MMLU questions on nine open models, no persona did significantly better than none (Zheng et al.). In PRISM, expert personas cut MMLU accuracy from 71.6% to 68.0% at best on 7-8B models, though reasoning-distilled models gained on coding from any persona.

The Reddit thread of 24 September came to the same view from a handful of side-by-side runs, which its author conceded were not blind. Its replacements: ask for the questions an experienced reviewer would ask first, and describe the audience instead of the speaker, which Zheng et al. found slightly better.

Put each rule in the layer that can hold it

Rules in AGENTS.md or CLAUDE.md are context, not enforcement: Claude Code's memory docs say to use a hook to block an action regardless of what Claude decides, and to keep the file under 200 lines. The map, the search rule and the trace rule fit there because they are short. The duplicate check belongs in CI or a review step, where it runs whether or not the agent remembered. Cursor reads AGENTS.md too, and its .cursor/rules files can scope a rule to matching paths (docs). The layers post sorts the rest for the AI coding tools developers list most.

If you see this, check that, fix it this way

You see Check Fix
A new helper beside an existing one, no search in the transcript Grep or read of the helper's file before the write Name the helper in the task; plan lists functions to reuse
A twin written after the helper's file was read The read in the transcript jscpd --changed --fail-on-new-clones, review prompt or /simplify
Twins of anything in shared folders Does AGENTS.md map shared code? Add the map and the same-commit update rule
A twin after a long session Compaction or new session between read and write Map in the root file; fresh session with the helper named
A null check at the line that threw Where the bad value is created Trace rule, with the boundary and review exceptions
A confident answer that is still wrong A role line in the prompt Ask for the reviewer's questions; describe the audience

Which of these do you use?

Nobody on Stackness runs mini-SWE-agent yet. Tick what you use and be the first.

Lonny skating the lakeside path, kickflipping onto a bench, grinding it and flipping offStackness gamesPlay Lonny Eagle Amateur Skater 4A pixel art skateboarding game. Signing up opens it straight away.+2 secret games you unlock as you goSign up to play