AI coding

How to Keep an AI Agent from Rewriting Working Code

Antonio Evans6 min read

A developer lowers a glass dome over a finished, glowing machine while gently steering a busy robot with a wrench toward an unfinished part beside it.
Key points

Key points

  1. 1Over-editing is when an agent changes more code than the task needs, and tests do not catch it because the rewritten code usually still works.
  2. 2An explicit instruction to preserve the original code reduces over-editing, and a rule that every changed line must trace back to the request gives reviewers a clear test.
  3. 3Instructions are not a guarantee, so declare which files a task may touch and how large its diff may be, and enforce that with a hook or a CI check.
  4. 4Give the agent a parking lot for improvements it notices, and use branches, small commits and diff review so any unwanted rewrite is easy to see and undo.

What over-editing is

You ask an agent to fix an off-by-one error. It fixes the error. It also renames two variables, adds input validation, extracts a helper function, reformats the file and converts a callback to async/await. Every test passes. The diff is enormous.

A researcher who studied this behavior calls it over-editing: "a model is over-editing if its output is functionally correct but structurally diverges from the original code more than the minimal fix requires" (Coding Models Are Doing Too Much). The study corrupted a set of programming problems in small, controlled ways, such as flipping a comparison operator, so that the correct fix was known to be tiny. It then measured not only whether models fixed the bug but how much else they changed. The answer to "do models over-edit?" was "Yes, even frontier ones."

The same post explains why this matters more than it seems. Over-editing "is invisible to test suites," because the rewritten code usually works. The cost lands on people instead. "Code review is already a bottleneck," and a model that rewrites whole functions, even correctly, "makes this job dramatically harder as the code is now completely unrecognizable."

A developer who runs autonomous coding agents described three concrete costs in a write-up on scope creep (diff budget write-up on DEV):

  1. Review cost explodes. A five-line fix takes a minute to check. A twelve-file diff with the fix buried in the middle takes real attention.
  2. Parallel agents collide. Two agents rarely fight over their assigned tasks. "They fight over the shared utility file both decided to 'tidy up.'"
  3. Rollbacks get messy. Reverting a bad fix should not also revert an unrelated refactor that later work now depends on.

Why agents rewrite code that works

The cause is not disobedience. The same write-up puts it well: "The agent doesn't experience its cleanup as 'unrelated.' From inside the task, renaming a confusing variable feels like part of doing a good job." Its first lesson learned is that "scope creep is a virtue in the wrong place."

The over-editing study draws a useful distinction between green-field work, building something new, and brown-field work inside an existing codebase. In brown-field work, "the existing code has been understood by the team and has been deliberately written the way it was" (Coding Models Are Doing Too Much). An agent that treats every task as a chance to write the code its own way is doing green-field work in a brown-field codebase.

The Scope Guard project describes the same pattern from the user's side: "'Add email OTP login' becomes a 12-file authentication framework," and "'Fix this type error' somehow requires restructuring half your app" (Scope Guard).

Three things make it worse:

  • Vague tasks. "Clean up the auth module" invites a rewrite. "Fix the null check in session.ts line 40" does not.
  • No stated scope. If the task does not say which files are in play, every file is.
  • Long sessions. The longer the agent works, the more code it reads, and the more it finds to "improve."

Say it in the prompt

Instructions help, and there is evidence for it. In the over-editing study, the researcher added one line to the prompt: "IMPORTANT: Try to preserve the original code and the logic of the original code as much as possible." With that line, "every model improves and reduces its Levenshtein Distance," a measure of how much the code changed, and most models also fixed more bugs correctly (Coding Models Are Doing Too Much). The author suggests that asking for a minimal edit "narrows the search space of possible fixes," steering the model toward the precise change that is more likely to be right.

A widely shared set of coding guidelines, packaged as a skill and based on Andrej Karpathy's observations about common model mistakes, turns this into rules you can put in a project context file (Karpathy guidelines skill):

  • "Don't 'improve' adjacent code, comments, or formatting."
  • "Don't refactor things that aren't broken."
  • "Match existing style, even if you'd do it differently."
  • If you notice unrelated dead code, mention it rather than delete it.
  • Remove only the imports, variables and functions that your own change made unused.

Its test for the whole diff is the one to remember: "Every changed line should trace directly to the user's request."

A minimal-change block for a project context file:

## Editing existing code
- Make the smallest change that completes the task.
- Do not rename, reformat, reorder or restyle code you were not asked to change.
- Do not refactor working code. If you think a refactor is needed, say so in
  your summary and stop.
- Every changed line must trace back to the task. List any exception in the
  summary with a reason.

These lines help. They are not enough on their own.

Fence the task with a scope and a diff budget

The developer who wrote about diff budgets tried the instruction first: "Only change what the task requires. Do not refactor unrelated code." It "helped a little, and not nearly enough." The conclusion: "Instructions describe intent. If you need a guarantee, you need a mechanism" (diff budget write-up on DEV).

The mechanism has two parts.

A scope and budget per task. Each task declares which paths it may touch and how large the change may be:

{
  "goal": "Fix off-by-one in pagination helper",
  "scope": {
    "allowed_paths": ["src/pagination/**", "tests/pagination/**"],
    "max_files": 4,
    "max_changed_lines": 80
  }
}

Budgets are set by task type, not by gut. Bug fixes get a tight budget, feature work a wider one, and refactors are their own task type with an explicitly large budget. As the author says, the goal is not to ban refactoring but to stop "refactoring smuggled inside something else."

