The question we field most often is not whether Claude Code can write code. It can. The question is whether it can be trusted inside a repository it did not create: eight years of accumulated decisions, a service layer that is half documented, and three competing ways of doing the same thing because three different people solved it in three different quarters.
Our answer is a qualified yes, and the reason has less to do with raw model capability than most people assume. What makes an agent useful in a mature repository is that it reads before it writes. It walks the project structure, greps for existing patterns, opens neighbouring files, and runs the test suite you already have. That behaviour, applied consistently, is the difference between a change that looks like it belongs and a foreign-looking file dropped into your repo.
This article covers the workflow that actually moves output quality in an existing or legacy codebase: discovery, a written technical implementation plan, Plan Mode, a CLAUDE.md that earns its tokens, task decomposition, executable tests, deliberate context resets, review, and explicit non-goals. Part two covers the harder ground: our Patch Rewrite method for merge conflicts, and the git safety rules we put around agents.
Why an Existing Codebase Is a Different Problem
Greenfield code has no conventions to violate
When an agent starts a project from nothing, every decision is internally consistent because the agent made all of them. There is nothing to contradict. A mature codebase is the inverse. It is a dense stack of prior decisions, some deliberate and some accidental, and the agent has to infer which is which from evidence that is often ambiguous.
The failure mode is plausible-but-wrong, not broken
Code that fails loudly is cheap to catch. The expensive failure in an existing codebase is code that runs, passes a shallow test, and quietly violates an architectural rule nobody ever wrote down. That is the specific outcome your workflow should be designed to prevent, and it is why review discipline matters more here than in a fresh project, not less.
Legacy repositories carry undocumented business logic
Old code frequently encodes rules that exist nowhere else: a rounding quirk that matches a client invoice, a nullable column that three downstream jobs depend on. Reading and mapping that kind of code is genuinely useful agent work, and SitePoint's hands-on legacy refactoring walkthrough is a reasonable illustration of the shape of that work, though as with any single write-up, treat the specific results as one team's experience rather than a benchmark.
Start With Discovery, Not With a Prompt
Before we ask for any change, we ask for a map. Discovery is cheap, reversible, and it surfaces the disagreement between what you assume the codebase does and what it actually does.
A discovery pass we find useful looks like this:
Read the repository and answer in writing, no code changes: 1. What are the top-level modules and what does each own? 2. Where does HTTP request handling end and domain logic begin? 3. List every distinct pattern used for database access, with file examples. 4. How are errors surfaced to the caller? Show two contrasting examples. 5. Which tests run fastest and give the broadest coverage? 6. List five things in this repo that look deprecated or half-migrated.
Run the equivalent checks yourself so you can verify the answer rather than trust it:
git shortlog -sn --since="18 months ago" git log --oneline --since="6 months ago" -- src/ | wc -l rg -n "class .*Repository" --stats rg -n "TODO|FIXME|DEPRECATED" --stats
Two outcomes matter here. First, question six almost always produces the list of patterns you need to forbid later. Second, if the agent's map is wrong, you have learned that for the price of one read-only session instead of one bad pull request.
Turn the Request Into a Technical Implementation Plan
A product requirement is written for humans, who fill gaps with tacit knowledge. It is not an implementation spec. Practitioners working with coding agents at scale broadly converge on separating three artefacts, and it matches what we see in client work:
- Product intent. What the change is for and who it serves.
- Technical design. Data model changes, API contracts, migration strategy, non-functional constraints.
- Implementation plan. Phased, ordered, task-level, sized to fit a single working session.
The second layer is the one teams skip, and it is the one that contains the decisions a requirements document was never meant to carry: how authentication is enforced on the new route, what the failure semantics are, what the tests must assert. If you hand an agent layer one and expect it to reconstruct layers two and three silently, it will produce something plausible, because producing something is what it is optimised to do.
Write the plan into the repository, not into a chat window. A file such as docs/plans/2026-08-orders-export.md survives context resets, can be reviewed in a pull request, and can be handed back to the agent verbatim tomorrow.
Plan Mode Is the Single Highest-Leverage Habit
What Plan Mode does
Plan Mode, toggled with Shift+Tab in the Claude Code terminal, changes the order of operations. Rather than reading your request and editing files, the agent analyses the requirement, proposes a multi-file strategy, and shows it to you before touching anything. You approve, amend, or reject it. Anthropic's own engineering guidance on Claude Code covers this planning-first posture and related workflow advice; interface details change between releases, so confirm the current shortcut against the docs and the in-session help before you standardise on it for a team.
Why the ordering matters more than it sounds
This is worth understanding rather than just following. A language model generates output token by token, conditioned on everything that came before. If the first artefact generated is a file edit, that edit becomes the context for every subsequent decision, including the wrong ones. If the first artefact is a plan, the plan becomes the context instead. You are not making the model smarter. You are changing what it reasons from.
It is also an economics argument. Rejecting a plan costs you a paragraph of reading. Rejecting an implementation costs you a diff across six files and a judgement call on every hunk.
When to skip Plan Mode
Single-file, obviously scoped work: renaming a local variable, adding a log line, correcting a string. Planning overhead on trivial changes is how teams learn to resent the habit and abandon it entirely. Reserve it for anything touching multiple files, anything near auth, billing, migrations, or anything where two reasonable approaches exist.
A model selection note, with caveats
A common community heuristic is to plan with a stronger reasoning model and execute with a faster one, on the grounds that planning rewards depth and execution rewards throughput. We think it is a sensible default to test rather than a law. Model naming, pricing, and capability shift frequently, so benchmark it on one real ticket in your own repository before you make it policy.
CLAUDE.md: Write Down What Your Codebase Assumes
CLAUDE.md sits in your repository and is read as project context. The useful content is not a description of your product. It is the set of rules a competent new hire would get wrong on day one.
# Project conventions ## Commands - Install: pnpm install - Test: pnpm test - Single test file: pnpm test -- path/to/file.test.ts - Lint: pnpm lint - Typecheck: pnpm typecheck ## Architecture rules - Domain logic lives in src/domain. It must not import from src/http. - Database access goes through repositories in src/data. No raw SQL in handlers. - Errors: throw typed AppError subclasses. Never return null to signal failure. ## Deprecated, do not extend - src/legacy/billing/* is frozen. Add new billing code in src/domain/billing. - The LegacyMailer class is being retired. Use NotificationService. ## Off limits - Do not edit db/migrations that are already applied. - Do not modify .github/workflows, infra/, or any *.lock file. ## Definition of done - pnpm test, pnpm lint and pnpm typecheck all pass. - New behaviour has a test that fails without the change. - No new runtime dependency without asking first. ## When unsure - If a requirement is ambiguous or two patterns conflict, stop and ask for direction rather than choosing an approach.
What does not belong in it
A general product description. The agent can read your code. What it cannot read is your team's unwritten agreements, and every token spent restating the obvious is a token not spent on the rules that prevent rework. Keep the file short enough that you would actually maintain it.
The line most CLAUDE.md files are missing
The final section above. Explicitly permitting the agent to stop and ask is, in our reading of how these tools behave, the highest-value single instruction in the file. An underspecified task rarely produces a refusal; it produces a confident guess. Granting permission to ask converts that silent guess into a question, which is almost always the cheaper outcome. Verify it works in your setup by handing the agent a deliberately ambiguous ticket and checking whether you get a question back.
Decompose the Work Until Each Task Fits One Session
The most repeated piece of advice among experienced agent users, and the one that matches what we see, is to stop asking for whole features and start asking for slices.
Practical sizing rules we apply:
- One task should touch a small, nameable set of files and be describable in two sentences.
- Foundation first: schema and contracts before handlers, handlers before UI.
- Each task ends in a state where the test suite passes and the branch is committable.
- If a task needs more than roughly an hour of agent and human time combined, split it.
Sequence matters more than parallelism. Running three agents on three overlapping slices of the same module produces integration debt that costs more than the time saved.
Tests Are the Executable Half of the Spec
The plan describes intent. Tests decide whether intent was met. In an existing codebase you usually have an advantage here, because there is already a suite the agent can run and imitate.
Our working sequence for each task:
- Ask for the failing test first, derived from the plan and matching existing test structure.
- Confirm the test fails for the right reason before any implementation.
- Let the agent implement until the test passes.
- Run the broader suite, not just the new test.
pnpm test -- path/to/new.test.ts pnpm test pnpm lint && pnpm typecheck git diff --stat git diff package.json pnpm-lock.yaml
That last command is worth keeping in muscle memory. Silent dependency substitution is a reported failure pattern with coding agents, and a diff of your manifest and lockfile catches it in seconds.
Context Is a Budget, Not an Unlimited Resource
Every file read, command output, and prior message occupies context. As a session runs long, the plan and conventions you carefully established compete for space with a hundred lines of test output from twenty minutes ago. In our experience quality tends to drift rather than fail outright, which is precisely why it is easy to miss.
Practical consequences:
- Start a fresh session per logical task instead of one continuous session all day.
- Re-establish the plan explicitly when you resume, by pointing the agent at the plan file rather than retelling it.
- Treat a noticeable drop in output quality as a signal to reset, not to argue.
- Keep planning conversations and execution sessions in separate threads so exploratory discussion does not pollute implementation context.
We would frame the underlying mechanics carefully: degradation under long sessions is widely reported by practitioners, and the practical remedy is cheap enough that it does not require you to settle the technical explanation first.
State the Non-Goals Explicitly
Telling an agent what not to do is as load-bearing as telling it what to do. We include a short non-goals block in every non-trivial task prompt:
- Do not refactor code outside the files named in the plan.
- Do not upgrade, add, or swap dependencies.
- Do not reformat files wholesale; keep the diff reviewable.
- Do not modify tests that are unrelated to this change to make the suite pass.
- Do not touch migrations, CI configuration, or infrastructure directories.
The point is reviewability. A 40 line diff you can read beats a 400 line diff that also happens to be correct.
Review Is the Non-Negotiable Step
Every AI-generated change on client work goes through the same review as human-written code. Speed of generation changes nothing about whether a change should be read before it merges. Our review pass asks four questions:
- Does this match the approved plan, or did the scope grow?
- Does it follow the conventions in CLAUDE.md, or introduce a fourth way of doing something?
- Would the test have failed before the change?
- What happens on the unhappy path, and is that behaviour tested?
That discipline is the same one behind our vibe coding to production work, and it is the difference between faster output and faster delivery.
What Our Team Actually Does
We use Claude Code with GitHub integration on real client work, alongside Cursor with Claude Sonnet for other development. They are not competing choices. Claude Code is a terminal-native agent that operates across a whole repository; Cursor is an editor. The work each suits is genuinely different, and we pick per task rather than per team.
The Build Versus Buy Reality
Adopting an agentic coding tool well is not a licence purchase, it is a workflow change. Someone has to write the CLAUDE.md, define the review gate, and decide what an agent is and is not allowed to touch in a production repository. Teams that skip that step get faster output and slower delivery, because the review burden moves rather than disappearing.
Where We Fit
If your team already has AI-assisted development working with real review discipline, treat this as confirmation rather than a pitch. Where we help is teams that want an existing codebase modernised, or AI-generated code reviewed and hardened before it reaches production users. Thirty dollars an hour, written scope before any billing starts. See vibe coding to production and Cursor AI development.

