Engineered, Not Vibed
The most dangerous thing about AI coding isn’t the code. It’s the decisions you never made.
Early this year I had this idea for an agentic team framework called Louis. A fleet of AI employees that take tasks from a dashboard, they pick the right tools, execute multi-step plans, and report back. Driven from chat, sandboxed, with no access to my passwords or credit card, but still able to access all my internet surfaces. That required building a custom agent harness, skills and integrations, a gateway server with real-time UI updates, a Kanban board, chat integrations, the works. Hundreds of issues tracked, debugged, and resolved across five codebases. The kind of thing you can only build by sitting with it for quite some time in deep focus. I disappeared in my office for 6 weeks, my wife explaining to the kids with that strange look on her face: “daddy is working on something”, and Louis became reality.
Here is the thing: I built most of it with AI. And by the end, as the project kept growing and the complexity rising I threw my hands in the air desperate to find a better way.
Not because AI writes bad code. It doesn’t… well not usually. I hated it because I was building on sand and I couldn’t put my finger on why everything kept shifting.
The random walk
Here’s what happens when you hand an agent a goal and let it rip. It works. Fast. Impressively fast. You describe a feature in a sentence, and five to ten minutes later there’s a working prototype. You feel like a wizard. You show your friends a demo and they go like: “wow that’s cool, BUT…”, “well wouldn’t it be nice if…”, and “it should really also do …”.
So you add the second feature. And the third. And somewhere around the tenth, you notice something: the code doesn’t make sense anymore. Not in the “it’s messy” way, but in the “nobody decided this should work like this” way. The data model is not wrong, but it’s also not quite right. The abstractions are in the wrong places. There are three or four different patterns for the same thing, because the agent picked a different approach each time and you never told it not to. Some things just make you scratch your head: “where did that come from?” Because the agent decided on their own that their way was the best way and never told you. Well you could have reviewed all the code changes that come flying by at a hundred miles an hour, but who’s got time for that during a vibe code session?
You start fixing things. But every fix uncovers two more problems, because the bad decisions aren’t at the surface. They’re at the root. You’re four levels deep in a design you didn’t choose, debugging consequences of bets you never made.
That’s not a quality problem. It’s a design and decision problem.
The design tree
Fred Brooks described this forty years ago, long before any AI even wrote a single line of code. Every piece of software is a path through a design tree. You start at the root: a blank directory and a goal, and at each node of that tree you make a decision. Starting with the root node: language, framework, architecture, data model. Error handling. Naming. Each choice opens some branches and closes others. Building the product you want to ship is one path through that tree. The end result you’d be happy with is a cluster of nearby leaves.
When you vibe code, you hand the agent the root node and walk away. It walks the tree for you, picking the most probable next path at every branching point, with some randomness from temperature. It is, quite literally, rolling dice at every decision point you didn’t make for it.
Sometimes it lands in the cluster of leaves you’d be happy with. Usually it doesn’t. And because early decisions constrain everything downstream, a wrong turn near the root, like the framework, the data model, or the auth strategy, stays invisible until you’re deep enough that going back and fixing it means rewriting half the project.
That’s the real cost of vibe coding. Not bad code. Hidden decisions that lead to undesired outcomes that cost you a lot to change down the road.
Eighty issues in a markdown file
With Louis, I felt this acutely. I started with one markdown file where I wrote down the idea for the project. The features. My ideal and preferred tech stack. When you are near the root of the design tree, keeping the mental map of where you want to go and how you got there in your head is possible.
Then as the project grew (after a day or two) that mental map was too large now to keep in the mind, so a piece of paper with written down notes that my brain could use as swap space. After another week, even the AI agents started to forget because the mental map didn’t fit into their 1M token context windows anymore. So I started tracking design notes, ideas, outstanding issues in a flat markdown file that the agent could reference as well. Eighty-plus items, checked off one by one. Real-time UI updates breaking silently. Context loss when tasks cycled back from review. Token counts not surfacing. The agent’s TUI printing escape codes when you pressed a key. Each fix was a firefight. Reasonable in isolation, exhausting in aggregate.
The problem wasn’t that the AI wrote buggy code. The problem was that I’d never specified what “right” looked like before the code existed. Every time I said “fix the real-time updates,” the agent had to guess what real-time updates should look like. Every time I said “improve the logging,” it had to invent a logging philosophy from scratch. And it invented a different one each time.
Still on a high from the initial velocity of code and product magically materializing in minutes every time I stated a desire it started dawning on me that I was the architect who never drew the blueprints, then blamed the builders for the crooked walls.
The insight
This way of working was clearly not sustainable if I ever wanted Louis and its AI employees to run my company. I paused. That’s a really hard thing to do when you are “in the zone”. Prompting the agent is like a drug you can’t put down. But I knew deep down in my heart that this wasn’t going to work for much longer. I realized that we’ve been there before, when the idea of software “engineering” was first born. When programmers working haphazardly in a chaotic fashion left code bases that were digital battlefields. Tamed and terraformed by process and discipline.
The problem: five decades of software engineering with all the hard lessons learned on what works and what doesn’t had never accounted for artificial intelligence. Everything we know about software engineering is about humans working with other humans to produce software for humans. Agile methodology with user stories, where you go interview stakeholders and discover requirements on the fly. Except now I was the stakeholder, some of the requirements in my head (most I haven’t even thought of yet), and the ones writing all the code were the AI agents. But they ignored agile: they didn’t have stories and even if they had they didn’t bother interviewing me to discover my requirements.
The turning point wasn’t a tool or a technique. It was a question: what if the agent told me where it would guess, make a decision, roll the dice or hallucinate a requirement, and diverge before it wrote anything?
Think about it. The agent knows something you don’t: where the ambiguity lives. Ask it “what are the open questions here? What would you decide, and what are the alternatives?” And by its very design it enumerates the branch points it sees. Not all of them matter. Most of them don’t. But the decision branches with deep subtrees are the ones with exponential impact. One fundamental architectural decision prunes half the tree. One very specific edge-case decision prunes a single node.
The highest-leverage move in working with the agent was to front-load the decisions with the most reach. That’s what vibe coding skips and it’s the only thing you actually need to do yourself.
Interestingly, this way of working completely inverts your job. You’re not writing specs from scratch. You’re letting the agent draft based on their enumeration of open decisions and where they would guess, and then you respond to that draft. Correct, answer, revise where you see fit. Reacting to a proposal is far cheaper than authoring one. The agent says “here’s what I think you want, am I right?” and you say “yes, very close but change X.” Guard rails where they are needed and bring real value, intuition everywhere else.
Pure vibe throws out the guard rails and gets flawed fancy. Pure process throws out the intuition and gets a boring knock-off. The goldilocks way of building software with AI lives in between.
It compounds
Here’s the part that changed how I think about this permanently: the decisions accumulate.
When you specify before you build, you create a record. Not documentation for documentation’s sake, but a decision logged and referenceable in the future. Why we chose this data model. Why the auth works this way. Why we rejected the obvious approach. Each logged decision narrows the design tree for the next branch point. The first feature needs heavy specification; by the twentieth, the accumulated decisions have pruned most branch points away automatically. You only specify what’s genuinely new about this feature.
That’s not a process overhead that pays off someday. It’s a curve you can measure: decisions per feature, rework rate, time to acceptable output. All three should fall as the corpus grows. And they do. The codebase stops being a battlefield and starts being a body of knowledge. The agent doesn’t guess at every step, instead it builds on top of past decisions that are now discoverable. The team doesn’t forget: the decisions are right there, version-controlled alongside the code. You can literally search for them 6 months down the road.
The Shift
The pain of building Louis the old way, with its rework spirals, context loss, and the creeping dread of a codebase nobody fully understood, became the reason to build something else alongside Louis that would make building Louis going forward much faster, and produce a better outcome in the end.
The AI Software Engineering (AiSE) Kit is the process codified as agent skills: specs before code, decisions captured at the branch points that matter, a growing corpus of decisions and learning that compounds with every feature. It’s not a framework or a platform. It’s a contract between AI agent and developer that treats building software as engineering, not as a slot machine.
Exponential is the coordination layer: a git-native issue tracker purpose-built for human-agent teams, where the backlog, the context, and the history live alongside the code. One binary, no server, agents access it natively through MCP and can pick up and deliver work autonomously.
Louis is still Louis: the AI workforce for everything beyond engineering. But now it’s built with the process, not despite its absence. The agents spec their own work. The decisions compound. The rework spirals stopped.
These tools exist because I needed them. Not as a product idea, but as a survival mechanism. The pain was real enough that building the tools was less work than continuing without them.
The ultra-lean thesis
But there’s a bigger idea here, and it’s the one I keep coming back to. The one that made me create Louis in the first place.
With how far AI has come, a tiny team of two or three people can now turn an idea into a real product and operate a real company around that as if they were staffed five times the size. Not a side project. Not a demo or MVP waiting for “real” engineering later. A product with thoughtful architecture, reliable infrastructure, and a team that handles research, writing, ops, and support without hiring a department for each one.
This isn’t a compromise. It’s a strategic advantage. It’s the “you can have your cake and eat it too” moment of the Lean Startup philosophy: small teams with low burn moving quickly and winning markets fast because their output rivals and surpasses that of larger orgs that are still sitting in a meeting by the time your AI agents have already delivered half a week’s worth of work by lunch. Low burn means patience. Patience means you negotiate from strength.
The Proof
I don’t expect you to take my word for this. Thanks to large language models, words are cheaper than ever before. We’re not selling a theory. We’re showing a working system. And that’s the whole point: the proof should be inspectable.
Our tools are built with AiSE. We track, prioritize and deliver all work through Exponential. Louis handles the operational side. Our roadmap, our decisions, our agent activity: it’s all there in the repos, in the git history, in the .aise/ and .xpo/ directories anyone can read.
If you’re a founder building with AI and hitting the vibe code wall, you know the problem already. That dread of a codebase that grows faster than your understanding of it. You just didn’t have a solution for it.
Now you do. Vibe coding is a random walk. Build deliberately instead.
For a deeper dive into the theory behind this, read Design Trees and The Problem with Vibe Coding.