AI coding agents are excellent at producing plausible code quickly. They are also excellent at producing plausible bugs, wrong abstractions, and "fixes" that break three neighboring files. The gap between demos and production is not model IQ. It is process.

I treat Cursor-style agents and Copilot-style completions the same way I treat a very fast junior engineer: useful, tireless, and completely willing to invent something that looks right. Your job is not to type less. Your job is to control context, demand proof, and reject confidently.

This post is a working playbook. No tool wars. Just habits that keep velocity without filling the repo with garbage.


1. Context is the real interface

Agents do not "understand your codebase." They pattern-match over whatever you put in front of them. Bad context produces confident wrong code. Good context produces boring, shippable diffs.

Feed the agent the smallest honest package:

  1. The goal in one sentence. "Add idempotent retry to the payment webhook handler for 5xx from Stripe." Not "improve error handling."
  2. The files that own the behavior. Handler, service, existing retry utility, and the test file. Not the whole module tree.
  3. Local conventions. How you name packages, how you log, how you structure errors, which HTTP client you use.
  4. Constraints. "No new dependencies." "Match existing retry policy." "Do not change the public API."
  5. Definition of done. "Unit tests cover duplicate event IDs and network timeouts. Existing webhook tests still pass."

If your prompt is vague, the agent will invent architecture. That invention is where most garbage starts.

Repo-level instructions help, whether that is Cursor rules, a project instruction file, or a short CONTRIBUTING snippet the agent always sees. Keep those rules short and operational:

  • Prefer existing helpers over new utilities
  • Do not rewrite unrelated files
  • Match the current error and logging style
  • Never silence failing tests by deleting them

Long manifesto-style rules get ignored. Short hard rules stick.


2. Manage the context window like a scarce resource

Context windows look huge until you paste half a monorepo into them. Once the window fills with noise, the model drops the important lines: your invariant, your edge case, the one function that already solved this.

Practical window hygiene:

  • Start a new chat for a new task. Thread history from yesterday's refactor will bias today's bugfix.
  • Prefer file references over huge pastes. Point at the real file so the agent sees current content, not a stale snippet.
  • Summarize decisions, not transcripts. When a chat gets long, write a short "so far" note and open a clean session with that note plus the active files.
  • Cut dead ends. If the agent went down a wrong path for three turns, do not keep stacking corrections. Restart with a clearer constraint.
  • Do not dump entire logs. Paste the failing assertion, the stack frame that matters, and one representative request payload.

A useful mental model: every token you include is competing with the tokens that explain the bug. Protect those.


3. Write the contract before you let the agent code

The highest leverage move with agents is still old-school: define behavior first.

Test-first with an agent looks like this:

  1. You write or sketch the failing test (or the type/interface) that captures the behavior you want.
  2. You ask the agent to make the test pass with the smallest change.
  3. You run the suite yourself. Do not trust the agent's "all green" claim until you see it.
  4. You review the diff for scope creep, then commit.

Why this works: tests and types shrink the search space. The agent is not inventing product requirements mid-diff. It is solving a closed problem.

Good agent tasks:

  • Implement a function that already has table-driven tests
  • Migrate a call site to a new API while keeping tests green
  • Add a null check path covered by an existing fixture
  • Generate boilerplate that your project already has three examples of

Bad agent tasks (until you tighten them):

  • "Build auth"
  • "Clean up this module"
  • "Make it more scalable"
  • "Fix the flaky test somehow"

If you cannot state the expected inputs, outputs, and failure modes, the agent cannot either. It will still produce code. That is the problem.


4. Use a review loop, not a vibe check

Reading AI output like a PR from a stranger is the whole job. Speed comes from short loops, not from skipping review.

A review loop that holds up in real repos:

  1. Diff scope first. Did it touch files outside the task? Revert those immediately. Unrelated "cleanup" is where subtle regressions hide.
  2. Read for intent, not style. Formatting can be fixed by tooling. Wrong control flow cannot.
  3. Trace the happy path by hand. Follow one request from entrypoint to return. Does the data shape match what callers expect?
  4. Force the failure path. Timeouts, empty lists, partial writes, duplicate events, unauthorized callers. Agents under-generate these.
  5. Run the narrowest honest test set, then a wider suite if the change crosses boundaries.
  6. Only then ask the agent for a second pass: "Here is what failed. Fix only that. Do not refactor."

Never accept multi-file rewrites because the first attempt almost worked. Prefer surgical patches.

Smell tests that save you:

  • New abstraction with one call site
  • Copied utility that already exists two folders away
  • Broad try/catch that returns null or empty success
  • "Temporary" flags with no removal plan
  • Comments that narrate the code instead of explaining a non-obvious constraint
  • Config changes with no explanation in the PR description

If two of those show up, the agent is improvising. Slow down.


5. When to reject AI code outright

Rejecting is a skill. Half-accepting bad structure and "just polishing it" often costs more than rewriting.

Reject and restart when:

  • The agent cannot explain why the change is correct in plain language
  • The fix depends on a library or API that does not exist in your project (or in reality)
  • It solves a different problem than the one in the ticket
  • Tests were edited to match broken behavior instead of fixing the code
  • Security-sensitive paths changed (auth, crypto, payments, tenancy) without a careful human design pass
  • The diff is large and you cannot hold the full behavior change in your head

Rewrite yourself when:

  • The domain logic is subtle and you already know the design
  • The change is ten lines and the agent keeps proposing a framework
  • You are learning the area and need the mental model more than the speed

Keep the agent when:

  • The pattern is repeated (CRUD endpoints, mappers, test tables)
  • Mechanical migrations with a compiler or test suite as a backstop
  • Drafting docs, commit messages, or review checklists from a known diff
  • Exploring two implementation options side by side for a small, well-scoped spike

Agents are accelerators for known shapes. They are poor substitutes for design ownership.


6. A daily workflow that does not ship garbage

Here is a loop I actually use on non-trivial work:

  1. Clarify the change in a short note. Goal, non-goals, files, risks.
  2. Lock the contract. Failing test, type, or API sketch first.
  3. Open a clean agent session with only the relevant files and the note.
  4. Ask for the smallest patch, not a redesign.
  5. Run tests and linters yourself.
  6. Review like a hostile code review. Scope, edge cases, security, performance.
  7. Either accept, request a targeted fix, or discard. No sunk-cost loyalty to a bad thread.
  8. Commit with a human-written message that says what and why. If you cannot write that message cleanly, you do not understand the change yet.

For tiny completions (rename, obvious null guard, test name), inline Copilot-style suggestions are fine. For anything that crosses a module boundary, switch to the fuller agent loop above.


7. Team habits matter more than personal tricks

Individual skill helps. Shared defaults keep the codebase healthy when five people all use agents differently.

Worth standardizing:

  • Required tests for agent-assisted PRs in the same areas you already require for human PRs
  • No drive-by refactors in feature branches, human or AI
  • Secret and prod access never pasted into prompts
  • A short project rules file checked into the repo
  • Reviewer checklist item: "Was this AI-assisted, and were failure paths exercised?"

You do not need a policy novel. You need agreement that speed does not excuse unreviewed diffs.


Bottom line

AI coding agents raise the ceiling on how fast you can produce candidate code. They do not raise the ceiling on how much unreviewed code your system can absorb.

Own the context. Write the contract first. Review like the author is clever and untrustworthy. Reject without guilt. Keep the agent on mechanical, well-bounded work and keep design decisions human.

Do that, and agents stop being a source of quiet production debt. They become what the demos promised: a sharp tool with a responsible operator.