maintainable-code

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Simple, Testable, Maintainable Code Principles

简洁、可测试、可维护的代码原则

Use these principles to keep code simple, testable, readable, and sustainable.
Core stance: clear business flow, explicit I/O, no hidden dependencies, no unnecessary fragmentation.
They are organized in the order of a change: first how to approach the work, then how to shape the system, then how to write the code, then how to name things, and finally how to communicate plans.
遵循这些原则,让代码保持简洁、可测试、可读且可持续。
核心立场:清晰的业务流程、明确的I/O、无隐藏依赖、无不必要的拆分。
这些原则按变更流程排序:首先是工作方法,然后是系统架构设计,接着是代码编写,再是命名规范,最后是方案沟通。

Language Policy

语言规范

During an agent session, reply in the language the user is using.
When writing code, comments, documentation, plans, or any other persistent repository content, write in English by default unless the user explicitly asks for another language.
The arc is:
txt
think → shape → write → name → explain
在Agent会话中,使用用户所用的语言回复。
编写代码、注释、文档、方案或其他持久化仓库内容时,默认使用英文,除非用户明确要求使用其他语言。
流程如下:
txt
think → shape → write → name → explain

Overview

概述

FamilyPrinciples
A — Process1. Investigate before you change.<br>2. Plan from the test; build the core first.<br>3. Aim at the final experience; loop back when you learn.
B — Architecture4. Keep few layers: clear flow, explicit I/O.<br>5. Inject dependencies explicitly; watch what crosses the layers.<br>6. Keep the generic generic.<br>7. Choose external dependencies deliberately.<br>8. Organize by feature, not by technical type.
C — Implementation9. Give each unit one coherent responsibility.<br>10. Keep support pure; isolate technical mini engines.<br>11. Lay out files top-down.
D — Naming & Language12. Reuse the domain's vocabulary.<br>13. Write repository content in international, intermediate English.<br>14. Name with symmetry.
E — Communication15. Present plans progressively.

类别原则
A — 流程1. 变更前先调研。<br>2. 从测试出发规划;优先构建核心功能。<br>3. 以最终体验为目标;学习后及时回退调整。
B — 架构4. 精简层级:流程清晰,I/O明确。<br>5. 显式注入依赖;关注跨层级传递内容。<br>6. 通用代码保持通用。<br>7. 审慎选择外部依赖。<br>8. 按功能组织,而非技术类型。
C — 实现9. 每个单元承担单一连贯职责。<br>10. 辅助代码保持纯逻辑;隔离技术微型引擎。<br>11. 文件内容自上而下编排。
D — 命名与语言12. 复用领域术语。<br>13. 仓库内容使用通用中级英文。<br>14. 命名保持对称。
E — 沟通15. 逐步呈现方案。

A — Process

A — 流程

1. Investigate before you change.

1. 变更前先调研。

Read how the system works today before changing it. The pattern you need often already exists somewhere: find it, understand it, and follow it when it still fits.
DRY matters, but do not turn every similarity into an abstraction. Reuse should protect the existing design, not create a second architecture or add layers the system does not need.
在修改系统前,先了解当前系统的工作方式。你需要的模式往往已存在于某处:找到它、理解它,若仍适用则遵循该模式。
DRY原则很重要,但不要将所有相似点都转化为抽象。复用应保护现有设计,而非创建第二种架构或添加系统不需要的层级。

2. Plan from the test; build the core first.

2. 从测试出发规划;优先构建核心功能。

Before coding, ask how the change will be tested. Sketch the main flow in rough pseudocode, especially across the main parts of the system: routing, use case, subtasks, connectors, and entry point.
Design connector interfaces early, but build the core behavior first. Start with the business flow and the subtasks that make it readable. Then wire routing, connectors, and the composition root. This keeps most of the work focused on behavior instead of infrastructure.
编码前,先思考如何测试变更内容。用粗略的伪代码勾勒主流程,尤其是跨系统主要部分的流程:路由、用例、子任务、连接器和入口点。
尽早设计连接器接口,但优先构建核心业务逻辑。从业务流程和提升可读性的子任务入手,再连接路由、连接器和组合根(composition root)。这样可让大部分工作聚焦于业务逻辑而非基础设施。

