AGENTIC CODING IN PRACTICE, 2 OF 3
How We Keep the Agent Inside the Lines

When we went back through our agent sessions on one project and sorted them by what the agent was doing, the surprise wasn’t what went wrong. It was where the time went. 51% of the sessions were reactive bug fixing and repetitive pull request (PR) triage. 15% were the feature itself.

That number is the argument for this article. Faster code generation doesn’t make delivery faster or safer on its own. Andrej Karpathy draws the line between casual vibe coding and something more rigorous he calls agentic engineering, and once the marginal cost of producing code drops, the whole problem becomes making that output turn into product progress.

At Amplified the agent does its best work inside a structured workflow. It cuts repetitive work, helps with debugging, raises test coverage, and gets us around large codebases. We don’t ask it to infer missing requirements, own the architecture, or ship anything unreviewed. This article is the playbook we built for that: what we don’t believe, the loop we run, where the friction comes from, and the files that keep the rules from living in one person’s head.

4 things we don't believe

We don’t believe agentic coding means handing a feature to the agent and trusting it end to end.

We don’t believe teams should measure success by how much code the agent produces.

We don’t believe agent-written code gets to skip review, testing, security checks, or architectural judgment.

And we don’t believe better prompting alone solves it. Prompting matters, but it’s 1 part of the workflow. The agent also needs clear task boundaries, reliable context, project rules, test expectations, and permission limits. When our team’s retro found that PR review and comment triage made up 28% of all conversations, the fix was 1 structured “address all reviewer feedback” session in place of a pile of near-identical ones, and no prompt would have done that on its own.

51% of the sessions were reactive bug fixing and PR triage. 15% were the feature.

The loop we run

The best workflows we’ve seen are collaborative in a specific way: a person defines the intent, the agent accelerates execution, and a person validates the result. Ours has 6 beats.

Six numbered steps in a vertical flow: define the task and get grilled, provide context and constraints, explore and propose a plan, review the plan, implement and check, then human review before CI and QA.

The loop we run on every non-trivial change

  • Define the task, then get grilled: the engineer states the goal and lets the agent interview them for edge cases
  • Context and constraints: relevant files, acceptance criteria, and what must not change
  • Explore and plan: the agent reads the codebase and proposes a scoped plan
  • Review the plan: the engineer adjusts it before a single edit
  • Implement and check: the agent makes the change, runs tests, lint, and build
  • Human review, then CI and QA: the engineer reads the diff; the team validates before merge

‍

The first beat is the one people skip. Instead of the passive “specs to code” trap, we run what Matt Pocock calls a grilling session: the agent interviews the engineer, relentlessly, to surface the edge cases before any code exists. 

Beats 2 and 3 are where context lives: the files that matter, the acceptance criteria, and the plan the agent proposes after reading the codebase. Beat 4 is a person reading that plan and changing it.

Beat 5 is execution, the only beat the agent owns: the scoped change, then tests, lint, and build. Beat 6 is human review, then CI and QA before anything merges. Maggie Appleton confirms affiliation makes the case for moving this collaboration into shared environments where product managers, designers, and developers can correct the agent together, in real time, instead of discovering the drift at review.

Kent C. Dodds has the image for what this does to the job: developers used to be judged on their aim, and agents are homing arrows, so the scarce skill is deciding exactly what to build. The bottleneck moves from writing every line to defining tasks, reviewing diffs, and understanding what a change does to the rest of the system.

Where the friction comes from

Agent-written code can look right and still be wrong in ways that pass compilation. We’ve seen flawed logic under a confident comment, invented packages, API parameters from 2 versions ago, and race conditions from naive state updates in concurrent code.

Without guardrails, the agent takes the path of least resistance through the architecture. It’ll put a SQL query in a React component because that’s where the data was needed, or write persistence code in an API router because the domain layer was 2 files away. It applies monolithic assumptions to distributed systems, so it imports a database client from another repo instead of calling the service, alters a shared payload to satisfy a local need, and breaks a downstream consumer nobody told it about.

Tests are the quiet failure. When the same model writes the implementation and its tests, a misunderstanding in the code becomes a test that confirms the misunderstanding. The suite goes green. We found a test that asserted a mocked configuration equaled itself, which is a test of the mock, not of anything we ship.

Five stacked cards naming the root causes found in a retrospective on a user roles and permissions feature: guessed component APIs, security defaults that failed open, missing state handling, tautological tests, and ambiguous specification.

What 17 bugs in 1 feature traced back to

  • Guessed component APIs: props the agent invented instead of reading; 6 build failures
  • Security defaults: middleware and checks that failed open
  • Missing state handling: loading, empty, and error states skipped
  • Tautological tests: tests that confirmed the implementation’s own assumptions
  • Ambiguous specification: a gap the agent filled with a guess

‍

In team’s retrospective on a user roles and permissions feature, 17 bugs sorted into 5 root causes: component APIs the agent guessed instead of checked, security defaults that failed open, missing loading and empty and error states, tautological tests, and places where the specification was ambiguous and the agent picked an answer. 6 build failures alone came from the guessed props. 5 of the 17 were repeats of a failure we had already seen once.

