python-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Python Design for CLI Scripts

Python CLI脚本设计

Design patterns and principles for writing maintainable Python CLI tools and utilities. Based on A Philosophy of Software Design (Ousterhout), adapted for scripting contexts.
编写可维护Python CLI工具与实用程序的设计模式和原则。 基于《软件设计的哲学》(Ousterhout)改编,适用于脚本开发场景。

When to Activate

适用场景

  • Writing or modifying Python files
  • Planning module decomposition
  • Code review of Python changes
  • Refactoring scripts that feel "messy"
  • Adding a new subcommand or utility function
  • 编写或修改Python文件
  • 规划模块拆分
  • Python代码变更的评审
  • 重构“杂乱无章”的脚本
  • 添加新子命令或实用函数

Core Thesis

核心论点

The central challenge is managing complexity, not adding features.
Complexity is anything that makes code hard to understand or modify. It has three symptoms:
  1. Change Amplification — A small change requires edits in many places
  2. Cognitive Load — You must hold too much context to make a safe change
  3. Unknown Unknowns — You don't know what you don't know (the most dangerous)
Complexity is incremental. It accumulates through hundreds of small decisions, not one catastrophic mistake. Therefore: sweat the small stuff.

核心挑战是管理复杂度,而非添加功能。
复杂度指任何让代码难以理解或修改的因素,有三个典型症状:
  1. 变更放大 —— 一个小变更需要在多个地方修改代码
  2. 认知负荷 —— 进行安全变更时需要记住过多上下文信息
  3. 未知的未知 —— 你不知道自己遗漏了什么(最危险的情况)
复杂度是逐步累积的,它源于数百个小决策,而非单一的灾难性错误。因此:重视细节,防微杜渐

Principle 1: Deep Modules

原则1:深度模块

A module's value is the ratio of functionality hidden vs. interface exposed.
Deep module (good):          Shallow module (bad):
┌──────────┐                 ┌──────────────────────────┐
│ simple   │                 │ complex interface        │
│ interface│                 │ many params, many methods │
├──────────┤                 ├──────────────────────────┤
│          │                 │                          │
│  rich    │                 │  thin implementation     │
│  impl    │                 │                          │
│          │                 └──────────────────────────┘
│          │
└──────────┘
Practical test: If a caller must understand how the module works internally to use it correctly, the module is too shallow.
模块的价值在于隐藏的功能与暴露的接口之比。
深度模块(优秀):          浅度模块(糟糕):
┌──────────┐                 ┌──────────────────────────┐
│ 简单接口 │                 │ 复杂接口                  │
│          │                 │ 多参数、多方法            │
├──────────┤                 ├──────────────────────────┤
│          │                 │                          │
│ 丰富实现 │                 │ 单薄实现                  │
│          │                 │                          │
│          │                 └──────────────────────────┘
│          │
└──────────┘
实用测试方法:如果调用者必须理解模块的内部实现才能正确使用它,那么这个模块就过于浅度了。

Example: Task Data Access

示例:任务数据访问

python
undefined
python
undefined

Shallow — caller must know JSON structure, file paths, error handling

浅度实现 —— 调用者必须了解JSON结构、文件路径和错误处理

def _read_json_file(path: Path) -> dict: with open(path, encoding="utf-8") as f: return json.load(f)
def _read_json_file(path: Path) -> dict: with open(path, encoding="utf-8") as f: return json.load(f)

Every caller does this independently:

每个调用者都要重复编写这些代码:

task_path = tasks_dir / name / "task.json" data = _read_json_file(task_path) title = data.get("title") or data.get("name", "") status = data.get("status", "planning") assignee = data.get("assignee", "")

```python
task_path = tasks_dir / name / "task.json" data = _read_json_file(task_path) title = data.get("title") or data.get("name", "") status = data.get("status", "planning") assignee = data.get("assignee", "")

```python

