elegant-code
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:
1. Clarify the problem
- Define inputs, outputs, invariants, constraints, failure modes
- Identify postconditions (what must be true after)
- List edge cases and "gotchas"
2. 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
3. Design shape before details
- Outline modules/functions and responsibilities
- Decide boundaries: pure logic vs side effects (I/O, database, network)
4. Write readable-by-default code
- Clear names, small units, straightforward control flow
- Make "happy path" obvious; handle errors intentionally
5. Add guardrails
- Validation, assertions (where appropriate), tests
- Define invalid input handling
6. Refine (remove, simplify, clarify)
- Remove duplication, tighten interfaces, reduce nesting
- Each unit should read like a single thought
7. 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
More from oleksandrkucherenko/e-bash
pester
PowerShell TDD testing framework guidance for Pester v5+. Use when writing, structuring, or debugging PowerShell unit tests; mocking cmdlets, native commands (bash, git, curl), or .NET types; isolating tests with TestDrive/TestRegistry; capturing output streams; generating code coverage or JUnit/NUnit reports for CI/CD; running parameterized or tagged tests; or troubleshooting Pester Discovery vs Run phase issues.
19skill-learning-patterns
Use when agents discover better patterns, find gaps or inaccuracies in existing skills, or need to contribute validated improvements to shared knowledge, or found unique experience that could be shared with others.
10gemini-cli
Use Gemini CLI as a complementary AI tool for tasks requiring massive context windows (1M tokens). Invoke when analyzing large codebases, requesting deep analysis with extended thinking, getting second opinions on complex problems, or when Claude's context limits are insufficient. Triggers include phrases like "use gemini", "analyze with gemini", "get second opinion", "deep analysis of codebase", or when processing files exceeding Claude's context capacity.
1