走到这一章,你已经有了设备、配好了环境、学会提问、用上了顺手的工具、也懂得在 linux 上把服务跑起来。万事俱备,最后一个问题来了:写什么,怎么写。

正确

正确是第一要求,Test Driven Development (TDD) 是一种开发规范,用测试保证你代码的正确性。不过虽然测试很重要,但要牢记过犹不及。过多的测试会变得冗余,拖累开发进度,实际帮助却不大。

可读性

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” - Martin Fowler

  • 命名要有意义:别用 atmp2data1 这种名字。变量叫 user_count,函数叫 calculate_average_score,一眼就知道其作用。
  • 命名长度要合适:导出给外部的接口要详细,内部变量可适当精简,date_of_birthdob 好懂,除非 dob 在你的领域里是约定俗成的。
  • 函数要短小:遵守单一职责原则,一个函数最好只做一件事。函数参数也别太多,超过三四个就考虑用一个结构体或字典收着,否则调用时容易把顺序搞错。
  • 用常量或枚举代替魔法数字:与其写 if age > 18,不如写成 ADULT_AGE = 18 再比较,魔法数应当是代码中严禁出现的东西。

提前退出

有些教程主张少用 continuebreak,尽量只有一个 return,保证函数单一出口,但实际写代码通常不会严格遵守这些要求。与其写一大坨嵌套的 if,不如把不满足条件的情况先 return 掉,主体逻辑自然就平铺下来。这种反转 if 判断、提前退出的写法叫卫语句(guard clause),能大幅减少缩进层级。

反面例子,层层嵌套:

def process(user):
    if user is not None:
        if user.is_active:
            if user.has_permission:
                do_work(user)

改成提前退出,立刻清爽:

def process(user):
    if user is None:
        return
    if not user.is_active:
        return
    if not user.has_permission:
        return
    do_work(user)

从例子中就可以看出,此方法对于 Python 这种需要拿游标卡尺写代码的语言的可读性有极大提升。

注释

注释不是为了凑字数,而是解释为什么——那些从代码本身看不出来的意图。

# 错误示范:复述代码
i = i + 1  # i 加 1

# 正确示范:解释原因
i = i + 1  # 跳过已下线的用户,避免统计脏数据

和测试一样,注释也要牢记过犹不及。过多的注释会起反作用,尤其是过时的注释比不写更糟。个人经验是:每个方法、接口、类以及类中的字段最好都有注释;逻辑复杂的方法,在方法中每个阶段写一个注释即可。

DRY

同一段逻辑出现在五个地方,改一处就得改五处,漏一个就是 bug。把重复的逻辑抽成一个函数,谁要用谁调用。DRY 即 Don’t Repeat Yourself,别重复自己。

反过来也别走极端:为了复用把两个其实不太相干的东西硬揉在一起,反而更乱。

KISS

Keep It Simple, Stupid。能简单就别复杂。初学者常犯的毛病是还没写几行就想上各种抽象、设计模式、泛型,结果代码绕来绕去,自己都看不懂。先写出能跑的直白版本,真遇到重复或扩展压力时再考虑抽象。最简单的方案往往最不容易出错,也最容易被别人接手。

实际写代码要分析场景,用最小的代价完成最多的事,软件工程没有 silver bullet,适合的才是最好的,trade-off 是贯穿全流程的判断。

错误处理要诚实

初学者常写:

try:
    do_something()
except:
    pass  # 吞掉所有错误

except 后面什么都不做,等于把问题藏进黑洞,将来排错会极为痛苦。要么真的处理(比如重试、用默认值),要么把错误原样抛出来或记到日志里,让问题暴露出来。

版本管理

前面章节提到过 git,这里再强调一次:从第一行代码开始就用 git。每次完成一个小功能就 commit 一次,写上清楚的提交信息。

git 给了你两样东西:出事了能回滚,协作时不会互相覆盖。这是写出可持续维护的好代码的基础设施,不是可选项。

CLAUDE.md

下面是笔者最近开发过程中整理出的一个 CLAUDE.md

# 全局规则 (Global Instructions)

以下规则适用于所有项目、所有会话,除非用户明确指示 otherwise。

## Git 与提交

- **不要私自执行 `git commit`、`git push` 或暂存改动(`git add`)。** 仅在用户明确要求时才提交或推送。
- 不要主动创建分支、stash 或改动工作区状态,除非任务需要或用户要求。

## 编码过程

