Skip to content

Instantly share code, notes, and snippets.

@SeverinAlexB
Created February 5, 2026 05:03
Show Gist options
  • Select an option

  • Save SeverinAlexB/d717b26b28485f90e22c5b243e4065f4 to your computer and use it in GitHub Desktop.

Select an option

Save SeverinAlexB/d717b26b28485f90e22c5b243e4065f4 to your computer and use it in GitHub Desktop.
Clean Code Skill
Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 3 column 1
---
name: clean-code
description: Pre-implementation guide for writing clean, maintainable code following SOLID principles, proper naming conventions, 
and dependency management. Use before implementing any feature to structure code with single responsibility, 
minimal complexity, comprehensive testing, and correct dependency flow.
---

Clean Code Rules - Pre-Implementation Guide

WHEN TO APPLY: Before implementing any feature, follow these rules to structure code properly from the start.

IMPORTANT: This skill has been written for typescript. Adapt it to the standards of the requested programming language. For example, rust uses snake_case instead of the here defined camelCase.


Core Principles

1. Obviousness for Others

Code must be immediately clear to other developers:

  • Descriptive names - Variables, methods, classes reveal intent without comments
  • Consistent naming - Follow project conventions throughout
  • No magic numbers - Use named constants
  • Single responsibility - Each unit does one thing well

2. No Duplication

Avoid repeating logic:

  • DRY principle - Extract shared code to methods/classes
  • Reuse over rewrite - Search for existing implementations first
  • Abstraction - Create shared utilities for common patterns

3. Minimal Complexity

Keep codebase lean:

  • Less code = less bugs - Code is liability
  • Minimal classes - Only create when necessary
  • Simple solutions - Avoid over-engineering
  • Delete unused code - Remove immediately, rely on git history

4. Comprehensive Testing

Every feature needs tests:

  • Test coverage - Aim for critical paths at minimum
  • Tests pass - All tests green before committing
  • Write tests first - TDD when appropriate

5. Easy Maintenance

Code will be read more than written:

  • Future-proof - Consider who maintains this in 6 months
  • Refactor continuously - Don't accumulate debt
  • Document why, not what - Code shows what, comments explain why

Design Principles

SOLID Principles

S - Single Responsibility Principle

  • Class has one reason to change
  • Each class/method does one thing
  • Split classes doing multiple jobs

O - Open/Closed Principle

  • Open for extension, closed for modification
  • Use inheritance/composition for new behavior
  • Avoid modifying existing working code

L - Liskov Substitution Principle

  • Subclasses replaceable for parent class
  • Don't break parent class contracts
  • Subclasses should extend, not restrict

I - Interface Segregation Principle

  • Many specific interfaces > one general interface
  • Clients shouldn't depend on unused methods
  • Split fat interfaces into focused ones

D - Dependency Inversion Principle

  • Depend on abstractions, not concretions
  • High-level modules shouldn't depend on low-level
  • Use interfaces/abstract classes for dependencies

Additional Principles

Encapsulation

  • Hide internal state and implementation
  • Expose only necessary public interface
  • Use private fields with public methods

Composition Over Inheritance

  • Prefer "has-a" over "is-a"
  • More flexible than inheritance
  • Easier to change behavior at runtime

Program to Interfaces

  • Depend on contracts, not implementations
  • Enables polymorphism and testability
  • Decouples components

Naming Conventions

Variables

  • Descriptive - userAge not x
  • Pronounceable - timestamp not ts
  • Searchable - MAX_RETRY_COUNT not 5
  • No abbreviations - customerList not custLst
  • Context matters - user.name not user.userName

Methods

  • Verbs - calculateTotal(), getUserById()
  • Reveal intent - isEligibleForDiscount() not check()
  • Consistent prefixes - get, set, is, has, create, delete
  • Avoid generic - Not processData(), handleInput()

Classes

  • Nouns - User, OrderProcessor, EmailService
  • Single responsibility - Name reflects one job
  • Avoid "Manager"/"Helper" - Usually signals unclear responsibility
  • Specific - UserValidator not Validator

Constants

  • ALL_CAPS - MAX_CONNECTIONS, DEFAULT_TIMEOUT
  • Descriptive - Explain purpose
  • Grouped - Related constants in enum/object

Method Design

Size Limits

  • 10 lines max - Longer? Extract methods
  • 3 parameters max - More? Use parameter object
  • Single level of abstraction - Don't mix high/low level operations
  • One purpose - Do one thing, do it well