Deep — caller gets what they need, module hides JSON/path/parsing

深度实现 —— 调用者直接获取所需数据,模块封装了JSON/路径/解析逻辑

@dataclass(frozen=True) class TaskInfo: name: str title: str status: str assignee: str priority: str directory: Path
def load_task(tasks_dir: Path, name: str) -> TaskInfo | None: """Load task by directory name. Returns None if not found.""" ...
def list_active_tasks(tasks_dir: Path) -> list[TaskInfo]: """List all non-archived tasks, sorted by priority.""" ...

The deep version absorbs complexity: JSON parsing, field defaults, directory scanning, archive filtering. Callers just work with typed data.

---
@dataclass(frozen=True) class TaskInfo: name: str title: str status: str assignee: str priority: str directory: Path
def load_task(tasks_dir: Path, name: str) -> TaskInfo | None: """按目录名称加载任务。如果未找到则返回None。""" ...
def list_active_tasks(tasks_dir: Path) -> list[TaskInfo]: """列出所有未归档的任务,按优先级排序。""" ...

深度版本吸收了复杂度:JSON解析、字段默认值、目录扫描、归档过滤。调用者只需处理类型化的数据。

---

Principle 2: Type-First Development

原则2:类型优先开发

Types define contracts before implementation. This workflow catches design problems early:
  1. Define data shapes — dataclass or TypedDict first
  2. Define function signatures — parameter and return types
  3. Implement to satisfy types — let the type checker guide completeness
  4. Validate at boundaries — runtime checks only where data enters the system
在实现前先用类型定义契约。这种工作流能提前发现设计问题:
  1. 定义数据结构 —— 先创建dataclass或TypedDict
  2. 定义函数签名 —— 参数和返回类型
  3. 按类型实现 —— 让类型检查器引导实现的完整性
  4. 在边界处验证 —— 仅在数据进入系统的边界进行运行时检查

Frozen Dataclasses for Internal Data

内部数据使用冻结Dataclass

python
from dataclasses import dataclass
from typing import Literal

@dataclass(frozen=True)
class AgentRecord:
    agent_id: str
    task_name: str
    worktree_path: Path
    platform: Literal["claude", "codex", "cursor"]
    status: Literal["running", "done", "failed"]
    branch: str
Frozen dataclasses are immutable — no accidental mutation, safe to pass around.
python
from dataclasses import dataclass
from typing import Literal

@dataclass(frozen=True)
class AgentRecord:
    agent_id: str
    task_name: str
    worktree_path: Path
    platform: Literal["claude", "codex", "cursor"]
    status: Literal["running", "done", "failed"]
    branch: str
冻结dataclass是不可变的——不会发生意外修改,可安全地在各处传递。

TypedDict for External JSON Shapes

外部JSON结构使用TypedDict

When the data comes from a file (task.json, config.yaml, registry.json), use TypedDict to document the expected shape:
python
from typing import TypedDict, Required, NotRequired

class TaskData(TypedDict):
    title: Required[str]
    status: Required[str]
    assignee: NotRequired[str]
    priority: NotRequired[str]
    parent: NotRequired[str]
    children: NotRequired[list[str]]
This eliminates scattered
.get("field", default)
calls — the shape is documented once.
当数据来自文件(task.json、config.yaml、registry.json)时,使用TypedDict记录预期的结构:
python
from typing import TypedDict, Required, NotRequired

class TaskData(TypedDict):
    title: Required[str]
    status: Required[str]
    assignee: NotRequired[str]
    priority: NotRequired[str]
    parent: NotRequired[str]
    children: NotRequired[list[str]]
这消除了分散的
.get("field", default)
调用——结构只需定义一次。

NewType for Domain Primitives

领域原语使用NewType

When two strings mean different things, make the type system enforce it:
python
from typing import NewType

TaskName = NewType("TaskName", str)    # directory name like "03-10-v040"
BranchName = NewType("BranchName", str)  # git branch like "feat/v0.4.0"

