Best Practices
30 minutes
intermediate

Long-Lived System Documents

Create persistent context documents that preserve knowledge across sessions and team members.

What you'll accomplish

Build a documentation system that compounds your AI effectiveness over time.

Prerequisites

  • Understanding of spec-driven development
  • Some project experience

The Context Problem

Every new AI session starts with a blank slate. You spend the first 10 minutes re-explaining:

  • What the project does
  • How it's structured
  • Coding conventions
  • What to avoid

This happens whether it's you tomorrow or a teammate next week. Knowledge leaks.

Long-lived system documents solve this by externalizing context into files the AI can reference.

The Document Hierarchy

your-project/
├── AGENTS.md           # AI-specific instructions
├── docs/
│   ├── ARCHITECTURE.md  # System design
│   ├── DECISIONS.md     # Why we built it this way
│   ├── PATTERNS.md      # Common code patterns
│   └── GLOSSARY.md      # Domain terminology
└── specs/
    └── [feature specs]

Each document serves a different purpose. Together, they form institutional memory.

AGENTS.md: The AI Instruction File

The most important document. Claude Code (and similar tools) automatically read it.

Structure

markdown
# Project: StackForDevs

## Overview
StackForDevs is a Backend-as-a-Service platform for SaaS teams.
It provides auth, billing, notifications, and multi-tenancy out of the box.

## Architecture

### Directory Structure
- `/src/app` - Next.js App Router pages and API routes
- `/src/services` - Business logic (auth, billing, notifications)
- `/src/lib` - Shared utilities (db, redis, stripe)
- `/prisma` - Database schema and migrations

### Key Patterns
1. All API routes use `withAuth` middleware
2. Database access only through Prisma client in `/src/lib/db`
3. Background jobs use BullMQ with Redis

## Coding Conventions

### TypeScript
- Strict mode enabled
- Use `interface` for object shapes, `type` for unions
- No `any` - use `unknown` if type is truly unknown
- Export types from dedicated `types.ts` files

### Components
- Server components by default
- `'use client'` only when needed
- Props interface in same file
- Named exports (not default)

### API Routes
- Always return proper status codes
- Errors wrapped in `ApiError` class
- Log errors to our logging service

## DO NOT
- Never store secrets in code - use environment variables
- Never import from `/src/app` in `/src/services`
- Never call external APIs directly - use service wrappers
- Never skip the auth middleware on protected routes

## Common Tasks

### Adding a new API endpoint
1. Create route in `/src/app/api/[resource]/route.ts`
2. Add service method in `/src/services/[resource]Service.ts`
3. Add types in `/src/types/[resource].ts`
4. Wrap with `withAuth` if protected

### Database changes
1. Modify `/prisma/schema.prisma`
2. Run `npx prisma migrate dev --name descriptive-name`
3. Update affected service methods
4. Add types for new fields

This AGENTS.md is from StackForDevs, a real production Backend-as-a-Service platform built using the methodology taught in this track. You can see it and other AI-built systems on our Showcase page.

What Makes Good AGENTS.md

Be prescriptive, not descriptive:

markdown
# Bad
We use TypeScript

# Good
TypeScript Rules:
- Strict mode is enabled
- Use `interface` for shapes, `type` for unions
- No `any` - use `unknown` for unknown types

Include anti-patterns:

markdown
## DO NOT
- Import services into components (use hooks)
- Call APIs directly (use service layer)
- Store state in global variables

Reference locations:

markdown
See example: `/src/services/authService.ts`
Pattern from: `/src/app/api/users/route.ts`

ARCHITECTURE.md: System Design

For understanding the big picture:

markdown
# System Architecture

## High-Level Overview

┌─────────────────┐ ┌─────────────────┐ │ Web Client │────▶│ API Gateway │ └─────────────────┘ └────────┬────────┘ │ ┌────────────────────────┼────────────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Auth Service │ │ Billing Svc │ │ Notify Service│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │ │ │ ▼ ▼ ▼ PostgreSQL Stripe API SendGrid/Twilio


## Service Boundaries

### Auth Service
- Handles: registration, login, OAuth, sessions
- Owns: users, sessions, api_keys tables
- Dependencies: PostgreSQL, Redis (sessions)

### Billing Service
- Handles: subscriptions, invoices, usage tracking
- Owns: subscriptions, invoices, usage tables
- Dependencies: PostgreSQL, Stripe

