| name | description |
|---|---|
grug-brain |
Use when designing features, writing code, reviewing implementations, considering refactoring, evaluating libraries, or applying DRY/SOLID principles - helps resist over-abstraction, premature generalization, unnecessary frameworks, and complexity through systematic questioning about actual needs vs theoretical benefits |
Core principle: Complexity is the enemy. Simple, obvious code beats clever abstractions. Question everything that adds indirection.
This skill helps you recognize and resist common complexity traps throughout the development lifecycle.
Use this skill during:
- Design/brainstorming - Before committing to approach
- Implementation - While writing code
- Code review - Evaluating existing code
- Refactoring decisions - Before restructuring
Before adding abstraction, creating patterns, or introducing dependencies, ask:
- "Feels messy" is not a problem
- "Could be cleaner" is not a problem
- "Might need X later" is not a problem
- Real problems: "I had to change 5 files for one feature" or "this function is 400 lines and I can't understand it"
- Not what I might need
- Not what would be elegant
- Not what's theoretically scalable
- What I need right now for this concrete use case
- 1 use = no abstraction (copy-paste is fine)
- 2 uses = probably no abstraction (duplication okay)
- 3+ uses = maybe abstract (if pattern is obvious)
- 15 lines of if/else vs 40 lines of lookup table pattern
- 3 inline validations vs framework with config
- Direct function calls vs strategy pattern with interfaces
- What's the simplest thing that gets 80% of the value?
- Can I accept imperfection instead of generalization?
Symptom: "This code could be cleaner/more elegant"
Ask:
- Cleaner how? What specific problem does "messy" code cause?
- Is the code hard to understand, or just repetitive-looking?
- Will the abstraction make changes easier or harder?
Example:
// "Feels repetitive"
if (type === 'A') { doA(); logA(); }
if (type === 'B') { doB(); logB(); }
if (type === 'C') { doC(); logC(); }
// Grug: This is fine. Each type does different things.
// Lookup table would scatter code and add indirection.
// Refactor when you have 10 types and it annoys you, not at 3.Symptom: "Let's make this extensible/pluggable/configurable"
Ask:
- Extensible for what? Do I have concrete second use case?
- Is this speculation about future needs?
- What happens if I just write the simple version now?
Grug says: Write code for the use case you have. When second use case appears, then consider abstraction. Most "future needs" never happen.
Symptom: "These 3 places have similar code - violates DRY"
Ask:
- Is this duplication of knowledge or duplication of coincidence?
- If requirements diverge, do I want them coupled?
- Is locality (code near its use) more valuable than DRY?
Grug says: Some duplication is good. Simple repeated code beats elaborate DRY abstraction. Prefer obvious code over clever code.
Example:
// Three forms with same validation
const emailError = !email.includes('@') ? 'Invalid' : null;
// Grug: Keep it. They're independent requirements that
// happen to match today. Tomorrow they might diverge.
// Don't create coupling for 1 line of code.Symptom: "Should I use library X? This seems like reinventing the wheel"
Ask:
- How many cases do I actually handle? (not how many exist)
- Is my code working? Is it 50 lines or 500?
- What's the library size/complexity vs my simple code?
Grug says: 20 lines of code you understand beats 100KB library that handles Swahili dates. Use library when simple code stops working, not because library exists.
Example:
// Parsing 2-3 date patterns
if (msg.includes('next Tuesday')) { /* calc */ }
if (msg.match(/\w+ \d+/)) { /* parse */ }
// Grug: If this handles your actual use cases, keep it.
// Add chrono-node when you need 20 patterns, not at 2.Symptom: "We need clean architecture/layers/hexagonal/DDD"
Ask:
- What specific problem am I solving?
- Is my business logic actually complex or just API orchestration?
- Will layers make changes easier or add ceremony?
Grug says: Route-level organization is fine for most apps. Introduce layers only when you have concrete need (like swapping implementations). Most apps never need swappable implementations.
Symptom: "This violates [Open/Closed|SOLID|DRY|etc] principle"
Ask:
- What concrete harm results from this "violation"?
- Am I naming principles to sound smart, or solving real problems?
- Would following the principle actually improve this code?
Grug says: Principles are guidelines, not laws. "Violates X principle" is not a problem unless you can point to concrete harm. Simple working code that "violates" principles beats complex correct code.
Keep related code together, even if it creates some duplication.
// GOOD: All booking logic in one place
function processBooking(booking) {
if (booking.type === 'rafting') {
sendRaftingEmail();
notifyRaftingGuide();
}
if (booking.type === 'camping') {
sendCampingEmail();
notifyCampingHost();
}
}
// BAD: Scattered across handlers/strategies
const handlers = { rafting: RaftingHandler, camping: CampingHandler };
// Now need to jump between files to understand one booking typeStart with inline code. Extract when duplication becomes genuinely annoying (5+ places, not 2).
Break complex expressions into named steps.
// GOOD: Debuggable, readable
const isValidUser = user.active && user.verified;
const hasAccess = isValidUser && user.role === 'admin';
if (hasAccess) { ... }
// BAD: Clever one-liner
if (user.active && user.verified && user.role === 'admin') { ... }Prefer stateless handlers and independent jobs over complex shared state.
// GOOD: Simple parallel calls
await Promise.all([
sendEmail(booking),
notifyGuide(booking),
logAnalytics(booking)
]);
// BAD: Complex state machine with locksWhen someone (including you) suggests complexity:
1. Question: "What problem does this solve?"
- If answer is vague ("better architecture", "more maintainable"), reject
- If answer is concrete ("changing X requires editing 8 files"), consider
2. Question: "What's the simplest solution?"
- Often: just copy-paste the code
- Or: extract one focused function
- Not: create extensibility framework
3. Question: "Can I do 80/20?"
- Full solution: support all date formats with NLP library
- 80/20: handle the 3 formats users actually send
- Choose 80/20 until it stops working
4. If still adding abstraction: "Will I regret this?"
- Small focused function: probably fine
- New abstraction layer/pattern: probably regret
With test-driven-development:
- Write test first (TDD)
- Make test pass with simplest code (grug)
- Don't refactor to patterns until 3+ uses (grug)
With systematic-debugging:
- Add logging for observability (both agree)
- Prefer simple code that's easy to debug (grug)
- Named intermediate variables for breakpoint debugging (grug)
With brainstorming:
- Explore options (brainstorming)
- Pick simplest that works (grug)
- Delay abstraction decisions (grug)
| Excuse | Reality |
|---|---|
| "Violates DRY/SOLID/Clean principles" | Naming principles doesn't make abstraction necessary |
| "Makes code more maintainable" | Abstraction without clear need reduces maintainability |
| "What if we need X later?" | Most future needs never happen. Add when needed. |
| "This will be more scalable" | Scalable to what? Current code handles current scale. |
| "Best practice is to..." | Best practice depends on context. Simple is best. |
| "The pattern is cleaner" | Cleaner how? Is it actually simpler? |
| "Industry standard approach" | Industry writes complex code. Don't copy bad habits. |
| "More testable this way" | Simple code is easiest to test |
| "This is how senior devs do it" | Senior devs write simple code, juniors write clever code |
| "What seems simple quickly becomes complex" | Ask about actual needs first, don't assume future complexity |
| "This is exactly where [pattern/library] pays off" | Asserting benefit without measuring actual need |
| "You'll spend weeks building what libraries solved" | Assumes you need everything the library solves |
| "Tech lead/code review requires it" | Push back with concrete questions about actual problems |
Stop and reconsider if you're saying:
- "Let's make this more abstract"
- "What if we need to support X in the future?"
- "This violates [principle]"
- "Industry best practice is..."
- "More maintainable/scalable/testable"
- "Let me create a framework for this"
- "We should use [library] instead of writing code"
All of these trigger grug alarm. Ask the questions above first.
Complexity is the enemy. Simple working code is the goal.
Question everything that adds indirection, abstraction, or dependencies. Most complexity comes from solving problems you don't have yet.
When in doubt: write the obvious code, make it work, resist the urge to make it "better."
Future you will thank you for simple code they can understand, not clever abstractions they have to decode.