Coding Agents: Driving In Gears
The Value of Understanding Code
In an agentic engineering world, what is the value of the code? We set the agent a goal, provide the requirements and the behaviors the system should exhibit, and ask it to write code to deliver them. When that is done, do we review the output, correct it, and iterate toward an outcome? Or is the code something ephemeral we regenerate as needed and never really read? Is it an artifact we care about, or a by-product? The answer decides where and how we apply human engineering effort, and how much work we let the agent do before any review: one task, all of them, or something in between.
In this post, we tackle that question:
- First, why the code is still worth reading, even when an agent writes it.
- Second, why that makes our certainty — not a fixed choice of human-in- or human-out-of-the-loop — the variable that should drive how we work.
- Third, a practical model, borrowed from Kent Beck, for turning that certainty into a decision about how big a step to take before we review: driving in gears.
A claim I'll defend along the way: natural language is how we talk about the model, not the language the model is written in.
Why does this question matter?
If we consider code an important artifact and operate with humans in the loop, your pipeline will be subject to Amdahl's Law, and we will be limited by the speed of the human review steps. In that case, it seems counterproductive to use more than a handful of agents (with sub-agents for context management or model swapping). Generating code faster will quickly lead to work queuing for the human reviewer. Your throughput can be no higher than the human reviewer's. Accelerating beyond a handful of agents increases complexity, such as managing merge queues or coordinating builds, without being able to move faster than the human capacity limit.
If we decide that code is not an important artifact, you can move humans out of the loop, and the constraint shifts to your ability to generate valuable requirements. Typically, that requirement takes the form of a specification. In that case, you want to move as fast as budget constraints allow. It makes sense to use a swarm or workflow to create a factory that spits out code that meets the requirement. (Specification is a very overloaded term in agentic coding. In this context, we mean a document that describes all the software's requirements. Having worked in the era when specifications were common, we know they are detailed, with numbered, tracked, and cross-referenced requirements and a supporting test pack. Creating that kind of specification is a considerable investment of human/agent time for a complex product. Much specification-driven development doesn’t refer to this; instead, it refers to collaboration between a human and an agent via a design document. More on that in another post.)
It's worth noting that engineering happens in both approaches: observing the code or creating a detailed specification. In economic models of automation, there are always “weak link” tasks that constrain how fast you can go.
This post takes the first path: code is an artifact we care about, and humans stay in the loop to read it. The swarm-and-specification factory of the second path deserves its own treatment, and I'll return to it. But even on this path, the binary is too crude. “Humans in the loop” is not a single setting; it is a dial. The rest of this post is about what sets that dial. The answer, as we'll see, is our certainty about the theory we are building. Hold that word; we'll come back to it once we've established why the code is worth reading at all.

Two roads. Answer the value question one way and the human reviewer is your ceiling; answer it the other way and specification quality is. This post takes the top road.
Why is Reading the Code Important
In the post Coding Is Dead, Long Live Programming, we discussed the idea that programming is theory-building. Briefly, since we moved on from assembly or C as a programming language to languages like C++, Java, C#, Go, etc., code is no longer simply an instruction set for registers and memory; instead, it's how we create a model that we can share with a compiler, a model that expresses our theory of how we can automate a solution to a business problem, and a model that the compiler can turn into an instruction set.
Coding is simply the act of recording the model we have designed in *code* that both we and the compiler can understand. TDD, done right, helps us produce a theory by exploring it through tests of its fitness for the problem space.
We noted that the code is important because a 3GL remains the optimal medium we have today for describing a computable theory to solve a business problem.
We need to read the code for several reasons:
- To gain fresh insights by reviewing the theory captured in the code and possibly update the theory as a result.
- To keep the understanding of the theory fresh and in line with the code.
- Maintain quality so the code can be easily changed to meet future requirements.
Building a Theory With An Agent
When we work with an agent using a process like specification-driven development, we discuss our program's theory with the agent and create a model. It's a dialogue, not a monologue; the agent serves as researcher and critic as we work. If we use an approach like specification-driven development (SDD), we may generate non-code artifacts here, such as requirements and design documents, and perhaps even UML diagrams. We work on these documents with the agent as we build our theory of the program.
When we are satisfied that the dialogue represents our theory of the program, the agent encodes the resulting theory to share it with us and the computer. Code is used to represent our theory, built from our conversation with the agent, facilitated by those requirements and design documents.
This also tells us where the theory lives when we decide code is ephemeral. If we regenerate the code at will, something more durable has to carry the theory forward, and that something is the specification and its test pack. The tests are not just a gate; they are the executable record of the behaviours our theory must exhibit. This is why the two paths from the start of the post are less opposed than they appear: on one, we keep the theory by reading the code; on the other, we keep it in the spec and tests that outlive any particular generation of code.
If we can read the code, we can verify that the model matches our theory. If it aligns with the agent and we have an agreement, we can compile and ship the encoded theory. If the model doesn't align with our theory, we go back to the agent for further discussion.
This is why the ability to read and write code becomes important; it is the shared modeling language. Our natural language is not the modeling language; it is the language we use to converse with the agent about the model. This distinction is important and often overlooked: a compiler does not take our natural-language statements and turn them into instructions; it compiles programming-language statements. These remain the language in which the model is defined, not the language of our conversation. Natural language is not a higher level of abstraction here, any more than it is between two human engineers discussing the model.

Where the theory lives. We converse with the agent in natural language, but the theory is encoded in code — the modelling language. The compiler never reads the conversation.
The Value of Insights
If the code is the artifact that describes the theory of process automation we have agreed upon with an agent, then as the theory takes shape, task-by-task, test-by-test, we gain fresh insights, realizing that there may be better models we can use.
It's worth noting that Geoff Huntley, the creator of the Ralph Loop, suggests that for production code, the loop is observed to facilitate insight and learning.
“It's important to watch the loop as that is where your personal development and learning will come from. When you see a failure domain, put on your engineering hat and resolve the problem so it never happens again.
In practice this means doing the loop manually via prompting or via automation with a pause that involves having to press CTRL+C to progress onto the next task. This is still ralphing as ralph is about getting the most out how the underlying models work through context engineering and that pattern is GENERIC and can be used for ALL TASKS.”
— Geoffrey Huntley, Everything is a Ralph Loop
Cognitive Debt
Without learning, we risk Cognitive Debt. Cognitive Debt (or Cognitive Drift) is the growing lack of understanding if we do not review the code. If the code expresses the theory, failing to observe it won't allow us to update our understanding of the theory in light of how it works or how it changes over time.
A useful test is whether you can “whiteboard how the code works.” Your goal is not the code's syntax but the software's design. Can you explain the key design decisions the code represents without looking at the code? If you can't, you have Cognitive Debt. It's the same problem you face when transferring to a new team and initially struggling to understand the design decisions that make up the software. You have to put in the work to read the documentation and code to understand the program design and the theory behind the code.
There is a team version of this that is easy to miss. Naur's theory is held by a group, not a lone author. If the agent holds the theory and no human on the team does, the debt is not yours alone; it belongs to the whole team. And it comes due at the worst moment: an incident, or a handover, when the one mind that understood the design turns out to be a context window that was cleared several tasks ago.
It can be dangerous to rely solely on a quick post-feature-completion skim, as the lack of effort makes it hard for the theory to “stick.”
“It's the difference between listening to audio books versus reading them yourself. You read the words on the page, actively try to understand them, fold them into the larger context of the story, and then develop your own understanding at your own pace. This is a rich, active, and engaged activity that requires your creativity and effort. Listening to an audio book is a passive one done at someone else's pace, leaving less time for careful consideration and understanding. The results aren't the same.”
- Aaron Stannard, Software Hyper-Delivery Is Retarding Us
The Problem of Quality
If we look at the findings of the Faros report, we note that AI code generation has a quality problem:
- Output is up (a 66.2% increase in epics completed, a 33.7% increase in tasks, and a 16.2% increase in PRs merged)
- But defects are as well (a 242% increase in production incidents and a 28.7% increase in bugs per PR)
- High engineering capability (as evidenced by DORA) offers no immunity here.
The agent reviews existing code for guidance on how to write future code and replicates the patterns it finds. As a result, bad idioms tend to replicate (agents generate from their existing context). If you fail to spot this, the cost to undo the pattern grows with each turn. The faster you generate code, the faster the idiom spreads. If the idiom has high coupling and low cohesion (such as duplicated knowledge), it is easy for the agent to accelerate your codebase into a big ball of mud faster than any human developer would.
The speed of agentic coding makes it an amplifier, and without oversight, it amplifies poor quality just as much as good quality. Focusing human attention on generating high-quality code from the start, or steering legacy code toward better patterns, can accelerate improvements.
A Brighter Episode
The quality problem above is not abstract. Here is what “reading the code” looks like in practice, and why the theory has to stay with a human. Watch how far the agent drifts each time I stop steering.
Claude, acting as reviewer, points out that for a new Brighter feature (replay outgoing messages from the outbox when the inbox indicates the message has been seen before, with opt-in), only “seams” tests exist, and there is no end-to-end test.
I ask Claude to address this issue. It writes some tests to do so.
These tests continue to “fake it”. The test directly inserts into the inbox and outbox to mirror messages that have already flowed through. Then it manually inserts a test message for a new flow into the Brighter Channel and runs the pump.
This use of testing seams does not meet the requirement for a real end-to-end test and is exactly what the reviewer criticized.
I push back
We need to:
- Use the
InternalBusto communicate - Use an
InMemoryMessageProducer - The
InMemoryInboxandInMemoryOutboxwill record the receipt of the message and the outgoing message. - Post via a command processor to a handler.
- The outgoing message will now be on the
InternalBus - Run the pump via a
Performerto process the bus. At the end of the test, stop thePerformerto terminate. - Now replay the message, using a
Postvia theCommandProcessor - The handler should not run, but we should queue the message to the
InternalBus(usePostto do this without the need for aSweeper) - Await the
InMemoryOutboxstatus of the message, changing to be outstanding instead of dispatched, with a timeout on the cancellation token
Claude can then write an effective test using these instructions. But it doesn't time out the polling loop for the outstanding message. I ask it to do that using a CancellationTokenSource with a timeout. It tries, but then checks whether the CancellationToken has been signaled via a call to Task.Delay instead of just checking IsCancellationRequested on the token. I steer again.
But it would not have succeeded without help. And we would have had lower quality because nothing was asserted about the flow. Or we might have had a weird “hack,” like using Task.Delay to trigger a timed-out exception instead of checking for cancellation.
Observe the Loop
If we decide that humans should observe the loop to grasp the theory, what does that mean in practice? Do we review at the end of the loop, or review in the loop? One big PR, or many small ones? Anyone who has reviewed a large PR knows that the best you can do (and the best agents will do) is to sample it, looking for key abstractions to review. So one obvious answer is that we want to work in chunks with a reasonable cognitive load. That is less about agentic engineering and more about how we work.
It's worth being precise about what is scarce here. It is not review clicks; it is human understanding. Agents generate code far faster than a human can build a theory of it, so Amdahl's constraint isn't the human's reading speed, it's their comprehension bandwidth. The gears that follow are a way of rationing that bandwidth: spending it where certainty is low, conserving it where certainty is high.
But we can be a little more helpful when we consider the effort we put into understanding the code.
Certainty and Feedback
The answer to where and when software engineers and agents interact is nuanced and not amenable to a binary decision. Like many things, it’s contextual. But there is an answer to what that context is: our certainty about the work. With this context, the different approaches people evangelize when using agents become techniques not manifestos.
Understanding that variable, certainty, allows us to make informed choices about how we work.
This also answers the question we opened with. Code has value because it is where our theory lives; but *how much* human effort we spend reading it is not fixed. It rises and falls with our certainty. The binary “does code matter?” was the wrong question. The better one is “how certain am I about this theory right now?” — and that question has a different answer task by task.
- With less certainty, we need feedback more quickly; we take smaller steps, measuring frequently and revisiting the design in response.
- With greater certainty, we can obtain feedback more slowly; we take bigger steps, measuring in batches and nudging the design in response.

Certainty sets the step. The less certain we are, the more often we seek feedback and the smaller the step we take before review.
Relating this to theory, then: if we have confidence in the theory, we have certainty and can move in larger chunks; if we don't, we move in smaller chunks.
That is useful because other techniques differ in how often they provide feedback, based on certainty, and we can rely on them for advice on how to work with agents.
Driving in Gears
In the book *TDD By Example*, Kent Beck uses a metaphor to describe how granular your tests should be: driving in gears. To paraphrase:
- Sometimes you can write tests at a very coarse-grained level, driving in high gear. You understand the behaviors you want, the interfaces you need, and how to implement them. Each test can exercise many lines of implementation. You make large jumps. More than ten minutes might pass between test runs. You are confident you won't go down the wrong path, so the potential waste if you have to revert is not top of mind. Perhaps you write your tests at the level of the HTTP API itself.
- Often, though, you need to slow down a little, driving in a middle gear. You want to explore the behavior of the code under test, taking your time to define the interfaces and figure out how you will implement them. You write less code with each test, moving in smaller jumps. Typically, only a handful of minutes pass between tests. You know you might have to revert the last chunk, but it’s a manageable risk. Most likely, you write code at the port in “ports and adapters” or in the service layer of a layered architecture, perhaps even dropping down to a facade over the domain.
- Finally, at times, the path becomes difficult, and you slow to a crawl, driving in 1st gear. You are unsure how to implement the code under test. You can't think about the interfaces because you don't understand how to implement them. You move in tiny jumps. The interval between tests is short. You regularly revert code after taking the wrong path; your code bears a strong resemblance to a spike. Most likely, you test the details directly, perhaps with a plan to discard the tests once you understand how to implement them.
The gears metaphor is useful because it captures the idea that we may apply a technique differently depending on our certainty.
What drives us here is the speed of feedback. How quickly can we get feedback on a decision? The less certain we are, the more feedback we need, since each decision is fraught with risk. The more certain we are, the less feedback we need, because the risk attached to each decision is lower.
Gears and Certainty
We can take this understanding from that: our certainty about the theory, the confidence I have in it, is expressed by how large a jump I make with each test. In other words, which gear I drive in depends on how much certainty I have in the theory.
When working on implementation, the agent takes our agreed-upon theory and turns it into a model. So, how much do we observe the model and the code as the agent generates them? Do we review each test before the agent implements it? Do we review the implementation after the agent completes it in response to the test? Do we review each task after it is completed? Do we allow the agent to finish the whole feature and then review the PR? Do we allow agents to review and push to production?
All strategies for when to add human guidance to the agent in the code are possible, and we may use any of them at different times. Instead of picking a strategy via a belief system about how AI Engineering should work, we recognize that any piece of work may call for a different approach depending on how certain we are. The certainty we have in the program's theory determines which gear we drive in, given our need for feedback.
So folks are mostly asking a binary question: human-in-the-loop vs. human-outside-the-loop, when they should realize that both are valid and depend on how certain you are at any given time.
Certainty is the main dial that causes us to shift gear, but we recognize it isn't the only one, as our comfort with lack of certainty can vary by blast radius. A boring, high-certainty change to an authentication path or a payments flow still causes us to downshift, because the cost of being wrong is high even when the odds of being wrong are low. If certainty is 'how likely am I to be wrong,' then blast radius is how much being wrong would cost. When both are favourable, drive in high gear; when the stakes are high, remain in a lower gear however certain you feel.

Two dials. Certainty picks the gear, but a high blast radius pulls you down one — keep a hand on the stick even when you feel sure.
The Gears at a Glance
Here is the whole model on one page; the sections that follow expand each column in turn.

Driving in gears: certainty sets the gear, and the gear sets how much the agent does before you look.
High Gear
At times, you can observe, at a very coarse-grained level, driving in high gear.
The theory is boilerplate: a simple HTTP API with a well-defined OpenAPI specification; a simple Kafka consumer with a well-defined AsyncAPI specification; and a simple CLI application.
Books, blogs, and documentation all cover exactly how to implement them. The agents' training set includes those books. You are bored by the details.
You want to hand over a specification and come back to working code.
The work may be slash-and-burn; you expect that if significant change is needed, you will return to the specification, modify it, and start over. The complexity and scope of the work make this approach economical.
Strategy
You agree on a design with the agent based on the requirements and acceptance criteria. The agent produces the design. You use an adversarial agent to review it. You skim the design yourself for surprises, but otherwise move quickly. You have the agent create the tasks and ask it to plan the work to be test-first, goal-seeking toward getting the tests to pass. You use adversarial agents to check the design and tasks. Before execution, you may skim the tasks for surprises.
You ask the agent to implement. A green test suite with good coverage is your definition of done.
You are aware that agents rely on existing code for style, but you are confident that your agent instructions and the model provider's training set can provide an adequate approach.
You rely heavily on adversarial agents to verify the code. After a task passes tests, the agent reviews the code, identifies refactoring opportunities, and implements them. A final adversarial review by an agent examines the code for issues that fall below a quality threshold. The agent iterates until the code meets that threshold. You review the final PR before merging. Perhaps you skim the program's theory for any wild gotchas and review for issues of “taste” by looking for code smells the agent may have missed. You refine with the agent if needed.
Tens of minutes might pass between asking the agent to implement the tasks and your review. This creates the risk that you might lose that time and any tokens used if you can't easily re-engineer the work. But you consider it a manageable risk for this problem and the theory needed to solve it.
If you are pushed to revert, you may realize the work is not the simple boilerplate you hoped for. Subtleties remain, and the theory does not turn out as expected, with missing cases or drifting. You downshift into a medium gear and revisit the design.
You ship to production. You are confident that if the tests pass, the theory will be correct, so the potential waste of having to revert is not top of mind. You rely on good observability to “test in production” with a fast MTTD and a low MTTR to respond to issues quickly. The code is simple, and you expect that any issues can be easily triaged and fixed by an agent for later review.
Boredom as a Signal
Boredom is a useful signal for High Gear. It signals that you have the theory and that what remains is the labor of implementation. When you get bored, your ideation and creativity no longer drive the theory. Instead, you are now on the long road of implementation. For the neurodiverse amongst us, we lose interest in the problem quickly. There is nothing novel here.
Boredom is a clear signal that it is time to shift into high gear and ask the agent to finish. The theory is clear to both of you; the details of implementation should hold no excitement. No alarms and no surprises.
Medium Gear
You don't know the theory at the outset, but you have solved similar problems before and expect to develop a workable theory quickly. You want to make steady progress and leave confident that you and the agent share a working theory. In design, you want to explore the behavior of the code under test, taking your time to define the interfaces and figure out how you will implement them.
You know your experience will guide you, helping you move at a steady pace. You are intrigued by the details of the implementation and are not bored by the prospect of the work.
You want to work with the agent to explore a solution to this requirement, not just delegate it.
The work needs to be sustainable and amenable to future change, so as to justify your investment in the theory. Because you want change, it's important that the theory is supple, allowing future modifications based on new requirements or insights.
Strategy
You agree on a design with the agent based on the requirements and acceptance criteria. There is back-and-forth between you and the agent over the design. You focus on the application's behaviors. You suggest key responsibilities and roles. From those, you work with the agent on key abstractions or code examples. You seek agreement with the agent on a theory of the design. It takes a couple of iterations for the design to reach the point where you are happy that the agent can represent the theory in code. You use adversarial agents to cross-check the design against the requirements and have the agent iterate until all issues above a threshold are resolved.
You have the agent create a task list. You emphasize that it should be test-first, goal-seeking toward getting the tests to pass. You review the tests before executing the tasks. You ensure the behaviors in the test suite match your expectations based on the acceptance criteria. Have an adversarial agent cross-check the tasks against the design and iterate until all issues above a threshold are resolved.
You ask the agent to implement.
You review after each task completes, or perhaps after two or three similar ones, only once your TDD tests go green. You seek to guide the agent toward refactoring opportunities. You do this because you want any fresh insights to update your understanding of the theory, too, to avoid cognitive debt. You look for **code smells**, looking for insights in the code that could improve the design. If changes are needed, you feed these insights back to the agent and work with it to adjust the design and the tasks. If you adjust the design for fresh insights, you may ask an agent to launch an adversarial review of the design changes for consistency after this change.
You are aware that agents rely on existing code for style, so you put effort into the early iterations, keen to establish the style for this work. This will slow initial iterations, but later ones will be faster as the agent learns how we want this code written from the code that has already been delivered.
Typically, only a handful of minutes pass between reviews. You know you might have to revert the last task, but it’s a manageable risk.
You feel comfortable that you hold the theory, despite the evolving solution. You are not bored, because the unfolding theory captures your interest.
At some point, though, you may become bored. The design is now stable, and fresh insights no longer appear with each review round. You are happy that the code style has now been established in the codebase. You expect the remainder of the implementation to follow what has gone before, so you switch up a gear to High.
Conversely, after a task, you might realize you don't understand the theory as well as you hoped. Some of the code is opaque to you. You don't recognize the work the agent just provided. Or perhaps you keep running into challenges with the agent’s decisions. You worry about the quality of the code the agent is writing. You keep reverting tasks. You switch down a gear into Low.
A Brighter Example
Brighter has a workflow driven by the /spec command hierarchy. For design, the flow proceeds from /spec:new to /spec:requirements to /spec:design. At any point, you can use /spec:review for an adversarial review. Once you have a design, use /spec:ralph-tasks, which breaks the design into a series of /test-first tasks that drive implementation in a TDD approach.
Once the task list is built, a user uses /spec:ralph-implement to run a loop, with Opus as the orchestrator using `auto` to avoid permission requests. The iterations of the loop before we halt is controlled by a variable, such as the number of tasks, the budget, or a stop file.
In Medium Gear, you set the /spec:ralph-implement to the number of Tasks to complete before stopping at 1. Once the design begins to settle, you can increase it to 2. Once you switch to High Gear, you set it to 3+ or even ask it to complete all remaining tasks. You decide how large a chunk of work to do before stopping. You think about context management and don't allow the context to grow so large that the agent becomes dumb and costs rise. Your task-by-task context-clearing strategy helps with this. Only your orchestrator builds context over the whole run. You can always restart that if you need, picking up at the first unfinished task.
Brighter's Ralph loop is sequential; it assumes that the human reviewer is the bottleneck. More confidence in high-gear might lead to a swarm or a dynamic workflow, with tasks run in parallel where possible. But costs will rise, and with a human review step, such speed is often not justified by the cost if you are driving in medium gear.
Low Gear
You are unsure of the theory because you have not solved a problem like this before. The requirements may be unclear and require further elicitation. The technologies or algorithms involved may be new to you. The domain may be unfamiliar, and you want to explore it iteratively and incrementally. You don't have a clear idea of the theory or of how to discuss it with the agent.
You may lack experience. You feel you need to explore alternative technology solutions to have an informed conversation with your agent rather than blindly accept what it tells you. Perhaps you are a junior engineer. Perhaps you are an experienced engineer, but the solutions require new skills. You need to upskill before you can move at a steady pace.
You are excited to explore what it will take to implement, as this is not something your existing knowledge helps with.
You want to work with the agent as a researcher to understand the solutions to this requirement, but you are not ready to just explain the theory to the agent.
You need to slow down.
The work needs to justify the higher cost. It may be enough that you are learning and will be able to apply those skills to future work, which can then go faster. It may be that this is core domain work that will yield a competitive advantage, so you need to push the boundaries beyond the agent's training set.
Strategy
You may be unsure about the requirements, in which case you engage the agent in a dialogue to establish them and their acceptance criteria. You get the agent to ask you questions. You use the agent as a researcher to help you explore design options for how you might tackle the problem. You ask it to ideate solutions and point you toward where you can find out more. You have the agent write code that explores unfamiliar tools or interfaces to understand their capabilities. You pause to read blog posts and technical documentation. You discuss how these ideas might work with the agent. Together, you refine them. What are the possible theories you could apply here?
You develop a design with the agent. There is back-and-forth between you and the agent. You focus on the application's behaviors. You suggest key responsibilities and roles. From those, you work with the agent on key abstractions or code examples. You seek agreement with the agent on a theory of the design. It takes a handful of iterations for the design to reach the point where you are satisfied that the agent can represent the theory in code. You use adversarial agents to cross-check the design against the requirements and have the agent iterate until all issues above a threshold are resolved.
You have learned and now know more about the problem and solution space than before. You have increased your confidence in the theory enough that the next step is working code, so you let the agent proceed.
You have the agent produce the task list. You ensure that the task description is test-first and goal-oriented, focused on getting the tests to pass. Given your uncertainty, you ensure that, in the task list, the agent will STOP after writing the test and before implementing it; the agent will then invite your feedback on the test. You will review the tests before executing the implementation. Your goal here is learning and feedback. You ensure that the behaviors in the test suite match your expectations from the acceptance criteria. You review the quality of the emerging interfaces. You seek crisp abstractions that are self-describing, invite correct usage, and are obvious without documentation. You make sure you understand the theory the agent will use to pass this test.
After each of your TDD tests goes green, you review. The refactoring step is guided by you. You seek to understand the theory behind the implementation. You look for **code smells** to see if there are any insights from the code that could improve the design. If needed, you feed these insights back, adjust the design, and update the future tasks to account for it (even the design documents).
If you adjust the design for fresh insights, you use an adversarial agent review to evaluate the design changes for consistency after the change.
Typically, a one- or two-minute pass occurs only between reviews. You know you might have to revert the last task, but it’s a manageable risk.
At some point, you may become confident. You rewrite the remaining tasks to eliminate the need for the agent to seek your approval after writing the test. Then you shift into Medium gear.
A Brighter Example
Instead of using /spec:ralph-tasks we can use /spec:tasks to build our task list. This generates a similar task list to /spec:ralph-tasks but has an explicit STOP after the agent writes a test, to wait for a human reviewer to approve (or ask for changes to) the test, before implementing.
This pause treats the test as an important part of the design process – it’s where we figure out how to express the system's behavior and what our 'interface' should look like. We go slow because we believe we need to learn, drive quality, or are uncertain enough to recognize that the design may emerge as we go.
When using /spec:tasks to run these tasks, work proceeds task-to-task, prompting a pause for each new test we write.
Earn Your Gear
We opened by asking what code is worth to engineers post-GenAI. The answer is that code has value because it is where the theory lives.
That was never really the question; we discussed code as the vehicle for the theory in the last blog.
The question is what cost we bear in sharing that theory from the agent to the team.
We suggest setting the price based on our certainty. When we are certain and trust that the code produced by the agent aligns with our theory, we can take large steps. When we hold the theory but are less certain the agent shares it, we reduce the size of our steps. And when we ourselves have no certainty about the theory the team wishes to share with the agent, we take small steps. At the same time, we adjust this based on the cost and the blast radius if the theory is wrong.
The binary that dominates our discourse — human in the loop or human out of it — is the not how we should frame this. We are not one kind of shop or the other. We are AI drivers, shifting programming gears as the road demands: high gear through the boilerplate we could write in our sleep, low gear where the domain is new and the design has to emerge. We shift within a feature, sometimes within an afternoon. Boredom and confidence are the upshifts; opacity and reverts are the downshifts.
What agentic engineering does not do is remove judgment. The skill is no longer writing code; the skill is calibrating our certainty and then deciding how much of that code to read.
And here is the trap.
The economics push us toward high gear because it is faster and cheaper. The Faros numbers — output up, but production incidents up 242% — are what high gear looks like when the certainty behind it is wishful. Boredom is an honest signal that we have earned our upshift. Impatience is not.
So drive in whatever gear the work demands, and be honest about which gear that is. The question was never whether to read the code. It was how certain we are that we have earned the right not to do so.
The other road — code treated as ephemeral, humans out of the loop, the theory carried instead by the specification and its test pack — deserves its own post.
Copyright © Ian Cooper 2024+ All Rights Reserved.