---
id: 71
title: "Product-led teams were vibe coding before the LLMs were"
status: current
kind: signal
date: 2026-09-25
ai_assisted: true
authors:
  - "Theo Zourzouvillys"
tags: [llm, culture, architecture, design]
summary: "The worries about LLM-written code — decisions nobody made on purpose, a data model that fell out, architecture by accident — describe how product-led teams have treated engineering for a decade. LLMs hide those decisions further, and the cure is the rigor we sidelined."
supersedes: null
superseded_by: null
aliases: []
references:
  - id: vibe-coding
    title: "Vibe coding (Andrej Karpathy, February 2025)"
    url: https://x.com/karpathy/status/1886192184808149383
    abstract: "The post that named the practice: describe what you want to an LLM, accept what it produces without reading the diff, and keep going as long as it seems to work. Karpathy framed it as fine for throwaway weekend projects, which is the part that got lost as the term spread."
  - id: no-silver-bullet
    title: "No Silver Bullet — Essence and Accident in Software Engineering (Fred Brooks, 1986)"
    url: https://en.wikipedia.org/wiki/No_Silver_Bullet
    abstract: "Brooks separates the accidental difficulty of software (languages, tooling, the mechanics of writing code) from its essential difficulty: specifying, designing and conceptually modelling the thing being built. Tools attack the accidental part; the essential part does not get cheaper, because it is the work of deciding what the system is."
---

## The shift I'm noticing

Listen to the worries about LLM-written code and the list is always the same. Nobody understands what
was built. Design decisions are made implicitly, at generation time, by nobody in particular. The data
model is whatever fell out of the first prompt that worked. The architecture is an accident that
happened to pass the tests, and the tests assert what the code does rather than what it should do. It
all works right up until the day it doesn't, and then nobody can say why it is shaped the way it is.

I agree with every item on that list. But I keep noticing something uncomfortable about it: it is an
accurate description of how a lot of product-led organizations have treated engineering for the last
decade. Describe the outcome, accept whatever implementation produces it, and treat *how* it was built
as somebody else's problem. "Product doesn't care how it's implemented" is a prompt. The
[vibe coding](ref:vibe-coding) people are rightly alarmed by is that same stance, with a model in the
seat that used to hold an engineering team.

## The old balance

Saying product doesn't care how something is engineered is exactly the same as engineering saying it
doesn't care how the product is marketed or sold. We would call the second one immature without a
second thought. The first one has somehow become a respectable operating model.

It got there as a correction. Engineering-led development had real failures — products built for the
people who built them, elegance nobody asked for, roadmaps set by what was interesting to implement. The
move to product-led fixed some of that, and then overshot: engineering became a service function that
takes tickets, and "how" became a detail product was allowed, even encouraged, not to hear about.

The trouble is that the how never stopped mattering. It just stopped being discussed. The clearest case
is the data model. When you build a genuinely new capability, the model you build is not implementation
detail — it decides which questions the product can ever answer, which states can exist, what can be
undone, and what the product will be able to show. The UI is a view of the model. A screen cannot
display a distinction the model never recorded, and a product can't offer an undo the model can't
express.

So the constraints still arrived; they just arrived late and under other names. "Technical debt."
"Why does this take six weeks?" The feature that can't ship because the model conflated two things
three years ago. Product-led teams have always been engineering-constrained. Most just aren't honest
about it in their own development lifecycle.

What absorbed the gap was engineers who cared anyway. The ones who pushed back — "if we store it that
way we can never answer X" — carried the model in their heads, and quietly did the architecture work the
process didn't ask for. That rigor was a subsidy, and because it was invisible, it was easy to believe
it wasn't necessary.

> [!aside] From the field
>
> I've watched this play out more than once over the years. A team builds a new capability and is told
> the implementation is engineering's business, so the model gets shaped by whatever the first few
> screens needed. A year or two later, product asks for something the model can't express — a history it
> never kept, two things it treated as one — and the answer is a migration measured in quarters. Nobody
> ever decided against that feature. It was decided implicitly, long before, in a model product had been
> told it didn't need to care about.

## What changes