- **编码过程中不要过度 review,也不要反复停下测试。** 实现完成后再进行统一的 review 和测试;中途只做必要的最小验证。
- 过程中频繁停下 review/测试会打断实现节奏,集中在结束时一次性做更高效。
- 实现完成后,先跑 `make fix` / `gofmt -w` 等格式化(如适用),再做统一 review。

## 类型标注

- **变量和参数尽量使用明确的类型标注,少用 `any`。** 能确定类型时显式标注(Python 用类型注解,TS 用具体类型),避免 `any`/`Any` 等泛化类型,提高可读性与可维护性。

## 代码复用

- **写代码时优先复用当前已有的代码,尤其是工具类、数据库类等通用方法。** 先搜索仓库中是否已有可实现相同功能的工具方法、DAO/service 或公共函数,确认没有再新写;避免重复造轮子导致同一逻辑存在多份实现、后续难以维护。

## 循环依赖

- **出现循环依赖时,优先通过调整包/模块结构来解决,而非取巧绕过。** 先考虑能否通过合理的拆包、合并、下沉等结构调整消除循环(这些操作必须基于职责划分的合理性,不能为了消解循环依赖而硬拆乱挪);结构调整确实无法解决时,再使用 `@Lazy` 注入、`TYPE_CHECKING` 等取巧手段作为兜底。

## Python 运行环境

- **运行 Python 代码时禁止直接运行,必须在 conda 环境下运行。** 不要直接使用系统 `python` 或裸 `python3` 执行,必须先激活一个明确的 conda 环境。
- 如果不确定应该使用哪个 conda 环境,先询问用户,不要擅自猜测或默认使用 `base`
## 注释

- **注释简洁,不要长篇大论。** 只写必要的注释,避免多段 docstring 或多行注释块,能用一行说清就不用多行。
- **修改代码时同步更新对应注释。** 代码改了注释没改等于误导,保持二者一致。
- **注释与代码不一致时,以代码为准并更新注释。** 不要照着过时注释去理解或改代码,先核实代码实际行为,再修正注释。
- **不要完全相信注释。** 注释可能过时、不准或本身就错,以代码实际行为为依据;发现错误注释时顺手修正。
- **注释中不要使用用户的口头禅。** 比如"1:1复制"、"对抗review"、"循环检查"等表达,注释应当用准确、规范的技术语言描述代码行为,而不是照搬用户随口说的话。
- **不要按当前唯一用途去限定类/变量的注释。** 当一个类暂时只有一个方法、或一个变量暂时只用于某一处时(例如某个 key 暂时只用来查 regeo、某个 MapService 暂时只有 regeo 方法),不要在该类/变量的注释里写死"用于 regeo"这类专门用途。当前用途是临时的,未来大概率会扩展出其他用途,把注释写死成单一用途会造成误导,让后来者以为这东西就只能干这个。注释应描述类/变量本身的职责与含义,而非某个时刻碰巧的唯一调用场景。

## Thrift 接口

- **修改 thrift 接口时,禁止修改已有字段的 thrift field id 编号。** 新增字段使用新的 id,绝不复用、改写或删除已存在的 field id —— 否则会破坏线上存量客户端/服务端的序列化兼容性。仅在用户明确要求时才允许改动已有 id。

## 子 Agent

- **使用子 agent 时,所有子 agent 必须与主 agent 使用相同模型。** 不要给子 agent 指定不同的 model(如 haiku/sonnet/opus 切换),保持与主 agent 一致,避免不同模型能力差异导致结果不可控。

## 测试

- **只写必要的测试。** 优先针对真实行为和边界情况写少量有针对性的测试,而不是为每个方法都做覆盖。只在行为不确定、容易出错的地方写测试;确定无疑的逻辑可以不写。
- **执行测试时,精确指定目标**——具体文件、测试名或 `-t` 过滤模式。禁止一次性执行整个测试套件或与当前改动无关的测试。
- **避免执行会写数据库的测试**(除非用户明确要求)。运行前先确认测试是否会修改持久化数据;若不确定,先问用户。
- 测试失败时如实报告输出,不要谎报通过。

## 网络搜索

- **需要搜索网络信息时,优先直接使用 WebFetch 抓取目标网页,不要依赖 WebSearch。** WebSearch 工具可能因地区限制(仅支持美国地区)不可用,直接 fetch 已知 URL 更可靠。
- 若确实需要搜索而非已知 URL,可先用 WebSearch 尝试;失败或无结果时立即改用 WebFetch 抓取相关页面。

项目分层

在大型项目中合理地对模块分层能提高你的开发效率和代码的可读性,浅谈后端项目分层 这篇是我对后端项目分层的一些拙见。