Every product team has lived this moment: a PM writes a spec, engineering reads it, and within fifteen minutes someone says, "This doesn't account for how our system actually works."
The spec described a "simple" user invitation flow. The codebase has a permission system with role-based access control, a multi-tenant organization model, three different email providers with fallback logic, and an invitation table with twelve foreign key relationships. The spec mentioned none of this. It assumed a world where inviting a user means inserting a row into a table and sending an email.
This is the spec-code gap. And it's the single largest source of friction in product development.
The Gap Is Structural, Not Personal
PMs don't ignore code complexity on purpose. The problem is structural: the tools we use to write specs have zero awareness of the codebase they're describing changes to.
Google Docs doesn't know your schema. Notion doesn't know your API routes. Linear doesn't know that the "simple" feature a PM described requires modifying a service that seven other modules depend on.
When a PM writes "add a field to the user profile," they're describing intent. When an engineer reads that spec, they're mentally translating it into: which entity, which migration, which DTOs need updating, which API endpoints return this data, which frontend components render it, and which tests need to change.
That translation happens in the engineer's head. Every single time. For every single spec.
Three Ways the Gap Kills Velocity
1. Specs That Are Impossible as Written
A real example: a product spec called for "adding a status field to projects with values: active, archived, completed." Reasonable on paper.
The codebase already had a project_status enum used in three different contexts, a status history table tracking every transition with timestamps and actor IDs, and a state machine plugin that enforced valid transitions. The spec's "simple enum field" would have conflicted with an existing system that took two months to build.
The engineer caught this in sprint planning. But the spec had already been reviewed, approved, and estimated. Two weeks of planning work, invalidated by a five-minute code review.
2. Estimates That Are Fiction
When specs don't reference the actual codebase, estimation becomes guesswork. A PM asks "how long to add search to the dashboard?" and gets a range of 2-8 weeks from the same team, because each engineer is making different assumptions about what "search" means given the current architecture.
Does the app already have a search service? Is there an existing index? Does the current query pattern support full-text search, or would this require a new infrastructure dependency like Elasticsearch? The spec doesn't say, because the spec doesn't know.
3. Engineers Who Stop Reading Specs
This is the most damaging outcome. After enough specs that don't reflect reality, engineers learn to treat specs as "rough vibes" rather than actionable plans. They skim the intent, ignore the details, and build what they think is right based on their understanding of the code.
Sometimes they're right. Often they're not — because they're missing the product context that the PM had but failed to connect to the technical reality.
The result: PMs feel ignored, engineers feel micromanaged, and the product drifts from the plan in ways nobody tracks.
What a Code-Aware Spec Looks Like
Consider the difference between these two spec excerpts for the same feature:
Traditional spec:
"Allow users to archive projects. Archived projects should not appear in the default project list but should be accessible via a filter."
Code-aware spec:
"Add an
archived_attimestamp column to theprojectstable (entity:src/projects/entities/project.entity.ts). The existingfindAll()method inProjectRepositoryalready filters bydeletedAt IS NULLvia theTypeormBaseRepositorybase class — add an analogous default filter forarchivedAt. TheGET /projectsendpoint currently returnsPaginatedResponse<ProjectResponseDto>— extend the query params to acceptinclude_archived: boolean. Frontend: theProjectListcomponent atsrc/components/projects/ProjectList.tsxusesuseProjects()hook — add a toggle to the existing filter bar."
The second spec isn't longer because it's verbose. It's longer because it's accurate. An engineer reading it knows exactly which files to touch, which patterns to follow, and which existing abstractions to use. No guesswork. No "what did the PM actually mean?" conversations.
The Fix: Make Specs Read Code
The spec-code gap exists because spec-writing tools don't have access to codebases. That's the root cause. Everything else — the bad estimates, the ignored specs, the planning waste — is downstream.
Stonewall closes this gap by reading your codebase before generating specs. When you describe a feature, Stonewall knows your schema, your API routes, your component tree, and your existing patterns. It doesn't guess what "add a field" means — it tells you which entity to modify, which migration to write, and which endpoints will need updating.
Specs should describe changes to a system. To do that, they need to know what the system looks like. That's not a nice-to-have. It's the baseline for specs that actually work.