def create_branch(task: TaskName) -> BranchName:
    return BranchName(f"task/{task}")
当两个字符串代表不同含义时,让类型系统强制执行区分:
python
from typing import NewType

TaskName = NewType("TaskName", str)    # 目录名称,如"03-10-v040"
BranchName = NewType("BranchName", str)  # Git分支,如"feat/v0.4.0"

def create_branch(task: TaskName) -> BranchName:
    return BranchName(f"task/{task}")

Discriminated Unions for State

状态使用可辨识联合类型

When an entity can be in distinct states with different data:
python
@dataclass(frozen=True)
class Pending:
    status: Literal["pending"] = "pending"

@dataclass(frozen=True)
class Running:
    status: Literal["running"] = "running"
    pid: int
    worktree: Path

@dataclass(frozen=True)
class Completed:
    status: Literal["completed"] = "completed"
    branch: str
    commit: str

AgentState = Pending | Running | Completed

def handle(state: AgentState) -> None:
    match state:
        case Running(pid=pid, worktree=wt):
            check_process(pid)
        case Completed(branch=br):
            create_pr(br)
        case Pending():
            pass
The type checker ensures every state is handled. No more
if data.get("status") == "running"
with forgotten branches.

当实体处于不同状态且携带不同数据时:
python
@dataclass(frozen=True)
class Pending:
    status: Literal["pending"] = "pending"

@dataclass(frozen=True)
class Running:
    status: Literal["running"] = "running"
    pid: int
    worktree: Path

@dataclass(frozen=True)
class Completed:
    status: Literal["completed"] = "completed"
    branch: str
    commit: str

AgentState = Pending | Running | Completed

def handle(state: AgentState) -> None:
    match state:
        case Running(pid=pid, worktree=wt):
            check_process(pid)
        case Completed(branch=br):
            create_pr(br)
        case Pending():
            pass
类型检查器会确保所有状态都被处理。不会再出现
if data.get("status") == "running"
却遗漏分支的情况。

Principle 3: Information Hiding

原则3:信息隐藏

Each module should encapsulate design decisions. When the same knowledge appears in multiple modules, information has leaked.
每个模块都应封装设计决策。当相同的知识出现在多个模块中时,就发生了信息泄露。

Common Leakage Patterns in Scripts

脚本中常见的信息泄露模式

JSON schema knowledge scattered everywhere:
python
undefined
JSON schema知识分散在各处:
python
undefined

BAD — 9 files all know how to iterate tasks and parse task.json

糟糕 —— 9个文件都知道如何遍历任务和解析task.json

for d in sorted(tasks_dir.iterdir()): if d.name == "archive" or not d.is_dir(): continue task_json = d / "task.json" if task_json.exists(): data = json.loads(task_json.read_text()) title = data.get("title") or data.get("name", "") ...

```python
for d in sorted(tasks_dir.iterdir()): if d.name == "archive" or not d.is_dir(): continue task_json = d / "task.json" if task_json.exists(): data = json.loads(task_json.read_text()) title = data.get("title") or data.get("name", "") ...

```python

GOOD — one module owns task iteration

优秀 —— 由一个模块负责任务遍历

common/tasks.py

common/tasks.py

def iter_active_tasks(tasks_dir: Path) -> Iterator[TaskInfo]: """Yield all active (non-archived) tasks.""" for d in sorted(tasks_dir.iterdir()): if d.name == "archive" or not d.is_dir(): continue info = _load_task_json(d) if info: yield info

**File format details leaking through layers:**
```python
def iter_active_tasks(tasks_dir: Path) -> Iterator[TaskInfo]: """生成所有活跃(未归档)的任务。""" for d in sorted(tasks_dir.iterdir()): if d.name == "archive" or not d.is_dir(): continue info = _load_task_json(d) if info: yield info

**文件格式细节渗透到多层代码:**
```python

