Back to Writing
Jul 23, 2026·EN·AI & Agent·Other Ideas

From Making Prompts AI Agents Love, to Making Codebases AI Agents Love

This post was originally written in Chinese and translated to English by Claude Fable 5. The original version is here.

Six months ago, my way of optimizing my agentic coding workflow was to write clearer and more specific prompts, more detailed AGENTS.md / CLAUDE.md files, and more complete SPECs.

My process back then: break a feature down into PR-level tasks, then expand each task into a full SPEC prompt with context, goal, and step-by-step implementation instructions. Even the tests were spelled out, down to the exact values to assert.

Here's the task breakdown table from when I refactored the canvas coordinate system for Clothes Box:

PR-level task breakdown table: each task includes Context, Acceptance Criteria, Effort, and Agent Test Instructions

The test instructions from one of those SPEC prompts looked like this:

Simulate a Canvas of 400x800. Simulate a user dropping an item at absolute coordinates x: 100, y: 200. Assert/Log that the system calculates the saved normalized coordinates as x: 0.25, y: 0.25. Then, simulate loading this item onto a smaller Canvas of 300x600...

The full SPEC prompt (click to expand, it's long)
We are working on a refactoring PR for our app: "[PR] 3.17.1: Normalize Coordinate System for Outfit Canvas".
 
# Context & Problem Statement
Currently, the outfit canvas (`OutfitCanvasScreen.tsx` and `DraggableClothingItem.tsx`) stores item positions (`x`, `y`) and `scale` using absolute pixel values.
This causes a critical issue: an outfit created on an iPhone 14 Pro Max will look completely broken or items will render off-screen when opened on an iPhone SE, because the absolute pixel coordinates don't scale.
 
# Goal
We need to refactor the coordinate system so that all positions are saved to the database as normalized values (percentages between 0.0 and 1.0) relative to the canvas size.
- `normalizedX = absoluteX / canvasWidth`
- `normalizedY = absoluteY / canvasHeight`
- When rendering: `absoluteX = normalizedX * canvasWidth`
 
# Step-by-Step Instructions
 
1. EXPLORE:
First, locate and read the relevant files. Look for:
- The Outfit Item type definition (where x, y, scale are defined).
- `src/components/outfit/DraggableClothingItem.tsx` (or similar, where Reanimated gestures handle translationX/Y).
- `src/screens/OutfitCanvasScreen.tsx` (where the canvas container is rendered and where items are saved).
- The Database/Storage service where outfits are saved.
 
2. PLAN & IMPLEMENT:
- Update `OutfitCanvasScreen` to capture the exact `canvasWidth` and `canvasHeight` using the `onLayout` event of the main canvas container. Do not render the draggable items until these dimensions are known.
- Update `DraggableClothingItem` to accept `canvasWidth` and `canvasHeight` as props.
- Modify the Reanimated `useSharedValue` initializations: convert the incoming normalized `x` and `y` from the database into absolute pixels for the UI to render.
- Modify the `onEnd` callback of the PanGestureHandler: calculate the new normalized `x` and `y` based on the final absolute position divided by the canvas dimensions. Pass these normalized values back up via an `onChange` or `onUpdate` callback so the parent screen has the correct normalized data for saving.
- Ensure `scale` is also handled gracefully (you can leave scale as a multiplier, but ensure the base width of the clothing item image is proportional to the canvas width, e.g., base width = canvasWidth * 0.4).
 
3. AGENTIC TESTING (CRITICAL):
Before you declare this task done, you must prove the math works.
- Create a temporary mock test script or component (e.g., `TestCoordinates.tsx` or just add temporary `console.log` statements in the save function).
- Simulate a Canvas of 400x800.
- Simulate a user dropping an item at absolute coordinates `x: 100, y: 200`.
- Assert/Log that the system calculates the saved normalized coordinates as `x: 0.25, y: 0.25`.
- Then, simulate loading this item onto a smaller Canvas of 300x600.
- Assert/Log that the initial rendered absolute coordinates are successfully calculated as `x: 75, y: 150`.
 
Please start by reading the files and outlining your plan. Ask for my confirmation if you need to radically change the component props structure.

This approach genuinely worked at the time. But looking back, the reason it worked is simple: the models weren't strong enough. If you didn't spoon-feed the implementation and test steps, the agent would go back and forth on rework, and the cost of that rework was higher than writing a long SPEC upfront.

Over the past year, though, coding capability has climbed fast:

Artificial Analysis Coding Index vs Release Date: mainstream models went from under 40 to over 75 in a bit more than a year

Today, for the same kind of task, all I need is a clearly defined goal and context. Handing a well-scoped task straight to the agent is usually far more efficient than writing a long plan first. Those step-by-step implementation instructions? As long as the task is scoped sensibly, the model plans them out better on its own.

For tasks that cross module boundaries, involve design choices, or have genuinely unclear requirements, I'll first align with the agent in read-only mode before any code gets written, for example with Matt Pocock's grilling skill, which has the agent interrogate me until we reach a shared understanding of what we're building. What we're aligning on is understanding. The implementation details still belong to the agent.

So I've increasingly come to believe: the prompt is not the thing that matters most.

Every time a coding agent starts up, it's like a new coworker with no memory. What determines whether it writes good code isn't just how you assign the task, but what kind of codebase it walks into: is the structure clear, are the rules explicit, does the program actually run, do errors surface quickly.

Here's the interesting part: none of these environmental requirements are new. Legible structure, fast feedback loops, independent code review. These have been recognized as good engineering practices for twenty years. The practices haven't changed, but the cost structure has changed a lot. A messy codebase used to cost you a human new hire ramping up a few weeks slower. Tolerable. Now you're spawning twenty amnesiac new coworkers a day, and the same flaws get amplified by two orders of magnitude. Reviewer and author used to be two different people by default; independent verification came free with the org structure. Now the agent writing the code and the agent verifying it are the same one by default, and that separation has become something you have to deliberately design.

This post is a record of three practices I've been using lately, ordered along the agent's working timeline: before it starts, can it understand the system; while it's writing code, can it catch its own mistakes; before merge, can the system catch what it missed.

1. Make the Codebase Legible to Agents, and Enforceable on Them

What Code and Docs Are Each Responsible For

I now split the written content in a project into three layers of responsibility:

Code, types, tests, and schemas express what the system actually does. ADRs, design docs, and a small number of comments explain why it's done this way and what alternatives were considered at the time. AGENTS.md tells the agent where to look, how to run things, and what project-specific conventions exist.

There's only one principle: don't use natural language to repeat facts the code can already express precisely.

Otherwise the same fact lives in both code and markdown, and the two will drift apart sooner or later. CodeAesthetic explains why in Don't Write Comments: code has tests, a compiler, and linters keeping it honest; comments and markdown have nothing. So comments can lie, but code cannot. Faced with two conflicting versions of the truth, an agent is no better than a human at figuring out which one is stale. Piling a layer of code-explaining docs on top for the agent looks like you're helping it. You're actually planting landmines.

Deep Modules and Progressive Disclosure

A good codebase shouldn't require the agent to read the entire repository upfront.

Top-level directories show the domain boundaries, module interfaces show the capabilities, types show the data structures. Only when it actually needs to modify internal implementation does the agent keep drilling down. This is progressive disclosure of complexity: let the agent see the map first, then the boundaries, and only then the implementation.

This is also where deep modules earn their keep. The concept comes from Ousterhout's A Philosophy of Software Design: lots of implementation tucked behind a simple interface. With complexity packed into modules with clear boundaries, both humans and agents can reason around the interface without loading the entire implementation into context every time. The opposite, a pile of shallow little modules that can all import each other, is a burden for humans and a disaster for an agent that starts exploring from zero every single time.

Soft Preferences Go in Docs, Hard Constraints Go in Tools

"Prefer the domain terms we already use for naming, don't invent your own jargon" is a preference that depends on judgment and can't be turned into a deterministic check, so it goes into AGENTS.md as soft guidance. "No imports across the domain layer" is mechanically decidable, so it should be caught by an architecture test, not by hoping the agent remembers the docs. "Every API handler must go through authentication" is also mechanically decidable, and the consequences are severe, so it must be guaranteed by the framework or lint, making non-compliant code unable to reach the main branch at all. Any constraint that can be compiled into tooling should not stay in a document.

OpenAI describes their agent-first repository in Harness engineering: fixed dependency directions, custom linters, structural tests, with even the lint error messages written as repair instructions for the agent. Essentially they're compiling architectural taste into feedback the agent cannot route around. I think this is exactly the right direction. Instead of trying to control the agent with more words, let the rule feedback in the environment force it to self-correct.

2. Give the Agent an Execution Environment Where It Can Self-Correct

Everything above is about whether the agent can understand the rules before it starts. But understanding the rules doesn't mean the code is right. The next question is: after the agent writes a piece of code, can it tell on its own that something is wrong?

My bar is that the agent should be able to run the entire local feedback loop independently, without going through me. Broken down:

Every item on this checklist is really just a DX standard for human developers. The difference is that humans can tolerate violations and agents can't. If the startup command is flaky, a human debugs their environment; an agent burns dozens of turns going in the wrong direction. If tests are slow, a human batches up changes and runs them once; an agent either does the same (and then faces a tangle of interleaved failures all at once) or simply stops running them. The speed of feedback is the agent's speed limit.

If all the agent can do after writing code is say "this should work," then we haven't actually delegated the work. We've only outsourced the typing. The verification still sits with us.

3. An Independent Quality Gate: Don't Let the Agent That Wrote the Code Be the Final Judge

The verification loop above is for the implementing agent to use while it works. But it's nowhere near enough on its own: the same agent can write the implementation, write the tests, and then declare that all tests pass. That's not independent verification. It's an author grading their own homework, with a rubric the author also wrote.

So the verification loop and the quality gate are two different things:

Verification loopQuality gate
WhenDuring implementationBefore merge
Used byThe implementing agentIndependent reviewers and CI
GoalHelp the agent converge on a correct implementationKeep bad code out of the main branch
Typical toolsUnit tests, typecheck, logsCI, human/AI review, evals

Before merge, I add an independent gate outside the implementing agent:

Two of these deserve a closer look.

AI review requires information isolation. Having another agent glance at the code and say LGTM is meaningless. The reviewer must have fresh context and see exactly four things: the original requirements, the acceptance criteria, the current diff, and the test and runtime evidence. It should not see the implementation agent's conversation history (if you use the same coding agent for review, you at least need to disable Codex/Claude Code memory to get session isolation). The moment it does, it inherits the implementation agent's explanation of the code, and the review degrades into a confirmation. Information isolation is the precondition for independent judgment, exactly as it is in human code review.

Evals are the regression tests of AI features. A prompt or model change can leave the build green and the types clean while visibly degrading a whole set of use cases, with no error anywhere. Evals do for AI features what regression tests do for ordinary code.

Finally, the human part. My current division of labor: the deterministic parts go to machines to verify, including types, unit tests, and whether interface contracts are met. AI does these fast and well. What I personally backstop is cross-module business logic, UX, and edge cases, especially anything involving taste (both UI/UX taste and code quality taste), plus the final smoke test.

For full-stack projects, the smoke test is deliberately kept out of the verification loop, and I do it myself. You can have an agent drive the UI through a browser or simulator, but agents are still very inefficient at this: one click, one screenshot, one round of inference. Putting that inside the verification loop drags the whole cycle down. Meanwhile a human can open the app, poke around for two minutes, and spot anything off at a glance. This happens to be where humans still hold a comparative advantage over agents, so there's no need to force it onto them.

Summary

References