Skip to content
enes
2026-03-28engineering3 min read

Spec-driven development: if the rule lives in Slack, the AI invents it.

Business rules living in Slack, in the PM's head, or in implicit code aren't seen by anyone. How to write specs your codebase, your AI, and your next dev consume as context.

You asked Claude Code to add an endpoint to cancel a subscription. It returns code. Tests pass. You ship it. Two days later a ticket lands: "We canceled a Platinum customer and they lost access immediately. That should only happen on Free." The AI didn't know that rule. It invented one.

The problem isn't the AI. The rule lived in a Slack thread from last year, in the PM's head, and in 14 lines of implicit code nobody flagged as "this is where the tier logic is." To the AI, that rule never existed.

The same thing happens to the new dev on the team. And to future-you three months from now.

Specs as context, not as documentation

Old way of thinking about specs: documentation you write after the code, so someone in the future understands what it does. Lives in Confluence, Notion, the Wiki. Nobody reads it. When it's needed, it's stale.

New way: specs are shared context between humans and AI, live in specs/ in the repo, versioned with the code. The AI reads them when reasoning about a change. The dev reads them when onboarding. Tests can reference them. Every consumer of the system, human or not, starts from the same source.

The format

One spec per bounded context, in markdown, frontmatter so they're filterable. Invariants and rules are written in absolute imperatives: MUST, CANNOT, ALWAYS, NEVER. Edge cases, explicit.

---
title: Order Management
bounded-context: orders
status: active
last-reviewed: 2026-04-20
---

## Invariants
Rules that MUST always hold. Code that violates them is a bug.

- An order MUST have at least 1 line item.
- Order total MUST equal sum(line_item.price × quantity) − discounts + tax.
- An order in SHIPPED status CANNOT be cancelled, use the return flow.
- A customer CANNOT have more than 3 pending orders simultaneously.

## Status Transitions

DRAFT → PLACED → PROCESSING → SHIPPED → DELIVERED
                       ↓
                  CANCELLED           RETURNED

- Only DRAFT and PLACED can be cancelled directly.
- Cancellation after PROCESSING requires manager approval.
- DELIVERED → RETURNED has a 30-day window.

## Edge cases

- A 50% discount on a $1 item rounds to $0.50, not $0.49.
- Orders over $500 require manager approval.

With specs/domain/orders.md in the repo, when you ask Claude Code to add a cancel endpoint, it reads the spec, sees that SHIPPED can't be cancelled, and returns code that respects the rule instead of inventing one. The next dev too, without asking anyone to explain. And you, three months later.

Who reads the spec

Diagram: a spec.md file at the top fans out to four consumers: backend, tests, AI assistant, and developer onboarding. The spec is the single source of truth.

Backend code that implements it, tests that verify it, AI that reasons over it, the new dev who onboards from it. One source of truth for every consumer.

The two rules

  1. If an important rule isn't in a file in the repo, assume the AI is going to invent it. And the new dev will invent a different one. And you in six months will invent a third. Slack and human memory are not durable storage.

  2. Write in absolute imperatives. "Large orders need approval" is ambiguous. "Orders over $500 require manager approval" is executable. Precision comes from the language, not from context you happen to carry in your head.

The mindset shift

Before: you document after the code, if you have time.

Now: the spec is an input to the code, not an output. You write it before asking the AI to implement, so the AI has context. And it stays as an artifact the next change can read too. Specs outlive conversations; a Slack thread disappears, a spec in specs/ keeps its history of changes for years.