Laws of Software Engineering,软件工程定律
在互联网上闲逛的时候发现了一个很有趣的网站整理了一些软件工程的定律,叫做 Laws of Software Engineering,于是利用 LLM 在这里整理了一下。
NOTE
本文由大模型整理并且由大模型翻译,经我检查后认为和原意相合,也因此选择公开。
Teams / 团队
Conway’s Law / 康威定律
组织会设计出与其内部沟通结构相对应的软件系统。
康威定律指出,软件系统的形态,往往会映射出构建它的组织结构。前端、后端、数据库分属不同部门的公司,通常会产出典型的三层架构;小而分散的团队更容易做出模块化服务,大而集中办公的团队则更容易形成单体系统。要对抗这种自然趋势,可以采用“逆向康威策略”,即先确定想要的架构,再按目标架构去调整团队边界与协作方式。
- 软件架构常常就是组织架构或团队结构的投影。
- 如果公司按孤岛式方式组织,软件模块也往往会彼此割裂、协作不畅。
- 想要得到某种目标架构,例如微服务,往往需要先把团队重组为与之匹配的形态。
- 项目一开始怎样划分团队,后续软件的边界大概率也会落在同样的位置。
Brooks’s Law / 布鲁克斯定律
向一个已经延期的软件项目继续加人,只会让它更晚。
布鲁克斯定律反驳了“人手可以线性切分工作量”的想法。现实中,新成员加入项目需要培训、上下文传递和协作磨合,还会增加沟通路径,这些成本在短期内往往会超过新增人力带来的收益。尤其在项目已经落后的情况下,新人既要学习系统,又可能引入新的问题,因此通常会让交付继续延后。它并不是说加人永远没用,而是提醒我们:把“加人”当成追赶延期的主要手段,通常适得其反。
- 对延期项目简单粗暴地“多加几个人”,起初通常只会让进度更慢。
- 新人熟悉系统会占用老成员的时间,培训和协调会直接拉低整体产出。
- 与其期待靠堆人解决延期,不如调整范围或时间表。
- “再招几个程序员就好了”通常不是解决延期的可靠办法。
Dunbar’s Number / 邓巴数
一个人能够稳定维持的社会关系数量,大约存在 150 左右的认知上限。
邓巴数意味着,一个约 150 人的工程组织还可能靠非正式关系顺畅运转;一旦规模继续扩大,就往往需要更明确的规则、沟通机制和管理层级。过去那种“我知道这件事该找谁”的隐性知识会逐渐失效,团队也会变得更陌生、更官僚。它提示我们要把大组织拆成更小的“团队中的团队”。在软件团队中,高信任协作通常发生在更小的群体里,10 到 15 人以上的团队就容易失去凝聚力,而 150 更适合作为部门或社区层面的上限,而不是日常协作单元的理想规模。
- 邓巴数约为 150,表示一个人能够认识并理解彼此角色的社区规模上限。
- 软件组织超过约 100 到 150 人后,通常需要更强的层级或拆分子团队来维持运转。
- 越亲密、越高协作要求的关系,其有效规模上限其实更小。
- 真正高质量的协作通常远低于 150 人,小团队更有优势。
The Ringelmann Effect / 林格曼效应
随着团队规模增大,个体生产率会下降。
林格曼效应指出,参与同一项工作的成员越多,单个人投入的努力往往越少。在软件团队里,这既来自协作与沟通成本,也来自“人多了我少做一点也没关系”的心理。它说明团队产出并不会随着人数增加而线性增长,反而可能出现人均效率下降。与布鲁克斯定律一起看,它提醒我们:团队必须通过清晰分工、明确责任和小而聚焦的协作单元来抵消规模带来的效率损失。
- 大团队里,部分人会因为贡献不够显眼或默认别人会补位而减少投入。
- 人越多,沟通、会议和对齐成本越高,真正做事的时间会被挤压。
- 人员增加到一定程度后,边际收益会递减,甚至变成负收益。
- 小团队或个人通常更有主人翁意识和责任感。
Price’s Law / 普莱斯定律
一个群体中,大约根号 N 的成员会完成一半的工作。
普莱斯定律描述了团队产出的高度不均衡:在软件组织里,往往只有相对少数的工程师创造了很大比例的价值。这和 80/20 原则类似,但在大团队里往往更极端。它说明单纯增加人数,并不一定能按比例提升产出;很多新增成员可能只在外围贡献,真正关键的价值仍集中在少数核心成员身上。这也提示管理者要重视招聘质量、关键人才保留与防止核心成员过劳,因为一旦这些人流失,整体生产力会受到明显冲击。
- 少数人常常贡献了结果中的大头,例如 100 人组织里大约 10 人完成一半产出。
- 团队规模扩大后,总产出会上升,但不会按人数线性增长。
- 理解这一点有助于做团队规划,也能解释为什么失去某些关键成员影响巨大。
- 应识别并留住那小部分对组织产出最关键的人。
Putt’s Law / 帕特定律
懂技术的人往往不负责管理,而负责管理的人往往又不够懂技术。
帕特定律讽刺了技术组织中常见的断层:真正技术最强的人通常不在管理岗位,而管理者又可能距离一线技术太远。它并非处处成立,但确实揭示了一种常见失调:管理技术人员需要足够理解他们的工作,而很多管理者要么背景不同,要么已多年脱离代码。其推论是,技术层级会逐渐形成“能力倒挂”,上层更不了解具体工作,下层则更缺少业务与组织视角,结果就是不切实际的计划、错误的优先级和相互误解。它提醒技术领导不能完全失去技术判断力。
- 深度技术能力与管理能力之间常有明显鸿沟,兼具两者的人并不多。
- 技术专家往往不在管理岗,而管理者通常不了解底层技术细节。
- 这会导致不合理预期、错误决策,以及工程师“没人真正理解我们的工作”的感受。
- 成熟组织会通过培养具备基本技术理解的管理者,或晋升技术型领导来弥合这种鸿沟。
Peter Principle / 彼得原理
在层级组织中,人们会不断晋升,直到达到自己无法胜任的位置。
彼得原理解释了为什么很多组织最终会出现平庸的管理层。一个优秀工程师被提升为技术负责人或经理后,可能并不擅长新岗位的工作,于是组织既失去了一个优秀工程师,也未必得到一个合格管理者。要减轻这个问题,组织需要承认:不是所有资深工程师都适合做管理,也应该为技术路线提供 Staff、Principal 等平行晋升通道;而被提拔的新经理也需要系统训练。否则,晋升机制会持续把人推向不适合的位置。
- 人们会因为当前岗位表现好而获得晋升,直到进入自己不再胜任的角色。
- 一个优秀开发者升任经理后,可能同时造成“少了个好开发,多了个差经理”。
- 组织应提供技术与管理双通道,避免把所有人都推向管理岗位。
- 新岗位需要新能力,不能默认“做得好”就自然会“管得好”。
Bus Factor / 巴士因子
项目中最少失去多少关键成员,就会让项目陷入严重风险。
巴士因子强调的是项目中的“人”的单点故障。许多软件团队里,总有一两个人掌握着遗留系统、关键算法、部署流程或某个历史包袱的全部知识。一旦这些人离开,其他成员很难迅速接手,项目就会被卡住。提高巴士因子,本质上就是做知识分散和风险治理,包括结对编程、代码评审、文档、导师制和轮岗等做法。
- 巴士因子为 1,意味着只要一个关键人缺席,项目就可能基本停摆。
- 这本质上是一个关于知识分布与组织风险的指标。
- 巴士因子越高越安全,说明关键知识被更多人共享。
- 团队应通过文档、评审、知识分享和轮换职责来提升巴士因子。
Dilbert Principle / 呆伯特原理
公司会把不胜任的员工提拔到管理岗位,以减少他们在一线岗位造成的损害。
呆伯特原理以讽刺方式指出,一些组织面对低绩效员工时,并不是解决问题,而是把他们调去管理岗位,仿佛这样“危害更小”。这之所以引发共鸣,是因为它点出了现实中的制度缺陷:很多公司默认管理是工程师的自然晋升路径,却没有认真评估这个人是否适合带人、定方向或做协调。当晋升替代了问责,管理层就容易充满既缺乏技术公信力、又缺少领导能力的人,最终损害工程师对领导层的信任。
- 一些组织会把表现不佳的人提到管理岗,以把他们从一线工作中移开。
- 它讽刺了“基层干活、部分管理层不创造价值”的组织现象。
- 技术优秀与带团队优秀是两套不同能力。
- 在一项能力上表现差,并不意味着适合去承担另一项更复杂的职责。
Planning / 规划
Premature Optimization (Knuth’s Optimization Principle) / 过早优化(Knuth 优化原则)
过早优化是万恶之源。
Knuth 的优化原则揭示了软件工程中的一组基本权衡:性能提升通常伴随着复杂度上升。如果还没弄清性能瓶颈在哪里,就急着做优化,结果往往是把系统变得更难读、更难改。开发早期,最该优先的是清晰的设计和正确的行为;如果过早追求速度,可能为了并非热点的代码引入额外复杂性、灵活性损失,甚至新的 bug。通常只有少量代码会消耗绝大部分运行时间,因此正确做法是先写简单、正确的代码,再基于分析和 profiling 去优化真正值得优化的部分。
- 大多数代码并不处在性能热点上,处处做微优化只会浪费时间并降低可维护性。
- 正如 Knuth 所说,绝大多数时候都不值得执着于细小效率,应优先保证设计清晰和功能正确。
- 优化后的代码往往更复杂、更难读,如果做得太早,就会提前承担不必要的成本。
- 先把它做对,再把它做快,最后再把它做漂亮。
Parkinson’s Law / 帕金森定律
工作会膨胀到恰好填满可用的完成时间。
帕金森定律描述了开发中常见的时间管理问题。如果一项两天能完成的任务被给了两周时间,它往往就真的会拖满两周。人们会更长时间地讨论细节、打磨边角,或者 simply 拖延启动,因为“反正时间还很多”。因此,过于宽松的期限常常会降低效率。它提醒团队设置清晰且现实的时间边界,但也不能过度压缩,否则又会碰上霍夫施塔特定律带来的估算偏差与延期风险。
- 给任务过长的时间,人们往往就会把这段时间全部用掉,或者拖到最后再做。
- 时间一多,团队更容易做镀金式打磨和并非必要的小修小补。
- 略微更紧但合理的期限,往往能带来更强的紧迫感和推进力。
- 截止时间需要现实,不是越紧越好。
The Ninety-Ninety Rule / 九零九零法则
前 90% 的代码占用前 90% 的开发时间,剩下 10% 的代码还要再花另外 90% 的时间。
九零九零法则刻画了项目在收尾阶段经常“卡住”的现象。团队前期开发核心功能时通常进展顺利,于是容易产生乐观预期;但到了集成、边界情况处理、性能调优和 bug 修复阶段,消耗的时间却远超预期,甚至不比前面的开发少。它提醒我们,最后那一点点从来都不是“小尾巴”,真正完成、打磨和交付需要预留足够大的收尾成本。
- 项目最后的打磨、边界处理、集成和修 bug,常常比预想中费时得多。
- 看起来像“收尾工作”的部分,往往藏着最多麻烦和未知数。
- “差不多完成了”常常只是时间意义上的一半。
- 计划项目时,必须给 finishing 留出实打实的空间。
Hofstadter’s Law / 霍夫施塔特定律
事情总会比你预想的更久,即使你已经把霍夫施塔特定律考虑进去了。
霍夫施塔特定律用一种递归式的幽默描述了估算悖论:无论经验多丰富,复杂项目还是会比预期更晚。你就算已经提醒自己“这次可能要翻倍”,它仍可能再超一截。软件开发充满隐藏工作、集成问题、需求变化和不确定性,因此延期几乎是常态。这条定律不是让人悲观,而是提醒我们在计划时保持谦逊,参考历史数据、预留缓冲,但同时接受“缓冲也可能被吃掉”的现实。
- 人类普遍不擅长估算复杂任务所需时间,明知会低估也依然会低估。
- 实施过程中总会冒出新的复杂度和此前没看到的问题。
- 日程中应加入缓冲,但缓冲本身也很可能被消耗掉。
- 做计划时不要过度乐观,要把延期视为需要显式管理的风险。
Goodhart’s Law / 古德哈特定律
当一个指标成为目标,它就不再是一个好指标。
古德哈特定律在软件团队里非常常见。比如管理者要求“这个月必须关闭 100 个 bug”,开发者在压力下就可能去关闭并未真正解决的工单,或者把一个问题拆成多个 ticket 来冲数字。最终指标变好了,但真实质量未必提升。一旦把指标当成目标,人们就会针对指标本身优化,导致它失去原本作为现实代理信号的意义。因此,成熟团队把指标当作线索,而不是唯一目标,并结合上下文来解读。
- 一旦把某个度量设成目标,人们就会想办法优化那个数字。
- 指标只是你真正关注目标的代理,一旦被当作考核目标,就容易偏离原意。
- 指标仍然有价值,但必须结合上下文和定性判断使用。
- 使用多个指标的组合,比单一指标更不容易被“游戏化”。
Gilb’s Law / 吉尔布定律
凡是你需要量化的东西,总能以某种方式被衡量,而这总比完全不衡量要好。
吉尔布定律是对“古德哈特定律式指标焦虑”的一个平衡。它强调,即便是粗糙或间接的度量,也通常比完全没有度量更有帮助。对于性能、用户满意度、代码可维护性这些重要但难以精确量化的东西,团队仍然应该尽量建立指标体系,因为没有数据就没有客观反馈。比如“代码质量”很难直接量化,但你仍可借助圈复杂度、lint 警告、缺陷率等指标来获得部分视角。它们不完美,但比毫无依据地凭感觉判断要好得多。
- 只要理解指标的局限性,有一些数据总比完全盲飞更好。
- 吉尔布定律提醒我们,不要因为担心误用指标,就干脆放弃度量。
- 可以先从基础指标开始,再逐步改进,度量本身会帮助团队发现趋势。
- 粗略、间接的测量,通常也比没有任何测量更有价值。
Architecture / 架构
Hyrum’s Law / 海勒姆定律
只要一个 API 的用户足够多,系统所有可观察行为都会被某些人依赖。
海勒姆定律描述了这样一种现象:在实际使用中,软件“文档里写的接口”与“真实被依赖的行为”之间的边界会越来越模糊。不管你在 API 契约里承诺了什么,只要使用者足够多,就一定会有人依赖那些你从未正式支持过的行为。久而久之,具体实现本身也变成了接口的一部分。于是,哪怕是看起来“内部”的调整,也可能破坏依赖了旧行为的用户代码,从而严重限制系统的可演化空间。
- 用户越多,系统里越多原本非正式的行为都会变成依赖点。
- 连副作用、边角行为,甚至 bug,都可能被某些用户当成“功能”来依赖。
- 维护者需要意识到,任何改动都可能影响某些意料之外的使用方式。
- 软件真正的契约,不只是文档或规范,而是用户在真实环境里观察到的全部行为。
Gall’s Law / 高尔定律
一个能够工作的复杂系统,几乎总是从一个能够工作的简单系统逐步演化而来。
高尔定律认为,成功的复杂系统,起点往往是一个已经可用的简单系统。若一开始就试图直接构建庞大复杂的体系,通常会失败,因为其中有太多尚未验证的假设。更可靠的方式是先做一个小而可用的核心,在真实使用中不断学习,再逐步添加功能和复杂性。这样每一次增长都建立在已被验证的基础上,系统也能在现实反馈中适应和演进。
- 不要试图从零直接造一个大而复杂的系统,先做一个能工作的简单版本。
- 成功的大系统往往是自然生长出来的,而不是一次设计完成的。
- 这条定律支持 MVP 思路:先做可用核心,再逐步扩展。
- 分阶段增长的系统更能承受复杂性,因为它在每一步都经过真实环境检验。
The Law of Leaky Abstractions / 抽象渗漏定律
任何稍微复杂一点的抽象,都会在某种程度上发生渗漏。
抽象渗漏定律指出,我们创造抽象本是为了隐藏复杂性,但在现实系统里,这种隐藏永远不可能彻底成功。总会有一些场景,底层细节会穿透抽象暴露出来,迫使开发者理解抽象背后的机制。比如 ORM 平时让你像操作对象一样操作数据库,但一旦性能异常,你就不得不去分析实际生成的 SQL。它并不是说抽象不好,而是提醒我们:抽象必不可少,但不能把它误以为是“屏蔽一切”的保护层。
- 无论设计得多好,抽象层都会存在依赖内部细节的边角场景。
- 使用高层工具不意味着可以完全不理解底层机制。
- 一旦出现性能、行为异常或 bug,你往往仍要回到底层系统去排查。
- 做抽象时应尽量减少渗漏,并明确说明抽象失效的条件与边界。
Tesler’s Law (Conservation of Complexity) / 特斯勒定律(复杂性守恒)
每个应用都存在一部分不可消除的固有复杂性,这种复杂性只能转移,不能消失。
特斯勒定律也叫复杂性守恒定律。它认为,每个系统都包含某种来自问题本身的复杂性,这部分复杂性无法被真正删除,只能决定由谁来承担。如果你把产品做得对用户极其简单,那么复杂性往往被转移到了系统内部;反之,若系统实现很“简单”,用户可能就要承担一堆手工步骤、配置或理解成本。好的设计不是消灭复杂性,而是尽量把复杂性从用户体验端转移到系统内部。
- 好设计通常要求开发者在系统内部吸收大部分复杂性,让用户操作尽量简单。
- 如果 UI 让用户管理太多设置和步骤,说明复杂性被放错了位置。
- 很多时候更好的产品设计,是让系统自己处理复杂问题,而不是交给用户。
- 好设计往往是隐藏复杂性,而不是否认复杂性存在。
CAP Theorem / CAP 定理
分布式系统在一致性、可用性和分区容错性三者中,最多只能同时完全保证两项。
CAP 定理指出,分布式系统无法同时完全满足三种保证:一致性、可用性和分区容错性。由于网络分区在真实世界中不可避免,因此分布式系统必须接受分区容错这个前提,真正的架构取舍通常落在一致性与可用性之间。CAP 是理解分布式系统设计的一个经典起点,虽然它并不覆盖全部现实复杂度,但它有效地提醒我们:网络故障发生时,不可能什么都要。
- 分布式系统在网络正常时看似什么都能兼得,但一旦分区发生,就必须做取舍。
- 网络分裂时,你要么更偏向一致性,要么更偏向可用性,无法两者都完全保住。
- 不同数据库和系统会根据场景选择不同侧重。
- CAP 不是全部答案,但它是分布式架构权衡的基础认识。
Second-System Effect / 第二系统效应
一个小而成功的系统之后,往往会出现一个过度设计、臃肿复杂的“升级版”。
第二系统效应描述了一种常见的工程陷阱:第一代系统因为资源有限、经验不足或目标聚焦,反而做得小巧而成功;等到做第二版时,团队因为有了第一次成功经验,就想把之前没做的增强、边界情况和愿望清单一次性全塞进去,结果系统变得臃肿、复杂、延期。它提醒工程师对“这次我们把所有问题一次解决”的宏大重构保持警惕,因为这种信心往往低估了复杂度。
- 第一代系统常因限制而保持精简,第二代则容易把所有遗憾一股脑补进去。
- 第一次成功容易带来自信膨胀,低估更大范围设计的难度。
- 第二系统效应常导致不必要的泛化、模块膨胀和功能堆积。
- 对承诺“这次全部重来并一次做对”的大改版应保持怀疑。
Fallacies of Distributed Computing / 分布式计算谬误
分布式系统新手常会不自觉地建立八个错误假设。
分布式计算八大谬误本质上是一份“不要想当然”的清单。它们都源自把分布式系统误当成本地调用:好像远程服务像本地函数一样稳定、快速、可信。这样的错误假设会导致严重后果,比如忽略网络故障、低估延迟、忽视安全认证、错误处理缺失等。把这些假设称为“谬误”,就是为了提醒设计者默认网络会失败、会变慢、会变化,你的系统必须主动适应这种不完美环境。
- 网络会丢包、延迟、带宽有限,也可能不安全,系统必须为此做设计。
- 假设“延迟为零”会让代码在本地没问题、上网络就慢得难以接受。
- 正确的分布式设计应包含重试、超时、安全机制、缓存和动态发现能力。
- 这些谬误提醒我们把失败视为常态,而不是例外。
Law of Unintended Consequences / 非预期后果定律
改动复杂系统时,要预期一定会出现意料之外的结果。
非预期后果定律强调,复杂系统中的结果从来无法被完全预测。系统内部存在复杂依赖,人和使用场景也会放大意外。添加一个新功能,可能因为和无关模块的交互而拖慢性能;简化一个 UI,反而可能因为使用量上升而让后端负载骤增。它提醒工程师:我们的认知模型永远不完整,实施改动时应默认会出现“未知的未知”。
- 无论计划多充分,复杂系统中的重要改动都可能产生预料之外的影响。
- 意外结果可能是正面的,也可能是负面的,甚至会让原问题变得更糟。
- 软件里常见表现是:修一个 bug,顺手引入另一个 bug,或导致别处性能下降。
- 变更设计和发布流程要为“惊喜”留出缓冲与回退空间。
Zawinski’s Law / 扎温斯基定律
每个程序都会不断膨胀,直到它能“读邮件”为止。
扎温斯基定律用戏谑方式描述了软件的功能蔓延趋势:一个程序会不断加功能,直到几乎什么都想做,甚至偏离最初目标。随着用户增长,产品会不断面临“再加一个能力”的压力,记事本工具可能后来加入聊天、协作、分享甚至平台能力。问题不在于加功能本身,而在于失去边界和焦点。若不加节制,功能扩张会削弱产品最初的价值,让系统更复杂、用户更困惑。
- 功能蔓延几乎是软件的自然倾向,久而久之容易导致臃肿。
- 一个原本精简的产品,一旦受欢迎,通常会被不断要求扩展到接近竞品复杂度。
- 用户和产品方总会提出“再加一个功能”的需求。
- 开发者需要主动保护产品焦点,抵抗无限平台化扩张。
Quality / 质量
The Boy Scout Rule / 童子军军规
离开时,让代码比你接手时更好一点。
童子军军规的核心意思是:每次碰到代码,都顺手把它变好一点。它并不是要求大规模重写,也不是要求一步到位地追求完美,而是强调持续的、渐进式的小改进。无论你是在开发新功能还是修 bug,只要发现附近有命名不清、重复逻辑、缺少测试等问题,就顺手清理一下。每次只多做几分钟,长期累积下来,对代码库健康的改善会非常明显。
- 不要让项目里的坏味道继续腐烂,至少顺手修一个你工作区域内的小问题。
- 更干净的代码库更容易阅读、修改和扩展,也更容易控制技术债。
- 当每个人都这么做,团队会形成对代码质量共同负责的文化。
- 小步改进比等一次“大清理”更现实,也更可持续。
Murphy’s Law / Sod’s Law / 墨菲定律
凡是可能出错的事,最终都会出错。
在软件世界里,墨菲定律常被用来解释线上事故与奇怪 bug:空指针、竞态、网络波动、边界输入,凡是存在出错可能,就迟早会在真实环境里出现,而且常常出现在最糟的时机。它促使开发者采用防御式编程,做好空值检查、异常处理、输入校验和优雅降级,也提醒运维与平台团队准备好监控、回滚和应急预案。它不是悲观主义,而是一种现实主义。
- 只要某种错误有可能发生,它迟早会在生产环境里出现。
- 要提前准备错误处理、备份机制和保护性检查。
- 边界情况不是“理论上的”,而是迟早会真实出现。
- 应为这些场景写测试,而不是指望用户永远按理想路径使用系统。
Postel’s Law / 波斯特尔定律
对外输出要保守严格,对外部输入要尽量宽容兼容。
波斯特尔定律最经典的表达是:发送时要严格遵循规范,接收时要尽可能兼容。比如服务器发 HTTP 响应时应精确符合协议,但在接收带有细微格式差异的请求时,只要能够安全理解,就尽量不要直接拒绝。这种原则曾大大增强互联网不同实现之间的互操作性。不过它也有边界:过度宽容可能纵容错误实现长期存在,在安全敏感场景中,过于宽松地接受畸形输入还会带来风险。因此,现代实践通常是在兼容性与安全性之间做更谨慎的平衡。
- 系统对外发出的数据应尽量严格遵守协议和标准。
- 接收外部数据时,可在安全前提下尽量容忍合理的差异和小错误。
- 宽容接收能提升兼容性,但过度宽容也可能掩盖问题。
- 在现代系统里,这条原则常需要结合安全考量一起使用。
Broken Windows Theory / 破窗理论
不要放任那些“破窗”式的小问题不修,它们会诱发更多混乱。
破窗理论在软件中的含义是:一个看起来被忽视的烂角落,会向团队传递“这里对质量要求不高”的信号。比如长期不修的失败测试、被忽略的构建警告、混乱不堪的模块结构,都会让后来者更容易继续走捷径、添加更多脏代码,最终加速系统熵增。相反,若团队习惯于快速修补小问题、维持高标准,就会形成一种维护质量的正反馈文化。
- 小 bug、小坏味道如果持续被放过,会让人误以为质量无所谓。
- 干净、被认真维护的代码库会鼓励工程师继续保持整洁。
- 混乱的代码库则会鼓励更多投机和退化。
- 问题还小时就修掉,远比等它变成系统性腐败更划算。
Technical Debt / 技术债
技术债是那些会让软件开发越来越慢的历史负担。
技术债描述了软件工程中速度与质量之间的张力。写出笨拙代码、跳过清理、留下临时 hack,本质上就像借了一笔贷款:你换来了短期交付速度,但之后要不断为这份复杂性支付“利息”。技术债并不总是坏事,如果是有意识地为了抢市场窗口、验证假设而做的短期权衡,并且有后续清偿计划,它可以是合理工具;真正危险的是把“临时方案”永远留在系统里。管理技术债的关键,是识别它、记录它,并在合适时间通过重构、补测试和优化设计去偿还它。
- 代码走捷径,本质上是在向未来借时间。
- 如果不偿还技术债,它会以更慢开发、更脏代码和更多 bug 的形式持续计息。
- 技术债并非天然邪恶,有时是合理的阶段性策略。
- 偿还技术债的方式包括重构、补测试和改善设计。
Linus’s Law / 林纳斯定律
只要有足够多双眼睛,所有 bug 都会变浅。
林纳斯定律抓住了开源协作的核心:当足够多的人能看到、使用和审查一段软件时,问题迟早会被某个人发现,甚至直接修掉。你眼中的棘手 bug,可能对另一个程序员来说一眼就能定位;成千上万的用户里,也总有人能碰到精准的复现路径。它说明公开性和协作性会提高软件的健壮性,不过前提是真有“很多双眼睛”在看,而不是代码虽然开源却无人问津。这个原则同样解释了代码评审和结对编程为何有效。
- 更多人的审查与使用,往往能更快暴露和解决问题。
- 这是开源软件的重要优势之一。
- 但前提是确实有活跃社区或有效评审,不是“开源了就自动变好”。
- 在团队内部,代码评审与结对编程也体现了这一思想。
Kernighan’s Law / 科尼汉定律
调试代码的难度,大约是写出它的两倍。
科尼汉定律指出,调试不仅要理解代码在做什么,还要理解它为什么没有按预期工作,而这通常比编写时更难。写代码时你有完整上下文和原始意图;调试时,面对的可能是别人的代码,也可能是你自己很久以前写的、上下文早已遗失的代码。因此,聪明但晦涩的写法是在给未来的自己埋雷。与其写“看起来很厉害”的代码,不如写简单、结构清晰、便于理解和排障的代码。
- 调试比编码更复杂,因为它要求你理解行为与错误来源两件事。
- 如果写代码时已经把自己“智商用满”,将来很难再把它调试清楚。
- 结构清晰、易读、文档充分的代码更容易排障。
- 当下能跑通不代表它将来好维护,过度复杂的代码非常脆弱。
Testing Pyramid / 测试金字塔
项目应拥有大量快速的单元测试、较少的集成测试,以及更少量的 UI 或端到端测试。
测试金字塔是一种测试分层的形象比喻。底层是数量最多、速度最快的单元测试;中间是数量较少的集成测试;顶层是最少、最慢、最脆弱的 UI 或端到端测试。越往上,测试编写和维护成本越高,反馈越慢,因此数量应越少。遵循金字塔结构,能够让大多数问题在最便宜的层级被发现;如果金字塔倒过来,测试套件就会变慢、脆弱且难维护。
- 单元测试是基础,运行快、成本低,因此可以写很多。
- 集成测试用来验证模块之间的协作,数量应少于单元测试。
- 端到端测试很重要,但执行慢、脆弱且维护成本高。
- 这样的测试结构能提供快速反馈,也让高层测试失败更可能指向真实问题。
Pesticide Paradox / 杀虫剂悖论
重复运行同一批测试,随着时间推移会越来越难发现新问题。
杀虫剂悖论借用了农业中的比喻:同一种杀虫剂反复使用,害虫会逐渐产生抗性;软件测试也是如此。一个新测试集刚建立时往往能发现许多 bug,但随着这些 bug 被修掉,如果测试集本身长期不变,它对新缺陷的发现能力就会下降。回归测试当然仍然有价值,但如果想发现新的问题,就必须不断更新测试思路、增加新的数据、补充新的边界场景,并结合探索式测试去发现静态回归用例覆盖不到的问题。
- 旧测试集随着时间推移,会越来越难发现新的缺陷。
- 测试不能一劳永逸,必须持续补充新的输入、场景和思路。
- 测试人员不能对现有脚本过度自信,以为它们永远足够。
- 通过扩展旧测试、引入新场景和探索式测试,可以“刷新杀虫剂”。
Lehman’s Laws of Software Evolution / 雷曼软件演化定律
映射现实世界的软件必须持续演化,而这种演化存在可预见的限制。
雷曼定律描述了长期运行、服务真实世界的软件系统所面对的基本现实:它们必须不断变化,否则就会逐渐失去价值。这种变化既不可避免,也不是免费的。随着演化持续进行,系统复杂度会不断累积;若没有持续重构与整理,内部混乱会增长,开发速度下降、变更风险上升、维护成本持续变高。与此同时,组织本身也有吸收变化的上限,知识、沟通和熟悉度都会限制一次版本能承载的变更量,单纯加人并不能突破这些限制。
- 成功的软件永远不会“真正完成”,不持续更新就会越来越不令人满意。
- 变化不断累积会推高复杂度,若不治理,后续开发只会越来越慢。
- 组织在单位时间内能吸收的变化量有限,不会无限加速。
- 用户感知质量会随着环境变化、期望上升和问题累积而逐渐下降。
Sturgeon’s Law / 斯特金定律
任何事物里,大约 90% 都是平庸甚至糟糕的。
在软件领域,斯特金定律提醒我们:绝大多数代码、功能、实验或技术尝试,实际价值可能都不高,真正重要的可能只是其中少数。它与 10x 工程师的某种解释相呼应:真正厉害的人,不是写出十倍代码,而是找出那 10% 能产生十倍价值的工作。真正危险的不是“大部分东西价值有限”,而是团队假装所有工作价值都一样,这会引入过多复杂性并拖慢整体节奏。它鼓励我们持续筛选、持续淘汰,把注意力放在真正有信号的部分。
- 它可以看作是对 80/20 法则更极端的一种表达。
- 软件里很多功能和代码路径其实几乎不创造价值。
- 关键不是平均对待所有事情,而是识别出高影响力的少数。
- 新概念和新技术大多不会成功,真正有价值的才会沉淀下来。
Scale / 规模
Amdahl’s Law / 阿姆达尔定律
并行化带来的加速上限,受限于那部分无法并行的工作。
阿姆达尔定律指出,即便增加再多 CPU 核心,并行加速也只会作用在可并行的那部分任务上;串行部分不会变快,并最终成为整体性能上限。若串行部分占 10%,理论最大加速比也只有 10 倍;若串行部分占 50%,最大加速比就只有 2 倍。这条规律不仅适用于硬件,也适用于系统与组织:如果数据库、审批节点或架构决策存在无法并行的瓶颈,那么单纯堆更多服务器或更多工程师,都无法突破上限。
- 串行部分决定了性能天花板,再多并行资源也无法彻底跨过去。
- 扩容会暴露瓶颈,而不是让瓶颈自动消失。
- 真正有效的扩展,往往要先减少串行路径,再谈并行化。
- 这条定律同样适用于团队与决策流程中的单点瓶颈。
Gustafson’s Law / 古斯塔夫森定律
通过扩大问题规模,并行处理仍然可以带来显著加速收益。
古斯塔夫森定律为并行计算提供了一个比阿姆达尔定律更乐观的视角。阿姆达尔假设问题规模固定,因此串行部分会限制加速上限;古斯塔夫森则观察到,现实里当计算资源变多时,人们往往会用这些资源去处理更大的问题,而不只是把同一个小问题做得更快。也就是说,随着处理器数量增加,可并行的工作量会随之放大,串行部分相对占比下降,于是“按规模扩展”的速度提升就可以接近线性。它强调的是“算更多”,而不仅仅是“算更快”。
- 资源增加后,可以在同样时间内解决更大规模的问题。
- 它反驳了“并行扩展一定很快撞墙”的过度悲观观点。
- 实际工程里,更大的机器与更多节点常常意味着更大的任务范围,而不是只追求同一任务的极限加速。
- 它鼓励把系统与算法设计成可横向扩展的形式。
Metcalfe’s Law / 梅特卡夫定律
网络的价值与用户数量的平方大致成正比。
梅特卡夫定律最初用于描述通信网络,后来被广泛应用到平台和生态系统。它认为,一个网络的价值会随着参与者数量增加而非线性上升,因为节点越多,潜在连接关系增长得更快。5 个节点可能有 10 种两两连接,12 个节点则有 66 种。正因如此,网络效应会产生“越大越强”的自增强效应,也解释了许多社交平台、即时通信和双边市场为何一旦跨过临界点就会出现爆发式增长。当然,这只是简化模型,因为并非每个用户都真的与每个用户连接,边际价值也可能递减。
- 用户数量翻倍,潜在连接机会通常会增长得更快。
- 每个新用户不只是新增一个人,还会给已有用户带来新的交互可能。
- 这解释了社交网络和平台产品常见的爆发式网络效应。
- 它是一个有启发性的近似模型,而不是精确公式。
Design / 设计
YAGNI (You Aren’t Gonna Need It) / YAGNI 原则
在真正需要之前,不要提前加入功能。
YAGNI 是敏捷开发中的核心理念之一:不要为尚未被要求、也还不确定会需要的功能预先写代码。如果你在做模块 A 时想着“未来也许会需要模块 B,所以我先把扩展钩子写好”,YAGNI 会建议你先别这么做,因为那个未来需求可能根本不会来,或者来时已经完全变样。它直接对抗过度设计。其前提是团队对后续重构有信心,相信在真正需要时,可以以可接受成本补上功能,而不是一开始就为虚构未来买单。
- 聚焦当前真实需求,而不是提前实现假想中的未来功能。
- 预判未来往往会带来过度设计,增加复杂度和维护负担。
- 它鼓励迭代式开发:先做最小可用方案,再按需要演进。
- 能践行 YAGNI 的前提,是团队具备良好的重构能力与测试保障。
DRY (Don’t Repeat Yourself) / DRY 原则
每一份知识都应只有一个清晰、明确、权威的表达位置。
DRY 的核心思想是,同一条事实、规则或逻辑不应在系统里重复出现多份。如果相同的业务规则、公式或配置散落在多个模块中,一旦需求变化,你就得逐一排查并修改所有副本,这会带来隐性维护成本和不一致风险。遵守 DRY 往往意味着把重复逻辑提取为统一函数、类或配置来源。不过,DRY 针对的是“知识重复”,而不只是“代码看起来相似”;有些相似代码承担的语义并不相同,生硬合并反而会把设计搞复杂。
- DRY 关注的是知识和规则的重复,不只是字面代码重复。
- 当需求变化时,理想状态是只需在一个地方修改。
- 重复逻辑会增加不一致和漏改的风险。
- 应谨慎应用 DRY,避免把语义不同的东西硬抽成一个抽象。
KISS (Keep It Simple, Stupid) / KISS 原则
设计和系统应尽可能保持简单。
KISS 原则提醒我们,把简单性当作设计目标本身。如果一个问题可以用 50 行脚本解决,就不必用 500 行复杂方案去实现。软件是给人理解和维护的,简单设计更易于新人上手、定位 bug,也更不容易产生牵一发动全身的副作用。KISS 并不是反对能力强,而是反对为了“聪明”而写出过分花哨或提前为未来问题买单的方案。
- 满足需求且设计简单的方案,通常优于更复杂的方案。
- 简单代码更容易理解、调试和维护,尤其是对未来的自己。
- 看起来聪明的代码往往会掩盖问题、制造后续排障成本。
- 任何不必要增加复杂度的做法,都违背 KISS 的精神。
SOLID Principles / SOLID 原则
五条经典设计原则,用于提升代码的可维护性、可扩展性和可测试性。
SOLID 是面向对象设计中的五条高层指导原则,分别是单一职责、开闭原则、里氏替换、接口隔离和依赖倒置。它们共同追求的是:让系统在变化面前依然保持模块化、可扩展和稳定。应用得当时,代码更容易测试、重构和扩展,而不会因为改动一个点就破坏一大片功能。SOLID 不是万能公式,但它为面向对象设计提供了一套经过长期验证的思考框架。
- 遵循 SOLID,通常会让代码更容易扩展、测试和重构。
- 单一职责、合理接口和依赖抽象,能显著提高维护性。
- 低耦合和良好封装能减少局部变更引发的连锁破坏。
- SOLID 不能保证完美设计,但能提供可靠的设计方向。
Law of Demeter / 迪米特法则
对象只应与自己的“直接朋友”通信,而不应越级接触陌生对象。
迪米特法则也常被称为“不要和陌生人说话”或“最少知识原则”。它主张,一个对象应尽量少了解系统的内部结构。例如,如果 A 持有 B,而 B 持有 C,那么 A 不应通过 a.b.getC().doSomething() 这种方式去直接依赖 C;更理想的做法是让 B 或 A 暴露更合适的接口。这样可以减少对象之间对内部层次结构的耦合,使结构变更时影响范围更小。它和信息隐藏的思想高度一致。
- 对象应只调用自己、直接成员、函数参数或自己创建的对象的方法。
- 不应穿过一个对象去访问另一个对象的内部结构。
- 这样可以降低耦合,使底层结构变化不会层层传导。
- 虽然可能会增加一些委托方法,但整体交互会更清晰、更稳健。
Principle of Least Astonishment / 最小惊讶原则
软件和接口的行为应尽量符合用户与开发者的直觉,不要制造意外。
最小惊讶原则适用于 UI、API、代码设计和文档。它的核心很简单:不要让使用者感到意外。系统的行为应尽量贴近用户对它的心理预期;在代码层面,名称、类型、默认值和上下文也应与开发者习惯一致。违背这条原则不一定会导致程序错误,但会削弱可信度、可用性和可学习性。优秀设计之所以顺手,很多时候不是因为功能强,而是因为它几乎不需要额外解释。
- 设计应尽量符合用户与开发者的自然预期。
- 遵循平台规范和通行约定,通常最不容易让人困惑。
- 这能提升可用性和开发体验,让人更容易预测系统行为。
- 意外的副作用、反直觉命名和异常默认值,都会制造错误和不信任。
Decisions
Dunning-Kruger Effect / 邓宁-克鲁格效应
一个人越不了解某件事,往往越容易对自己的判断过度自信。
邓宁-克鲁格效应揭示了自信与能力之间的错位。对某个领域了解很少的人,往往缺乏评估自身水平所需的认知,因此更容易高估自己对问题的理解。随着学习深入,人们开始看到更多复杂性和未知,自信反而会下降,进入常说的“绝望之谷”;等真正积累起经验后,自信才会重新回升,而且更接近真实能力。这对工程判断很重要,因为很多“说得最满的人”未必真的最懂。
- 没有足够上下文支撑的自信往往并不可靠。
- 学习初期常会先降自信,因为你开始看见自己不知道的东西。
- 真正的专家更倾向于谈范围、概率与权衡,而不是对一切都斩钉截铁。
- 识别这种效应有助于改进评审、估算和技术决策。
Hanlon’s Razor / 汉隆剃刀
不要把本可由无知、疏忽或失误解释的事情,轻易归因于恶意。
汉隆剃刀建议我们在面对问题时,先排除更简单的人为错误,而不是立即假设对方有恶意。一个 commit 引入安全漏洞,大概率是失误而非故意破坏;构建失败,多半是配置错误而不是有人存心搞砸。这条原则并不是否认恶意行为的存在,而是在日常工程协作中,帮助我们保持理性、减少指责,把注意力放在修复问题和理解原因上。
- 系统出问题时,首先考虑失误、误解或粗心,而不是阴谋论。
- 不要一上来就认为“系统被黑了”或“有人故意搞破坏”。
- 同事写出有问题的代码,通常不是恶意,而是赶工或缺少信息。
- 用提问和澄清代替指责,协作会更健康有效。
Occam’s Razor / 奥卡姆剃刀
在多个解释或方案里,通常最简单的那个更可能是正确的。
奥卡姆剃刀是一条经典的问题求解原则:面对多个可能答案时,优先考虑最简单的解释。在软件里,它与 KISS 高度一致,提醒我们避免无意义的复杂化。如果一个系统用单体架构加一个数据库就能满足目标,就没必要一开始拆成五个微服务、接两个数据库再加一堆中间件。额外组件会引入更多集成问题、运维成本和认知负担。它的重点不是“越简单越好”,而是“不要无端引入更多假设与结构”。
- 简单代码更容易理解、维护和调试,复杂结构则拥有更多失败点。
- 排查问题时,先从最直接、最简单的解释开始。
- 不要为了显得高级而引入不必要的技术与架构层级。
- 很多时候,少即是多。
Sunk Cost Fallacy / 沉没成本谬误
即使离开会更好,人们仍会因为已经投入了时间和精力而继续坚持错误选择。
沉没成本谬误是一种常见认知偏差。人们会不理性地高估已经投入但无法收回的成本,因此即便当前证据表明应该停下,也会因为“都做到这一步了”而继续往前。软件开发里,这种现象非常常见:一个 feature、重写项目或技术路线已经明显不划算,但团队仍因为之前投入太多而不愿放弃。克服它的关键在于,未来决策应基于当前事实和未来收益,而不是过去已经花掉、无论如何都回不来的成本。
- 不要因为“已经投入了一年”就继续做一件明显行不通的事。
- 健康的工程组织会主动转向或停止不再合理的项目。
- 应设置清晰的检查点和退出条件,例如某个季度达不到目标就重评。
- 团队需要有承认失败和停止错误投入的心理安全感。
The Map Is Not the Territory / 地图不等于领土
我们对现实的描述、模型和图纸,不等于现实本身。
“地图不等于领土”提醒我们,模型、文档、图表和心理认知都只是现实的抽象,而不是现实本身。在软件工程中,我们到处都在画“地图”:需求文档是在描述用户需求,架构图是在描述组件关系,脑中的系统理解也是一种主观地图。它们当然必要,否则无法规划复杂系统;但真正的问题在于,人们很容易忘记这些抽象的局限性。纸面设计看起来合理,不代表实现时不会遭遇网络延迟、数据耦合、用户行为或运营约束等现实地形。
- 设计文档、UML 和架构图都是抽象,不是正在运行的真实系统。
- 落地实现时,总会出现最初设计里没有体现的新因素。
- 模型有用,但必须随现实反馈持续校正。
- 当真实系统与原先设计不一致时,应优先相信证据,而不是死守图纸。
Confirmation Bias / 确认偏误
人会倾向于只关注支持自己既有观点的信息,而忽视相反证据。
确认偏误是人类很普遍的思维捷径。只要你先形成了某种判断,就会下意识搜集能支持它的证据,忽略与之冲突的迹象。在软件工程里,这在调试时尤其典型:开发者认定问题出在模块 A,就会拼命审查 A,而对真正有问题的模块 B 视而不见。意识到这种偏差,意味着我们要主动问自己“如果我错了,会看到什么信号”,并在团队决策中引入不同观点和客观证据。
- 代码审查或排障时,要警惕自己是不是只在寻找支持既定假设的证据。
- 主动问“如果我错了,应该会看到什么”,能有效打破偏见。
- 技术选型和架构决策时,应引入持不同意见的人参与讨论。
- 自动化测试、性能数据和实验结果,比主观感觉更能纠正偏误。
The Hype Cycle & Amara’s Law / 技术炒作周期与阿玛拉定律
人们往往高估技术的短期影响,却低估它的长期影响。
这条规律描述了科技领域熟悉的“先过热、再幻灭、后成熟”的循环。Gartner 的炒作周期展示了这一轨迹:新技术先引发过高期待,现实落地不及预期后跌入低谷,随后其中一部分会逐渐成熟,最终进入真正有生产力的阶段。阿玛拉定律则强调,人们常高估短期变化,却低估长期影响。在软件工程里,许多热门概念一开始被吹成革命性方案,后来热度骤降;但其中一些在多年后会以更务实的形态真正改变行业。正确态度不是盲从,也不是一棍子打死,而是保持耐心和辨别力。
- 技术刚兴起时,人们往往对其短期能力抱有不现实的期待。
- 长期来看,真正有价值的技术最终可能产生比最初想象更深远的影响。
- 工程师和技术领导应避免被短期热度裹挟,要看实际价值而非 buzzword。
- 创新预算是有限的,大多数场景应优先采用稳定成熟的技术。
The Lindy Effect / 林迪效应
某种东西被持续使用的时间越久,它未来继续存在的概率往往越高。
林迪效应与直觉相反。对生物来说,越老越接近终点;但对技术、思想和工具来说,能长时间存活反而说明它更可能继续存在。时间像一层筛子,脆弱、无关或质量不足的事物会被淘汰,留下来的往往更有普适价值。这条原则能有效对抗“追逐新玩具”的冲动,提醒开发者应更多投资在经过时间考验的语言、算法、协议和设计原则上,而不是把大部分精力都押在最新框架上。
- 一项技术、工具或概念存在得越久,通常越可能继续存在下去。
- 对开发者来说,应优先投资经久耐用的基础能力和成熟技术。
- 评估技术时,时间本身可以作为一种质量过滤器。
- 这也是“选择无聊技术”常被推荐的原因之一。
First Principles Thinking / 第一性原理思维
把复杂问题拆到最基本的组成部分,再从那里重新构建答案。
第一性原理思维要求我们不要只是复制现有做法,而是回到问题本身:我们究竟要解决什么?哪些是客观约束,哪些只是沿袭下来的习惯?这种方法在做根本性变革时尤其有价值。比如在系统设计里,不只是问“别人都怎么做”,而是问“如果今天从零开始,为什么要这么做”。它能帮助我们跳出路径依赖,但代价是思考成本较高,并非每个问题都需要从头推翻重来。许多场景下,成熟模式仍然是更高效的选择。
- 不要把现有做法当成不可质疑的前提,应拆解问题本身和真实约束。
- “大家都用 Framework Y”不是充分理由,关键是为什么。
- 做估算或设计时,从基本组成重新分析,常能发现历史类比并不适用。
- 这类思维适合重大问题和核心决策,不必对所有小事都从零推导。
Inversion / 逆向思考
通过先思考相反结果,再倒推回来解决问题。
逆向思考的核心是,很多关键洞见只有从反面看问题时才会显现。在软件工程里,与其只问“怎样把系统设计好”,不如也问“系统会怎样坏掉”。数据库挂了怎么办?延迟暴涨怎么办?用户恶意调用怎么办?这样思考后,你自然会引入熔断、重试、限流、故障转移等防御性设计。它和第一性原理相辅相成,本质上是一种面向失败的思考方式。
- 对每个目标,都要反过来问一句:什么会让它失败。
- 预演失败的 pre-mortem 是非常实用的逆向思考工具。
- 在设计和测试里,从“怎么被搞坏”出发,往往更容易发现风险。
- 逆向思考能帮助系统更健壮,而不是只在理想情况里成立。
Pareto Principle (80/20 Rule) / 帕累托原则(80/20 法则)
80% 的结果,往往来自 20% 的原因。
帕累托原则是一种广泛出现的经验观察,而非严格定律。软件工程中常见的表述是:程序 80% 的运行时间花在 20% 的代码上。这对性能优化非常关键,因为它告诉我们不该平均对待所有部分,而应先找到真正的热点。它同样影响产品规划:少数核心功能往往满足了大多数用户需求,因此 MVP 不应试图覆盖一切。它的核心思想是,资源分配要有重点,不同部分的价值和影响力并不相等。
- 找出那 20% 决定 80% 结果的关键因素,并优先投入资源。
- 不必对所有代码、功能或问题平均用力。
- 比例不一定总是精确的 80/20,但“不均衡分布”几乎无处不在。
- 无论是个人还是团队,都应识别真正创造大部分价值的关键工作。
Cunningham’s Law / 坎宁安定律
在互联网上,得到正确答案的最快方式往往不是提问,而是先发一个错误答案。
坎宁安定律源于在线社区,特别是 wiki 和论坛环境。它观察到,人们对“错误断言”的反应,往往比对“真诚提问”的反应更强烈。你在 Stack Overflow 上问问题可能没人理,但一旦有人发了个似是而非的答案,其他人常会迅速跳出来纠正。放到工程协作中,它提醒我们:与其无限抽象地讨论“该怎么做”,不如先抛出一个草案、原型或初步方案。哪怕它不完全对,也能更快激发有效反馈。
- 人们通常更愿意纠正错误,而不是从零回答空泛问题。
- 提问可能无人响应,但一个不完全正确的具体方案常会激起讨论。
- 在团队里,先给出草案或原型,往往比空谈更容易收集反馈。
- 关键是把讨论从抽象层面拉到具体对象上。