BAD — caller knows it's JSON, knows the path convention

糟糕 —— 调用者知道这是JSON,知道路径约定

registry_path = trellis_dir / "registry.json" data = json.loads(registry_path.read_text()) data["agents"][agent_id] = {...} registry_path.write_text(json.dumps(data, indent=2))
registry_path = trellis_dir / "registry.json" data = json.loads(registry_path.read_text()) data["agents"][agent_id] = {...} registry_path.write_text(json.dumps(data, indent=2))

GOOD — module hides storage format

优秀 —— 模块隐藏了存储格式

registry = AgentRegistry(trellis_dir) registry.add(agent_id, task=task_name, platform="claude")

---
registry = AgentRegistry(trellis_dir) registry.add(agent_id, task=task_name, platform="claude")

---

Principle 4: Pull Complexity Downward

原则4:将复杂度向下转移

When complexity is unavoidable, the module should absorb it internally rather than pushing it to callers. A module has few developers but many users — it's better for the module author to handle complexity once than for every caller to handle it independently.
python
undefined
当复杂度不可避免时,模块应在内部吸收它,而非推给调用者。一个模块的开发者很少,但使用者很多——让模块作者一次性处理复杂度,比让每个调用者独立处理更好。
python
undefined

BAD — pushes complexity to every caller

糟糕 —— 将复杂度推给每个调用者

def run_git(args: list[str]) -> subprocess.CompletedProcess: return subprocess.run(["git"] + args, capture_output=True, text=True)
def run_git(args: list[str]) -> subprocess.CompletedProcess: return subprocess.run(["git"] + args, capture_output=True, text=True)

Every caller must: check returncode, decode stderr, handle encoding,

每个调用者都必须:检查returncode、解码stderr、处理编码、

strip whitespace, handle repo not found, etc.

去除空白、处理仓库未找到等情况。

GOOD — absorbs complexity

优秀 —— 吸收复杂度

def run_git(args: list[str], *, cwd: Path | None = None) -> str: """Run git command, return stdout. Raises GitError on failure.""" result = subprocess.run( ["git"] + args, capture_output=True, text=True, encoding="utf-8", errors="replace", cwd=cwd, ) if result.returncode != 0: raise GitError(args[0], result.stderr.strip()) return result.stdout.strip()
undefined
def run_git(args: list[str], *, cwd: Path | None = None) -> str: """运行Git命令,返回标准输出。失败时抛出GitError。""" result = subprocess.run( ["git"] + args, capture_output=True, text=True, encoding="utf-8", errors="replace", cwd=cwd, ) if result.returncode != 0: raise GitError(args[0], result.stderr.strip()) return result.stdout.strip()
undefined

Anti-patterns of Pushing Complexity Up

将复杂度向上转移的反模式

  • Returning raw
    subprocess.CompletedProcess
    and letting callers check
    .returncode
  • Raising generic exceptions that callers must parse
  • Using configuration parameters to avoid making decisions
  • Returning
    dict
    when a typed object would let callers skip validation

  • 返回原始的
    subprocess.CompletedProcess
    ,让调用者自己检查
    .returncode
  • 抛出通用异常,让调用者自行解析
  • 使用配置参数来避免做出决策
  • 当类型化对象能让调用者跳过验证时,却返回
    dict

Principle 5: Define Errors Out of Existence

原则5:从设计上消除错误

Exception handling is a major source of complexity. The best strategy is to design semantics so error conditions simply aren't errors.
python
undefined
异常处理是复杂度的主要来源之一。最佳策略是设计语义,让错误场景不再被视为错误。
python
undefined

BAD — raises if key doesn't exist

糟糕 —— 键不存在时抛出异常

def remove_agent(registry: dict, agent_id: str) -> None: if agent_id not in registry["agents"]: raise KeyError(f"Agent {agent_id} not found") del registry["agents"][agent_id]
def remove_agent(registry: dict, agent_id: str) -> None: if agent_id not in registry["agents"]: raise KeyError(f"Agent {agent_id} not found") del registry["agents"][agent_id]

