# Vague spec = vague output
Prompt: "build a website"
Output: Generic template. Could be anyone's site.
# Detailed spec = detailed output
Prompt: "e-commerce platform, OAuth2 auth, Redis-cached cart,
Stripe payments, saga-based order processing,
role-based access for customers vs merchants"
Output: Specific implementation that matches what you described.
Same principle as image diffusion models: the output can't contain more information than the input. A vague prompt produces a vague result. A detailed prompt produces a detailed result.
Complexity is Conserved
Simple spec — Agent guesses — Complex debugging
Detailed spec — Agent resolves — Working system
The total work is the same. You choose when you do it: before generation (in the spec) or after (in debugging).
Example: User Registration
Watch how adding constraints eliminates bugs:
# Spec v1: Minimal detail
"Users can register with email and password"
# What the agent might produce:
def register(email, password):
user = User(email=email, password=password)
db.save(user)
return user
# Problems:
# - Password stored in plaintext
# - No email validation
# - No duplicate check
# - No confirmation flow
# Spec v2: Add constraints
"Users register with email and password.
Hash passwords with bcrypt (cost 12).
Validate email format. Reject duplicates."
# What the agent now produces:
import bcrypt, re
def register(email, password):
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', email):
raise ValueError('Invalid email')
if User.exists(email):
raise ValueError('Email already registered')
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
user = User(email=email, password_hash=hashed)
db.save(user)
return user
# Better, but still missing:
# - Email confirmation
# - Password strength requirements
# Spec v3: Production-ready detail
"Users register with email and password.
Password: min 8 chars, 1 uppercase, 1 number, 1 special.
Hash with bcrypt cost 12.
Validate email format, reject duplicates.
Send confirmation email, mark unverified.
Return 201 + user object on success.
Return 400 + validation errors on failure."
# What the agent produces:
import bcrypt, re, secrets
from email_service import send_confirmation
def register(email, password):
# Validate email
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', email):
return {'error': 'Invalid email format'}, 400
# Check duplicate
if User.exists(email):
return {'error': 'Email already registered'}, 400
# Validate password strength
if len(password) < 8:
return {'error': 'Password must be at least 8 characters'}, 400
if not re.search(r'[A-Z]', password):
return {'error': 'Password must contain uppercase letter'}, 400
if not re.search(r'\d', password):
return {'error': 'Password must contain a number'}, 400
if not re.search(r'[!@#$%^&*]', password):
return {'error': 'Password must contain special character'}, 400
# Hash password
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# Create user
token = secrets.token_urlsafe(32)
user = User(
email=email,
password_hash=hashed,
verified=False,
confirmation_token=token
)
db.save(user)
# Send confirmation email
send_confirmation(email, token)
return {'user': user.to_dict()}, 201
Same feature. Three levels of spec detail. Three levels of output quality. The agent didn't get smarter between v1 and v3. The spec got more specific.
When your spec says "handle authentication," you haven't saved work. You've moved it from "writing the spec" to "debugging why the agent picked session cookies when you needed JWT."
This has formal backing. Tesler's Law: every system has irreducible complexity, and someone has to deal with it. Kolmogorov complexity: you can't compress a spec below the minimum description length without losing information.
The Tradeoff
Writing detailed specs is slow. But debugging vague specs is slower. Pick which kind of slow you prefer.