Guessing the component API is the one we fixed first, with a skill that forces the agent to read the actual component exports before it writes JSX. The build failures from invented props stopped.

Security defaults got a mandatory review step for anything touching authentication, and “fail closed” became a written constraint rather than an assumption.

Tautological tests have a rule that the agent explains what each test verifies and what real failure would make it fail, before it writes the test file.

So the inputs the agent needs are the ones a new engineer would need, written down: a design doc, a source-of-truth hierarchy, the security failure modes, the component API references, a test strategy, and a production readiness checklist. Without them the agent fills the gaps with assumptions, and some of those are expensive.

5 workflows with edges

A workflow that’s useful on a real project has edges. It says what the agent may do, when it must stop, and what gets verified before anything moves.

Five stacked cards, implementation, debugging, testing, PR review, and documentation, each listing what the agent does inside that workflow.

5 bounded workflows

  • Implementation: inspect the files, propose a plan, wait for approval; ship in vertical slices; summarize what changed
  • Debugging: read the error output, form hypotheses, run targeted commands; document the confirmed root cause
  • Testing: test plan first, red-green-refactor, explain what each test verifies; no tests of the mock
  • PR review: batch the reviewer comments, group by file or risk; produce 1 summary of changes
  • Documentation: explain the architecture, draw the diagram, name the entry points; turn implicit knowledge into a doc

‍

Implementation is for scoped feature work, and we ask for tracer bullets: thin vertical slices through database, API, and UI, rather than broad horizontal layers built in isolation. Debugging is where the agent has been strongest for us, as long as it writes down the confirmed root cause and stays out of unrelated refactors. Testing runs before or during implementation, with a test plan first and mocked-config-equals-itself tests refused. PR review is the one that ate 28% of our conversations, so it now runs as 1 batched session. Documentation is the pleasant surprise from Part 1.

Prompts, constraints, and tests that mean something

A good prompt is specific, constrained, and about an outcome. “Can you implement this?” gets you a guess. “Implement this using the existing service layer. Do not create a new service. Use these component APIs. Propose a plan before coding. After coding, run build, lint, and the relevant tests. Do not create commits or PRs without approval.” gets you something reviewable.

The habits that raised our hit rate: anchor every request in exact file paths; describe expected versus actual behavior as a small specification; point at existing code as the source of truth (“match the pattern in this module” beats a page of style rules); work in loops of 1 change, 1 run, 1 correction; and split structural cleanup from behavioral changes so the diff stays readable.

Constraints work best as short, absolute sentences the agent can’t misread. Ours include: do not create a new service layer; use the existing access-control service; all routes must fail closed; no hardcoded colors; do not create commits or PRs without explicit approval; security TODOs are blockers; feature flags are checked in the query’s enabled condition. John Ousterhout‘s deep modules are the constraint underneath the constraints: simple interfaces hiding complex internals are easier for a person to reason about, and much easier for an agent, which struggles with many small files tangled together.

Verification isn’t optional, so it runs before submission every time: component API checks, route protection, input validation, loading and empty and error states, permission gates, security TODOs, and then build, lint, and tests. The agent doesn’t get to write a test file until it has said what behavior each test verifies, what bug it would catch, and which tests would be tautological.

The strongest version of this is separation of verification. The person who used the agent to build the feature either designs the tests by hand or has them written by a separately prompted model that sees only the specification, never the implementation. That breaks the loop where the agent validates its own assumptions.

Rules in files, skills on demand

Everything above only works if it survives the end of a conversation. So the rules live in the repository: an AGENTS.md or CLAUDE.md with MUST, SHOULD, and NEVER lines covering accessibility, UI behavior, design tokens, component libraries, security, performance, and test expectations. That’s the baseline every session inherits without anyone repeating it.

What doesn’t go in that file is everything. Pocock’s warning about the dumb zone is the reason: pile every rule, guideline, and API detail into 1 giant prompt and the agent’s attention degrades. We use progressive disclosure instead. The baseline stays short, and the situational knowledge lives in skills, small composable instruction sets like /tdd, /diagnose, or /improve-codebase-architecture that load only when a task needs them.

The last layer is enforcement that doesn’t depend on anyone reading markdown. Boundary linting in CI, with tools like ArchUnit or SonarQube or a custom AST check, rejects a PR where data access leaks into presentation, where a direct query bypasses the service wrapper, or where a service reaches across a domain boundary. And the agent works through published SDKs and OpenAPI contracts rather than the whole repository, so the velocity we get from it never comes at the cost of the system’s shape.

The rules live in the repo, not in the person who learned them the hard way.

At Amplified the agent is an accelerator for disciplined engineering and not a replacement for the judgment that makes engineering disciplined. It performs best inside clear boundaries: well-scoped tasks, explicit constraints, prompts that name files, reusable rules, tests that mean something, CI checks, and a person reading the diff. Agent-written code is a draft until a human has validated it for correctness, maintainability, security, and product fit.

If you’d like a second pair of eyes on your own rule files, we’ll compare them with ours and tell you what we’d change. Let's build together.

‍