What Humans Should Do While Coding Agents Write Code
When a coding agent generates modules, interfaces, and tests, the quality gap is set less by prompt length than by what people do during those minutes. This article explains how to use that interval to build an independent model of data-flow transforms, failure handling, invariants, bottlenecks, and review questions so generated code can be accepted as an engineering increment rather than judged only by whether it runs.
After coding assistants became common, many teams settled into the same rhythm: hand a design note to a coding agent, wait for modules, interfaces, and tests, then decide whether to merge. The quality gap is rarely set by how long the prompt is. It is set by what people do during the minutes or tens of minutes while the agent is typing. Treat that interval as a break or a scroll through messages, and review collapses into “it runs, so it passes.” Use it to form an independent judgment, and the same generated result becomes an engineering increment you can actually accept.
The gap is for parallel thinking, not for watching keystrokes
Coding agents are good at expanding an implementation inside clear boundaries: filling in types, wiring calls, and writing error handling in familiar patterns. They are not good at taking system responsibility for you. That responsibility includes who the path serves, which invariants must always hold, what the business may lose on failure, and what must be observed before the system counts as healthy. If those judgments wait until the code exists, the implementation details tend to lead. You see “it was already written this way,” not “this is how the system should work.”
The first rule of the gap is therefore parallel work, not supervision. Do not try to follow every line as it appears. Beside the agent, build a mental model that belongs only to you. Do not hand that model to the tool as a prompt; otherwise it may use your sketch to smooth over a design you have not finished thinking through. Once the implementation lands, collide the model with the code. The differences are where the information is.
Draw the transforms before talking about the implementation
Take a data-processing path as the example. The entry may be a message, a file, or an API call. The exit may be a database write, a downstream event, or a report. Every step in between is a transform: filter, join, aggregate, idempotent deduplication, or format normalization. What is worth drawing in the gap is not a pile of class diagrams. It is a sequence of transforms from entry to exit, with three notes on each step.
First, the shape of the input and the output. Are fields nullable? Are identifiers stable? Do timestamps carry a time zone? Are batches and single records mixed? If the shape is unclear, later validation and retries will be attached in the wrong place.
Second, the failures this step is allowed to have. Transient faults that can be retried, dirty data that must be rejected, noncritical enrichment that can be skipped with degradation, and consistency breaks that must stop the whole path are four different cases. They must not collapse into one generic “throw an exception.”
Third, the cost of the step. That cost may be CPU, an external call, a lock, or state storage. The most expensive step is usually a bottleneck candidate, but mark it on the diagram as a hypothesis. Do not conclude from a feeling after the fact.
After the diagram, write the path invariant in one sentence. For example: within the observation window, the same business key produces at most one valid output; late data must not overwrite a confirmed result; a partial failure must not let downstream consumers see a half-finished product. The invariant matters more than the arrows, because it is the ruler you will later hold against the code.
Write failure modes before the implementation exists
The most common kind of surface completeness in generated code is a try-catch on every function and plenty of logs, while the failure semantics stay vague. During the gap, list failure modes by stage and decide the handling in advance. Do not wait for an error and then patch it.
On the entry side, ask how duplicate delivery is recognized, how out-of-order input is handled, how an oversized payload is rejected, and whether missing authentication or tenant information is dropped or sent to a quarantine area. On the transform side, ask whether an external-dependency timeout fails the whole batch or isolates one record, and whether a missing dimension lookup blocks, uses a default, or continues with a flag. On the exit side, ask whether a successful write followed by a failed acknowledgment can double-write, and whether one invalid record in a batch commit rolls back everything.
Write the handling as a short table: symptom, detection point, action, whether recovery can be automatic, and whether a human queue is required. That table later becomes the catalog of acceptance tests. An agent can turn the table into tests, but a person should define the table. The tradeoffs are about the business and stability, not about syntax.
Design bottleneck hypotheses together with observation points
Many people add logs only after performance is already poor. A steadier approach is to reserve an observation question for every transform during the gap: how many items this step processed, how many failed, what the latency distribution looks like, whether in-flight backlog is growing, and whether the external-dependency error rate is rising. You do not need to invent concrete metric names or vendor interfaces. Agree on four kinds of signals—counts, latency, error classification, and backlog—and on which boundary each one hangs from.
Bottleneck hypotheses should be written early as well. For example: if the join step issues a synchronous query for every record, latency will worsen linearly with batch size; if aggregation holds too wide a time window, memory will hit its limit before CPU does; if the exit waits synchronously for downstream acknowledgment, throughput will be capped by the slowest dependency. The value of a hypothesis is that you know what to look for in the implementation: a remote call inside a loop, an unbounded buffer, missing backpressure, or retries without a cap and jitter. If the code does not match the hypothesis, either correct the hypothesis or require a change to the implementation. Both are more useful than “it looks fine.”
Compare against the implementation by contract, not by line count
When the implementation is done, do not start the first pass by reading downward from the top of each file. Ask questions from your own diagram and let the code answer.
Where is the entry? At which boundary does a request or message come in, and do tenant and trace identifiers run all the way through? Does each transform actually exist, or has it been merged into one large function that is hard to test, or split so finely that the invariant has nowhere to live? Does failure handling match the table you wrote earlier: does what should be isolated take an isolation path, and does what should stop still continue writing after swallowing an exception? Is there an explicit mechanism for idempotency and ordering, or only a comment that duplicates are “unlikely”? Are resources bounded: do queues, caches, concurrency, and retry counts have limits? Are observations placed on the boundaries, rather than a single success log at the outermost layer?
A tiny sketch can fix the review questions. It is not a runnable product. It only turns the checklist into code:
for step in pipeline:assert step.has_input_contract()assert step.failure_policy in {retry, isolate, reject, halt}assert step.bounded_resources()assert step.emits_count_latency_error()assert pipeline.invariant_holds_on_duplicate_and_reorder()
If a question has no place to land in the code, record it as a gap. Do not silently complete it in your head. The agent’s next revision should target the gap, not “optimize the style a bit more.”
A practical boundary for human and agent work
What fits the agent is expansion after the contract is stable: boilerplate transforms, routine validation, test scaffolding, and branches filled in according to a decided policy. What should stay with the person is the contract itself, failure tradeoffs, capacity hypotheses, and the definition of done. The gap is the time for that second set of work. Do not spend it watching the generation unfold.
A repeatable rhythm is: spend ten minutes to half an hour, independently, on the data flow, the failure table, the invariants, and the bottleneck hypotheses; then receive the implementation; then review the differences with the same question list; finally write only those differences back as change requests. The review record should keep “hypothesis, evidence, conclusion,” so someone else can check it and so the next round of conversation does not lose context.
If the path will run on common cloud capabilities such as object storage, message queues, container platforms, and logging and monitoring alerts, the gap should also check the boundaries: where state lives, who owns retries, who owns alert escalation, and whether secrets and configuration have entered the code. These are category-level responsibilities. They do not need to be tied to one proprietary interface. The goal is generated code that, in a real runtime, can be observed, rate-limited, and replayed—not code that only passes a local sample.
Closing
The interval while an agent writes code is not an empty stretch of productivity. It is where design responsibility is most concentrated. The data flow, failure modes, invariants, and bottleneck hypotheses a person builds alongside the agent decide whether the later comparison is an acceptance review or an impression score. Use that time to think through the transforms and risks from entry to exit, then let the implementation answer questions. Generative collaboration then moves from “written quickly” to “changed accurately and ready to run.”
