Clean Code
Pragmatic coding standards for writing clean, maintainable code — naming, functions, structure, anti-patterns, and pre-edit safety checks. Use when writing new code, refactoring existing code, reviewi
Pragmatic coding standards for writing clean, maintainable code — naming, functions, structure, anti-patterns, and pre-edit safety checks. Use when writing new code, refactoring existing code, reviewi
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Be concise, direct, and solution-focused. Clean code reads like well-written prose — every name reveals intent, every function does one thing, and every abstraction earns its place.
npx clawhub@latest install clean-code
| Principle | Rule | Practical Test |
|---|---|---|
| SRP | Single Responsibility — each function/class does ONE thing | "Can I describe what this does without using 'and'?" |
| DRY | Don't Repeat Yourself — extract duplicates, reuse | "Have I written this logic before?" |
| KISS | Keep It Simple — simplest solution that works | "Is there a simpler way to achieve this?" |
| YAGNI | You Aren't Gonna Need It — don't build unused features | "Does anyone need this right now?" |
| Boy Scout | Leave code cleaner than you found it | "Is this file better after my change?" |
Names are the most important documentation. A good name eliminates the need for a comment.
| Element | Convention | Bad | Good |
|---|---|---|---|
| Variables | Reveal intent | , , | , , |
| Functions | Verb + noun | , | , |
| Booleans | Question form | , | , , |
| Constants | SCREAMING_SNAKE | , | , |
| Classes | Noun, singular | , | , |
| Enums | PascalCase values | string | |
Rule: If you need a comment to explain a name, rename it.
| Anti-Pattern | Problem | Fix |
|---|---|---|
Cryptic abbreviations (, ) | Unreadable in 6 months | Spell it out — IDE autocomplete makes long names free |
Generic names (, , , ) | Says nothing about purpose | Use domain-specific names that reveal intent |
Misleading names ( returns one user) | Actively deceives readers | Match name to behavior, or change the behavior |
Hungarian notation (, , ) | Redundant with type system | Let TypeScript/IDE show types; names describe purpose |
| Rule | Guideline | Why |
|---|---|---|
| Small | Max 20 lines, ideally 5-10 | Fits in your head |
| One Thing | Does one thing, does it well | Testable and nameable |
| One Level | One level of abstraction per function | Readable top to bottom |
| Few Args | Max 3 arguments, prefer 0-2 | Easy to call correctly |
| No Side Effects | Don't mutate inputs unexpectedly | Predictable behavior |
Flatten nested conditionals with early returns. Never nest deeper than 2 levels.
// BAD — 5 levels deep function processOrder(order: Order) { if (order) { if (order.items.length > 0) { if (order.customer) { if (order.customer.isVerified) { return submitOrder(order); } } } } throw new Error('Invalid order'); }// GOOD — guard clauses flatten the structure function processOrder(order: Order) { if (!order) throw new Error('No order'); if (!order.items.length) throw new Error('No items'); if (!order.customer) throw new Error('No customer'); if (!order.customer.isVerified) throw new Error('Customer not verified');
return submitOrder(order); }
When a function needs more than 3 arguments, use an options object.
// BAD — too many parameters, order matters createUser('John', 'Doe', 'john@example.com', 'secret', 'admin', 'Engineering');// GOOD — self-documenting options object createUser({ firstName: 'John', lastName: 'Doe', email: 'john@example.com', password: 'secret', role: 'admin', department: 'Engineering', });
| Pattern | When to Apply | Benefit |
|---|---|---|
| Guard Clauses | Edge cases at function start | Flat, readable flow |
| Flat > Nested | Any nesting beyond 2 levels | Reduced cognitive load |
| Composition | Complex operations | Small, testable pieces |
| Colocation | Related code across files | Easier to find and change |
| Extract Function | Comments separating "sections" | Self-documenting code |
// BAD — god function doing everything async function processOrder(order: Order) { // Validate... (15 lines) // Calculate totals... (15 lines) // Process payment... (10 lines) // Send notifications... (10 lines) // Update inventory... (10 lines) return { success: true }; }// GOOD — composed of small, focused functions async function processOrder(order: Order) { validateOrder(order); const totals = calculateOrderTotals(order); const payment = await processPayment(order.customer, totals); await sendOrderConfirmation(order, payment); await updateInventory(order.items); return { success: true, orderId: payment.orderId }; }
Functions should return consistent types. Use discriminated unions for multiple outcomes.
// BAD — returns different types function getUser(id: string) { const user = database.find(id); if (!user) return false; // boolean if (user.isDeleted) return null; // null return user; // User }// GOOD — discriminated union type GetUserResult = | { status: 'found'; user: User } | { status: 'not_found' } | { status: 'deleted' };
function getUser(id: string): GetUserResult { const user = database.find(id); if (!user) return { status: 'not_found' }; if (user.isDeleted) return { status: 'deleted' }; return { status: 'found', user }; }
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Comment every line | Noise obscures signal | Delete obvious comments; comment why, not what |
| Helper for one-liner | Unnecessary indirection | Inline the code |
| Factory for 2 objects | Over-engineering | Direct instantiation |
with 1 function | Junk drawer file | Put code where it's used |
| Deep nesting | Unreadable flow | Guard clauses and early returns |
| Magic numbers | Unclear intent | Named constants |
| God functions | Untestable, unreadable | Split by responsibility |
| Commented-out code | Dead code confusion | Delete it; git remembers |
| TODO sprawl | Never gets done | Track in issue tracker, not code |
| Premature abstraction | Wrong abstraction is worse than none | Wait for 3+ duplicates before abstracting |
| Copy-paste programming | Duplicated bugs | Extract shared logic |
| Exception-driven control flow | Slow and confusing | Use explicit conditionals |
| Stringly-typed code | Typos and missed cases | Use enums or union types |
| Callback hell | Pyramid of doom | Use async/await |
Before changing any file, answer these questions to avoid cascading breakage:
| Question | Why |
|---|---|
| What imports this file? | Dependents might break on interface changes |
| What does this file import? | You might need to update the contract |
| What tests cover this? | Tests might fail — update them alongside code |
| Is this a shared component? | Multiple consumers means wider blast radius |
File to edit: UserService.ts ├── Who imports this? → UserController.ts, AuthController.ts ├── Do they need changes too? → Check function signatures └── What tests cover this? → UserService.test.ts
Rule: Edit the file + all dependent files in the SAME task. Never leave broken imports or missing updates.
Before marking any task complete, verify:
| Check | Question |
|---|---|
| Goal met? | Did I do exactly what was asked? |
| Files edited? | Did I modify all necessary files, including dependents? |
| Code works? | Did I verify the change compiles and runs? |
| No errors? | Do lint and type checks pass? |
| Nothing forgotten? | Any edge cases or dependent files missed? |
Detailed guides for specific clean code topics:
| Reference | Description |
|---|---|
| Anti-Patterns | 21 common mistakes with bad/good code examples across naming, functions, structure, and comments |
| Code Smells | Classic code smells catalog with detection patterns — Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers |
| Refactoring Catalog | Essential refactoring patterns with before/after examples and step-by-step mechanics |
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.