Design Trees and The Problem with Vibe Coding

aisoftware-engineeringaisedesign

Remember Fred Brooks’ Design of Design from 2010?

In this extraordinary collection of insights into the design process, Brooks introduces the design tree concept in Chapter 2 as the central artifact of what he calls the Rational Model of design, the dominant mental model engineers use (often implicitly) to think about how design works.

Here is how it works: you first start with a goal, a set of desired outcomes (secondary goals, weighted), and a set of constraints. The designer makes a decision, then within the design space now narrowed down by that decision makes another. At each node they could have taken one or more paths, so one can think of the design process as the systematic exploration of a tree-structured design space.

The canonical example Brooks borrows from his earlier book Computer Architecture is an alarm clock design tree. The root splits into dimensions like clock visibility (luminous dial vs. electric light vs. plain dial), alarm sound (buzzer vs. chime vs. radio), and alarm setting control (set-stop-once, daily, off, slumber). The choices in one branch are linked to those in others by exclusion, affinity, or trade-off.

The key structural property of the design tree is that each design is constituted by the nodes traversed from the root on the left to the leaves on the right. Thus, the entire design space, all possible designs, is a tree of trees. In the word processor example Brooks uses in research contexts, if a program can reside locally, on the cloud, or in both, and data can reside in those same three places, you get 9 (or 16 with replicated combos) distinct design alternatives, each being one path through the tree.

When “vibe coding” with an AI agent, the agent (and you through the instructions to the agent) are bound to follow that same process of working through a tree-shaped design space. You start with a root node (“here is a blank directory”) and a stated goal (“build me an alarm clock app”). When you hit enter, the agent goes off and will soon hit the first decision points (what framework to use, how should the app be structured). Without user guidance, the decision outcome will be a probabilistic one: the AI model picks the most likely completion of tokens built into the model (if the model was trained primarily on Cobol, it will likely pick that), with some random variation (your set temperature for the model).

So in essence what the AI agent is working through is a tree of trees where each edge has an associated probability of being taken, and the walk through the tree is of a random nature. Now it may very likely happen that the “app” you had in your mind is some set of leaf nodes in this tree (“the acceptable outcomes”), they are all subtly different but all within the range of solutions you’d accept as “great, done!”. Whether your AI agent arrives at one of those leaf nodes or not is up to a random process: at every decision point roll the dice. Robert Frost would have a field day with “The Road Not Taken.”

This is where the “engineering” aspect of “software engineering” needs to come back into play. And it is where Brooks makes a very powerful argument: any systematization of this process is a great step forward compared to “let’s hack it.” We require a system that forces you to think before you build. It helps avoid wasted effort searching parts of the design space that are untenable or lead to undesirable outcomes not aligned with your goal, secondary goals or constraints. And it provides a structure to organize work and aid communication, and can be readily taught to new project members.

So instead of “vibe coding”, we propose a more systematic approach. Systematic doesn’t have to mean tedious or less enjoyable, because arguably one of the strongest pull factors of vibe coding is the sense of freedom one gets from “just letting loose,” giving short English sentences and “letting the agent rip” freely. That is until about four to five levels deep into your tree where things rapidly get very ugly and the sense of dread and frustration far outweighs the initial rush of dopamine. Systematic means you put in a little more work upfront, still assisted by the AI agent, to prevent that dread and frustration altogether, so you can arrive at your desirable leaf nodes with a much higher probability. Even better: since a systematic process prunes away entire trees we get efficiency: faster time to the solution, and less wasted cost (time, tokens, mental burnout).

But Wait!

You are correct to stop me here and bring up some deep structural failures in this train of thought.

The tree is not known in advance

Absolutely right. For most software projects beyond trivial, the tree is not known a-priori, but instead discovered. Unless what you are building has no factor of novelty, you simply cannot sit down and map out the design tree. Unforeseen constraints and side-effects, new requirements that emerge, learning from what works in practice when testing with users and what doesn’t. They all lead to a process where you discover the space as you work through it.

It’s like the fog of war in Warcraft II: you know your start point (your base), and your stated goal (win over an opponent) but the path to getting there, including what resources exist in which locations, what units and buildings you require when and where. You cannot know, and the map is hidden behind a veil of black fog that only clears as you work your way across the map.

So, in practice, designers make a decision, then see the alternatives it opens and closes for the next consequent decision, and that opens and closes the subtrees in the decision space, and leaves entire other trees untouched. This is fundamentally different from searching a pre-existing structure. However, our argument is that the design of the systematic process itself can be such that the discovery of that path is deliberate instead of randomized.

