zacharyck.com / writeups / a program from scratch
If you wish to make a program from scratch
...you must first invent the universe
Now that AI has been around for a few years, I think most of us have seen this happen.
You've got an idea for an app. Maybe it's good, maybe not, at this point it doesn't matter. You type out your thoughts to an LLM and there's a good chance it will put together a mockup without you even asking for it.
It happens so fast it feels like magic, especially if you remember how slow prototyping stuff used to be. But that pace rarely holds up. Every additional change starts to take longer and longer. Not because there's more work for the LLM to do, but from the accumulation of unchallenged assumptions that inevitably break down over time.
It took one prompt to make the prototype, but maybe 3 or 4 to fix your nav bar.
Then your page starts to load slower, and the more you try to fix it, the more the model starts rewriting entire sections of your site - without even fixing the bug... You can throw more intelligent models at the issue or bump up the reasoning level, but all this does is delay the onset.
All this rewriting creates code churn. It isn't a new problem, but AI makes it very easy to generate more of it.
So where did we go wrong exactly? - Three places.
Don’t Let Your Agents Fill in the Blanks#
The first place to improve is our design, or the lack of one. When you start building before setting out your goals, your AI agents are guaranteed to fill in the blanks with their own assumptions.
Some of those assumptions will hurt you in the long run. The longer they go unchallenged, the more work that's needed to fix them.
The best thing to do is state your goals upfront. Start with a README.md, then build on it with a FEATURES.md or ARCHITECTURE.md as you flesh out your idea. Not only do these documents give your AI agents a foundation to build off, but they also give you something you can revise and build on deliberately.
Remember, a design isn't a prediction you need to get right on the first try, it's a rough sketch you follow and refine along the way.
A bit of context goes a long way here, so do some modest design work upfront. It will pay off in time, tokens, and quality.
Build the Universe Around the Program#
As LLMs have progressively improved, we've seen more businesses choose stacks and languages based on how well AI agents work with them. The most startling of which was Avi Press' announcement that Scarf will shift their backend stack from Haskell to Python. He gave many reasons for this change, but I want to focus on just one...
The important metric: how long does your entire development feedback cycle take?
Just as we enjoy quick feedback on our work, so do AI agents.
I'm not just referring to tests, either. Every aspect of your environment eventually becomes a part of the prompt. Your documentation, tooling, testing harness, and even your deployment platform all become variables in your agent's context.
We want to build a code environment that promotes high initial accuracy and fast iteration. In general you want each of the following:
- Fail Fast - Type checks, linters, and analysers catch whole classes of mistakes before your agents even run the tests.
- Rapid Feedback - Fast, meaningful tests let your agents check expected behaviour and correct mistakes on their own.
- Dev/Prod Parity - Your local environment should mirror your production one as closely as possible.
Good Boundaries Contain Bad Decisions#
The final piece of the puzzle is compartmentalisation. If uncaught mistakes by code-generating agents can be limited to a small section of the software, they become easier to fix without introducing new ones.
I feel many people are subconsciously aware of this as there's been a consistent shift. I've seen more developers favour vertical slice architecture, particularly in the .NET community. A style that organises code around individual features and limits the coupling between them.
If we accept that AI agents can make bad decisions, then it becomes our responsibility to structure the project in a way that limits their spread.
You can use these four rules as guidelines:
- Prefer small, independent slices with clear responsibilities.
- Accept duplicated code when the alternative has no clear benefits.
- Use tests to keep duplicated behaviour consistent where it needs to be.
- Non-behavioural changes that touch dozens of files are a warning sign, don't ignore them!
Try applying these guidelines yourself. They'll help keep changes small and development moving, whether you're using one agent or several.
Consistency is Key#
Getting a prototype in one prompt is impressive, but it doesn't tell you much about how the next hundred will go. A more capable model might get you further, but it doesn't remove the need to give it something solid to work with.
That's what these points are really about, they're a framework for keeping your progress from slowing down. Design gives your agent a direction. The environment helps it discover when it’s gone wrong. Boundaries limit how much damage a missed mistake can do.
So while you don't need to invent the universe, you do need to take responsibility for the one your agents work in. Writing the code is only part of building software. The goal isn't just to make the first version quickly, it's to keep making progress after it already exists.