关于 pragmatic-programmer
面向开发者和工程团队的软件工艺技能,借鉴《The Pragmatic Programmer》指导架构、代码审查与工程实践。它强调让系统易于理解和变更的习惯,并提供原则诊断及改进方向。
主要功能
- 01用 DRY 识别知识重复
- 02设计正交组件
- 03采用示踪弹与原型
- 04定义契约和断言
- 05处理破窗与技术债
- 06优先选择可逆决策并采用区间估算
适用场景
- 01审查架构耦合与变更影响
- 02规划端到端的精简实现
- 03设定模块前置条件与不变量
- 04评估供应商选择和工期估算
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wondelai/ skills --skill pragmatic-programmer安装后在 Claude Code 中输入/pragmatic-programmer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/broken-windows.mdcontracts-assertions.mddry-orthogonality.mdestimation-portfolio.mdreversibility.mdtracer-bullets.md详细介绍
The Pragmatic Programmer 框架
Hunt 和 Thomas 的《The Pragmatic Programmer》(20 周年纪念版)提出的软件工艺系统级方法。在设计系统、评审架构、编写代码或为工程文化提供建议时,应用这些元原则——思考如何理解软件,而不只是如何编写软件。
核心原则
关心自己的工艺。 软件开发要求持续学习、严谨实践和个人责任——务实的程序员不会只关注眼前的问题,还会考虑上下文、权衡取舍和长期影响。优秀的软件源于良好的习惯:坚决避免重复,保持组件正交,并将每一行代码都视为必须证明其价值的活资产。目标不是追求完美,而是打造易于修改、易于理解、值得信赖的系统。
评分
目标:10/10。 根据七项快速诊断逐项评分:每项回答“是”得约 1.4 分(7 项均为“是”即得 10 分)。然后按以下区间划分结果:
- 9-10:所有原则均得到遵守——知识符合 DRY、各层正交、有可运行的 tracer 切片、边界处有契约、没有破窗、供应商和数据库选择可逆、估算有范围。
- 5-6:存在 1-2 项会造成实际变更成本的违规(例如业务逻辑与数据库耦合、只给出单点估算)。
- <=3:重复无处不在、存在全局状态,或破窗不断累积——熵正在占上风。
始终说明评分,指出未通过的诊断项,并给出“行动”栏中列出的具体修复措施,以达到 10/10。
七大元原则
构建持久软件的七项原则:
1. DRY(Don't Repeat Yourself,不要重复自己)
核心概念: 系统中的每一项知识都必须有且只有一个明确、权威的表示。DRY 针对的是知识,而非代码——重复的逻辑、业务规则或配置远比重复的语法危险。
有效原因: 重复的知识必须在多个地方修改;迟早会有一处被遗漏,导致不一致。DRY 能缩小 bug 的影响范围,让系统更易于修改。
关键见解:
- DRY 适用于知识和意图,而非文本相似性——两个相同的代码块若服务于不同的业务规则,就不属于重复
- 重复有四种类型:外部强加(环境导致)、无意造成(开发者没有意识到)、急于求成(懒得抽象)和开发者间重复(多人各自重复实现)
- 复述代码内容的注释违反 DRY——应解释为什么,而非是什么
- 数据库架构、API 规范和文档都会造成知识重复,除非它们由单一来源生成
- DRY 的反面是 WET:“Write Everything Twice”(所有内容都写两遍)或“We Enjoy Typing”(我们乐于敲代码)
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 配置值 | 单一事实来源 | 在一个 env 文件中设置数据库连接,并在各处引用 |
| 验证规则 | 共享架构 | 客户端和服务器共用一个 JSON Schema 或 Zod 架构 |
| API 契约 | 根据规范生成 | 使用 OpenAPI 规范生成类型、文档和客户端代码 |
参见:references/dry-orthogonality.md,用于判断具体的重复情况,或确定两个代码块是否确实表达了相同的知识——其中提供了四种重复类型各自的示例和缓解措施。
2. 正交性
核心概念: 如果一个组件的变化不会影响另一个组件,那么这两个组件就是正交的。设计自包含、相互独立且用途明确的系统组件。
为何有效: 解耦能将变化限制在局部——一个模块中的修复不会波及无关模块,因此影响范围始终可控。修改数据库层不应导致 UI 出错;更换身份验证提供方,业务逻辑也不应受影响。
关键见解:
- 思考:“如果我大幅更改某个函数背后的需求,会影响多少个模块?”答案应该是一个
- 消除无关事物之间的影响——日志记录的更改绝不应导致计费出错
- 分层架构有助于实现正交性:表示层、领域逻辑、数据访问层
- 避免全局数据——每个使用全局状态的使用方都会与之耦合
- 强制要求继承其类的框架会降低正交性
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 架构 | 分层分离 | Controller -> Service -> Repository,每层都可替换 |
| 依赖 | 依赖注入 | 传入 Notifier 接口,而不是 SlackClient 具体类 |
| 测试 | 隔离的单元测试 | 不依赖数据库、网络或文件系统来测试业务逻辑 |
参见:references/dry-orthogonality.md,了解如何衡量耦合度或重构为解耦的层——其中包括变更影响测试与陌生人测试、分层架构图以及直升机类比。
3. 曳光弹与原型
核心概念: 曳光弹是贯穿系统所有层、功能精简的端到端实现。与一次性原型不同,曳光弹代码是生产代码——轻量但真实。
为何有效: 曳光弹能让你在投入完善每项功能之前,立即获得端到端反馈。用户能看到真实的成果,开发者有了可供构建的框架,集成问题也能及早显现。
关键见解:
- 曳光弹:贯穿系统(UI -> API -> DB)的精简但完整路径——保留使用
- 原型:针对单个高风险方面进行探索——完成后丢弃
- 在“摸黑开火”时使用曳光弹——例如需求模糊或架构尚未验证时
- 如果曳光弹没命中,就调整后再次发射——迭代成本很低
- 明确标注原型仅供丢弃——绝不能让原型成为生产代码
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 新项目 | 纵向切片 | 一个端到端功能:按钮 -> API -> DB -> 响应 |
| 技术尚不确定 | Spike 原型 | 在决定采用 WebSocket 之前测试其性能 |
| 微服务 | 行走骨架 | 让 Hello-world 服务跑通完整的 CI/CD 流水线 |
参见:references/tracer-bullets.md,了解在新项目中如何选择曳光弹或原型,或如何构建行走骨架——其中包括“摸黑开火”的决策方法、迭代循环和常见陷阱。
4. 契约式设计与断言式编程
核心概念: 通过前置条件(之前必须满足的条件)、后置条件(之后能够保证的条件)和不变量(始终成立的条件),定义并强制执行软件模块的权利与责任。契约遭到违反时,立即明确地失败。
为何有效: 契约能让假设明确化。系统不会悄悄破坏数据,也不会在无效状态下勉强运行,而是在问题发生处直接崩溃——已停止运行的程序不会说谎。
关键见解:
- 前置条件:调用方的责任——“我只接受正整数”
- 后置条件:例程的保证——“我会返回一个已排序的列表”
- 不变量:始终成立——“账户余额永远不会为负”
- 尽早崩溃:停止运行的程序造成的损害远少于瘫痪的程序
- 对绝不应发生的情况使用断言;对可能发生的情况使用错误处理
- 在动态语言中,通过运行时检查和守卫子句实现契约
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 函数入口 | 前置条件守卫 | 在函数开头使用 assert age >= 0, "Age cannot be negative" |
| 类状态 | 不变量验证 | 每次状态变更后调用 validate! |
| API 边界 | 模式验证 | 处理请求前,根据模式验证请求正文 |
参见:references/contracts-assertions.md,了解何时为例程添加契约,或如何在断言与错误处理之间做选择——其中包含前置条件、后置条件和不变量的示例模式、动态语言中的守卫子句,以及断言与错误处理的适用边界。
5. 破窗理论
核心概念: 一扇破窗——设计糟糕的代码、错误的管理决策,或“以后再修”的权宜之计——会开启腐化的开端。一旦系统显露出被忽视的迹象,熵就会加速,纪律也会瓦解。
为何有效: 这是心理作用。代码整洁时,开发者会感受到保持整洁的社会压力;代码已经杂乱时,继续制造混乱的门槛就会降至零。质量是团队习惯,而非个人英雄主义。
关键见解:
- 不要让破窗(糟糕的设计、错误的决策、质量低劣的代码)得不到修复
- 如果现在无法修复,就先把它封起来:添加附带工单的 TODO、禁用功能,或使用存根
- 成为变革的催化剂:向大家展示未来可行的样子(石头汤)
- 留意缓慢退化(温水煮青蛙)——持续监控技术债指标
- 第一个权宜之计代价最高,因为它为之后所有的权宜之计开了先例
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 遗留代码 | 封住破窗 | 添加功能前,先用整洁的接口封装糟糕的代码 |
| 代码审查 | 对新增技术债零容忍 | 拒绝添加 // TODO: fix later 却没有关联工单的 PR |
| 技术债 | 技术债预算 | 每个 sprint 拿出 20% 的时间修复破窗 |
参见:references/broken-windows.md,了解团队何时开始将忽视问题视为常态,或需要推动扭转局面——其中包含修复策略、“石头汤”催化方法,以及如何建立质量文化。
6. 可逆性与灵活性
核心概念: 没有最终决定。构建系统,让你可以轻松改变对数据库、框架、供应商、架构和部署目标的选择——变更成本应与变更范围成正比。
为何有效: 需求会变化,供应商会被收购,技术也会失宠。如果你的架构将其中任何假设硬编码进去,每次变更都会变成重写;灵活的架构将决策视为配置,而非结构。
关键见解:
- 将第三方依赖抽象在自己的接口之后——绝不让供应商 API 渗入业务逻辑
- “岔路口”测试:你能在一周内从 Postgres 切换到 DynamoDB 吗?如果不能,说明耦合过紧
- 由元数据驱动的系统(配置文件、功能标志)比硬编码逻辑更灵活
- YAGNI 同样适用于过早抽象——暂时不需要的灵活性,不要提前构建
- 可逆性不是预测未来,而是不把自己逼入死角
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 数据库 | Repository 模式 | 业务逻辑调用 repo.save(user),而不是 pg.query(...) |
| 外部 API | 适配器/包装器 | PaymentGateway 接口封装 Stripe;之后可切换到 Braintree |
| 功能标志 | 运行时开关 | 将新的结账流程置于功能标志之后,几秒内即可回滚 |
参见:references/reversibility.md,了解提交采用某个供应商或框架时,或权衡决策需要多大程度的可逆性时可参考的内容——包括分层可逆性模式、“岔路口”测试,以及何时不应以可逆性为优化目标。
7. 估算与知识投资组合
核心概念: 通过理解范围、构建模型、拆分组件并设定区间,学会可靠地估算。像管理金融投资组合一样管理学习:定期投入、分散配置并重新平衡。
为何有效: 诚实的估算能建立与利益相关者之间的信任(“1-3 周”胜过自信却错误的“2 周”)。知识投资组合能让你在技术变迁时保持竞争力——停止学习的程序员也会逐渐失去效能。
关键见解:
- 问问“这个估算用于什么?”——场景决定精度(预算规划还是迭代规划)
- 使用 PERT:(乐观估算 + 4 × 最可能估算 + 悲观估算)/ 6
- 拆分为多个组件并分别估算;总和比单次猜测更准确
- 记录估算日志:将估算与实际情况对比并进行校准
- 投资组合规则:定期投入(每周学习)、拓宽技能范围、不局限于当前技术栈,兼顾稳健和投机型选择,尽早学习新兴技术(低价买入)
代码应用:
| 场景 | 模式 | 示例 |
|---|---|---|
| 迭代规划 | 区间估算 | “3-5 天”,并注明置信度,而不是给出单一数字 |
| 新技术 | 限时探索 | “花 2 天评估;之后我就能准确估算” |
| 学习 | 每周投入 | 每周花 1 小时学习一门新语言、一种新工具或一个新领域 |
参见:references/estimation-portfolio.md,了解如何给出需要承担责任的估算,或校准过去的估算偏差——包括 PERT 和拆分步骤、估算日志校准循环,以及投资组合再平衡。
常见错误
| 错误 | 失败原因 | 修正方法 |
|---|---|---|
| 将用途不同但看起来相似的代码 DRY 化 | 耦合了无关概念;修改一处会破坏另一处 | 只对知识应用 DRY,而非巧合的代码相似性 |
| 跳过追踪弹,逐层构建 | 集成问题很晚才会浮现;缺少端到端反馈 | 先构建一个精简的垂直切片 |
| 因为“以后会重构”而忽视破窗 | 熵会加速增长;以后永远不会到来;士气会下降 | 立即修复,或用有跟踪记录的工单将其封起来 |
| 将估算作为单点承诺 | 未能兑现时,虚假的精确性会侵蚀信任 | 始终提供范围和置信度 |
| 一开始就让所有东西都“灵活” | 过度设计;没有证据表明需要抽象 | 有具体证据表明需要灵活性时再添加 |
| “为了性能”而移除生产环境断言 | 断言原本能捕获的错误会悄悄破坏数据 | 保留关键断言;移除前先进行基准测试 |
| 为了“方便”而使用全局状态 | 破坏正交性;一切都彼此耦合 | 使用依赖注入和显式参数 |
快速诊断
| 问题 | 如果否 | 行动 |
|---|---|---|
| 我能否在不触及业务逻辑的情况下更改数据库? | 违反正交性 | 引入仓储/适配器模式 |
| 我是否有一个可运行的端到端切片? | 缺少追踪弹 | 扩展之前先构建一个垂直切片 |
| 每条业务规则是否都只在一个地方定义? | 违反 DRY | 找出权威来源;移除重复项 |
| 新开发者会称这个代码库“整洁”吗? | 存在破窗 | 安排专门的清理冲刺 |
| 我的估算是否包含范围和置信度? | 估算有问题 | 改用 PERT 或基于范围的估算 |
| 我能否在 5 分钟内回滚此次部署? | 可逆性不足 | 添加功能标志和蓝绿部署 |
| 我是否每周都在学习新东西? | 知识储备停滞 | 安排每周学习时间并跟踪进度 |
延伸阅读
- Andrew Hunt 和 David Thomas 著,《程序员修炼之道:通往务实的最高境界(20 周年纪念版)》
关于作者
Andrew Hunt 和 David Thomas 共同创立了 Pragmatic Bookshelf,也是《敏捷宣言》17 位原始作者中的两位。Thomas 创造了“DRY”和“Code Kata”这两个说法,并合著了 Programming Ruby(Pickaxe 一书);Hunt 专注于团队如何学习、沟通和维护质量。他们共同撰写了 The Pragmatic Programmer,这是有史以来最具影响力的软件书籍之一。