GOOD — guarantees postcondition: agent is not in registry

优秀 —— 保证后置条件:代理不在注册表中

def remove_agent(registry: dict, agent_id: str) -> None: """Ensure agent_id is not in the registry after this call.""" registry["agents"].pop(agent_id, None)

```python
def remove_agent(registry: dict, agent_id: str) -> None: """确保调用此方法后,agent_id不在注册表中。""" registry["agents"].pop(agent_id, None)

```python

BAD — raises if directory already exists

糟糕 —— 目录已存在时抛出异常

def init_workspace(path: Path) -> None: if path.exists(): raise FileExistsError(f"{path} already exists") path.mkdir()
def init_workspace(path: Path) -> None: if path.exists(): raise FileExistsError(f"{path} already exists") path.mkdir()

GOOD — guarantees postcondition: directory exists

优秀 —— 保证后置条件:目录存在

def ensure_workspace(path: Path) -> Path: """Ensure workspace directory exists. Returns the path.""" path.mkdir(parents=True, exist_ok=True) return path

The key insight: define the operation by its **postcondition** ("after this call, X is true") rather than its precondition ("X must be true before calling").

---
def ensure_workspace(path: Path) -> Path: """确保工作区目录存在。返回路径。""" path.mkdir(parents=True, exist_ok=True) return path

核心见解:通过**后置条件**(“调用此方法后,X为真”)而非前置条件(“调用前X必须为真”)来定义操作。

---

Principle 6: KISS and Rule of Three

原则6:KISS原则与三次法则

KISS — Keep It Simple

KISS —— 保持简单

Choose the simplest solution that works. Complexity must be justified by concrete (not hypothetical) requirements.
python
undefined
选择最简单的可行方案。复杂度必须由具体的(而非假设的)需求来证明其合理性。
python
undefined

Over-engineered — registry pattern for 3 formatters

过度设计 —— 为3个格式化器使用注册表模式

class FormatterRegistry: _registry: dict[str, type] = {} @classmethod def register(cls, name: str): ... @classmethod def create(cls, name: str): ...
class FormatterRegistry: _registry: dict[str, type] = {} @classmethod def register(cls, name: str): ... @classmethod def create(cls, name: str): ...

Simple — just a dictionary

简单实现 —— 只用一个字典

FORMATTERS = {"json": format_json, "text": format_text, "table": format_table}
def format_output(fmt: str, data: Any) -> str: formatter = FORMATTERS.get(fmt) if not formatter: raise ValueError(f"Unknown format: {fmt}") return formatter(data)
undefined
FORMATTERS = {"json": format_json, "text": format_text, "table": format_table}
def format_output(fmt: str, data: Any) -> str: formatter = FORMATTERS.get(fmt) if not formatter: raise ValueError(f"Unknown format: {fmt}") return formatter(data)
undefined

Rule of Three

三次法则

Wait until you have three instances of a pattern before extracting an abstraction. Two is coincidence; three is a pattern. Premature abstraction is worse than duplication because:
  • It couples unrelated code through a shared abstraction
  • It makes each instance harder to understand independently
  • It creates pressure to fit future cases into the abstraction even when they don't fit
However: when you do hit three, extract immediately. Don't let it reach nine.

当你遇到三次相同的模式实例后,再提取抽象。两次是巧合,三次才是模式。过早的抽象比重复更糟糕,因为:
  • 它通过共享抽象将不相关的代码耦合在一起
  • 它让每个实例更难独立理解
  • 它会迫使未来的案例即使不适合也要适配这个抽象
:当你遇到三次时,立即提取抽象。不要等到出现九次才行动。

Principle 7: Single Responsibility and Module Boundaries

原则7:单一职责与模块边界