LLMs remove the subsidy. An agent does what the prompt describes, and prompts describe outcomes.
Nobody in that loop is going to say "if we model it this way, you can never offer that." The decisions
still get made — about the model, the boundaries, the invariants, what's reversible — but now they're
made implicitly, faster, in more places, and hidden a second time: not just from product, but from any
engineer who might once have held them in their head.

And here is the irony. Look at the practices people now prescribe to make LLM-assisted development
safe: write the spec before the code, design the data model first, review the interface rather than
the implementation ([ZFN-66](/zfn/66-agonize-over-the-interface/)), keep decision records
([ZFN-1](/zfn/1-engineering-decision-records/)), test invariants rather than behaviour, make the error
contract explicit ([ZFN-58](/zfn/58-errors-are-part-of-the-contract/)), design deletion on day one
([ZFN-57](/zfn/57-deletion-is-a-feature/)). That isn't new LLM hygiene. It is engineering rigor — the
exact work product-led cultures discounted as overhead. Good practice for building with LLMs is just
good practice, rediscovered with a new excuse. It was right before the models, and it would be right
without them.

Brooks drew this line forty years ago: tools attack the [accidental difficulty](ref:no-silver-bullet)
of software, the mechanics of writing it, and leave the essential difficulty — deciding what the system
*is* — untouched. LLMs are the most powerful attack on the accidental part we've ever had. That makes
the essential part more of the job, not less.

## The hypothesis

When implementation gets cheap, engineering judgment — the model, the boundaries, the invariants, the
irreversible decisions — becomes the scarce input, and it can't stay invisible, because there's no
longer a team quietly absorbing it.

That doesn't mean engineering should lead. It means the thing between product and engineering — the
model, the spec, the contract — has to be owned by both. The useful test for where product has to care
about "how" is reversibility: a decision that outlives the UI and is expensive to undo once data or
callers depend on it is a product decision, whatever layer it lives in. Button placement isn't. The
data model almost always is.

My bet is that the organizations that already know how to talk about the data model with product will
get enormous leverage from LLMs, and the ones that treat "how" as not-product will ship faster, straight
into the walls they used to hit slowly. It's the same pattern as
[ZFN-28](/zfn/28-llm-brute-force-prs-understanding/), scaled up from a single engineer to a whole
organization: capability without understanding, now at the level of the product.

## What I'm watching for

- Product specs that start to include the data model — the nouns, the states, what can be undone —
  rather than only the screens.
- Teams' LLM guidance documents that read like architecture documents, because that's what they
  turn out to need.
- Failures in LLM-built products that trace back to a modelling decision nobody knew had been made,
  rather than to a bug in the code.
- Design and architecture work becoming a visible, named part of the lifecycle again, rather than
  something senior engineers do unasked.

## Why I might be wrong

- **The models may learn to push back.** An agent that asks "do you need to tell these two apart?"
  before it builds the model supplies the subsidy itself. Some already do, a little.
- **Rewrites are getting cheaper** ([ZFN-23](/zfn/23-iterate-and-rewrite-implementations/)), so a wrong
  model costs less to replace. Code, though. The data already written in the wrong shape is the part
  that doesn't get cheaper, and that's most of what makes a model expensive to change.
- **Much product work really is shallow.** For a lot of software the how genuinely doesn't matter, and
  I'm generalizing from the kind of systems work where it does.
- **Nostalgia is a bias.** The engineering-led era shipped its share of junk, and it's easy to remember
  the rigor and forget the self-indulgence.
- **I have a stake in this.** I'm an engineer arguing that engineering judgment is about to matter
  more. Discount accordingly.

## References

- [ZFN-66](/zfn/66-agonize-over-the-interface/) — spend the deliberation at the boundary; the data model
  is the boundary product sees.
- [ZFN-28](/zfn/28-llm-brute-force-prs-understanding/) — capability without understanding, the
  individual version of this signal.
- [ZFN-23](/zfn/23-iterate-and-rewrite-implementations/) — stable models make implementations
  disposable; the counter-argument's strongest form.
- [ZFN-57](/zfn/57-deletion-is-a-feature/), [ZFN-58](/zfn/58-errors-are-part-of-the-contract/) —
  two decisions that look like implementation detail and are product behaviour.
- [ZFN-1](/zfn/1-engineering-decision-records/) — making decisions visible so they can't be made by
  nobody.

## Changelog

- **2026-09-25**: Opened as a signal.
