There is a familiar piece of software advice: do not start with messy code. Design the right abstractions, separate your responsibilities, choose the boundaries carefully, and build something that can grow. It is good advice for a system that already has a known shape.
It is less useful when the product idea itself is still uncertain. Before I know whether an interaction makes sense, whether a workflow is worth keeping, or whether anyone wants the thing at all, I may be spending time polishing a structure for a product that does not exist yet.
Working thesis: when the goal is to validate an idea, ask the coding agent for the smallest real implementation that can produce useful evidence. Let the code be simple or even monolithic for a while. If the idea survives, come back and give the implementation the structure it has earned.
The first build answers a product question
A prototype is not a smaller version of the finished product. It is an instrument for learning. Its job is to answer a question that is still open: Does this flow make sense? Is this the right information at the right moment? Does the user understand what to do next? Does the system produce a result that feels useful?
Agentic coding makes it cheap to turn those questions into something people can touch. That changes the first engineering decision. Instead of asking an agent to design the whole system, I can ask it to build one narrow path all the way through and leave the rest alone.
01
Name the thing to learn
Start with one product question, not a list of technical tasks or a request for a complete architecture.
02
Build one visible path
Connect enough of the real experience that someone can use it and react to the result.
03
Keep the internals plain
Prefer direct code and a short feedback loop over abstractions that only make sense for a future version.
04
Learn from the running thing
Use behavior, feedback, and the next requested change to decide what the implementation needs next.
The important word is real. A mockup can answer questions about layout. A working blockout can answer questions about behavior, timing, state, and whether the idea holds together once all the pieces meet. The first build should be thin, but it should not be fake.
A prototype is a low-poly model
Game developers use blockouts and grayboxing to test a level before spending time on final art. A room made from simple shapes can reveal that the player cannot see the doorway, the jump is too far, or the whole sequence feels boring. Nobody mistakes the boxes for the finished environment. They are there to test the shape of the experience.
I think implementation detail works the same way. A first pass can have a low polygon count. It can use one large file, simple state, direct calls, and a few assumptions that would not belong in a mature system. If it lets us test the product’s shape, those choices may be reasonable for the moment.
| Stage | Implementation shape | Question it should answer |
|---|---|---|
| Blockout | One narrow path, direct logic, simple state, and very few abstractions. | Does the interaction or workflow make sense at all? |
| Mid-poly | Repeated behavior starts to move behind clear helpers and replaceable seams. | Can the idea survive the next few changes without slowing learning down? |
| Production | Durable boundaries, explicit failure handling, tests, observability, and safe data behavior. | Can other people safely use, operate, and extend the system? |
The mistake is not having low-poly code. The mistake is forgetting which stage you are in. A blockout that stays in production becomes a maintenance problem. Production architecture built before the product has a shape becomes expensive guesswork.
Why agents change the tradeoff
Coding agents make this pattern more practical because they reduce the cost of the first build and the cost of the next build. An agent can turn a clear product question into a running vertical slice quickly. It can also help move that slice into a better structure once the next set of requirements is known.
The same capability creates a temptation to ask for too much too soon. An agent can produce a polished architecture full of interfaces, services, configuration layers, and generalized components before anyone has used the core flow. The result may look thoughtful while still answering none of the product questions that matter.
The shift I want in agent coding patterns is simple: stop asking for production-shaped code before we know what deserves to become a product. Ask for an observable blockout first. Then use what we learn to decide where higher-quality structure will pay for itself.
Example prototype handoff
Optimize for learning, not ceremony
Goal:
Build the smallest working version of the new item-review flow so a person can add one item, review its details, and see the saved result.
Prototype constraints:
- Optimize for a fast, testable vertical slice.
- Keep the implementation in as few files as practical.
- A direct or monolithic implementation is acceptable at this stage.
- Do not add generalized abstractions for behavior used only once.
- Do not use fake success, hardcode secrets, or silently discard user data.
Success criteria:
- The complete flow works from the visible interface.
- The saved result is still present after the screen is revisited.
- The run steps and known shortcuts are documented.
- The likely refactor seams are named for the next pass.
This kind of handoff gives the agent permission to move quickly without making the future invisible. It says what may stay rough, what must be true, and what evidence should come back. The agent does not need to pretend that the prototype is production-ready, and the person reviewing it does not have to guess which shortcuts were intentional.
What can stay rough—and what cannot
“Prototype” should describe the level of investment, not excuse a system that lies or puts people at risk. The line is not between clean code and ugly code. The line is between reversible implementation shortcuts and broken product behavior.
Usually safe to keep rough
- File boundaries for behavior that only exists in one place.
- Abstractions for hypothetical providers, platforms, or future features.
- Internal naming that will become clearer after the product vocabulary settles.
- Edge cases outside the path being tested, as long as they are named rather than hidden.
- Temporary layout and styling that are good enough to make the interaction understandable.
Must work from the first useful pass
- Product truth: do not report success when the important action did not happen.
- Data safety: do not silently delete, overwrite, or lose information to save implementation time.
- Security: do not put secrets in source code or weaken a boundary just to make a demo run.
- Core rules: preserve the product constraint that makes the idea worth testing.
- Basic usability: the test path must be understandable and usable by the people who are evaluating it.
A rough prototype can still have strong standards. It simply spends those standards on the behavior we are trying to learn, rather than spreading equal effort across every future concern.
The second build raises the polygon count
Once the idea has earned another pass, the code should earn one too. This is not a vague promise to “clean it up later.” It is a second build with a different purpose. The first build produced evidence. The second build turns the parts that survived into a system that can carry more weight.
The evidence should guide the refactor. If every new change edits the same large function, that is a real seam. If state ownership is unclear, make it explicit. If a provider or data source is now likely to change, give it a boundary. If tests are difficult because the behavior is tangled, separate the decision from the side effect.
01
Preserve what was learned
Record the behavior, feedback, and product decisions that justify keeping the idea before changing its shape.
02
Find the pressure points
Look for repeated edits, hidden coupling, unclear ownership, and failure modes—not style issues in isolation.
03
Extract stable boundaries
Move durable concepts into focused modules only where the product has shown that the boundary matters.
04
Verify the same behavior
Keep the working flow intact while the internals improve, then add the tests and failure handling the next stage needs.
The game analogy helps here too. You do not add a high-detail mesh to every surface at the same time. You add geometry where the player will see it, where the camera will get close, or where the object carries important meaning. In software, raise the polygon count where change, risk, and shared ownership make structure valuable.
Refactoring is a product decision
Not every prototype deserves a production conversion. Some ideas should be abandoned. Some should remain small scripts. Some should be rebuilt from scratch because the first implementation taught you enough to know that its shape was wrong.
That is why I do not want to clean up every experimental line as soon as it is written. Refactoring is not only a code-quality decision. It is an investment decision. The product has to provide enough evidence that better structure will help more than starting over or leaving the experiment alone.
When that evidence exists, the refactor is no longer polishing for its own sake. It is a way to protect the learning we have already paid for and make the next round of learning cheaper.
A practical agent loop
This pattern gives me a simple loop for working with a coding agent:
- State the product question. Tell the agent what we are trying to learn and what a person needs to be able to do.
- Ask for a narrow vertical slice. Keep the scope small enough that the whole path can run and be reviewed.
- Allow temporary simplicity. Say which internal shortcuts are acceptable instead of forcing a premature architecture.
- Run the result. Use the actual behavior to find out what the idea does, not what the prompt imagined it would do.
- Capture the evidence. Record what worked, what surprised you, and which parts people asked to change.
- Ask for a refactor map. Once the direction is clearer, have the agent identify the seams that will reduce the cost of the next changes.
- Raise the detail one area at a time. Refactor around real pressure points and keep verifying the product behavior.
The short version: the first build should be cheap enough to throw away and real enough to teach you something. The second build should keep what survived and give it the structure needed to grow.
Bottom line
I do not think the best agent-generated code is always the most structured code it can produce on the first attempt. The best first implementation is the one that shortens the distance between an idea and useful feedback without hiding its shortcuts.
Start with the blockout. Let the agent make the idea playable. If the idea holds up, come back with better boundaries, clearer ownership, stronger tests, and the rest of the details a real product needs.
Good software still deserves good design. I just want to spend that design effort after we know which parts of the world are worth building.