Two Ways to Direct Coding Agents
I work with coding agents in two modes. For larger features, I write a detailed spec before any code. For smaller tasks, I skip the spec and set guardrails instead. Both work — they solve different problems.
Full Spec
For new features or anything with non-obvious architectural decisions, I write everything out first — data flow, DB schema, API shape, edge cases. I have a spec skill that interviews me about all of this before any code gets written. What comes out is a document like:
## Data Model
- users table with email, hashed_password, created_at
- sessions table with user_id, token, expires_at
- No soft deletes — hard delete on account removal
## API
- POST /auth/register — validate, hash, insert, return session token
- POST /auth/login — verify, create session, return token
- Auth middleware checks session token on protected routes
## Edge Cases
- Duplicate email returns 409, not a generic error
- Expired sessions return 401, frontend sends user to login
The agent follows this. It doesn’t get to suggest alternatives or rethink things during execution. This eliminates drift — there’s no room to wander.
Guardrails Instead of a Spec
For smaller tasks — bug fixes, refactors, wiring up something that follows an existing pattern — I skip the full spec. I describe the problem and add constraints. A real prompt looks like:
The /auth/login endpoint returns 500 when the email doesn't exist.
It should return 401 with { error: "invalid_credentials" }.
Constraints:
- Do not modify the database schema
- Do not change more than 20 lines
- Keep changes in src/routes/auth.ts
- Run the existing auth tests after fixing
The constraints keep the agent from over-engineering it. “Do not modify the database schema” prevents the agent from deciding it needs a login_attempts table. “Keep changes in src/routes/auth.ts” stops it from refactoring the error handling across three files.
I couple these with validation — linting, type checks, tests — to catch anything that goes outside the boundaries.
When I Use Which
New system, multiple services, design decisions that affect the whole project? Full spec. A bug in a well-understood module, or a refactor that follows an existing pattern? Guardrails and go.
The gray area is medium-sized tasks — adding a feature to an existing system where the pattern is clear but there are a few decisions to make. I usually start with guardrails and tighten them if the agent drifts. If I find myself adding more than four or five constraints, that’s a sign I should write a spec instead.