pragmatic-architecture
Prevents over-engineering by enforcing industry-proven principles: AHA (Avoid Hasty Abstractions),YAGNI, Colocation, and Rule of Three. Use when designing features, planning architecture,"architect this", "design the structure", or when the code-architect agent is dispatched.Triggers: file structure, module organization, abstraction decisions, component splitting.
When & Why to Use This Skill
The Pragmatic Architecture skill is designed to prevent software over-engineering by enforcing industry-standard principles such as AHA (Avoid Hasty Abstractions), YAGNI (You Ain't Gonna Need It), Colocation, and the Rule of Three. It serves as a strategic guide for developers and architects to build maintainable, scalable systems by prioritizing simplicity and delaying abstraction until concrete needs arise, effectively reducing technical debt and maintenance overhead.
Use Cases
- Feature Planning & Structure: Use this skill when starting a new feature to determine the most efficient file organization and module boundaries, ensuring related code stays together through colocation.
- Abstraction Decision Making: Apply the 'Rule of Three' framework to decide whether to extract shared logic or maintain duplication, preventing the 'wrong abstraction' trap that complicates future changes.
- Codebase Simplification: Identify and remove 'speculative generality'—code added for future requirements that never materialized—to streamline the current architecture.
- Refactoring Guidance: Use the decision framework to evaluate if a complex system should be inlined and re-duplicated to allow a more natural and maintainable pattern to emerge.
- Architectural Guardrails: Act as a 'code-architect' agent to block anti-patterns like 'Shotgun Surgery' or premature creation of abstract factories and generic repositories.
| name | pragmatic-architecture |
|---|---|
| description | | |
| Prevents over-engineering by enforcing industry-proven principles | AHA (Avoid Hasty Abstractions), |
| Triggers | file structure, module organization, abstraction decisions, component splitting. |
| allowed-tools | [] |
Pragmatic Architecture
Design for today's requirements. Let complexity grow incrementally as needed.
The Four Principles
1. Rule of Three (Don Roberts)
"The first time, just do it. The second time, wince at duplication but do it anyway. The third time, refactor."
Abstract on the 3rd occurrence, not before.
| Occurrences | Action |
|---|---|
| 1 | Implement directly |
| 2 | Duplicate, note similarity |
| 3+ | Now extract abstraction |
2. AHA - Avoid Hasty Abstractions (Kent C. Dodds)
"Prefer duplication over the wrong abstraction." — Sandi Metz
Duplication is cheaper than the wrong abstraction. Wrong abstractions accumulate parameters and conditionals until unmaintainable.
Signs of wrong abstraction:
- Adding parameters to handle "just one more case"
- Conditional paths through shared code
- Comments explaining which caller uses which path
Recovery: Inline the abstraction, re-duplicate, let the right pattern emerge.
3. YAGNI - You Ain't Gonna Need It (Ron Jeffries)
"Always implement things when you actually need them, never when you just foresee that you need them."
No hooks for future features. No "we might need this someday."
| ❌ Speculative | ✅ Pragmatic |
|---|---|
options?: ExtensionConfig unused |
Add when extension exists |
| Abstract factory for one impl | Direct instantiation |
| Event system for single listener | Direct call |
| Plugin architecture for no plugins | Hardcoded behavior |
4. Colocation (Kent C. Dodds)
"Place code as close to where it's relevant as possible."
Minimize file splitting. Keep related code together.
| ❌ Over-split | ✅ Colocated |
|---|---|
types/user.ts, utils/user.ts, constants/user.ts |
Single user.ts with all |
Separate styles/, hooks/, utils/ folders |
Component folder with all related |
| 10 files for one feature | 1-3 files max per feature |
Decision Framework
Before creating abstraction, ask:
1. Does this exist 3+ times? → No? Don't abstract
2. Are the use cases truly identical? → No? Don't abstract
3. Would someone unfamiliar understand this? → No? Simpler design
4. Am I solving today's problem? → No? YAGNI
File Organization Rules
Prefer:
- Feature-based folders over type-based folders
- Fewer, larger files over many tiny files
- Colocated tests (
foo.test.tsnext tofoo.ts) - Single source of truth per concept
Avoid:
types/,utils/,constants/,helpers/folders that scatter related code- Files under 50 lines (usually should be merged)
- More than 3 files per feature (unless genuinely complex)
Anti-Patterns to Block
Speculative Generality (Fowler)
"Oh, I think we'll need the ability to do this someday"
Symptoms: Abstract classes doing nothing, unused parameters, delegation that could be direct.
Block: Any "for future use" justification.
Shotgun Surgery (Fowler)
"One change requires modifications across many files"
Caused by over-splitting. Fix by consolidating related code.
Block: Designs requiring 5+ file changes for simple features.
Premature Abstraction
Creating BaseHandler, AbstractService, GenericRepository before concrete needs exist.
Block: Any abstract class without 2+ concrete implementations in the plan.
Output Requirements
When designing architecture:
- Justify every new file — Why can't this live in an existing file?
- Justify every abstraction — Where are the 3 concrete uses?
- Prefer boring — Standard patterns over clever solutions
- Count the files — If feature needs 5+ files, reconsider
References
For detailed patterns and examples:
references/file-organization.md— Colocated vs scattered patternsreferences/abstraction-examples.md— Good vs bad abstraction decisions