Structure

  • Guard clauses first - Handle edge cases early, return
  • Main logic - Core functionality at single abstraction level
  • No side effects - Don't modify state unexpectedly
  • Query/Command separation - Either return value OR modify state, not both

Patterns to Follow

// Good - guard clauses
function processOrder(order) {
  if (!order) return null
  if (!order.isValid()) return null
  if (order.isEmpty()) return null

  return order.process()
}

// Bad - nested conditions
function processOrder(order) {
  if (order) {
    if (order.isValid()) {
      if (!order.isEmpty()) {
        return order.process()
      }
    }
  }
  return null
}

Class Design

Size and Responsibility

  • <200 lines - Larger? Split class
  • Single responsibility - One reason to change
  • High cohesion - Methods use most fields
  • Low coupling - Minimal dependencies on other classes

Structure Order

  1. Static constants
  2. Static fields
  3. Instance fields
  4. Constructors
  5. Public methods
  6. Private methods (organized by caller proximity)

Avoid Patterns

  • God classes - Do everything
  • Data classes - Only getters/setters, no behavior
  • Utility classes - Random static methods
  • Manager classes - Vague responsibility
  • Function bloat - Many loose functions sharing data/context that should be a class

When Functions Should Be a Class

Convert standalone functions to a class when:

  • Shared state - Functions pass same data between each other (hash, element, config)
  • Cohesive operations - Functions operate on same concept/entity
  • Internal helpers - Private functions only called by other functions in same file
  • Lifecycle - Operations have setup/teardown or state transitions
// Bad - function bloat
function applyBlur(element, hash) { ... }
function createOverlay(hash) { ... }
function insertOverlay(element, overlay) { ... }
function unblurImage(element, hash) { ... }

// Good - cohesive class
class BlurManager {
  constructor(private element: MediaElement) {}

  blur(hash: string): void { ... }
  unblur(): void { ... }

  private createOverlay(): HTMLElement { ... }
  private insertOverlay(overlay: HTMLElement): void { ... }
}

Signs of function bloat:

  • Functions in same file passing same parameters
  • Helper functions only used internally
  • Functions that logically "belong together"
  • File named *Manager.ts or *Utils.ts with loose functions

Conditional Logic

Simplification Rules

  • Extract to methods - if (user.isActive()) not if (user.status === 'active')
  • Guard clauses - Early returns for special cases
  • Polymorphism - Replace type-checking switches
  • Null objects - Instead of null checks everywhere
  • Boolean variables - const isValid = condition then if (isValid)

Pattern Examples

// Good - polymorphism
class Shape {
  abstract area(): number
}
class Circle extends Shape {
  area() { return Math.PI * r * r }
}

// Bad - type checking
function area(shape) {
  if (shape.type === 'circle') {
    return Math.PI * shape.r * shape.r
  }
}

Data Organization

Primitives vs Objects

  • Value objects - For domain concepts (Email, Money, UserId)
  • No primitive obsession - Don't use string for everything
  • Type safety - Objects provide validation and behavior
  • Explicit intent - Email class > string

Collections

  • Encapsulate - Don't expose raw arrays/lists
  • Immutability - Return copies, not references
  • Meaningful names - activeUsers not list1
  • Single type - Avoid mixed-type collections

State Management

  • Immutable by default - Use readonly/final
  • Value objects - Immutable data structures
  • References when needed - Shared mutable state only when necessary
  • No global state - Pass dependencies explicitly

Comments and Documentation

When to Comment

  • Why, not what - Code shows what, comments explain why
  • Business rules - Explain domain logic
  • Warnings - Gotchas or consequences
  • Legal - Copyright, licenses
  • TODO - With ticket number and date

When NOT to Comment

  • Obvious code - Self-documenting names instead
  • Redundant - // increment i above i++
  • Outdated - Maintain or delete
  • Commented code - Delete it, use git history
  • Journal comments - Use git log instead

Better Than Comments

  • Extract method - Complex block → named method
  • Rename - Variable/method with better name
  • Constants - Magic number → named constant
  • Type system - Use types to encode constraints

Error Handling

Exceptions

  • Use exceptions - Not error codes
  • Specific exceptions - Custom types for different errors
  • Meaningful messages - Include context
  • Handle at boundaries - Let exceptions bubble up
  • Don't catch generic - Catch specific exception types

