How do I get an AI coding agent to reuse existing code instead of creating duplicate functions?

Mira points to an open notebook beside Ivo’s laptop as they look for code the project already has.

TL;DR

When a coding agent misses the helper you already have, it writes a new one, and near-duplicates pile up that each behave slightly differently. Make finding existing code a step before any new function is planned: search by name and by behavior, point the agent at canonical examples, and require a stated reason for any new helper. Judge near-matches by their contract, then review each diff for duplicates. New helpers then arrive with a reason a reviewer can check.

Working code that ignored what already existed

In March 2026 someone whose team of eight uses Claude Code heavily asked on Hacker News how others keep agents aligned with codebase standards. Their agents wrote code that worked but ignored existing patterns, creating a new database query instead of using the service class the team had already built, and the shared CLAUDE.md and markdown docs they tried went stale quickly. They asked how everyone else was handling it.

In February 2025, a Cursor user asked on the Cursor forum how to make Cursor reuse existing code after seeing it generate duplicate code in a JPA repository. Other users replied that they saw the same thing.

Neither team lacked the code. The agent either did not find it or did not recognize it as the thing to use.

Why the agent does not find what you already have

Coding agents mostly find code by searching and reading files. Cursor's current search documentation describes a local grep-based index and an exploration subagent, and says it does not store embeddings of your codebase for search (Cursor search); Cursor staff described the older semantic index as being retired in mid-2026. Claude Code and Codex also search with text tools and exploration subagents. So an existing helper is found when the agent searches for a term that appears in it, or when you tell it where to look.

Two consequences follow. A helper with an unexpected name is effectively invisible to a search that uses the expected one. And instructions about where utilities live must reach the part of the agent doing the searching: Claude Code's documentation notes that its built-in Explore and Plan subagents skip CLAUDE.md files (Claude Code subagents), so a line there saying "money formatting lives in src/utils/money.ts" does not reach them. Put that pointer in the prompt or in a scoped rule the searching step will load.

Search by name and by behavior before planning a new function

Name search catches the obvious cases and misses the rest. To show the difference, Paqad's pinned code index and name-similarity scorer at commit 29548baf were run on a small hand-written TypeScript fixture.

So the pre-planning search has two halves. Search by name, verb and noun, for what the new function would be called. Then search by behavior: the key expression, the input and output types, the external call it makes, and the callers of anything similar. The second search is the one that catches the duplicate you would otherwise write.

Illustration of Noor in a wheeled chair pulled up beside Ivo's desk, pointing to the laptop balanced on her knee; Ivo turns from his keyboard toward her, half relieved and half embarrassed.
A search by behavior, not just by name, can turn up the helper you already have.

Judge the near-match by its contract, not its name

A flagged match is a question, not an answer. Before reusing it, check what it actually promises: the inputs and their types, the output and its format, side effects such as database writes, network calls or logging, how it reports errors, who calls it and which module owns it. Then choose one of three outcomes and write down why:

Decision guide

OutcomeWhenWhat it takes
Reuse as isThe contract already matches the new needCall it; add a test through the new call site
ExtendThe need is close and the change stays compatible for existing callersAn optional parameter or a small variant, plus the existing callers' tests
Keep separateThe contract genuinely differsA distinct, descriptive name and a one-line note on why it is not the existing helper

Extending shared code changes behavior for code you are not touching, so that choice belongs to whoever owns the module, not to the agent alone. The compact currency format from the table above is a good example: adding an option to formatCents touches three callers, while a separate formatCentsCompact adds a second formatter. Either can be right; it should not be decided silently.

Paqad makes that fork an explicit step. When a plan is compiled, each planned new construct is scored against its code index, and a match at or above the threshold opens a create-versus-reuse decision carrying the existing symbol's file, last-modified date, caller count and similarity; the recommendation is reuse once the existing symbol has three or more callers. That happens under the strict setting; under the default setting the fork is reported without pausing. The match is still a candidate: the index has to be current, the scorer reads names, and its coverage is fullest for TypeScript and JavaScript. Paqad's planning documentation describes the create-versus-reuse decision and its settings.

Illustration of Ivo leaning on a high table in an office kitchenette, counting on three fingers as he makes his case, while Mira, cradling her mug, weighs it before deciding.
Extending shared code changes it for every caller, so the module's owner makes that call.

Point the agent at the canonical examples

The cheapest reuse is the one the agent does not have to discover. Claude Code's best practices show the pattern with a before-and-after prompt: instead of asking for a new widget, point at an existing widget file, say to follow its pattern, and use only libraries already in the codebase (Claude Code best practices). Its example prompt for an investigation subagent asks whether there are existing utilities to reuse. Codex's best practices recommend naming the relevant files and conventions in the prompt's context and constraints. Cursor's rules documentation advises pointing to canonical files rather than copying their contents into rules (Cursor rules).

For durable pointers, use rules scoped to the folders where they matter, a Claude Code rule with paths:, a nested AGENTS.md or a Cursor rule with globs, with lines like "for money formatting, use formatCents in src/utils/money.ts". Keep the list short and current. Paqad generates a reuse catalog from its index, listing the most-called exported symbols per module, and regenerates it when the index is rebuilt, which is one way to keep such a list from going stale. When the agent does not stay within the files it was asked to change, the problem is scope rather than reuse, and keeping a small fix in scope covers how to declare and check it.

Review for duplicates and test the reused path

A review step catches what the search missed. Claude Code's /simplify runs review agents in parallel, one of whose areas is reuse of existing helpers, and its local /code-review also reports reuse cleanups (Claude Code commands). Codex's /review and Cursor's Bugbot and Agent Review accept custom review instructions but do not document duplicate detection, so write the check into their instructions yourself (Codex code review, Cursor Bugbot).

Whatever runs the review, three checks do most of the work:

  • Every new exported function in the diff gets a search. Name and behavior, as above.
  • Reuse is proven by a test through the new call site. If the agent extended a shared helper, the existing callers' tests run as well.
  • Reuse does not cross a boundary it should not. Calling another module's internal helper to avoid three lines of code can create coupling that is worse than the duplicate.

If the agent is new to the codebase, the article on starting a coding agent on an existing codebase safely helps you pick a first task whose impact you can contain.

Frequently Asked Questions

Ask for the search result before the plan: have the agent list the closest existing functions and say why each does or does not fit, then approve the plan. A justification written before the code exists is easier to check than a duplicate found in review.

Search for the expression at the heart of the new function, not only its likely name. In the fixture above, a search for the cents-to-string conversion found formatCents immediately, while the name score for centsToDisplayString was 0.33.

Read the function's tests along with the function itself. Tests show the formats and edge cases the owners rely on, which is where a near-match usually differs; a difference the new use cannot accept rules it out as it stands.

Extending is safer when the old behavior stays the default and existing callers' tests cover it. Because the change reaches code outside the task, the module's owner or a reviewer who knows it decides, not the agent alone.

A test through the new call site, the existing callers' tests passing unchanged, and an import check: if the reuse adds a dependency on another module's internals, move the helper to a shared location or keep the separate function.