software-design-philosophy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseA Philosophy of Software Design Framework
《软件设计的哲学》框架
A practical framework for managing the fundamental challenge of software engineering: complexity. Apply these principles when designing modules, reviewing APIs, refactoring code, or advising on architecture decisions. The central thesis is that complexity is the root cause of most software problems, and managing it requires deliberate, strategic thinking at every level of design.
这是一个应对软件工程核心挑战——复杂度的实用框架。在设计模块、评审API、重构代码或提供架构决策建议时,均可应用这些原则。核心论点是:复杂度是大多数软件问题的根源,管理复杂度需要在设计的每个层面进行审慎的、策略性的思考。
Core Principle
核心原则
The greatest limitation in writing software is our ability to understand the systems we are creating. Complexity is the enemy. It makes systems hard to understand, hard to modify, and a source of bugs. Every design decision should be evaluated by asking: "Does this increase or decrease the overall complexity of the system?" The goal is not zero complexity -- that is impossible in useful software -- but to minimize unnecessary complexity and concentrate necessary complexity where it can be managed.
编写软件的最大局限在于我们理解所创建系统的能力。 复杂度是敌人,它让系统难以理解、难以修改,还会滋生bug。每个设计决策都应通过这个问题来评估:“这会增加还是降低系统的整体复杂度?” 我们的目标不是实现零复杂度——这在实用软件中是不可能的——而是将不必要的复杂度降至最低,并将必要的复杂度集中到可管理的地方。
Scoring
评分标准
Goal: 10/10. When reviewing or creating software designs, rate them 0-10 based on adherence to the principles below. A 10/10 means deep modules with clean abstractions, excellent information hiding, strategic thinking about complexity, and comments that capture design intent. Lower scores indicate shallow modules, information leakage, tactical shortcuts, or missing design documentation. Always provide the current score and specific improvements needed to reach 10/10.
目标:10/10。 在评审或创建软件设计时,根据以下原则的遵循程度为其评分(0-10分)。10分意味着模块深度足够、抽象清晰、信息隐藏出色、对复杂度有策略性思考,且注释能准确传达设计意图。低分则表明模块浅层次、存在信息泄露、采用战术性捷径,或缺失设计文档。评分时需始终给出当前分数,以及达到10分所需的具体改进措施。
The Software Design Framework
软件设计框架
Six principles for managing complexity and producing systems that are easy to understand and modify:
六条用于管理复杂度、打造易于理解和修改的系统的原则:
1. Complexity and Its Causes
1. 复杂度及其成因
Core concept: Complexity is anything related to the structure of a software system that makes it hard to understand and modify. It manifests through three symptoms: change amplification, cognitive load, and unknown unknowns.
Why it works: By identifying the specific symptoms of complexity, developers can diagnose problems precisely rather than relying on vague notions of "messy code." The two fundamental causes -- dependencies and obscurity -- provide clear targets for design improvement.
Key insights:
- Change amplification: a simple change requires modifications in many places
- Cognitive load: a developer must hold too much information in mind to make a change
- Unknown unknowns: it is not obvious what needs to be changed, or what information is relevant (the worst symptom)
- Dependencies: code cannot be understood or modified in isolation
- Obscurity: important information is not obvious from the code or documentation
- Complexity is incremental -- it accumulates from hundreds of small decisions, not one big mistake
- The "death by a thousand cuts" nature of complexity means every decision matters
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Change amplification | Centralize shared knowledge | Extract color constants instead of hardcoding |
| Cognitive load | Reduce what developers must know | Use a simple |
| Unknown unknowns | Make dependencies explicit | Use type systems and interfaces to surface what a change affects |
| Dependency management | Minimize cross-module coupling | Pass data through well-defined interfaces, not shared global state |
| Obscurity reduction | Name things precisely | |
See: references/complexity-symptoms.md
核心概念: 复杂度指与软件系统结构相关、使其难以理解和修改的任何因素。它通过三种症状表现出来:变更放大、认知负荷和未知的未知。
为何有效: 通过识别复杂度的具体症状,开发者可以精准诊断问题,而非依赖“代码混乱”这类模糊的概念。复杂度的两个根本成因——依赖关系和模糊性——为设计改进提供了明确的目标。
关键见解:
- 变更放大:一个简单的变更需要在多个地方进行修改
- 认知负荷:开发者在进行变更时,必须在脑中记住大量信息
- 未知的未知:不清楚需要修改什么,也不知道哪些信息是相关的(最严重的症状)
- 依赖关系:代码无法独立理解或修改
- 模糊性:重要信息无法从代码或文档中直接获取
- 复杂度是逐步累积的——它源于数百个小决策,而非某个重大错误
- 复杂度的“千刀万剐”特性意味着每个决策都至关重要
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| 变更放大 | 集中共享知识 | 提取颜色常量,而非在20个文件中硬编码 |
| 认知负荷 | 减少开发者需了解的内容 | 使用简单的 |
| 未知的未知 | 明确依赖关系 | 使用类型系统和接口来展示变更会影响的范围 |
| 依赖关系管理 | 最小化跨模块耦合 | 通过定义清晰的接口传递数据,而非使用共享全局状态 |
| 模糊性降低 | 精准命名 | 使用 |
参考:references/complexity-symptoms.md
2. Deep vs Shallow Modules
2. 深度模块与浅度模块
Core concept: The best modules are deep: they provide powerful functionality behind a simple interface. Shallow modules have complex interfaces relative to the functionality they provide, adding complexity rather than reducing it.
Why it works: A module's interface represents the complexity it imposes on the rest of the system. Its implementation represents the functionality it provides. Deep modules give you a high ratio of functionality to interface complexity. The interface is the cost; the implementation is the benefit.
Key insights:
- A module's depth = functionality provided / interface complexity imposed
- Deep modules: simple interface, powerful implementation (Unix file I/O, garbage collectors)
- Shallow modules: complex interface, limited implementation (Java I/O wrapper classes)
- "Classitis": the disease of creating too many small, shallow classes
- Each interface adds cognitive load -- more classes does not mean better design
- The best abstractions hide significant complexity behind a few simple concepts
- Small methods are not inherently good; depth matters more than size
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Deep module | Hide complexity behind simple API | |
| Shallow module | Avoid thin wrappers that just pass through | A |
| Classitis cure | Merge related shallow classes | Combine |
| Method depth | Methods should do something substantial | A |
| Interface simplicity | Fewer parameters, fewer methods | |
See: references/deep-modules.md
核心概念: 最佳模块是深度模块:它们通过简单的接口提供强大的功能。浅度模块的接口复杂度与其提供的功能不成正比,反而会增加系统复杂度。
为何有效: 模块的接口代表了它给系统其他部分带来的复杂度,而实现则代表了它提供的功能。深度模块的功能与接口复杂度之比很高。接口是成本,实现是收益。
关键见解:
- 模块深度 = 提供的功能 / 带来的接口复杂度
- 深度模块:简单接口,强大实现(如Unix文件I/O、垃圾回收器)
- 浅度模块:复杂接口,有限实现(如Java I/O包装类)
- “类泛滥症(Classitis)”:创建过多小型浅度类的问题
- 每个接口都会增加认知负荷——类越多并不意味着设计越好
- 最佳抽象会通过几个简单概念隐藏大量复杂度
- 小方法本身并非优点;深度比大小更重要
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| 深度模块 | 通过简单API隐藏复杂度 | |
| 浅度模块 | 避免仅做转发的薄包装 | 避免将 |
| 类泛滥症解决 | 合并相关浅度类 | 将 |
| 方法深度 | 方法应实现实质性功能 | |
| 接口简洁性 | 更少参数,更少方法 | 使用带有合理默认值的 |
参考:references/deep-modules.md
3. Information Hiding and Leakage
3. 信息隐藏与信息泄露
Core concept: Each module should encapsulate knowledge that is not needed by other modules. Information leakage -- when a design decision is reflected in multiple modules -- is one of the most important red flags in software design.
Why it works: When information is hidden inside a module, changes to that knowledge require modifying only that module. When information leaks across module boundaries, changes propagate through the system. Information hiding reduces both dependencies and obscurity, the two fundamental causes of complexity.
Key insights:
- Information hiding: embed knowledge of a design decision in a single module
- Information leakage: the same knowledge appears in multiple modules (a red flag)
- Temporal decomposition causes leakage: splitting code by when things happen forces shared knowledge across phases
- Back-door leakage through data formats, protocols, or shared assumptions is the subtlest form
- Decorators are frequent sources of leakage -- they expose the decorated interface
- If two modules share knowledge, consider merging them or creating a new module that encapsulates the shared knowledge
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Information hiding | Encapsulate format details | One module owns the HTTP parsing logic; callers get structured objects |
| Temporal decomposition | Organize by knowledge, not time | Combine "read config" and "apply config" into a single config module |
| Format leakage | Centralize serialization | One module handles JSON encoding/decoding rather than spreading |
| Protocol leakage | Abstract protocol details | A |
| Decorator leakage | Use deep wrappers sparingly | Prefer adding buffering inside the file class over wrapping it externally |
See: references/information-hiding.md
核心概念: 每个模块都应封装其他模块不需要知道的知识。信息泄露——即一个设计决策体现在多个模块中——是软件设计中最重要的预警信号之一。
为何有效: 当信息被隐藏在模块内部时,对该知识的修改只需改动这一个模块。当信息泄露到模块边界之外时,变更会在整个系统中传播。信息隐藏同时减少了依赖关系和模糊性这两个复杂度的根本成因。
关键见解:
- 信息隐藏:将设计决策的知识嵌入单个模块
- 信息泄露:同一知识出现在多个模块中(预警信号)
- 时间分解会导致泄露:按事件发生时间拆分代码会迫使不同阶段共享知识
- 通过数据格式、协议或共享假设进行的“后门”泄露是最隐蔽的形式
- 装饰器是常见的泄露来源——它们暴露了被装饰的接口
- 如果两个模块共享知识,考虑将它们合并,或创建一个新模块来封装共享知识
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| 信息隐藏 | 封装格式细节 | 由一个模块负责HTTP解析逻辑;调用者获取结构化对象 |
| 时间分解 | 按知识组织,而非按时间 | 将“读取配置”和“应用配置”合并到单个配置模块中 |
| 格式泄露 | 集中序列化逻辑 | 由一个模块处理JSON编码/解码,而非在各处散布 |
| 协议泄露 | 抽象协议细节 | |
| 装饰器泄露 | 谨慎使用深度包装 | 优先在文件类内部添加缓冲功能,而非在外部包装它 |
参考:references/information-hiding.md
4. General-Purpose vs Special-Purpose Modules
4. 通用模块与专用模块
Core concept: Design modules that are "somewhat general-purpose": the interface should be general enough to support multiple uses without being tied to today's specific requirements, while the implementation handles current needs. Ask: "What is the simplest interface that will cover all my current needs?"
Why it works: General-purpose interfaces tend to be simpler because they eliminate special cases. They also future-proof the design since new use cases often fit the existing abstraction. However, over-generalization wastes effort and can itself introduce complexity through unnecessary abstractions.
Key insights:
- "Somewhat general-purpose" is the sweet spot between too specific and too generic
- The key question: "What is the simplest interface that will cover all my current needs?"
- General-purpose interfaces are often simpler than special-purpose ones (fewer special cases)
- Push complexity downward: modules at lower levels should handle hard cases so upper levels stay simple
- Configuration parameters often represent failure to determine the right behavior -- each parameter is complexity pushed to the caller
- When in doubt, implement the simpler, more general-purpose approach first
Code applications:
| Context | Pattern | Example |
|---|---|---|
| API generality | Design for the concept, not one use case | A |
| Push complexity down | Handle defaults in the module | A web server that picks reasonable buffer sizes instead of requiring callers to configure them |
| Reduce configuration | Determine behavior automatically | Auto-detect file encoding instead of requiring an |
| Avoid over-specialization | Remove use-case-specific methods | One |
| Somewhat general | General interface, specific implementation | A |
See: references/general-vs-special.md
核心概念: 设计“适度通用”的模块:接口应足够通用,以支持多种用途,同时不局限于当前的特定需求;而实现则专注于满足当前需求。可以问自己:“能覆盖所有当前需求的最简单接口是什么?”
为何有效: 通用接口通常更简单,因为它们消除了特殊情况。它们还能让设计面向未来,因为新的用例往往能适配现有的抽象。然而,过度通用会浪费精力,还可能通过不必要的抽象引入复杂度。
关键见解:
- “适度通用”是过于专用和过于通用之间的平衡点
- 关键问题:“能覆盖所有当前需求的最简单接口是什么?”
- 通用接口通常比专用接口更简单(更少特殊情况)
- 将复杂度向下转移:底层模块应处理复杂情况,让上层模块保持简洁
- 配置参数通常代表未能确定正确行为——每个参数都是推给调用者的复杂度
- 如有疑问,先实现更简单、更通用的方案
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| API通用性 | 针对概念设计,而非单一用例 | 使用 |
| 复杂度向下转移 | 在模块内处理默认值 | Web服务器自动选择合理的缓冲区大小,而非要求调用者配置 |
| 减少配置 | 自动确定行为 | 自动检测文件编码,而非要求传入 |
| 避免过度专用 | 移除特定用例的方法 | 使用单个 |
| 适度通用 | 通用接口,专用实现 | |
参考:references/general-vs-special.md
5. Comments as Design Documentation
5. 作为设计文档的注释
Core concept: Comments should describe things that are not obvious from the code. They capture design intent, abstraction rationale, and information that cannot be expressed in code. The claim that "good code is self-documenting" is a myth for anything beyond low-level implementation details.
Why it works: Code tells you what the program does, but not why it does it that way, what the design alternatives were, or what assumptions the code makes. Comments capture the designer's mental model -- the abstraction -- which is the most valuable and most perishable information in a system.
Key insights:
- Four types: interface comments, data structure member comments, implementation comments, cross-module comments
- Interface comments are the most important: they define the abstraction a module presents
- Write comments first (comment-driven design) to clarify your thinking before writing code
- "Self-documenting code" works only for low-level what; it fails for why, assumptions, and abstractions
- Comments should describe what is not obvious -- if the code makes it clear, don't repeat it
- Maintain comments near the code they describe; update them when the code changes
- If a comment is hard to write, the design may be too complex
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Interface comment | Describe the abstraction, not the implementation | "Returns the widget closest to the given position, or null if no widgets exist within the threshold distance" |
| Data structure comment | Explain invariants and constraints | "List is sorted by priority descending; ties are broken by insertion order" |
| Implementation comment | Explain why, not what | "// Use binary search here because the list is always sorted and can contain 100k+ items" |
| Cross-module comment | Link related design decisions | "// This timeout must match the retry interval in RetryPolicy.java" |
| Comment-driven design | Write the interface comment before the code | Draft the function's contract and behavior first, then implement |
See: references/comments-as-design.md
核心概念: 注释应描述代码中不明显的内容。它们记录设计意图、抽象原理以及无法用代码表达的信息。“好代码自文档化”的说法,除了底层实现细节外,都是不成立的。
为何有效: 代码告诉你程序做了什么,但不告诉你为什么这么做、有哪些设计备选方案,或者代码基于哪些假设。注释记录了设计者的思维模型——即抽象——这是系统中最有价值也最容易丢失的信息。
关键见解:
- 四种类型:接口注释、数据结构成员注释、实现注释、跨模块注释
- 接口注释最重要:它们定义了模块呈现的抽象
- 先写注释(注释驱动设计),在编写代码前理清思路
- “自文档化代码”仅适用于底层的“是什么”,无法解释“为什么”、假设和抽象
- 注释应描述不明显的内容——如果代码已经清晰表达,就不要重复
- 注释应靠近其描述的代码;代码变更时同步更新注释
- 如果注释难以撰写,说明设计可能过于复杂
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| 接口注释 | 描述抽象,而非实现 | “返回最接近指定位置的widget;如果阈值范围内没有widget,则返回null” |
| 数据结构注释 | 解释不变量和约束 | “列表按优先级降序排列;优先级相同时按插入顺序排序” |
| 实现注释 | 解释原因,而非内容 | “// 此处使用二分查找,因为列表始终有序且可能包含10万+条数据” |
| 跨模块注释 | 关联相关设计决策 | “// 此超时时间必须与RetryPolicy.java中的重试间隔匹配” |
| 注释驱动设计 | 先写接口注释再写代码 | 先草拟函数的契约和行为,再进行实现 |
参考:references/comments-as-design.md
6. Strategic vs Tactical Programming
6. 策略式编程与战术式编程
Core concept: Tactical programming focuses on getting features working quickly, accumulating complexity with each shortcut. Strategic programming invests 10-20% extra effort in good design, treating every change as an opportunity to improve the system's structure.
Why it works: Tactical programming appears faster in the short term but steadily degrades the codebase, making every future change harder. Strategic programming produces a codebase that stays easy to modify over time. The small upfront investment compounds -- systems designed strategically are faster to work with after a few months.
Key insights:
- Tactical tornado: a developer who produces features fast but leaves wreckage behind; often celebrated short-term but destructive long-term
- Strategic mindset: your primary job is to produce a great design that also happens to work, not working code that happens to have a design
- The 10-20% investment: spend roughly 10-20% of development time on design improvement
- Startups need strategic programming most -- early design shortcuts compound into crippling technical debt as the team grows
- "Move fast and break things" culture (early Facebook) vs design-focused culture (Google) -- Google engineers were more productive on complex systems
- Every code change is an investment opportunity: leave the code a little better than you found it
- Refactoring is not a special event -- it is part of every feature's development
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Tactical trap | Resist quick-and-dirty fixes | Don't add a boolean parameter to handle "just this one special case" |
| Strategic investment | Improve structure during feature work | When adding a feature, refactor the module interface if it has become awkward |
| Tactical tornado | Recognize and intervene | A developer who writes 2x the code but creates 3x the maintenance burden |
| Startup discipline | Invest in design from day one | Clean module boundaries and good abstractions even under time pressure |
| Incremental improvement | Fix one design issue per PR | Each pull request improves at least one abstraction or eliminates one piece of complexity |
| Design reviews | Evaluate structure, not just correctness | Code reviews should ask "does this make the system simpler?" not just "does it work?" |
See: references/strategic-programming.md
核心概念: 战术式编程专注于快速实现功能,每个捷径都会累积复杂度。策略式编程额外投入10-20%的精力用于良好设计,将每个变更都视为改进系统结构的机会。
为何有效: 战术式编程在短期内看似更快,但会持续恶化代码库,让未来的每个变更都更困难。策略式编程打造的代码库长期保持易于修改的特性。前期的小投入会产生复利效应——几个月后,策略式设计的系统会更易于开发。
关键见解:
- 战术龙卷风:快速交付功能但留下烂摊子的开发者;短期内常被称赞,但长期具有破坏性
- 策略式思维:你的首要工作是打造优秀的设计,而这些设计恰好能运行;而非先写出能运行的代码,再考虑设计
- 10-20%的投入:将约10-20%的开发时间用于设计改进
- 初创公司最需要策略式编程——早期的设计捷径会随着团队规模扩大,演变成严重的技术债务
- “快速行动,打破常规”文化(早期Facebook)与注重设计的文化(Google)——Google工程师在复杂系统上的生产力更高
- 每个代码变更都是投资机会:让代码比你接手时更好一点
- 重构不是特殊事件——它是每个功能开发的一部分
代码应用场景:
| 场景 | 模式 | 示例 |
|---|---|---|
| 战术陷阱 | 抵制快速粗糙的修复 | 不要添加布尔参数来处理“仅此一个特殊情况” |
| 策略式投入 | 在功能开发时改进结构 | 添加功能时,如果模块接口变得笨拙,就重构它 |
| 战术龙卷风 | 识别并干预 | 某个开发者编写的代码量是别人的2倍,但带来的维护负担是3倍 |
| 初创公司纪律 | 从第一天起就投入设计 | 即使时间紧张,也要保持清晰的模块边界和良好的抽象 |
| 渐进式改进 | 每个PR修复一个设计问题 | 每个拉取请求至少改进一个抽象,或消除一处复杂度 |
| 设计评审 | 评估结构,而非仅评估正确性 | 代码评审应问“这会降低还是增加系统复杂度?”,而非仅问“它能运行吗?” |
参考:references/strategic-programming.md
Common Mistakes
常见错误
| Mistake | Why It Fails | Fix |
|---|---|---|
| Creating too many small classes | Classitis adds interfaces without adding depth; each class boundary is cognitive overhead | Merge related shallow classes into deeper modules with simpler interfaces |
| Splitting modules by temporal order | "Read, then process, then write" forces shared knowledge across three modules | Organize around information: group code that shares knowledge into one module |
| Exposing implementation in interfaces | Callers depend on internal details; changes propagate everywhere | Design interfaces around abstractions, not implementations; hide format and protocol details |
| Treating comments as optional | Design intent, assumptions, and abstractions are lost; new developers guess wrong | Write interface comments first; maintain them as the code evolves |
| Configuration parameters for everything | Each parameter pushes a decision to the caller, increasing cognitive load | Determine behavior automatically; provide sensible defaults; minimize required configuration |
| Quick-and-dirty tactical fixes | Each shortcut adds a small amount of complexity; over time the system becomes unworkable | Invest 10-20% extra in good design; treat every change as a design opportunity |
| Pass-through methods | Methods that just delegate to another method add interface without adding depth | Merge the pass-through into the caller or the callee |
| Designing for specific use cases | Special-purpose interfaces accumulate special cases and become bloated | Ask "what is the simplest interface that covers all current needs?" |
| 错误 | 失败原因 | 修复方案 |
|---|---|---|
| 创建过多小型类 | 类泛滥症增加了接口数量但未提升深度;每个类边界都会带来认知负担 | 将相关的浅度类合并为接口更简单的深度模块 |
| 按时间顺序拆分模块 | “读取→处理→写入”的拆分迫使三个模块共享知识 | 按知识组织:将共享知识的代码分组到单个模块中 |
| 在接口中暴露实现细节 | 调用者依赖内部细节;变更会在整个系统中传播 | 围绕抽象而非实现设计接口;隐藏格式和协议细节 |
| 将注释视为可选 | 设计意图、假设和抽象丢失;新开发者会错误使用模块 | 先写接口注释;随着代码演进同步维护注释 |
| 一切都用配置参数 | 每个参数都将决策推给调用者,增加认知负荷 | 自动确定行为;提供合理默认值;最小化必填配置 |
| 快速粗糙的战术修复 | 每个捷径都会增加少量复杂度;长期来看系统会变得无法维护 | 额外投入10-20%的精力用于良好设计;将每个变更视为设计改进的机会 |
| 转发方法 | 仅做转发的方法增加了接口复杂度但未提升深度 | 将转发方法合并到调用方或被调用方中 |
| 针对特定用例设计 | 专用接口会累积特殊情况,变得臃肿 | 问自己“能覆盖所有当前需求的最简单接口是什么?” |
Quick Diagnostic
快速诊断
| Question | If No | Action |
|---|---|---|
| Can you describe what each module does in one sentence? | Modules are doing too much or have unclear purpose | Split into modules with coherent, describable responsibilities |
| Are interfaces simpler than implementations? | Modules are shallow -- they leak complexity outward | Redesign to hide more; merge shallow classes into deeper ones |
| Can you change a module's implementation without affecting callers? | Information is leaking across module boundaries | Identify leaked knowledge and encapsulate it inside one module |
| Do interface comments describe the abstraction, not the code? | Design intent is lost; developers will misuse the module | Write comments that explain what the module promises, not how it works |
| Is design discussion part of code reviews? | Reviews only catch bugs, not complexity growth | Add "does this reduce or increase system complexity?" to review criteria |
| Does each module hide at least one important design decision? | Modules are organized around code, not around information | Reorganize so each module owns a specific piece of knowledge |
| Can a new team member understand module boundaries without reading implementations? | Abstractions are not documented or are too leaky | Improve interface comments and simplify interfaces until they are self-evident |
| Are you spending 10-20% of time on design improvement? | Technical debt is accumulating with every feature | Adopt a strategic mindset; include design improvement in every PR |
| 问题 | 如果答案为否 | 行动 |
|---|---|---|
| 你能用一句话描述每个模块的功能吗? | 模块职责过多或不明确 | 拆分为职责清晰、可描述的模块 |
| 接口比实现更简单吗? | 模块是浅度模块——将复杂度泄露到外部 | 重新设计以隐藏更多细节;将浅度类合并为深度模块 |
| 你能修改模块实现而不影响调用者吗? | 信息在模块边界之间泄露 | 识别泄露的知识,将其封装到单个模块中 |
| 接口注释描述的是抽象而非代码吗? | 设计意图丢失;开发者会错误使用模块 | 编写解释模块承诺的注释,而非其实现方式 |
| 设计讨论是代码评审的一部分吗? | 评审仅能发现bug,无法阻止复杂度增长 | 将“这会降低还是增加系统复杂度?”加入评审标准 |
| 每个模块至少隐藏一个重要设计决策吗? | 模块是围绕代码而非知识组织的 | 重新组织,让每个模块负责特定的知识领域 |
| 新团队成员无需阅读实现就能理解模块边界吗? | 抽象未被文档化或过于泄露 | 改进接口注释并简化接口,直到它们不言自明 |
| 你是否将10-20%的时间用于设计改进? | 每个功能都会累积技术债务 | 采用策略式思维;每个PR中包含设计改进 |
Reference Files
参考文件
- complexity-symptoms.md: Three symptoms of complexity, two causes, measuring complexity, the incremental nature of complexity
- deep-modules.md: Deep vs shallow modules, interface-to-functionality ratio, classitis, designing for depth
- information-hiding.md: Information hiding principle, information leakage red flags, temporal decomposition, decorator pitfalls
- general-vs-special.md: Somewhat general-purpose approach, pushing complexity down, configuration parameter antipattern
- comments-as-design.md: Four comment types, comment-driven design, self-documenting code myth, maintaining comments
- strategic-programming.md: Strategic vs tactical mindset, tactical tornado, investment approach, startup considerations
- complexity-symptoms.md:复杂度的三种症状、两个成因、复杂度衡量、复杂度的渐进性
- deep-modules.md:深度与浅度模块、功能与接口复杂度之比、类泛滥症、深度设计
- information-hiding.md:信息隐藏原则、信息泄露预警信号、时间分解、装饰器陷阱
- general-vs-special.md:适度通用方法、复杂度向下转移、配置参数反模式
- comments-as-design.md:四种注释类型、注释驱动设计、自文档化代码误区、注释维护
- strategic-programming.md:策略式与战术式思维、战术龙卷风、投入式方法、初创公司考量
Further Reading
延伸阅读
This skill is based on John Ousterhout's practical guide to software design. For the complete methodology with detailed examples:
- "A Philosophy of Software Design" by John Ousterhout (2nd edition)
本技能基于John Ousterhout的实用软件设计指南。如需完整方法学及详细示例:
- John Ousterhout所著《A Philosophy of Software Design》(第二版):亚马逊链接
About the Author
关于作者
John Ousterhout is the Bosack Lerner Professor of Computer Science at Stanford University. He is the creator of the Tcl scripting language and the Tk toolkit, and co-founded several companies including Electric Cloud and Clustrix. Ousterhout has received numerous awards, including the ACM Software System Award, the UC Berkeley Distinguished Teaching Award, and the USENIX Lifetime Achievement Award. He developed A Philosophy of Software Design from his CS 190 course at Stanford, where students work on multi-phase software design projects and learn to recognize and reduce complexity. The book distills decades of experience in building systems software and teaching software design into a concise set of principles that apply across languages, paradigms, and system scales. Now in its second edition, the book has become a widely recommended resource for software engineers seeking to improve their design skills beyond correctness and into clarity.
John Ousterhout 是斯坦福大学Bosack Lerner计算机科学教授。他是Tcl脚本语言和Tk工具包的创造者,还共同创立了Electric Cloud和Clustrix等多家公司。Ousterhout获得了众多奖项,包括ACM软件系统奖、加州大学伯克利分校杰出教学奖和USENIX终身成就奖。他从斯坦福大学的CS 190课程中发展出《软件设计的哲学》,该课程中学生参与多阶段软件设计项目,学习识别和降低复杂度。这本书将他数十年构建系统软件和教授软件设计的经验提炼为一套简洁的原则,适用于各种语言、范式和系统规模。目前已出第二版,成为软件工程师提升设计技能(超越正确性,追求清晰性)的广泛推荐资源。