Best Practices
35 minutes
intermediate

Spec-Driven Development

Write explicit behavioral and architectural contracts before any code to achieve predictable AI output.

What you'll accomplish

Create comprehensive specs that dramatically improve AI code generation quality.

Prerequisites

  • Understanding of the execution loop
  • Experience with AI coding assistants

Why Specs Transform AI Output

The difference between good and great AI output often isn't the model - it's the prompt quality. And the best prompt for a coding task is a spec.

Without a spec:

"Build a user authentication system"

The AI guesses at every decision point. Email? OAuth? Sessions? Tokens? Each guess has a 50% chance of being wrong.

With a spec:

"Implement auth per AUTH_SPEC.md:
- Email/password with bcrypt
- JWT tokens (15min) with refresh tokens (7d)
- Rate limiting: 5 attempts/minute
- Store sessions in Redis"

The AI executes. Decisions are already made.

The Three Layers of Specs

Layer 1: Behavioral Specs

What should happen from the user's perspective.

markdown
# Feature: Shopping Cart

## User Stories

**As a** shopper
**I want to** add items to my cart
**So that** I can purchase multiple items together

## Acceptance Criteria

### Adding Items
- [ ] Clicking "Add to Cart" adds item with quantity 1
- [ ] If item exists, quantity increments
- [ ] Cart badge updates with total item count
- [ ] Toast notification confirms addition

### Updating Quantities
- [ ] Quantity can be changed with +/- buttons
- [ ] Quantity can be typed directly
- [ ] Minimum quantity is 1
- [ ] Maximum is available inventory

### Removing Items
- [ ] "Remove" button removes item completely
- [ ] Swipe left on mobile triggers remove
- [ ] Confirmation only for quantities > 1

## Edge Cases
- Empty cart shows "Your cart is empty" state
- Out-of-stock items show disabled "Add to Cart"
- Price changes update in real-time
- Guest carts merge on login

Layer 2: Architectural Specs

How should it be built.

markdown
# Architecture: Shopping Cart Service

## Component Structure

src/ ├── features/cart/ │ ├── components/ │ │ ├── CartItem.tsx │ │ ├── CartSummary.tsx │ │ └── CartBadge.tsx │ ├── hooks/ │ │ └── useCart.ts │ ├── services/ │ │ └── cartService.ts │ └── types/ │ └── cart.ts


## Data Flow

1. User clicks "Add to Cart"
2. `useCart.addItem()` called
3. Optimistic update to local state
4. API call to `/api/cart/items`
5. On success: confirm state
6. On failure: rollback + show error

## State Management

- Use Zustand for cart state
- Persist to localStorage for guests
- Sync with server for logged-in users
- Cart ID stored in cookie (httpOnly)

## API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/cart | Get current cart |
| POST | /api/cart/items | Add item |
| PATCH | /api/cart/items/:id | Update quantity |
| DELETE | /api/cart/items/:id | Remove item |

Layer 3: Technical Constraints

Non-functional requirements and boundaries.

markdown
# Constraints: Shopping Cart

## Performance
- Cart operations complete in < 200ms
- Cart loads in < 100ms (cached)
- No layout shift on quantity changes

## Security
- Cart ID is UUID, not sequential
- Rate limit: 100 operations/minute
- Validate item existence server-side

## Compatibility
- Works without JavaScript (SSR fallback)
- Supports offline mode (queue operations)
- Mobile responsive (touch-friendly)

## Integration Boundaries
- Cart service talks only to Inventory service
- Pricing handled by Pricing service
- No direct database access - API only

Writing Effective Specs

Be Explicit, Not Implicit

Bad:

Handle errors appropriately

Good:

Error Handling:
- Network errors: Show toast, enable retry
- Validation errors: Inline field messages
- Auth errors: Redirect to login
- 500 errors: Log to Sentry, show generic message

Include the "Why"

Bad:

Use Redis for sessions

Good:

Use Redis for sessions because:
- Fast reads for auth middleware
- Built-in TTL for session expiry
- Cluster support for horizontal scaling
Note: Not SQLite - need distributed access

Reference Existing Code

Bad:

Follow existing patterns

Good:

Follow patterns from:
- Auth: See src/services/auth/authService.ts
- API routes: See src/pages/api/users/[id].ts
- Error handling: See src/lib/errors/ApiError.ts

Spec Templates

API Endpoint Spec

markdown
# Endpoint: POST /api/cart/items

## Request
```json
{
  "productId": "prod_123",
  "quantity": 2,
  "variant": "blue-xl"
}

Success Response (201)

json
{
  "cartId": "cart_456",
  "item": {
    "id": "item_789",
    "productId": "prod_123",
    "quantity": 2,
    "price": 29.99,
    "total": 59.98
  },
  "cartTotal": 59.98
}

Error Responses

  • 400: Invalid quantity (< 1)
  • 404: Product not found
  • 409: Insufficient inventory
  • 429: Rate limited

Notes

  • Creates cart if none exists
  • Idempotent for same product/variant
  • Price calculated at add time

### Component Spec

```markdown
# Component: CartItem

## Props
```typescript
interface CartItemProps {
  item: CartItem;
  onQuantityChange: (id: string, qty: number) => void;
  onRemove: (id: string) => void;
  disabled?: boolean;
}

Behavior

  • Displays product image, name, price
  • Quantity controls (+/- buttons)
  • Remove button (trash icon)
  • Shows line total (qty × price)

States

  • Default: Interactive
  • Loading: Disable controls, show spinner
  • Error: Show error message, enable retry
  • Removing: Fade out animation

Accessibility

  • Quantity input is number type
  • Remove button has aria-label
  • Focus management on quantity change

## The Spec Workflow

  1. Think → 2. Spec → 3. Review → 4. Execute ↑__________________________|

### 1. Think

Before writing the spec:
- What problem are we solving?
- What are the constraints?
- What already exists?

### 2. Spec

Write the spec with:
- User perspective (behavioral)
- Developer perspective (architectural)
- Constraints and boundaries

### 3. Review

Before implementation:
- Does the spec answer all questions?
- Are edge cases covered?
- Is it actionable?

### 4. Execute

Give the spec to the AI:

Implement the shopping cart feature per CART_SPEC.md. Start with the CartItem component.


## Common Mistakes

<InlineFAQ>
<FAQItem question="Specs feel like extra work">
Compare:
- 30 minutes writing a spec
- 3 hours of AI back-and-forth without one

Specs front-load thinking. That thinking happens anyway - specs just make it explicit.
</FAQItem>

<FAQItem question="How detailed should specs be?">
Detailed enough that you wouldn't need to answer follow-up questions.

If you're clarifying the same things repeatedly, add them to the spec.
</FAQItem>

<FAQItem question="What if requirements change?">
Update the spec first. Then ask the AI to update the implementation.

The spec becomes the source of truth. Changes are tracked. Decisions are documented.
</FAQItem>

<FAQItem question="Should I spec everything?">
Focus on:
- New features
- Complex changes
- Integrations
- Anything you'll revisit later

Skip for:
- Bug fixes with clear reproduction
- Trivial changes
- Explorations/spikes
</FAQItem>
</InlineFAQ>

<VerifyChecklist
  items={[
    "Write a behavioral spec with acceptance criteria",
    "Create an architectural spec with data flow",
    "Document technical constraints",
    "Explain why specs improve AI output"
  ]}
/>

---

**Next:** Creating long-lived system documents.