Each module should have one reason to change. When a module grows beyond ~300 lines, check if it has multiple responsibilities.
每个模块应该只有一个变更理由。当模块超过约300行时,检查它是否承担了多个职责。

Decomposition Signals

拆分信号

Split when:
  • A file has multiple "sections" separated by comment headers
  • You need to import only one function from a large module
  • Tests for different parts of the module have no shared setup
  • Changes to one responsibility don't require understanding the other
出现以下情况时应拆分模块:
  • 文件中有多个用注释标题分隔的“章节”
  • 你只需要从一个大模块中导入一个函数
  • 模块不同部分的测试没有共享的设置步骤
  • 修改一个职责时无需理解另一个职责

How to Split

如何拆分

Split by information hiding (what knowledge is encapsulated), not by execution order (what runs when).
python
undefined
信息隐藏(封装了哪些知识)拆分,而非按执行顺序(何时运行)拆分。
python
undefined

BAD — split by execution order (temporal decomposition)

糟糕 —— 按执行顺序拆分(时间分解)

step1_parse_args.py, step2_validate.py, step3_execute.py

step1_parse_args.py, step2_validate.py, step3_execute.py

All three must know the command structure

这三个模块都必须了解命令结构

GOOD — split by responsibility

优秀 —— 按职责拆分

task_store.py — owns task.json read/write, schema, iteration

task_store.py —— 负责task.json的读写、schema、遍历

task_cli.py — owns argparse, subcommand routing

task_cli.py —— 负责argparse、子命令路由

task_display.py — owns formatting, colors, table output

task_display.py —— 负责格式化、颜色、表格输出


---

---

Principle 8: Consistent Shared Infrastructure

原则8:一致的共享基础设施

When multiple scripts need the same capability, provide it once in
common/
.
CapabilityShould Live InNot In
JSON file read/write
common/io.py
Each script's
_read_json_file
Terminal colors + logging
common/log.py
Each script's
Colors
class
Git command execution
common/git.py
_run_git_command
prefixed private
Task data access
common/tasks.py
Ad-hoc task.json parsing
Path constants
common/paths.py
(existing)
Hardcoded strings
Naming: If a function is used by other modules, it's public API — don't prefix it with
_
.

当多个脚本需要相同的功能时,在
common/
目录中统一提供。
功能应存放位置不应存放位置
JSON文件读写
common/io.py
每个脚本的
_read_json_file
终端颜色 + 日志
common/log.py
每个脚本的
Colors
Git命令执行
common/git.py
前缀为
_run_git_command
的私有函数
任务数据访问
common/tasks.py
临时的task.json解析代码
路径常量
common/paths.py
(已存在)
硬编码字符串
命名规则:如果函数被其他模块使用,它就是公共API——不要用
_
作为前缀。

Principle 9: Structured CLI Output Parsing

原则9:结构化CLI输出解析

When parsing output from shell commands (git, grep, etc.), respect semantic whitespace:
python
undefined
解析shell命令(git、grep等)的输出时,要尊重语义空白:
python
undefined

BAD — .strip() destroys semantic whitespace

糟糕 —— .strip()会破坏语义空白

git submodule status prefix: ' ' = initialized, '-' = uninitialized, '+' = changed

git submodule status前缀: ' ' = 已初始化, '-' = 未初始化, '+' = 已修改

line = output_line.strip() # Loses the prefix character!
line = output_line.strip() # 丢失了前缀字符!

GOOD — strip only trailing newlines

优秀 —— 只去除末尾的换行符

line = output_line.rstrip("\n\r") prefix = line[0] if line else " "

Always document what each field position means when parsing structured command output.

---
line = output_line.rstrip("\n\r") prefix = line[0] if line else " "

解析结构化命令输出时,务必记录每个字段位置的含义。

---

Red Flags Quick Reference

警示信号速查