3. Aim at the final experience; loop back when you learn.

3. 以最终体验为目标;学习后及时回退调整。

Keep the final experience in view while building: UX for the user, DX for the developer, API shape for consumers, CLI behavior for operators, and integration contract for external systems.
The plan from #2 is a starting point, not a prison. If implementation reveals that the UX, DX, data shape, or architecture is wrong, return to the design with what you learned and adjust the plan.

开发过程中始终关注最终体验:用户的UX、开发者的DX、消费者的API形态、运维人员的CLI行为,以及外部系统的集成契约。
第2条中的规划只是起点,而非束缚。若实现过程中发现UX、DX、数据形态或架构存在问题,结合所学返回设计阶段调整方案。

B — Architecture

B — 架构

4. Keep few layers: clear flow, explicit I/O.

4. 精简层级:流程清晰,I/O明确。

Prefer one clear spine:
txt
composition root → routing → use cases → subtasks → connectors
The composition root creates connectors and wires dependencies. Routing chooses the flow. Use cases hold the main business flow. Subtasks move secondary details out of that flow when they make it harder to read. Connectors handle the outside world: env, args, process spawn, process I/O, fetch, files, random, time, external services, and any other I/O.
Prefer pure logic when it is natural: data in, decision out, no I/O. But do not force purity or split code into tiny functions when that makes the business flow harder to understand. Use cases and subtasks may coordinate ports or infrastructure dependencies when needed, as long as those dependencies are explicit and injected.
The goal is not purity for its own sake. The goal is cohesive business flow, clear dependencies, and controlled side effects. Keep the spine; do not invent layers or micro-abstractions the code has not earned.
优先采用清晰的核心流程:
txt
composition root → routing → use cases → subtasks → connectors
组合根创建连接器并注入依赖。路由选择流程。用例承载主要业务流程。当次要细节影响主流程可读性时,将其移至子任务中。连接器处理外部交互:环境变量、参数、进程启动、进程I/O、网络请求、文件、随机数、时间、外部服务及其他I/O操作。
在自然的情况下优先使用纯逻辑:输入数据、输出决策、无I/O。但不要为了追求纯逻辑而将代码拆分为极小的函数,导致业务流程难以理解。若需要,用例和子任务可协调端口或基础设施依赖,只要这些依赖是显式且注入的即可。
目标并非单纯追求纯逻辑,而是实现连贯的业务流程、清晰的依赖关系和可控的副作用。保留核心流程,不要创建代码尚未需要的层级或微抽象。

5. Inject dependencies explicitly; watch what crosses the layers.

5. 显式注入依赖;关注跨层级传递内容。

Dependencies should enter through constructors, factories, parameters, or the composition root. Avoid hidden globals and avoid business code reaching directly into infrastructure APIs.
Also watch the data structures moving through the layers. Avoid god objects and large context objects passed everywhere. They hide dependencies and make every function look like it depends on the whole system.
A shared context can make sense in systems built for extensibility, plugins, workflow execution, or framework-like behavior. In those cases, the context is part of the extension contract. For ordinary business services, prefer explicit dependencies and explicit data.
依赖应通过构造函数、工厂、参数或组合根注入。避免隐藏全局变量,避免业务代码直接调用基础设施API。
同时关注跨层级传递的数据结构。避免全能对象(god objects)和全局上下文对象随处传递,它们会隐藏依赖关系,让每个函数看起来都依赖整个系统。
在为扩展性、插件、工作流执行或类框架行为构建的系统中,共享上下文可能有意义。这种情况下,上下文是扩展契约的一部分。对于普通业务服务,优先使用显式依赖和显式数据。

6. Keep the generic generic.

6. 通用代码保持通用。

If a component is an engine, framework, runner, executor, or other generic mechanism, do not solve one runtime case by hardcoding case-specific values into it. Generic code should not know business-specific data that belongs to callers, configuration, or runtime input.
Solve the problem at the right abstraction level: improve the contract, configuration, data model, extension point, or caller responsibility. Do not protect a local shortcut by damaging a generic design.
若某个组件是引擎、框架、运行器、执行器或其他通用机制,不要通过硬编码特定场景值来解决单个运行时问题。通用代码不应包含属于调用方、配置或运行时输入的业务特定数据。
在正确的抽象层级解决问题:改进契约、配置、数据模型、扩展点或调用方职责。不要为了局部捷径而破坏通用设计。