### Notification Service
- Handles: email, SMS, in-app notifications
- Owns: notification_preferences, notification_log tables
- Dependencies: PostgreSQL, SendGrid, Twilio

## Data Flow: User Signs Up

1. Client POSTs to `/api/auth/signup`
2. Auth service validates input
3. Creates user in PostgreSQL
4. Creates session in Redis
5. Triggers welcome email via Notification service
6. Returns JWT + session cookie

## Integration Points

| Service | External API | Purpose |
|---------|--------------|---------|
| Billing | Stripe | Payment processing |
| Notifications | SendGrid | Transactional email |
| Notifications | Twilio | SMS |
| Analytics | Segment | Event tracking |

DECISIONS.md: Why We Built It This Way

Capture the rationale behind decisions:

markdown
# Decision: Use Redis for Sessions

**Date**: 2024-01-15
**Status**: Accepted
**Deciders**: @dom, @alex

## Context
Need to store user sessions for the auth system.

## Options Considered

### Option 1: Database (PostgreSQL)
- ✅ Already have PostgreSQL
- ✅ Transactional with user data
- ❌ Slower reads for every request
- ❌ More database load

### Option 2: Redis
- ✅ Sub-millisecond reads
- ✅ Built-in TTL for expiry
- ✅ Horizontal scaling
- ❌ Additional infrastructure
- ❌ Data loss on restart (mitigated by persistence)

### Option 3: JWT Only
- ✅ Stateless
- ❌ Can't revoke tokens
- ❌ Large token size
- ❌ No real-time session management

## Decision
Use Redis with Redis persistence enabled.

## Consequences
- Need to provision Redis instance
- Session data is eventually consistent
- Can invalidate sessions immediately

PATTERNS.md: Reusable Code Patterns

Document patterns that repeat:

markdown
# Code Patterns

## API Route with Auth

```typescript
// src/app/api/[resource]/route.ts
import { withAuth } from '@/lib/auth/middleware';
import { ApiError, handleApiError } from '@/lib/errors';

export const GET = withAuth(async (req, { user }) => {
  try {
    const data = await resourceService.getByUser(user.id);
    return Response.json(data);
  } catch (error) {
    return handleApiError(error);
  }
});

Service Method

typescript
// src/services/resourceService.ts
export async function create(
  userId: string,
  input: CreateResourceInput
): Promise<Resource> {
  // 1. Validate
  const validated = createResourceSchema.parse(input);

  // 2. Check permissions
  await assertCanCreate(userId, validated);

  // 3. Perform action
  const resource = await db.resource.create({
    data: { ...validated, userId },
  });

  // 4. Side effects
  await notifyService.send(userId, 'resource_created', resource);

  return resource;
}

React Hook for API

typescript
// src/hooks/useResource.ts
export function useResource(id: string) {
  return useQuery({
    queryKey: ['resource', id],
    queryFn: () => api.resources.get(id),
    staleTime: 1000 * 60, // 1 minute
  });
}

## GLOSSARY.md: Domain Terminology

Define domain-specific terms:

```markdown
# Glossary

## Tenant
A customer organization using the platform. Each tenant has isolated data.

## Workspace
A subdivision within a tenant. Users can belong to multiple workspaces.

## API Key
A token for programmatic access. Scoped to a workspace.

## Usage Record
A single billable event (API call, storage unit, etc.).

## Billing Cycle
The recurring period for subscription billing. Default: monthly.

Maintaining Documents

When to Update

EventDocuments to Update
New featureAGENTS.md, possibly PATTERNS.md
Architecture changeARCHITECTURE.md, DECISIONS.md
Bug from unclear conventionAGENTS.md (add to DO NOT)
New team member confusedGLOSSARY.md or AGENTS.md

Keep Documents Alive

Review monthly: Are they still accurate?

Update on change: When you explain something to the AI twice, document it.

Delete outdated content: Old information is worse than no information.

Common Issues

Frequently Asked Questions

Include document updates in your definition of done. A feature isn't complete until docs are updated.

Use the AI to help: "Update AGENTS.md to reflect the new auth flow we just implemented."

If the AI needs to know it to do its job, include it. If it's implementation details it can figure out from code, skip it.

Focus on: conventions, boundaries, gotchas, patterns.

  • Specs: Feature-specific, temporary, in /specs/
  • System docs: Project-wide, permanent, in /docs/ or root

Specs may become patterns. Graduate them when they repeat.

Verify Your Understanding

You've completed this lesson if you can:

0/4

Next: Using MCPs, RAGs, and Skills for context preservation.