stonewall.dev
Back to Blog
stonewall-method philosophy

Specs Should Know About Code

Stonewall · · 7 min read

There's a belief embedded in how most product organizations work: specs describe what to build, and code is how it gets built. Two separate concerns. Two separate teams. Two separate documents.

This separation is a lie. Not a malicious one — it emerged from reasonable organizational design. PMs own the "what," engineers own the "how," and clean boundaries make management easier. But the boundary between spec and code is artificial, and every team that enforces it pays a tax in rework, misalignment, and wasted cycles.

Specs should know about code. Here's why.

The Separation Was Always Leaky

Think about the last spec you wrote or read. If it was any good, it wasn't purely about "what." It included constraints: "must work with our existing auth system," "should use the same data model as the current dashboard," "needs to be backward-compatible with the v1 API."

Those constraints are code. They're statements about the existing implementation that bound what the new feature can be. Every spec that references "our current system" is implicitly referencing code — it's just doing it from memory, or from a conversation with an engineer, or from documentation that may be out of date.

The question isn't whether specs should reference code. They already do. The question is whether they should do it accurately.

What the Separation Costs

Cost 1: Specs That Can't Be Built as Written

A PM writes a spec for a feature that allows users to merge two accounts. The spec describes the user flow, the confirmation dialogs, the data migration expectations. It's well-written product thinking.

The codebase has a users table that serves as a foreign key target for 23 other tables. The user_id column appears in payments, projects, comments, activity_logs, sessions, api_keys, team_memberships, notification_preferences, and 15 other tables. Merging two accounts means updating or reconciling foreign key references across every one of these tables, handling unique constraint conflicts (what if both users are members of the same team?), and preserving audit trails.

The spec described a two-week feature. The implementation is a two-month project. Not because the PM was wrong about the product need — account merging is genuinely valuable — but because the spec had no visibility into the technical scope.

A code-aware spec would have surfaced those 23 foreign key relationships upfront. The PM might have scoped the feature differently: merge profiles and transfer ownership of active projects, but don't attempt to merge historical activity. That's a realistic two-week scope. The PM needed the technical context to make a good product decision.

Cost 2: Engineers Who Build Something Different

When a spec doesn't match technical reality, engineers face a choice: build what the spec says (even though it doesn't make sense given the code), or build what makes sense (even though it doesn't match the spec).

Most engineers choose the second option. They're not being insubordinate. They're being practical. An engineer who reads "add a status field to projects" and discovers the codebase already has a state machine for project lifecycle isn't going to add a second, conflicting status mechanism. They'll extend the existing state machine — which is the right call technically, but means the feature ships differently than specified.

Now multiply this across every feature, every sprint, every quarter. The PM's mental model of the product diverges from the engineer's mental model, which diverges from what's actually in production. Nobody has an accurate picture of the system.

Code-aware specs prevent this divergence. When the spec says "extend the existing ProjectStateMachine in src/projects/state-machine.ts to include a paused state with transitions from active and to active," the PM and engineer are working from the same reality.

Cost 3: The "Simple Feature" Problem

Every PM has experienced this. You write a spec for what seems like a simple feature. You estimate a few days. Engineering comes back with a two-week estimate. The PM is frustrated. The engineer is frustrated. Both are right.

The PM is right that the feature is simple from a product perspective. The engineer is right that the implementation is complex given the current architecture.

This mismatch is entirely caused by the spec-code gap. If the spec had surfaced the technical complexity — the middleware that needs modification, the migration that needs to run, the five services that need updating, the test suite that needs new cases — the PM would have understood the estimate immediately. Or better: they would have scoped the feature differently to avoid the complexity.

The "simple feature" problem isn't a communication problem. It's an information problem. Specs don't have access to the information that determines implementation complexity.

Why Now?

The spec-code separation persisted for decades because there was no practical alternative. A PM couldn't reasonably be expected to read the codebase before writing a spec. Codebases are large, complex, and constantly changing. Even if a PM could read code fluently, keeping up with every architectural decision and schema change across a growing system is a full-time job.

AI changes this equation. A language model can read an entire codebase, understand its structure, and surface the relevant technical context for any proposed feature — in seconds. The cost of making specs code-aware dropped from "impossible" to "automatic."

The organizational barrier also mattered. Specs and code lived in different tools, owned by different teams, in different formats. Bridging them required manual effort that nobody had time for. When the bridge is automated — when the spec tool reads the code as part of generating the spec — the barrier disappears.

What Code-Aware Specs Look Like in Practice

A code-aware spec isn't a code document. It's still a product document. It still describes the user need, the success criteria, the scope, and the acceptance criteria. But it adds a layer that traditional specs lack: grounding in the actual system.

Traditional acceptance criterion: "Users can filter their dashboard by date range."

Code-aware acceptance criterion: "The DashboardPage component (src/pages/Dashboard.tsx) renders data from the useProjectMetrics hook, which calls GET /api/metrics with startDate and endDate query params. The API endpoint already accepts these params but the frontend doesn't expose them. Add a DateRangeFilter component to the existing DashboardFilters bar. No backend changes required."

The second version isn't more complex — it's more accurate. An engineer reads it and knows exactly what to do. No Slack thread asking "which endpoint does the dashboard use?" No discovery session to figure out if the backend already supports date filtering.

The Objection: "PMs Don't Need to Know About Code"

The most common pushback to code-aware specs is that PMs shouldn't need technical knowledge. That's true — and code-aware specs don't require it.

The PM doesn't need to understand the codebase. The tool does. The PM describes the feature in product terms. The tool translates that into a spec that accounts for the codebase. The PM reviews the output and makes product decisions informed by technical context they didn't have before.

This isn't about turning PMs into engineers. It's about giving PMs information that improves their product decisions. When a PM learns that "add a field" actually touches 12 files and requires a migration, they can make an informed decision about whether to include it in the current scope or defer it. That's a product decision, informed by technical reality.

The Future Is Merged

Specs and code are converging. Not because PMs are learning to code, and not because engineers are writing product docs. They're converging because AI can bridge the gap automatically.

Five years from now, a spec that doesn't reference the codebase will feel like a map that doesn't reference the terrain. Technically possible to create. Functionally useless for navigation.

Stonewall exists because we believe specs should know about code. Not as an option. Not as a nice-to-have. As a baseline for specs that actually work.

Related Posts