7. Choose external dependencies deliberately.

7. 审慎选择外部依赖。

Open-source dependencies are good when they reduce real complexity and are clearly stronger than what the team should build itself. Prefer dependencies that are widely adopted, battle-tested, actively maintained, quick to patch, and have a good security record.
Keep the dependency surface small, but not by replacing mature libraries with complex, fragile, or overly technical custom code. Avoid dependencies for trivial problems; avoid handmade solutions for hard problems that mature libraries already solve well.
The more a dependency owns state, affects architecture, touches security, or spreads its vocabulary through the system, the higher the adoption bar. When useful but not semantically central, hide it behind a small support layer. Embrace it directly only when it deserves to become part of the project's shared language.
当开源依赖能降低实际复杂度,且明显优于团队自行开发的方案时,可使用。优先选择被广泛采用、经过实战检验、持续维护、补丁更新及时且安全记录良好的依赖。
缩小依赖范围,但不要用复杂、脆弱或过度技术化的自定义代码替代成熟库。避免为琐碎问题引入依赖;对于成熟库已能很好解决的复杂问题,避免自行实现。
依赖对状态的掌控、对架构的影响、对安全性的触及,或其术语在系统中的传播范围越大,采用门槛就越高。当依赖有用但并非语义核心时,用一个小型辅助层封装它。只有当它值得成为项目共享语言的一部分时,才直接使用。

8. Organize by feature, not by technical type.

8. 按功能组织,而非技术类型。

Prefer folders named after product or domain features, not after framework roles like
models
,
controllers
,
services
, or
repositories
. A tree organized only by technical type tells the reader little about what the system does.
It is fine to repeat the feature name in filenames when it helps clarity:
txt
billing/
  billing.model.ts
  billing.use-case.ts
  billing.controller.ts

users/
  users.model.ts
  users.use-case.ts
  users.controller.ts
The folder structure should reveal the product before it reveals the framework.

优先使用产品或领域功能命名文件夹,而非框架角色(如
models
controllers
services
repositories
)。仅按技术类型组织的目录结构,无法让读者快速了解系统功能。
为了清晰,文件名中重复功能名称是可行的:
txt
billing/
  billing.model.ts
  billing.use-case.ts
  billing.controller.ts

users/
  users.model.ts
  users.use-case.ts
  users.controller.ts
目录结构应先展示产品功能,再展示框架角色。

C — Implementation

C — 实现

9. Give each unit one coherent responsibility.

9. 每个单元承担单一连贯职责。

A function, method, class, or module should have one clear reason to exist. But that does not mean every small step needs its own function. Keep the main business logic together when reading it in one place is clearer than jumping across many tiny helpers.
Extract subtasks when they remove secondary detail from the main flow: parsing, normalization, validation, enrichment, retries, state transitions, formatting, or technical coordination. Prefer subtasks to be pure, but allow explicit infrastructure dependencies when that keeps the design simpler and the effect visible.
Do not mix unrelated concerns in one body just because they happen in sequence. But also do not fragment a cohesive flow into micro-functions just to look clean.
函数、方法、类或模块应有明确的存在理由。但这并不意味着每个小步骤都需要单独的函数。当在一处阅读主业务逻辑比跳转到多个小辅助函数更清晰时,应将主逻辑放在一起。
当次要细节影响主流程可读性时,提取子任务:解析、标准化、验证、增强、重试、状态转换、格式化或技术协调。优先让子任务保持纯逻辑,但为了简化设计且让效果可见,也允许显式的基础设施依赖。
不要仅因为顺序相关就将无关关注点混在同一代码块中。但也不要为了看起来整洁而将连贯的流程拆分为微函数。

10. Keep support pure; isolate technical mini engines.

10. 辅助代码保持纯逻辑;隔离技术微型引擎。

