Use when tempted to add features "for later". Use when building "production-ready" systems before needed. Use when adding flexibility that isn't required yet.
YAGNI (You Ain't Gonna Need It)
Overview
Don't implement something until you actually need it.
Every feature has a cost: code to write, tests to maintain, complexity to manage. Speculative features often go unused while creating real burden.
When to Use
- Adding features "users might want later"
- Building "production-ready" infrastructure upfront
- Adding flexibility "in case requirements change"
- Implementing patterns "because best practices"
- Creating abstractions for hypothetical use cases
The Iron Rule
NEVER build features until they're actually required.
No exceptions:
- Not for "users will probably want this"
- Not for "it's easy to add now"
- Not for "production systems need this"
- Not for "best practices say include this"
Detection: The "Might Need" Smell
If your justification includes "might", "probably", "eventually", or "in case", STOP:
// ❌ VIOLATION: Asked for 3 endpoints, built 15
// Request: GET/POST/DELETE todos
// Built: pagination, filtering, sorting, soft delete, restore,
// rate limiting, health checks, metrics, audit logs,
// batch operations, search, tags, priorities, due dates...
// ✅ CORRECT: Build exactly what was asked
app.get('/todos', (req, res) => { /* list todos */ });
app.post('/todos', (req, res) => { /* create todo */ });
app.delete('/todos/:id', (req, res) => { /* delete todo */ });
The Cost of Speculative Features
Every unneeded feature costs:
| Cost Type | Impact |
|---|---|
| Development time | Hours building unused code |
| Testing burden | Tests for features nobody uses |
| Maintenance | Updates, security patches, dependency management |
| Complexity | More code = more bugs, harder onboarding |
| Cognitive load | Developers must understand unused systems |
| Technical debt | Speculative abstractions often wrong |
Correct Pattern: Minimal First
Build the minimum that solves the actual problem:
// ❌ YAGNI VIOLATION: "Production-ready" todo API
// - Zod validation with inference
// - Pagination with cursors
// - Full-text search
// - Soft delete + restore
// - Rate limiting
// - Request tracing
// - Health endpoints
// - Graceful shutdown
// - Structured logging
// ... for a simple todo list
// ✅ CORRECT: What was actually requested
interface Todo {
id: string;
title: string;
completed: boolean;
}
const todos: Todo[] = [];
app.get('/todos', (req, res) => res.json(todos));
app.post('/todos', (req, res) => {
const todo = { id: crypto.randomUUID(), title: req.body.title, completed: false };
todos.push(todo);
res.status(201).json(todo);
});
app.delete('/todos/:id', (req, res) => {
const index = todos.findIndex(t => t.id === req.params.id);
if (index === -1) return res.status(404).json({ error: 'Not found' });
todos.splice(index, 1);
res.status(204).send();
});
// Add pagination WHEN users have enough todos to need it
// Add search WHEN users ask for search
// Add rate limiting WHEN there's abuse
Pressure Resistance Protocol
1. "Production Systems Need This"
Pressure: "Real production apps have logging, monitoring, health checks..."
Response: Add production features when going to production. Not during prototyping.
Action: Build MVP first. Add infrastructure when deploying for real.
2. "It's Easy to Add Now"
Pressure: "While I'm here, might as well add sorting and filtering"
Response: Easy to add now = easy to add later. Don't pay the maintenance cost until needed.
Action: Add it when someone actually needs it.
3. "Best Practices Say..."
Pressure: "Best practices recommend pagination, rate limiting, etc."
Response: Best practices are for problems you have. Don't solve problems you don't have.
Action: Follow best practices for your actual requirements.
4. "Users Will Want This"
Pressure: "Users will probably want to filter by date"
Response: "Probably" is not a requirement. Wait for actual user requests.
Action: Ship without it. Add when users actually ask.
Red Flags - STOP and Reconsider
If you notice ANY of these, you're violating YAGNI:
- "While I'm at it, I'll also add..."
- "Users might want to..."
- "In case we need to..."
- "Let's make it production-ready"
- "Best practices recommend..."
- Building abstractions for single use cases
- Adding configuration for things that won't change
All of these mean: Stop. Build only what's required.
What YAGNI Is NOT
YAGNI doesn't mean:
- Write bad code (quality is always needed)
- Skip error handling (that's required)
- Ignore security (that's required from day 1)
- Avoid good structure (clean code is required)
YAGNI means: Don't add features and capabilities until they're needed.
Quick Reference
| Speculative | Wait Until |
|---|---|
| Pagination | List exceeds reasonable size |
| Rate limiting | Actual abuse occurs |
| Soft delete | Users request undo capability |
| Full-text search | Users request search |
| Audit logging | Compliance requires it |
| Multi-tenancy | Second tenant exists |
| Caching | Performance problems measured |
Common Rationalizations (All Invalid)
| Excuse | Reality |
|---|---|
| "Production systems need this" | Add when going to production. |
| "It's easy to add now" | Then it's easy to add later too. |
| "Users will probably want it" | Wait until they actually do. |
| "Best practices say..." | For problems you have, not might have. |
| "It'll be harder later" | Usually false. Context will be clearer later. |
| "We'll need it eventually" | Eventually isn't now. |
The Bottom Line
Build what's needed. Nothing more.
When tempted to add "just one more feature": stop, check if it's required NOW, ship without it. The best code is code you didn't have to write.
You Might Also Like
Related Skills

coding-agent
Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.
openclaw
add-uint-support
Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.
pytorch
at-dispatch-v2
Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
pytorch
skill-writer
Guide users through creating Agent Skills for Claude Code. Use when the user wants to create, write, author, or design a new Skill, or needs help with SKILL.md files, frontmatter, or skill structure.
pytorch
implementing-jsc-classes-cpp
Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
oven-sh
implementing-jsc-classes-zig
Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.
oven-sh