AgentSkillsCN

clean-code

DRY 与 SOLID 编码标准。在编写、审查或重构任何代码时,可使用此标准,以确保代码简洁、易于维护且避免重复。

SKILL.md
--- frontmatter
name: clean-code
description: DRY and SOLID coding standards. Use when writing, reviewing, or refactoring any code to ensure clean, maintainable, and non-duplicated code.
user-invocable: false

Clean Code — DRY & SOLID Standards

Philosophy

Write code that is clean, versatile, and maintainable. Eliminate duplication when it provides real value — but never at the cost of readability or when abstraction adds more complexity than the duplication it removes. Three similar lines are better than a premature abstraction that nobody understands.

DRY — Don't Repeat Yourself

Default: Extract at 2nd Occurrence

  • 2 occurrences = extract by default — duplication is a bug waiting to happen
  • The pattern is already clear at the 2nd occurrence in most cases — don't wait
  • Business rules and domain logic MUST have a single source of truth — NEVER scatter the same validation, calculation, or rule across files
  • Constants, config values, magic numbers = define once, reference everywhere

Tolerate Duplication Only When

  • The two snippets look similar but serve genuinely different purposes and will evolve independently
  • The abstraction would require 3+ parameters or complex generics just to handle both call sites — it's too forced
  • It's glue code, wiring, or boilerplate that is inherently contextual (e.g., route handlers, component props)
  • It's test setup code — readability and independence matter more than DRY in tests
  • The duplicated code is trivial (1–2 lines) AND has no risk of diverging silently

SOLID Principles

S — Single Responsibility

  • One module/class/function = one reason to change
  • Functions do ONE thing — if the name requires "and", split it
  • Files: max ~200–300 lines of meaningful logic; split when responsibilities diverge
  • Separate concerns: data access, business logic, presentation, orchestration

O — Open/Closed

  • Extend behavior through composition, plugins, strategy patterns — not by editing existing code
  • Use discriminated unions + exhaustive switches for variant handling
  • Prefer config/data-driven behavior over hardcoded conditionals
  • When adding a new case, you should ideally add a new file/module, not modify 5 existing ones

L — Liskov Substitution

  • Subtypes must be usable wherever their parent type is expected — no surprises
  • NEVER narrow, override, or throw in a subtype method that the parent doesn't
  • Prefer composition over deep inheritance chains (max 2 levels)
  • If a function accepts a base type, it must work correctly with any subtype

I — Interface Segregation

  • Small, focused interfaces — clients should not depend on methods they don't use
  • Prefer multiple narrow interfaces over one fat interface
  • Split interfaces by consumer role: Readable, Writable > ReadWritable
  • Component props: pass only what the component needs, NEVER spread an entire entity

D — Dependency Inversion

  • High-level modules depend on abstractions (interfaces/types), not concrete implementations
  • Inject dependencies — NEVER hardcode imports to specific implementations inside business logic
  • Use factory functions or DI containers for wiring
  • Side effects (DB, API, filesystem) behind interfaces — makes testing trivial

Code Quality Best Practices

Naming

  • Names reveal intent — if you need a comment to explain what a variable is, rename it
  • Avoid abbreviations except universally understood ones (id, url, api)
  • Booleans: is/has/can/should prefix
  • Functions: verb + noun (fetchUser, validateEmail, calculateTotal)
  • Avoid generic names: data, info, item, thing, result — be specific

Functions

  • Max 20–30 lines — if longer, extract sub-steps
  • Max 3 parameters — use an options object beyond that
  • Pure functions by default — isolate side effects at the edges
  • Early returns over nested conditionals — fail fast, happy path last
  • NEVER use boolean parameters to toggle behavior — use separate functions or an enum/union

Modules & Files

  • One concept per file — UserService in user-service.ts, not mixed with OrderService
  • Group by feature/domain, not by technical layer when possible
  • Keep coupling low: a module change should not cascade to unrelated modules
  • Circular dependencies = architectural smell — refactor immediately

Error Handling

  • Handle errors at the appropriate level — not too early (loses context), not too late (crashes)
  • Use Result/Either pattern for expected failures — reserve exceptions for truly exceptional cases
  • NEVER silently swallow errors — at minimum log them
  • Error messages must be actionable: what happened, why, and what to do about it

Comments & Documentation

  • Code should be self-documenting — extract well-named functions instead of adding comments
  • Comments explain why, never what (the code says what)
  • Delete commented-out code — version control exists
  • Document public APIs, complex algorithms, and non-obvious business rules only

Refactoring Signals

Extract or refactor when you see:

  • Long parameter lists (4+) — group into an object or rethink the design
  • Feature envy — a function uses more data from another module than its own
  • Shotgun surgery — one change requires edits in 5+ files
  • Divergent change — one file changes for multiple unrelated reasons
  • Primitive obsession — using strings/numbers where a domain type adds safety
  • Large conditionals — switch/if chains with 4+ branches → polymorphism or strategy pattern
  • Dead code — delete it, do not comment it out

Performance Awareness

  • Prefer Map/Set for lookups in loops — O(1) vs O(n)
  • Avoid unnecessary allocations in hot paths
  • Lazy evaluation: compute only when needed
  • Cache expensive computations — but invalidate correctly
  • Measure before optimizing — premature optimization is the root of all evil