Some support code is a small helper: validation, parsing, normalization, type guards, deterministic formatting, and simple pure transformations. Keep this code small, deterministic, and free of external I/O by default.
Some support code is bigger than a helper and smaller than a framework: state machines, workflow runners, queue/dequeue controllers, concurrency limiters, retry controllers, decorators, schedulers, and similar mechanisms. Keep these as isolated technical primitives. They may manage technical state and lifecycle, but they should not hardcode business-specific runtime values or perform hidden I/O.
A mini engine can be internal or external. When adopting a library for this role, either wrap it or embrace it deliberately. Wrap it behind a small semantic support layer when its API should not spread through the system. Embrace it directly only when it is mature, widely adopted, stable, and valuable enough to become part of the project's shared language.
部分辅助代码是小型工具:验证、解析、标准化、类型守卫、确定性格式化和简单的纯转换。默认情况下,这类代码应保持小巧、确定性且无外部I/O。
部分辅助代码比工具大,但比框架小:状态机、工作流运行器、队列控制器、并发限制器、重试控制器、装饰器、调度器及类似机制。将这些作为独立的技术原语。它们可能管理技术状态和生命周期,但不应硬编码业务特定的运行时值或执行隐藏I/O。
微型引擎可以是内部或外部的。当采用库来实现此角色时,要么封装它,要么审慎地直接使用。当库的API不应在系统中传播时,用一个小型语义辅助层封装它。只有当库成熟、被广泛采用、稳定且足够有价值成为项目共享语言的一部分时,才直接使用。

11. Lay out files top-down.

11. 文件内容自上而下编排。

Put the main function of the file at the top. Below it, place the functions it calls, in order of first use; then the functions those functions call; and so on.
The file should read from general to specific:
txt
mainFunction
  helperUsedFirst
  helperUsedNext
    subHelper
Keep function bodies free of blank lines; when a function wants visual sections, extract the secondary detail. The file should read in the order the reader needs to understand it.

将文件的主函数放在顶部。在其下方,按首次使用顺序放置它调用的函数;然后放置这些函数调用的函数,依此类推。
文件内容应从通用到特定:
txt
mainFunction
  helperUsedFirst
  helperUsedNext
    subHelper
函数体内不要留空行;若函数需要视觉分段,提取次要细节。文件应按读者理解所需的顺序编排。

D — Naming & Language

D — 命名与语言

12. Reuse the domain's vocabulary.

12. 复用领域术语。

Define the main concepts and taxonomy as early as possible, then carry those terms into the code consistently. Avoid inventing new words for concepts the system already named.
Prefer familiar software terms when they are accurate enough. A slightly broader term that most developers understand is often better than a very precise term that feels obscure or academic. Precision is valuable, but not when it makes the code harder to read.
尽早定义核心概念和分类体系,然后在代码中一致地使用这些术语。避免为系统已命名的概念创造新词汇。
当通用软件术语足够准确时,优先使用。大多数开发者能理解的稍宽泛术语,往往比晦涩或学术性的精确术语更好。精确性很重要,但不应以降低代码可读性为代价。

13. Write repository content in international, intermediate English.

13. 仓库内容使用通用中级英文。

Write code names, comments, documentation, plans, and other persistent repository content in English that an intermediate non-native reader can follow. Avoid rare idioms, overly fluent expressions, or vocabulary that only advanced speakers catch.
Technical software terms are the exception. If a technical term is widely used in the industry, use it. Its popularity already gives readers context.
代码名称、注释、文档、方案及其他持久化仓库内容,应使用中级非母语读者能理解的英文。避免罕见习语、过于流畅的表达或仅高级使用者能理解的词汇。
技术软件术语除外。若某个技术术语在行业中被广泛使用,则直接使用。其普及性已为读者提供了上下文。

14. Name with symmetry.

14. 命名保持对称。

Related things should be named in parallel. Keep the same verb choices, the same verb-or-noun order, and the same singular or plural pattern unless there is a clear reason to differ.
Symmetry makes code predictable. A reader should be able to guess nearby names before opening the file:
txt
createUser
updateUser
deleteUser
findUser
Avoid accidental asymmetry:
txt
createUser
userUpdate
removeUsers
getSingleAccount

