clean-code
GitHub提供跨语言的代码质量工程标准,指导编写、重构和审查代码。涵盖命名规范、单一职责、降低复杂度及TDD实践,旨在提升代码可读性、可维护性和清晰度。
Trigger Scenarios
Install
npx skills add operately/operately --skill clean-code -g -y
SKILL.md
Frontmatter
{
"name": "clean-code",
"description": "Clean-code engineering standards for writing, refactoring, and reviewing code in any programming language. Use this whenever the user asks to write clean code, follow clean-code principles, refactor for clarity, improve naming, reduce complexity or duplication, separate concerns, tighten error handling, work test-first or do TDD, or otherwise raise code quality, readability, and maintainability. Also use when writing or reviewing Operately APIs or Ecto queries. Apply these rules by default when producing or changing code for a quality-conscious user."
}
Clean Code
These are mandatory engineering standards for any code you write, change, or review while this skill is active. They are language-agnostic: apply them in whatever language the project uses, respecting that language's idioms.
If there is tension between speed and cleanliness, choose the clean solution unless there is a demonstrated operational emergency. If a shortcut is unavoidable, contain it, make it obvious, and leave the code safe to clean up later.
For detailed rationale, examples, and design heuristics, read reference.md — consult it when a rule needs interpretation or when you face a non-obvious design decision.
Core Rules
1. Leave the code cleaner
Every change must improve at least one of: clarity, naming, simplicity, cohesion, testability, error handling, duplication removal, or separation of concerns. Do not add code that is merely functional — add code that is understandable.
2. Optimize for readers
- Write code for the next engineer, not for impressiveness.
- Prefer straightforward code over clever code.
- Keep control flow obvious.
- If code is hard to explain, treat that as a design problem.
3. Use strong names
- Names must reveal intent. Use domain language consistently.
- Prefer specific names over generic ones. Avoid
data,info,thing,stuff,manager,helper,util,misc,handle,temp,objunless they are genuinely correct in the domain or an external protocol. - Boolean names read like predicates (
active?,isActive,hasItems,retryable?), following the language's convention. - Collection names are plural.
- Function names describe the action performed, not vague activity
(
calculate_invoice_total, notdo_invoice).
4. Keep methods small and focused
- A method should do one thing at one level of abstraction.
- Do not mix business rules, formatting, persistence, external calls, and error recovery in one method. Extract helpers when a method grows multiple phases.
- Prefer zero, one, or two parameters; three should trigger scrutiny; more usually means a missing value object or domain object.
- Do not use boolean parameters to switch behavior when separate methods would express intent more clearly.
5. Keep abstraction levels separate
- Do not mix high-level policy with low-level mechanics in one method.
- Keep orchestration separate from implementation detail, and domain logic separate from I/O, framework glue, and serialization.
6. Make side effects explicit
- Avoid methods that both answer a question and perform an action.
- Side effects must be visible in naming and placement; avoid hidden mutation.
- Prefer explicit dependencies over implicit globals.
7. Control conditionals
- Prefer guard clauses over deep nesting; keep branching shallow.
- Extract complex predicates into intention-revealing names.
- Avoid repeated conditional logic across multiple places.
- Prefer positive conditions where they read more clearly.
8. Treat errors as design
- Fail fast on invalid assumptions; do not swallow errors silently.
- Raise or return errors with useful, diagnostic context.
- Keep the happy path readable.
- Do not blur validation errors, domain errors, and infrastructure failures without reason.
9. Keep modules and classes cohesive
- Each unit should have one clear reason to change. Keep public surfaces small.
- Avoid god objects, god modules, and catch-all utility namespaces.
- Prefer composition over tangled inheritance.
10. Remove duplication at the right level
- Remove duplicated logic, decisions, business rules, and meaningful literals.
- Do not invent premature or unstable abstractions just to merge superficially similar lines. Deduplicate at the right level of meaning.
11. Work test-first
- Default to test-driven development. Write or update a failing test first when changing behavior, fixing a bug, or adding a feature.
- Test behavior, not incidental implementation details.
- Tests must be readable, deterministic, independent of order, and named by the expected outcome. Keep setup small and intention-revealing.
- Add regression coverage for bugs before fixing them whenever practical.
- If strict test-first is genuinely blocked (missing harness, legacy constraints), say so explicitly and add the nearest useful coverage immediately, leaving the area more testable than before.
12. Refactor while you work
- Improve weak names, break up long methods, and remove dead code in the area you touch.
- Keep tests passing while refactoring.
- If a full cleanup is out of scope, improve the nearest meaningful boundary and leave a clear note.
13. Respect language idioms — without hiding intent
- Use the target language's idioms only when they remain obvious to a competent engineer in that language.
- Prefer small, expressive units over procedural scripts with shared mutable state.
- Avoid metaprogramming, magic, or DSLs when plain code would be clearer at both the call site and the definition site.
- Isolate external systems (databases, APIs, queues, frameworks) behind clear boundaries; keep vendor-specific knowledge from leaking everywhere.
14. Prefer association joins and database-side recursion (Ecto)
When writing Ecto queries in this codebase:
- Prefer
join: ... in assoc(...)over manualjoin: ..., on: ...when a schema association already expresses the relationship. Manualon:is fine when joining CTEs/string tables, adding conditions beyond the association, or when no association exists. - Prefer recursive SQL (Ecto recursive CTEs via
recursive_ctes/2,with_cte/3, andunion_all/2) over Elixir recursion that hits the database on every iteration. Walk trees and hierarchies in one query when the database can do it. - Prefer Ecto's query DSL over hand-written SQL strings (
Repo.query, interpolatedWITH RECURSIVE ...) whenever Ecto can express the same query. Reserve raw SQL for cases the DSL cannot express cleanly.
See reference.md for examples from this codebase.
When loading requester-aware resources, use Operately.Repo.Getter. Read
getter.md before adding Getter profiles, alternate access-context
paths, a collection load, or a custom loading wrapper.
15. Model Operately API types and serializers explicitly
- Represent closed value sets with TurboConnect
enum, not:string. Derive values from the domain or Ecto schema source of truth, use the enum in API inputs and outputs, and convert atoms/strings only at required boundaries. - Keep endpoint modules focused on authentication and orchestration. Serialize
domain structs through an
OperatelyWeb.Api.Serializableimplementation inapp/lib/operately_web/api/serializers/; never define inline endpoint serializers. - Register serialized models with
object :name, for: Module, ensure the relevant types module participates inOperatelyWeb.Api.TypeNames, and callSerializer.serialize/1from endpoints. - Regenerate affected clients and cover enum and serialization behavior at the appropriate API or serializer boundary.
16. Definition of done
A task is done only when:
- the implementation is correct
- the design is understandable and the names are clear
- the touched code is cleaner than before
- relevant tests exist and pass (test-first unless a real constraint was noted)
- no obvious duplication or dead code remains in the changed area
- query code prefers
assoc, database-side recursion, and the Ecto DSL when those apply (see rule 14)
Version History
- 379b061 Current 2026-08-20 12:01