Validation

  • Fail fast - Validate at entry points
  • Guard clauses - Check preconditions early
  • Assertions - Document assumptions
  • Type system - Use for compile-time checks

Dependencies and Coupling

Dependency Rules

  • Explicit > implicit - Constructor injection over global state
  • Abstractions - Depend on interfaces
  • Minimal dependencies - Only what's needed
  • No circular - A depends on B, B on A = wrong

File Organization

  • One class per file - Unless tightly coupled
  • Related files near - Feature folders, not type folders
  • Clear structure - Predictable locations
  • Encapsulation - Internal implementation hidden

Dependency Hierarchy

Use Clean Architecture as proposed by Robert C. Martin. Core idea: separate business logic from external concerns (UI, databases, frameworks). Inner layers contain policies/rules, outer layers contain mechanisms/details. Dependencies point inward only—inner layers never know about outer layers. This makes business logic testable, framework-independent, and easy to change.

Code must form a directed acyclic graph from entry point (src/main.ts) down.

Entry Point (index.ts/main.ts)
    │
    ▼
Bootstrap/Composition Root
    │
    ├──▶ High-Level Orchestrators (Controllers)
    │         │
    │         ├──▶ Domain Services
    │         │         │
    │         │         └──▶ Domain Models
    │         │
    │         └──▶ Infrastructure (API clients, storage)
    │
    └──▶ Framework Integration (event listeners, UI)

Rules:

  • Dependencies flow DOWN only - never up or sideways
  • Lower layers NEVER import from higher layers
  • Each layer only knows about the layer directly below, potentially skipping a layer or two but not more

Module-Level Code

Allowed at module level:

  • Type definitions and interfaces
  • Constants (primitive values only)
  • Class/function declarations
  • Exports

Forbidden at module level:

  • new keyword (except in composition root)
  • Function calls with side effects
  • Event listener registration
  • Async operations
// Good - declarations only
export class MyService { ... }
export const MAX_RETRIES = 3
export type Config = { ... }

// Bad - side effects at import
const instance = new MyService()  // executes on import
initializeListeners()  // side effect on import
chrome.storage.get(...)  // async operation on import

Bootstrap Checklist

Before implementing:

  1. Identify entry point - Where does execution start?
  2. List all classes - What needs to be instantiated?
  3. Map dependencies - What does each class need?
  4. Order creation - Dependencies before dependents
  5. Create bootstrap - Single function composing everything

Verify:

  • Entry point only calls bootstrap
  • All new keywords in composition root (except value objects)
  • No static service methods
  • No module-level instantiation
  • Dependencies flow downward only
  • Classes receive dependencies via constructor

Pre-Implementation Checklist

Before writing code:

  1. Understand requirement - What problem are we solving?
  2. Check existing code - Is this already implemented?
  3. Plan structure - Which classes/methods needed?
  4. Consider tests - How will we test this?
  5. Name things - Draft names before coding
  6. Keep it simple - What's the simplest solution?
  7. Single responsibility - Is each component focused?
  8. Dependencies - What do we depend on? Can we abstract it?

During implementation:

  1. Write tests - For expected behavior
  2. Implement incrementally - Small steps
  3. Refactor continuously - Clean as you go
  4. Follow conventions - Match existing patterns
  5. Avoid duplication - Extract shared code immediately
  6. Keep methods small - Extract when >10 lines
  7. Meaningful names - Rename when purpose becomes clear
  8. Run tests - Verify after each change

Language-Agnostic Patterns

Good Patterns

  • Factory methods for complex construction
  • Builder for objects with many parameters
  • Strategy for interchangeable algorithms
  • Observer for event handling
  • Null object for default behavior
  • Template method for algorithm structure

Anti-Patterns to Avoid

  • Singletons (global state)
  • God objects (too many responsibilities)
  • Anemic domain models (data classes)
  • Premature optimization
  • Speculative generality
  • Copy-paste programming

Summary

Before implementing, ensure:

  • Clear, obvious names for everything
  • No duplication - reuse existing code
  • Minimal complexity - simplest solution
  • Tests planned/written
  • Easy to maintain - for future you

Key metrics:

  • Methods: ≤10 lines, ≤3 parameters
  • Classes: ≤200 lines, single responsibility
  • Zero code duplication
  • 100% test pass rate
  • Self-documenting code with minimal comments

Remember: Clean code is code others can understand and modify with confidence. Write for humans first, machines second.


Sources: Refactoring.Guru Clean Code, Design Patterns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment