Elegant Code
Elegant code = Correctness + Clarity + Minimal complexity + Natural efficiency + Easy change.
Not "shortest" or "cleverest." The solution that makes readers think: "Of course. That's simple, right, and hard to mess up."
Optimization Priority (in order)
-
Correctness & safety — does what it claims, handles edge cases
-
Clarity of intent — reader answers "what/why/how" from code itself
-
Simplicity — minimal concepts that still solve the problem
-
Changeability — modifications are localized and low-risk
-
Efficiency — good algorithms; performance from good design, not micro-optimizations
Elegant vs "Smart" Code
Elegant Smart/Clever
Clarity & solution Cleverness & brevity
Humans first Machine first (humans decode)
Low complexity High complexity
Easy to debug/change Fragile and opaque
"Of course!" "Wait… how?"
The Workflow
Use this sequence when writing or refactoring:
- Clarify the problem
-
Define inputs, outputs, invariants, constraints, failure modes
-
Identify postconditions (what must be true after)
-
List edge cases and "gotchas"
- Choose simplest correct approach
-
Pick simplest algorithm/data structure meeting constraints
-
Prefer fewer concepts over more layers
-
If adding abstraction, state what complexity it removes
- Design shape before details
-
Outline modules/functions and responsibilities
-
Decide boundaries: pure logic vs side effects (I/O, database, network)
- Write readable-by-default code
-
Clear names, small units, straightforward control flow
-
Make "happy path" obvious; handle errors intentionally
- Add guardrails
-
Validation, assertions (where appropriate), tests
-
Define invalid input handling
- Refine (remove, simplify, clarify)
-
Remove duplication, tighten interfaces, reduce nesting
-
Each unit should read like a single thought
- Verify against reality
-
Run tests; benchmark if performance matters
-
Confirm behavior matches original problem statement
Core Rules
Rule 1 — Preserve intent above everything
Reader must answer: What does this do? Why? What constraints? What can go wrong?
-
Comments explain why, tradeoffs, constraints—not what code does
-
If you need comments to explain what, the code is unclear
Rule 2 — Minimize concepts, not lines
Keep distinct "ideas" (types, abstractions, layers, config knobs) minimal.
-
Prefer one good function over a mini-framework
-
No "future-proofing" without concrete known need
-
If abstraction adds indirection without removing complexity, remove it
Rule 3 — Common case simple; edge cases explicit
-
Happy path easy to follow
-
Edge cases via early returns, guard clauses, explicit validation
-
Avoid deep nesting hiding the main story
Rule 4 — Small, cohesive units
-
One primary responsibility per function/module/class
-
Group related logic; separate unrelated concerns
-
If name needs "and" (parseAndSave), it's doing too much
Rule 5 — Explicit data flow over hidden state
-
Data through parameters and return values, not globals/singletons/mutable shared state
-
If you can't test without elaborate setup, hidden context exists
-
If call order matters, make it explicit
Rule 6 — DRY knowledge, not syntax
-
Unify if two places must change together
-
Allow small obvious repetition if abstraction is harder to read
-
Duplicate code sometimes cheaper than leaky abstraction
Rule 7 — Right algorithmic shape
-
Choose algorithms/data structures appropriate to constraints
-
Prefer clarity unless profiling demands complexity
-
Prefer asymptotic wins over micro-optimizations
Rule 8 — Make invalid states unrepresentable
-
Represent domain constraints in types/structures so illegal combinations are impossible
-
Validate at boundaries, convert to trusted internal representations
Rule 9 — Local reasoning
-
Understand a unit without chasing definitions across codebase
-
Small interfaces, directness over indirection
-
Configuration close to usage or centralized with clear naming
Rule 10 — Idiomatic but readable
-
Follow language/project conventions
-
Don't use obscure tricks only experts recognize
-
If idiom is compact but unclear, choose clearer form
Micro-Rules
Naming
-
DO: Names encode intent and domain meaning, not mechanics
-
DO: Concrete nouns/verbs: calculate_total , is_valid , parse_header
-
AVOID: Vague names: data , process , handle , doThing , tmp , manager
-
DO: One concept → one term (consistent vocabulary)
Functions
-
Short enough for working memory
-
Inputs/outputs obvious and stable
-
Side effects clear from name or context
-
Favor pure functions for core logic
-
I/O at edges ("functional core, imperative shell")
Control Flow
-
Avoid deep nesting; use guard clauses
-
Early exits for invalid conditions
-
Straightforward over clever
Error Handling
-
Decide: recover, retry, fallback, or fail fast—then implement consistently
-
Errors carry enough context to debug
-
Validate at boundaries
-
Never swallow errors silently
Dependencies
-
Depend on stable interfaces, not unstable internals
-
Minimal and purposeful dependencies
-
Inject dependencies where it improves testability
Self-Review Checklist
Before finalizing code, verify:
-
Correctness: Meets requirements; edge cases handled; failures intentional
-
Clarity: Names meaningful; reads top-to-bottom; minimal mental jumps
-
Simplicity: No unnecessary abstractions; minimal moving parts
-
Cohesion: Each unit has one job; responsibilities not mixed
-
Coupling: Dependencies minimal; interfaces small and stable
-
Data flow: Inputs/outputs explicit; minimal hidden state
-
Error handling: Consistent strategy; errors include context
-
Efficiency: Algorithm/data structures fit constraints; no obvious waste
-
Consistency: Patterns match codebase norms
-
Testability: Core logic testable easily; tests exist for tricky parts
Refactor Decision Rules
Refactor if:
-
Function/module cannot be summarized in one sentence
-
Must read twice to trust it
-
One change requires edits in many unrelated places
-
Bugs cluster in same area repeatedly
-
Keep adding special cases ("just one more flag")
Avoid refactoring if:
-
No tests and behavior unclear (add tests first)
-
Code stable and rarely changed, improvements purely aesthetic
-
Near deadline and risk is high (smallest safe improvements only)
Detailed References
-
Measuring elegance: See references/scorecard.md for the elegance scorecard, objective metrics, and improvement checklist
-
Anti-patterns: See references/anti-patterns.md for what kills elegance and "smart code" smells
-
Continuous improvement: See references/continuous-improvement.md for the Continuous Elegance Loop and practices