相关内容应采用平行命名方式。保持动词选择、动名词顺序、单复数形式一致,除非有明确理由不同。
对称命名让代码更具可预测性。读者在打开文件前,应能猜到相关名称:
txt
createUser
updateUser
deleteUser
findUser
避免意外的不对称:
txt
createUser
userUpdate
removeUsers
getSingleAccount

E — Communication

E — 沟通

15. Present plans progressively.

15. 逐步呈现方案。

Start with the useful answer first. Then layer context, details, edge cases, caveats, risks, and tradeoffs in the order the reader needs them.
Use short paragraphs, plain language, and one main idea per paragraph. Use headings and bullets only when they reduce effort. Keep important nuance, but do not make the answer dense, corporate, over-polished, or artificially brief.
The goal is to leave the reader more oriented, not more impressed.

先给出有用的核心答案。然后按读者需求的顺序,逐层添加上下文、细节、边缘情况、注意事项、风险和权衡。
使用短段落、平实语言,每段一个核心观点。仅当有助于降低理解难度时才使用标题和项目符号。保留重要细节,但不要让答案过于冗长、生硬、过度修饰或刻意简短。
目标是让读者更清晰地理解,而非留下深刻印象。

One line each

一句话总结每条原则

  1. Investigate before you change.
  2. Plan from the test; build the core first.
  3. Aim at the final experience; loop back when you learn.
  4. Keep few layers: clear flow, explicit I/O.
  5. Inject dependencies explicitly; avoid hidden globals and god contexts.
  6. Keep the generic generic; do not hardcode one case into an engine.
  7. Choose external dependencies deliberately.
  8. Organize by feature, not by technical type.
  9. Give each unit one coherent responsibility.
  10. Keep support pure; isolate technical mini engines.
  11. Lay out files top-down.
  12. Reuse the domain's vocabulary.
  13. Write repository content in international, intermediate English.
  14. Name with symmetry.
  15. Present plans progressively.

  1. 变更前先调研。
  2. 从测试出发规划;优先构建核心功能。
  3. 以最终体验为目标;学习后及时回退调整。
  4. 精简层级:流程清晰,I/O明确。
  5. 显式注入依赖;避免隐藏全局变量和全能上下文。
  6. 通用代码保持通用;不要在引擎中硬编码特定场景。
  7. 审慎选择外部依赖。
  8. 按功能组织,而非技术类型。
  9. 每个单元承担单一连贯职责。
  10. 辅助代码保持纯逻辑;隔离技术微型引擎。
  11. 文件内容自上而下编排。
  12. 复用领域术语。
  13. 仓库内容使用通用中级英文。
  14. 命名保持对称。
  15. 逐步呈现方案。

Cross-references

交叉参考

  • #4 and #9 are the same instinct at different scales: clear system flow and coherent local units.
  • #4 and #5 work together: few layers only stay clear when dependencies and data crossing those layers are explicit.
  • #6 and #10 protect generic mechanisms: engines and support primitives should not absorb business-specific shortcuts.
  • #7 and #10 decide when to build, adopt, wrap, or embrace reusable technical behavior.
  • #11 and #15 share the same reader-first idea: top-down in code, most-useful-first in prose.
  • #12, #13, and #14 make the code easier to recognize, search, discuss, and extend.
  • #4和#9是不同尺度的同一理念:清晰的系统流程和连贯的局部单元。
  • #4和#5相辅相成:只有当跨层级的依赖和数据是显式的,精简层级才能保持清晰。
  • #6和#10保护通用机制:引擎和辅助原语不应包含业务特定的捷径。
  • #7和#10决定何时构建、采用、封装或直接使用可复用的技术行为。
  • #11和#15共享以读者为中心的理念:代码自上而下, prose先呈现最有用的内容。
  • #12、#13和#14让代码更易于识别、搜索、讨论和扩展。

Before applying these principles

应用这些原则前的注意事项

Apply these principles deliberately. If you break one, make the reason visible: clearer flow, safer dependency boundaries, simpler testing, or less accidental complexity.
审慎应用这些原则。若违反某条原则,需明确理由:如更清晰的流程、更安全的依赖边界、更简单的测试或更少的意外复杂度。