Nodes are not decisions

At each node of the design tree, you don’t face a simple choice between a few options, but (and that’s especially true early on when you are higher up in the tree) a choice for a tentative complete design: a fully specified alternative design at that level of detail. What makes this worse is that ordering of decisions in the tree matters profoundly (Brooks cites David Parnas’s paper on designing for ease of extension here). The combinatorics of these complications will make your mind spin!

We argue that a systematic process needs to lift this problem up and make it explicit. Modern agents are starting to acknowledge this: instead of simply making a choice randomly themselves, they give you a choice from three options A, B, or C. But that is not enough, since these are alternate design specs that matter deeply, a systematic process needs to pause at these branch points, fully describe the alternatives and their implications, let you explore and experience the alternatives so you can make an informed decision, since reverting on that decision later becomes more painful and costly the deeper you go down the tree.

You can’t see the forest because of the trees

At each given node, with the branching decision laid out in front of you, you still cannot know whether taking a specific branch will undoubtedly take you to your desired outcome state. Or, phrased differently: the goodness function cannot be evaluated incrementally since exploring all downward branches to all their leaves depends on subsequent design detailing. You simply cannot know whether a choice is “good” until you have fully implemented all its consequences. This defeats the whole purpose of the search tree. But then again our goal here is not to optimize a path through a known structure: the systematic process intends to provide guard rails that make arriving at the desired outcome more likely, though it cannot guarantee it.

This is however where the human factor comes in: great engineers who excel at building amazing products have a “secret sauce” that is impossible to capture in a system: they have great intuition. In “vibe coding” we let that intuition reign free throwing all guard rails overboard leading to flawed fancies. On the opposite end, a systematic design process excluding intuition leads to boring knock-offs. The truth is in the middle: we argue that the system must provide a balanced level of guard rails with room to express intuition freely.

So What Then?

Brooks proposes concrete approaches to navigating those failures:

Iterate rapidly: build something concrete early, because you cannot know what you want until you interact with an approximation of it. Implementing a set of known requirements allows the final design to be guided by successive iterations of refinements. Or in other words: go with the flow because predictability and great design are not friends.

Let the budget dominate: every design has one truly scarce resource that cannot be optimized. When you identify these constraints early and note them clearly you prune away large subtrees in the search space essentially for free, significantly narrowing down your error space.

Constraints are friends not enemies: like the budget, constraints collapse the search space dramatically and make it tractable. It’s been true throughout history that bold design decisions lead to better outcomes. Through choosing one strong design direction and committing, you enable coherence.

Preserve conceptual integrity: design should feel as if it came from one mind even if it was built by many. This is a core principle carried forward from The Mythical Man-Month. Software should not be a collection of disconnected features that lack cohesion. Only when conceptual integrity is followed do users feel comfortable using different parts of the software because they feel familiar and safe.

Stand on the shoulders of giants: rather than reasoning from first principles down the entire design tree, great designers draw and reason from past designs that were successful. Past successful products and designs serve as a compressed and curated design space exploration that one person could never reason or replicate from scratch. They have thousands of hours of implicit decision making, reasoning, exploration of what worked and what did not built in.

Towards a Systematic Model

How can we capture this strong theoretical foundation in practical applications of building software with artificial intelligence? In the agentic coding era, Brooks’ framework becomes both a warning and a lens.

Problem: Agents collapse the design tree invisibly. When you delegate a coding task to an agent like Claude Code or Codex, the agent makes dozens of design decisions implicitly, from data representations to error handling strategies, API shapes, module boundaries or names. They are not truly random, as they reflect the model’s training corpus with a bias towards common patterns. But in the end they amount to the agent taking a path through the tree on your behalf without surfacing any of the alternatives it did not take.

Resolution: We need explicit statements of which design dimensions are open (agent decides) vs. closed (human decides) vs. constrained (narrowed by principle). Without that, you are not delegating design; you are outsourcing it blindly.

Problem: Agents are great at the Rational Model. Agents excel at systematic, sequential decision-making given a prior set of criteria. They are essentially tree-search machines. What they are terrible at is discovering that the tree they are searching was the wrong tree, or that a design dimension they were never given exists. Brooks’ core insight, that great design requires stepping outside the assumed structure and discovering new dimensions, is exactly the capability agents lack without explicit prompting to do so.

Resolution: We need concrete and planned injection points for human intuition. For example, an agent asked to “design a caching layer” will traverse a known design tree for caching layers. It will not notice that the caching problem should be dissolved by restructuring the data model upstream. This is exactly the insight and guidance that needs to flow back from human to agent in a positive feedback loop: agent builds, human top-hats, agent adjusts.

Problem: Agents accelerate into local optima. Because agents iterate fast and generate plausible-looking implementations quickly, the human is tempted to accept the first complete design rather than treat it as a tentative design at an intermediate node. This is the agentic version of Brooks’ “premature design freeze.” The very speed that makes agents attractive also makes it easier to skip the exploratory phase where bold design decisions are made. Rapid generation without explicit design tree exploration produces exactly the pedestrian follow-ons Brooks warned the Rational Model produces.

Resolution: We need to build in concrete stepping stones that allow users with the help of the agent to look at a design from all angles and viewpoints to fully understand and weigh the different design options against each other before boldly committing to one.

The Agent as an Active Collaborator

So far we’ve explored how building software is analogous to finding a path in a design tree, how “vibe coding” is basically a sequence of coin flips to decide on which path to take, and how a fully systematic process leads to boring knock-offs because all intuition gets lost.

We argue that used differently, agents could be the most powerful design tree exploration tool ever built. Rather than asking an agent to produce one implementation, a delegation flow that explicitly uses the plan phase to generate multiple distinct design alternatives, different paths through the design tree, before committing to one, turns the agent’s generative speed into design space coverage.

The core goal of this process is still to systematically prune the design tree so the AI agent’s implementation converges on the desired outcome, not a random one, but doing so in a way that lets human intuition and decision making flow into the process to avoid the issues described earlier. In the model of leveraging the agent as an active collaborator rather than a dumb executor, the agent actively participates in every step, and in some steps, it’s better positioned than the developer to identify branch points.

The agent knows something the developer doesn’t: where it would diverge. When an agent reads a vague spec, it doesn’t think “this is vague”; it just picks a path. But if you ask it before it builds, “what are the open questions here, what would you need to decide,” it can enumerate the branch points it sees and let you see and explore the design alternatives. The developer can then resolve the ones that matter and explicitly leave the rest to the agent’s judgment.

This inverts the dynamic. Instead of the developer trying to anticipate every branch point (which they can’t), the agent surfaces the branch points and the developer analyzes and weighs them. The core principle here becomes: “The agent proposes, the developer disposes.” It is always easier to react to a draft than to write from scratch. The agent writes “Here’s what I think you want. Is this right?” and the developer says “yes, but change X.” This cuts the developer’s effort from authoring to editing, which is a fundamentally different cognitive load.

The developer starts with whatever they have: a sentence, a frustration, a market observation. The agent’s job is to help them think clearly, not to think for them. It does this by reflecting back what it heard in structured form, surfacing implicit assumptions, and asking the questions that would most narrow the product space.

Example interaction:

Developer: "I want to build a game for kids to learn
elementary school math while having fun."

Agent: "Let me reflect back what I'm hearing, and surface
some questions that would shape what we build:

WHAT I HEARD:
  A game (not a drill app, not a classroom tool) aimed at
  kids, covering elementary math, where engagement is a
  first-class goal alongside learning.

ASSUMPTIONS I'M MAKING (correct me):
  - 'Kids' means roughly ages 6–12 (elementary school age)
  - 'Elementary math' means addition, subtraction,
    multiplication, division, possibly fractions and
    basic geometry
  - 'Game' means something with progression, reward
    mechanics, and a reason to come back - not just
    problems with a score counter
  - 'While having fun' means the game should be
    intrinsically enjoyable, not just gamified worksheets

QUESTIONS THAT WOULD MOST NARROW WHAT I BUILD:
  1. Is this for kids to use independently, or with a
     parent/teacher?
  2. Web app, mobile app, or both?
  3. Single-player or social/multiplayer?
  4. Curriculum-aligned or freeform?"

The agent didn’t ask “tell me more about your idea.” It reflected the idea back in structured form, made its assumptions explicit (so the developer can correct them), and asked only the questions with the highest pruning power: the ones where different answers lead to fundamentally different outcomes.

Pruning power is not uniform

Early, high-level decisions have exponential pruning power because they constrain everything downstream. Late, low-level decisions have minimal pruning power because the tree has already narrowed.

One architectural decision at the root prunes HALF the tree.
One edge case decision at a leaf prunes ONE node.

High-pruning-power activities. A single statement can eliminate hundreds of branches:

Low-pruning-power activities. Specifying these barely narrows the tree:

The optimization target: the best process creates the highest prune-to-effort ratio: maximum elimination of undesirable outcomes with minimum developer time. The process should front-load high-leverage decisions and defer the rest, letting the agent handle low-stakes choices and recording them for future reference.