Use during code review and self-review:
SignalWhat It Means
Shallow ModuleInterface is nearly as complex as implementation
Information LeakageSame JSON schema / file format knowledge in multiple modules
Duplicated UtilitySame helper function copied to multiple files
God ModuleFile > 500 lines with multiple unrelated responsibilities
Pass-Through FunctionFunction just forwards args to another with similar signature
Magic
.get()
Chains
data.get("x") or data.get("y", "")
— missing type definition
sys.path Hacking
sys.path.insert(0, ...)
— fix package structure instead
Private-Named Public API
_function
imported by 3+ external modules
Raw Dict ThreadingPassing
dict
through 4+ function calls — use a dataclass
Repeated IterationSame directory scan / file parse pattern in 3+ locations
Broad Exception Catch
except Exception:
without re-raising — hides bugs
Temporal DecompositionModules split by "what runs when" instead of "what knows what"

用于代码评审和自我评审:
信号含义
浅度模块接口复杂度几乎与实现相当
信息泄露相同的JSON schema / 文件格式知识出现在多个模块中
重复的实用函数相同的辅助函数被复制到多个文件中
上帝模块文件超过500行且包含多个不相关职责
透传函数函数只是将参数转发给另一个签名相似的函数
链式魔法.get()
data.get("x") or data.get("y", "")
—— 缺少类型定义
sys.path hack
sys.path.insert(0, ...)
—— 应修复包结构
私有命名的公共API
_function
被3个以上外部模块导入
原始字典传递
dict
传递经过4个以上函数调用 —— 使用dataclass
重复遍历相同的目录扫描 / 文件解析模式出现在3个以上位置
宽泛的异常捕获
except Exception:
但不重新抛出 —— 会隐藏bug
时间分解模块按“何时运行”而非“了解什么”拆分

Design Checklist (Before Writing Code)

设计检查清单(编写代码前)

  1. Types first: Define the data shape before writing logic
  2. Module depth check: Will the interface be simpler than the implementation?
  3. Duplication scan:
    grep -r "pattern" .
    before creating new utilities
  4. Responsibility check: Does this belong in an existing module?
  5. Error design: Can you define the error out of existence?
  6. Naming precision: Does the name convey meaning without reading the implementation?
  1. 类型优先:在编写逻辑前定义数据结构
  2. 模块深度检查:接口会比实现简单吗?
  3. 重复扫描:创建新实用函数前用
    grep -r "pattern" .
    检查是否已有实现
  4. 职责检查:这个功能属于现有模块吗?
  5. 错误设计:你能从设计上消除错误吗?
  6. 命名精准性:名称是否无需查看实现就能传达含义?

Design Checklist (During Code Review)

设计检查清单(代码评审中)

  1. Red flags scan: Check the table above against the diff
  2. Type safety: Are new data shapes documented with types?
  3. Information hiding: Does the change leak implementation details?
  4. Consistency: Does it follow the existing patterns in the module?
  5. Depth: Is the common path simple for callers?

  1. 警示信号扫描:对照上表检查代码差异
  2. 类型安全性:新的数据结构是否用类型记录了?
  3. 信息隐藏:变更是否泄露了实现细节?
  4. 一致性:是否遵循了模块中的现有模式?
  5. 深度:调用者的常用路径是否简单?

Strategic Investment

战略性投入

Spend roughly 10-20% of each change improving surrounding design.
Working code is necessary but not sufficient. The increments of software development should be abstractions, not just features. Each change should leave the codebase slightly better than you found it.
This is not perfectionism — it's compound interest. Small design improvements accumulate into a system that's dramatically easier to work with over time.
在每次变更中,花费大约10-20%的时间改进周边的设计。
可运行的代码是必要的,但还不够。软件开发的增量应该是抽象,而非仅仅是功能。每次变更都应让代码库比你接手时稍微好一点。
这不是完美主义——这是复利效应。小的设计改进会累积成一个随着时间推移大幅提升开发效率的系统。