Enforcement before and after the edit. A pre-edit hook in the agent tool checks each file write against the allowed paths and blocks anything outside them. A second check on the final diff enforces the file and line limits before the change is committed or handed to review. If your tool has no hooks, a CI job that compares the changed files against the task's declared paths does the same job later in the pipeline.

The detail that makes this work is the message the agent sees when it is blocked. The author's second lesson: "Block with an explanation, never silently." A bare "denied" makes the agent try the same thing another way. A message that explains the two options, request a scope extension with a reason or write the idea down for later, "gets a sensible next move almost every time." In the author's words, "The error message is a prompt. Write it like one."

Give the agent a parking lot

An agent that notices a real problem outside its task has found something useful. Blocking it outright throws that finding away. The answer is a parking lot: a file where the agent writes down improvements it noticed instead of making them.

The diff budget write-up calls this the piece "that made everything else work." Its third lesson: "Every constraint needs an answer to 'okay, then what do I do with this?' Without the parking lot, I was throwing away real findings. With it, the constraint became a routing rule instead of a muzzle" (diff budget write-up on DEV).

A simple version:

## Parking lot (append, do not act)
- src/pagination/helpers.ts: `getPage` and `pageOf` duplicate logic. Candidate refactor.
- src/api/users.ts: no input validation on `limit`. Possible bug.

Review the parking lot regularly and turn the good entries into their own tasks, each with its own scope and budget. The Karpathy guidelines express the same idea for a single session: unrelated dead code gets mentioned, not deleted.

Use git as the safety net

Prompts and fences reduce over-editing. Git makes whatever slips through visible and reversible. Espressif's developer workshop on agent-assisted coding lays out a simple routine (Espressif):

  1. Start from a known state. Run git status and git diff before a task, and commit or set aside existing changes. This "prevents a new task from becoming mixed with unfinished work."
  2. Use a branch per task. Keep the default branch stable.
  3. Review the complete change before committing. "Check that every changed file belongs to the task."
  4. Commit after each verified step. "Small commits make it easier to review the agent's work, compare behaviour between steps, and recover from a later mistake."

The workshop also suggests letting the agent help with the review, without letting it commit:

Review the current Git diff. Identify any changes unrelated to this task,
summarise the remaining changes, and propose a concise commit message.
Do not stage or commit anything.

When you run more than one agent at a time, give each its own git worktree. The Nx team describes worktrees as a way to have several branches checked out in separate folders at once, so parallel agents do not edit the same working copy (Nx). Separate working copies keep one agent's cleanup from landing in another agent's task.

Review for over-editing

Even with all of the above, review is where over-editing is finally caught. Read the diff with one question in mind: does this line need to change for the task to be done?

[ ] Every changed file is inside the task's declared scope
[ ] No renames, reformatting or reordering outside the lines being fixed
[ ] No helper functions, options or validation that the task did not need
[ ] No changes to unrelated tests, config or dependencies
[ ] The diff is within the budget for this task type
[ ] Anything the agent wanted to improve is in the parking lot, not the diff

When a diff fails the check, do not trim it by hand. Send it back with a specific instruction, such as "Revert all changes outside src/pagination/ and keep only the fix to the loop bound." Then record the lesson where the agent will see it next time. One commenter in a Hacker News discussion of the over-editing study described doing exactly that: explaining the mistake, having the agent fix it, then asking it "to record what it learned in the relevant project-specific skills" (Hacker News).

Over time, the project context file, the task budgets and the parking lot do most of the work, and review becomes a quick confirmation instead of an excavation.

FAQ

FAQ

Is it ever right for an agent to refactor while fixing a bug?

Only when the fix cannot be made safely without it, and then it should say so before doing it. Treat that refactor as its own task with its own scope and review. Mixing a refactor into a fix makes both harder to review and to revert.

Will a stronger instruction like "NEVER change other code" fix this?

It will help somewhat but not reliably. Evidence from a study of over-editing shows a calm, explicit preservation instruction reduces the problem, but the agent still sees cleanup as good work. Back the instruction with a declared scope and an enforced check.

How do I choose a diff budget?

Set defaults by task type rather than one global number. Bug fixes get a tight limit on files and lines, features a wider one, and refactors their own larger budget. Adjust when one area keeps needing extensions, because that may point to a design problem.

What if the agent says the out-of-scope change is required?

Let it ask for a scope extension with a one-line reason, and decide yourself. Sometimes the reason is real and the task was scoped too tightly. Sometimes the agent is rationalizing a cleanup, and the parking lot is the right place for it.

Do tests protect against over-editing?

Not directly. Over-edited code usually passes the tests, which is why the problem is easy to miss. Tests protect behavior, while scope rules, diff limits and review protect the shape of the code your team understands.

Sources

Sources

  1. https://nrehiew.github.io/blog/minimal_editing/
  2. https://dev.to/yureki_lab/how-i-stopped-my-ai-coding-agents-scope-creep-with-a-diff-budget-47lj
  3. https://github.com/andreahlert/scope-guard
  4. https://github.com/multica-ai/andrej-karpathy-skills/blob/main/skills/karpathy-guidelines/SKILL.md
  5. https://developer.espressif.com/workshops/ai-agent-coding-for-esp-idf/optional-git-workflow/
  6. https://nx.dev/blog/git-worktrees-ai-agents
  7. https://news.ycombinator.com/item?id=47866913
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles