人月神话读后感与现代软件工程思辨
《人月神话》读后感:经典与现代软件工程的思辨与启示
第一章 焦油坑 (The Tar Pit)
编程系统产品
首章以“焦油坑”比喻大型软件项目的困境:众多庞大的项目如同史前巨兽陷入焦油潭,拼命挣扎却难以脱身。布鲁克斯指出,开发一个**“编程系统产品”**(即可供他人使用、可扩展维护并可作为系统组件的完整软件产品)需要付出远超开发一个简单程序的代价——大约是单个程序的九倍之多。也就是说,两人在车库中写出上千行代码的原型程序,与一个由大型团队打造、适配各种环境并经过完整测试的商业软件相比,后者的工作量要高出数量级。人月神话由此开篇:软件工程的复杂性和规模效应让许多“小作坊奇迹”难以复制到工业级项目。
布鲁克斯解释说,一个简单程序要转变为编程产品,需做到对各类输入输出健壮处理、经过彻底测试并编写完善文档,其开发成本起码是原程序的三倍。而若将该程序作为编程系统中的组件单元使用,还必须遵循标准接口、资源约束,并与众多其他组件集成测试,成本也将至少增加三倍。当要求同时满足产品化和系统集成,两者叠加使成本高达九倍。这正是大项目举步维艰的原因:软件开发仿佛陷入“焦油坑”,单个问题都能解决,但纠缠在一起就使团队举步维艰。
职业的乐趣
尽管软件项目困难重重,布鲁克斯也热情洋溢地描述了编程这份职业的乐趣。首先,编程带来创造的纯粹快乐:程序员“凭空运用想象建造自己的城堡”,像诗人一样在纯思维的空间中工作。相较其他工程介质,软件的塑造极其灵活,可随时推倒重来,实现思想的直接表达。这种创造的自由使得程序员体验到类似上帝创造世界的奇妙乐趣。
其次,编程的成果能真正有用于他人,这种实用价值带来成就感。看到自己开发的软件帮助了用户,程序员会感到由衷的满足。第三,软件运行时如魔法般按预期输出结果,赋予开发者一种掌控复杂机器的魔力体验。第四,编程充满不断学习的新挑战,每个问题都多少有新意,促使程序员持续学习新知识,无论实践层面还是理论层面。最后,布鲁克斯强调软件开发所操作的介质“易于驾驭”,程序员几乎只受限于思想本身,很少有比编程更易于反复精炼的创造媒介了。这种可反复打磨和重构的特性,让人乐在其中。
“程序员就像诗人一样,几乎仅仅工作在纯粹的思考中。程序员凭空地运用自己的想象,来建造自己的‘城堡’。很少有这样的介质——创造如此灵活,易于精炼和重建,如此容易将概念实现。”这段话生动地道出了编程的魅力所在。
职业的苦恼
然而,布鲁克斯接着一针见血地指出了软件开发的内在苦恼。首先,编程要求绝对的精确与完美:只要代码中一个字符错误,整个程序就可能失灵。现实世界很少要求如此苛刻,因而人类本能并不习惯追求完美。这使初学编程者常常痛苦地调整心态,适应“一处错误即可全盘皆输”的严格要求。
其次,程序员往往目标和资源由他人设定,个人缺乏对项目整体的控制权。管理学上称之为“权责不匹配”——程序员对完成任务负责,却无法决定许多影响工作的因素。这种受制于人的境况在大型项目中尤为明显。再次,系统程序员不得不依赖他人的工作,而别人的模块可能设计拙劣、接口不合理、文档缺失甚至 bug 丛生。为了集成整个系统,开发者不得不花大量时间阅读他人代码、排错和协调整合,这过程充满挫折。第四,与有形工程不同,软件的结构无形且不可见,难以直观把握和度量。这种“看不见的复杂性”让管理和沟通变得困难(这一点在后文“没有银弹”中被归结为软件的本质困难之一)。
最后,布鲁克斯提到软件项目的成功与否经常受限于沟通和协作的效率,而非个人编程能力。随着项目规模增大,程序员要投入越来越多时间在交流、协调、会議上,而非编写代码本身。这意味着程序员不仅要面对技术挑战,还要应对复杂的人际协作挑战。
综上,第一章通过“焦油坑”的生动比喻,呈现出软件工程“乐在其中”的创造成就与“困于其中”的复杂痛苦并存的景象。一方面,软件开发充满创造的喜悦,另一方面,大型项目的复杂度与沟通成本又使之成为令人生畏的泥潭。理解这一点,有助于管理者和开发者在后续章节中更好地权衡技术与管理的方法。
现代视角:复杂性与软件工程的永恒挑战
半个世纪后的今天,软件工程师和经理人依然能体会到“焦油坑”隐喻的现实意义。尽管工具和技术日新月异,软件的内在复杂性始终伴随大型项目。布鲁克斯那句振聋发聩的警示——“向进度落后的项目增加人力,只会让进度更加落后”——在人工智能与云原生时代依然高悬在团队头顶,如达摩克利斯之剑。当今的软件系统规模更庞大、场景更复杂,从传统企业级应用到微服务架构、分布式云原生系统,无不面临对抗复杂性的永恒挑战。
然而,相比1970年代,我们也拥有了更丰富的方法论和工具来应对“焦油坑”。首先,敏捷开发方法的兴起正是为了将庞大问题拆解成可管理的小步迭代,避免一次性陷入无法收拾的复杂泥潭。Scrum、Kanban 等敏捷实践鼓励小规模团队频繁交付、快速反馈,以控制复杂度蔓延。正如很多技术博主所体会的,布鲁克斯强调的团队沟通、任务划分和避免盲目人海战术等原则,在敏捷和 DevOps 团队运作中“不谋而合”。跨职能的小团队、持续集成、快速反馈循环,这些现代理念本质上都是在减少沟通障碍和应对变化,从而规避人月神话中提到的陷阱。
其次,DevOps 与持续交付进一步将开发、测试、运维融为一体,缩短了软件交付的反馈周期,也降低了不同角色之间的沟通成本。当开发人员对部署和运维负责,问题更早暴露、更快解决,团队整体在复杂环境中行动更敏捷。布鲁克斯描述的大型项目沟通难、集成难,在今天部分通过自动化流水线和团队文化改进而得到缓解。
第三,人工智能辅助开发开始崭露头角。现代IDE、静态分析工具、甚至智能编程助手(如GitHub Copilot、ChatGPT等)都成为程序员的“利器”,帮助捕获错误、生成代码和文档。这些尖端工具(sharp tools)的运用在一定程度上减轻了布鲁克斯时代程序员面对的繁琐细节负担。不过需要指出,AI 虽然提高了编码效率,却并未从根本上消除软件的复杂性——正如后文“没有银弹”论断的,没有哪种单一技术能令软件生产率出现数量级飞跃。AI带来的或许是意料之外的强力工具,但仍然需要人在复杂性和正确性方面小心掌舵。
总之,焦油坑的隐喻在现代仍具启发:软件工程的本质依然是人与复杂性的搏斗。我们欣喜地看到敏捷、DevOps等实践有效缓解了一些痛点,但也必须认识到,正如布鲁克斯所言,软件开发没有奇迹捷径,唯有尊重复杂、拥抱变化、持续改进。在这个意义上,《人月神话》所蕴含的智慧依然照亮着当代软件工程师和经理人的道路:技术在变,人和协作的因素却在软件成败中亘古不变。
第二章 人月神话 (The Mythical Man-Month)
进度失控与乐观主义
布鲁克斯开宗明义地指出:“在众多软件项目中,缺乏合理的进度计划是项目滞后的最主要原因,它比其他所有因素加起来影响还大。”也就是说,进度管理不善几乎注定项目延误。在他看来,这种“普遍性灾难”的成因有五点:
- 盲目乐观:开发者天生乐观,总是假设一切会顺利进行。“这次它肯定能跑起来”“我刚刚解决了最后一个 bug”——这种根深蒂固的心理倾向导致低估困难。
- 人月混淆:经理人常用“人月”作为工作量单位,隐含假设人和月可以互换,将进度与工作量错误地混为一谈。
- 缺乏估算决心:由于对自己估算缺乏信心,软件经理往往没有耐心持续改进估算方法,导致估算草率且不被坚持。
- 进度跟踪欠缺:软件项目缺乏像其他工程领域那样严格的里程碑跟踪和例行检查,导致偏差未被及时发现纠正。
- 人员误增:当发现项目进度落后时,下意识的传统反应是增加人力试图赶工。但布鲁克斯警告,这就像“拿汽油救火,只会让事情更糟”。新加入的人手因为沟通和学习曲线,反而使进度雪上加霜,形成恶性循环。
以上五点中,乐观主义是特别值得关注的根源。布鲁克斯分析了程序员何以普遍乐观:一方面,编程这种“现代魔术”本就吸引了相信美好结局的人;另一方面,大量挫折让悲观者中途离场,只剩下习惯聚焦结果的乐观者。此外,计算机的易于驾驭让人产生错觉——不像木工、油漆那样受物理介质掣肘,编程只要思路正确就似乎不会有困难。正因介质“听话”,程序员往往低估实现过程中可能遇到的问题。然而现实是构思总有漏洞,实现过程必有大量意料之外的 bug。因此,盲目乐观并不合理,大型项目包含众多任务和前后依赖,“一切都正常”的概率几乎为零。认识到这一点,项目管理就必须预留充足的时间与余量,而不能乐观假设最理想情况。
人月神话与布鲁克斯定律
本章题为“人月神话”,其核心论点在于:把人力和时间简单相乘来衡量工作量,是一种危险且具欺骗性的幻觉。传统管理将“一个人干12个月”视为等价于“12个人干1个月”,但软件开发并非如此线性可加。布鲁克斯用几个情境图来解释这一点:
- 若任务可完全独立拆分,无需彼此沟通,则人手和时间确可互换(如多人割麦子,各收自己的一亩田)。这种情况在复杂软件中几乎不存在。
- 若任务存在顺序限制(某些步骤无法并行),则无论增加多少人也无法缩短时间。正如**“无论多少个母亲也不能一个月孕育出一个婴儿”**,某些软件工作(特别是调试、测试阶段)本质上无法并行加速。
- 若任务可部分并行但子任务之间需要沟通协调,则增加人手会引入额外的沟通成本。布鲁克斯指出,沟通负担包括新人培训(随人数线性增长)和人际交流(随人数呈n(n-1)/2组合爆炸)。例如,两人项目有一条沟通通道,三人就要三倍沟通量,四人则需要六倍。大量会议和协商甚至可能抵消并行带来的好处,使项目回到几乎串行的状态。
由此引出了举世闻名的布鲁克斯定律:“向进度落后的项目增加人手,只会使之更落后。”增加人员不仅不能缩短工期,反而因为新人上手和团队沟通开销,拖延了整体进度。这一定律是对传统人月观的当头棒喝。在人月神话的幻觉中,管理者忽视了软件开发本质是系统性工作——各部分错综依赖、需要紧密协作。因此简单堆砌人力换时间行不通,反倒会导致灾难。
书中用生动比喻强化了这一点:当项目进度落后时,“加人就如同用汽油灭火”;而项目按时完成的概率随着任务数量增加而迅速趋近于零。布鲁克斯把“人月可互换”称作一个危险的神话。他告诫经理人切勿被表面的数学迷惑——软件进度不像搬砖,十个人九个月的工作,绝非九十人一个月就能搞定。
系统测试与进度规划
在剖析“人月”误区后,布鲁克斯进一步讨论了系统测试对项目进度的影响。他指出,很多项目在编码阶段进度正常,一到集成测试就集体崩盘——因为测试中遇到的缺陷数量远超预期,而系统测试时间往往被严重低估。他给出了一个经验法则:将开发总周期按1/3规划、1/6编码、1/4组件测试、1/4系统测试来分配时间。也就是说,规划设计应占整个周期的33%,编码只占17%,调试加初步测试25%,最后整体集成测试25%。这个分配相比业界常规显得前期规划更多、后期测试更多。布鲁克斯解释,大部分项目并未给测试预留一半时间,但实际上测试常常耗费了一半周期甚至更多。许多团队直到系统测试阶段前都勉强跟上进度,但在最终集成测试时发现问题丛生而造成大延期。
因此,他强烈建议管理者在制订计划时充分考虑测试和收尾的时间。因为如果把延期“压轴”到项目最后,临近发布日期才暴露问题,那时的坏消息对用户和经理来说都是沉重打击,而且代价最为高昂。人员已全部就位、投入已达高峰,任何延期都成本巨大;更糟糕的是,如果软件与硬件上线计划捆绑,最后时刻延期会对整个业务造成严重商业损失。这就要求我们早暴露风险,宁可在中途承认进度问题,也不要把风险留到最后爆发。
为此,布鲁克斯提出两条对策:一是推动业界建立生产力基准、缺陷率数据和科学估算规则,用数据驱动估算改进;二是在科学估算方法未成熟前,项目经理要有骨气坚持自己的合理估算,不要轻易接受客户拍脑袋的工期要求。总之,进度管理要有理有据,拒绝拍脑袋乐观估计,同时要给测试和集成留出充分余量,才能避免“空泛的估算”带来的杯水车薪。
重复的进度灾难
本章结尾部分,布鲁克斯总结道:当一个软件项目落后时,传统反应总是加人,而结果往往是重复上演进度灾难。这也解释了书名中“神话”的深意:许多管理者迷信人月神话,一错再错地以为可以通过人海战术追回时间,却不知正踩入陷阱。一系列失败案例表明,如果不打破这一迷思,项目延期将成为反复重演的梦魇。
针对这种状况,布鲁克斯倡导从根本上改变观念和做法,例如采用“小团队+长周期”的模式而非“大团队+短周期”,合理规划各阶段时间,提高沟通效率等。在后续章节,他将提出更具体的方法(如外科手术队伍、结对工作等)来解决这些管理难题。但在此先立下核心结论:生产率不是简单线性累加的,人月观的直觉是靠不住的。只有认识并正视软件开发中沟通和复杂性的高成本,才能走出不断重蹈的进度悲剧。
现代视角:敏捷开发如何打破人月神话
在当代软件工程实践中,布鲁克斯的“人月神话”论断依然具有强烈的现实警示意义。尤其是布鲁克斯定律,已成为软件项目管理的常识:靠往失败项目砸人力不能挽救进度,反而会延误更甚。敏捷方法的崛起,很大程度上正是对这一教训的回应。通过敏捷,我们尝试打破传统人月神话,用全新的开发节奏和团队组织来避免进度灾难。
首先,敏捷强调小型跨职能团队和迭代式开发,实质是在控制任务规模和沟通半径,降低增员带来的开销。当需求通过一个个用户故事逐渐交付,小团队可以高效协作完成每次迭代,无需大兵团同时作战。这样,即使需求变化或进度偏差,也能迅速在下一次迭代中调整,而不至于像瀑布模型那样尾大不掉。这种小步快跑的方法,避免了在庞大任务上孤注一掷,因此更不容易陷入布鲁克斯所说的“最后集成测试阶段方现崩盘”的局面。
其次,结对编程、每日站会、持续集成等敏捷实践旨在强化沟通并提早暴露问题。例如持续集成让组件集成和系统测试变成日常活动,而非最后的大爆发,从而平摊了风险;每日站会和看板让团队对进度心中有数,可以及早发现瓶颈。布鲁克斯当年痛陈“缺乏进度跟踪”导致问题晚发现,而今天Scrum的燃尽图和看板机制正是进度可视化的有效工具。可以说,敏捷将进度管理内嵌在开发过程里,防止了“乐观拖延症”。
第三,产品负责人(PO)与开发团队合作估算,践行了布鲁克斯倡导的“有骨气的估算”。敏捷中的规划扑克和故事点机制,让开发团队以群体智慧给出相对可靠的工作量估计,而非拍脑袋承诺不现实的截止日期。同时PO负责优先级排序,接受团队的速率和现实约束,从源头上减少了不合理工期要求。这与布鲁克斯建议的数据驱动估算和坚持理性估算的思想是一脉相承的。
除了敏捷之外,DevOps思想也从另一个层面印证了人月定律:过去大型项目常把开发、测试、运维拆成不同部门,沟通壁垒严重,相互推诿浪费大量时间。DevOps提倡开发运维一体化,用自动化和团队文化打破部门墙,减少人员间交接带来的延迟和信息失真。这实际上是缩短了“人员增加带来沟通成本”那个公式中的n,使人员规模在满足需求的前提下尽可能小而精干。
然而,即使有敏捷和DevOps,我们仍需警醒“人月神话”随时可能卷土重来。当今不少大公司在项目迫延期时依然不自觉地选择加人加班,在短期压力下抛弃敏捷原则。这往往导致代码质量下降、新人无法有效融入团队,结果适得其反。因此遵守布鲁克斯定律应是每个技术经理的铁律:遇到进度问题,先找沟通和范围的原因,而非一味增加人月投入。
更进一步,现代大量的软件项目并非内部开发,而是交付外包团队或开源社区协作,更凸显了沟通协作效率的重要。一个有全球上千贡献者的开源项目,如果没有良好沟通机制和模块边界,也会像当年的OS/360一样面临庞杂失控的问题。事实上,Linux等成功开源项目在治理上非常强调模块化和社区共识,以避免沟通洪流压垮进度。这些做法可以看作是对布鲁克斯理论在更大尺度上的验证:无论团队内外,只要人与人协作,就要谨慎管理因沟通带来的复杂性。
最后值得一提的是,在人工智能时代,是否存在某种“AI银弹”可以加速软件开发,甚至取代人?目前看来,AI可以帮助生成代码、自动测试,提升个人效率,但它并没有消除多人协作的必要。复杂系统设计、架构演进仍需要人的创造和判断。而多人协作就必然涉及人月神话的那些问题。因此,哪怕工具再先进,软件工程管理的核心规律依旧不变。我们可以期望AI降低“偶然困难”(如编码琐事)的成本,但“本质困难”——复杂性、沟通、需求变化等仍需经验和智慧来应对。
综上,敏捷和DevOps为代表的现代实践在很大程度上呼应并发展了布鲁克斯的思想。通过小团队迭代、持续反馈和自动化协作,我们找到了部分走出“焦油坑”和打破“人月神话”的方法。然而,布鲁克斯定律依然是项目管理的黄金法则,提醒着我们:软件开发终究是复杂的智力活动,人不是可以随意堆砌的砖块。敬畏这一点,才能在新的时代继续谱写成功的软件项目故事。
第三章 外科手术队伍 (The Surgical Team)
问题:小团队还是大团队?
布鲁克斯在前章揭示了大团队协作带来的低效,那么理想情况是否可以用**“小而精”的团队来开发所有系统?他提到一些年轻经理热衷于用“一流高手组成的小团队”替代上百人的大阵仗。直觉上,这似乎能提高效率、减少沟通。然而现实并非如此简单:真正的大型系统必须在合理时间内完成,往往仍需要众多人员投入。一个两三个人的梦幻团队也许可以创造奇迹,但面对数万工作人月的大项目,他们可能完成不了或者需要太长时间**。
布鲁克斯引用Sackman等人的研究指出,优秀程序员和差劲程序员的生产率可相差多达10倍。这意味着雇佣精英开发者确实能显著提高效率。然而,他紧接着计算了一个严酷的现实例子:IBM的OS/360操作系统项目曾投入约5000人年,在高峰期有逾1000人参与。假设用一个10人的超强团队替代,且他们每人效率是普通人的7倍,同时小团队沟通更顺畅带来7倍整体效率提升,理论上这相当于10×7×7=490人年的年产出。即便如此,5000人年的工作量仍需约10年才能完成!十年后产品才问世,需求和技术早已沧海桑田,显然不现实。
因此我们陷入两难:为了效率和概念一致性,想用少数能手开发;但为了按时完成,又不得不用大量人手。布鲁克斯称之为“非常残酷的进退两难”。那么有没有办法调和这两方面呢?
Mills的建议:外科手术式小团队
IBM的软件工程专家Harlan Mills提出了一个创造性的解决方案:把大型项目划分为若干小队,但每个小队内部像外科手术团队一样组织,而不是传统的人人平等、各自负责一块功能的方式。Mills的核心思想是:与其让每个成员各做一部分,不如让一个人主导攻克问题,其他人提供支持。正如在手术室里,由主刀外科医生负责手术,其他助手、麻醉师、护士各司其职配合,同理,一个小团队中由“首席程序员”主导设计实现,其余成员担任辅助角色,提高整个团队生产力。
布鲁克斯形象地将这些角色比喻为手术团队成员,那么在软件开发的小组里,他们各自对应谁,又如何分工运作呢?
- 外科医生(首席程序员):核心角色,相当于主刀医生。由技术顶尖的程序员担任,亲自编写主要代码,并负责定义功能、性能规格,设计系统,调试测试,撰写技术文档。首席程序员需要极高天赋、丰富经验和广博的系统知识,担当整个小组的“大脑”。
- 副手:相当于副刀医生,首席的备份和共谋。副手经验略逊但能胜任各部分工作,主要职责是与首席不停地讨论、思考设计,提出建议和备选方案。他熟知所有代码细节,是首席的“保险”,必要时可顶上。他也代表小组与其他团队协调接口。
- 管理员:类似手术室的护士长或行政管家,处理琐碎管理事务。首席程序员是团队领袖,但不能将时间浪费在行政事务上,故需要管理员负责人员安排、财务预算、设备和进度报告等,对接组织的管理体系。大型项目中管理员可能全职,否则可一人服务多个团队。
- 编辑:负责文字加工,协助首席整理和完善文档。首席必须亲自写技术规格和主要文档(保证清晰准确),但编辑可以对草稿做结构调整、补充参考资料,维护不同版本并监督文档发布流程。确保内外部文档高质量而不加重首席负担。
- 两个秘书:分别辅助管理员和编辑处理日常事务。管理员的秘书负责会议协调、进度记录等非产品文件工作;编辑的秘书整理技术资料、排版发行文档等。
- 程序职员:相当于手术器械护士,管理程序库和版本。他维护整个团队的源代码和可执行文件库,把控配置管理。所有代码变更、测试输出都通过他汇总归档。程序职员确保团队产出的“工作产品”得到很好维护和质量控制,让程序员专注技术工作。
- 工具维护人员:类似医疗器械技师,负责开发工具的可靠运行。现代开发离不开编辑器、编译器、调试器等工具。工具人确保所有关键工具服务稳定,并根据团队需要编写或改进专用工具脚本。他可能开发一些实用程序、构建宏库等,为首席和团队提升效率。
- 测试人员:独立于首席,专职设计和执行测试用例。他既扮演“魔鬼”角色为各功能设计严苛的系统测试,又协助首席进行日常调试的数据准备。测试人员规划测试步骤,搭建测试环境,确保每个模块和整体都有充分的测试覆盖。
- 语言专家:相当于手术中的麻醉师或专科顾问,精通编程语言和棘手技术。在大型项目中,总有一两人乐于钻研复杂晦涩的问题,比如新语言特性、编译器技巧等。语言专家为团队提供咨询,解决疑难技术点。他通常服务于2~3个团队,因为需求不至于每时每刻。这个角色确保团队在遇到特殊技术挑战时有高手指点。
以上十种角色组成了一个典型的10人外科手术式编程小组。这套结构的特点是:绝大部分设计和思想由一人(首席)产生,实现细节也由他把关,因而产品在概念上高度一致;同时,团队其他成员专业化分工,各尽所能支持首席,把他的高效产出放大数倍。
如何运作:沟通与一致性
这样的外科手术队伍如何实际运作呢?布鲁克斯强调了与传统团队的两个重要区别:
其一,传统小团队通常将任务划分,每人独立负责某一部分的设计和实现,各管一摊。这样带来的问题是:每个人对整体系统只能了解自己负责的局部,彼此之间需要不断沟通接口,且可能因为意见分歧而妥协,造成方案折中不统一。反之,在外科手术团队中,首席和副手了解全盘设计和代码。所有模块的关键思路都出自首席一人之脑(副手深度参与讨论),因此整体架构具有一致的风格和策略。例如,不会出现不同模块各搞一套缓冲区管理或参数风格——因为这些决定皆由首席一人拍板,保持了统一性。这极大减少了模块集成时的概念摩擦,保证了概念完整性(conceptual integrity)。
其二,传统团队各成员地位平等,遇见技术路线分歧时,往往需要反复会议讨论、妥协让步。设计思路上的不一致最终会反映为接口和子系统的不协调,影响系统概念统一。而在手术团队中,不存在这种平行竞争的矛盾:首席说了算,副手辅助建议但不强行左右设计。决策者唯一,团队其他人遵循执行,就如同手术室里不会有两个医生争抢主刀。这种小范围**“贵族专制”消除了冗长的争论流程,用权威决策**换取了效率和一致性。当然,布鲁克斯也提醒首席程序员应虚心听取副手和专家的意见,但最终拍板权归他,以免决策陷入拉锯。
此外,外科队伍的专业化分工也让沟通更简单高效。每个人有明确定义的职责领域,接口清晰:首席与副手密切沟通设计,首席通过管理员与组织联系,通过程序职员掌控代码库,通过测试员获取反馈。由于大多数成员的任务是支撑首席工作,他们之间无需像传统团队那样大量横向协调**(沟通网络从全互连缩减为星型)**。这样团队内部的沟通复杂度降到最低,尽管人员也有10人之多,却避免了一般10人团队所需应付的繁杂交流(通常10人需要将近45条可能的沟通路径)。图3.1示意了这种沟通模式的改进:以首席为中心的星状通信大大减少了路径数量,提高了效率。
当然,采用“首席-副手”结构也有一些显著挑战。首先是寻找并培养合格的首席程序员。这要求极高水平的人才,而且他们必须愿意承担重责并乐于分享思路。这有点类似选拔主刀医生,需要长年训练和经验积累。其次,一旦首席离职或失能,对项目影响很大,因此副手的存在和及时顶替机制非常关键。布鲁克斯认为,通过副手制度和良好文档,可以减轻对单个人的依赖程度,但风险仍存在。
最后,布鲁克斯指出,小型外科队伍概念并非完美无缺。例如,对于真正特大型的系统,单靠若干小队并行可能还是太慢。并且,如果每个部分都交由不同的小队,各队产出如何整合也是问题。不过,他相信通过树状分解架构(第9章会讨论组织架构),可以让多个外科队伍在更高层面协同工作。关键仍在于:高层架构师制定总体规范,每个小队按规范高内聚地完成自己的部分,从而既发挥小队效率又保障全局一致。
现代实践对比:高手主导 vs. 团队协作
“外科手术队伍”概念提出至今已有几十年,现实中并不多见严格按照布鲁克斯描述来组织团队的案例。但它的核心思想——由强有力的架构师主导设计,实现概念完整性——在现代软件工程中依然有重要影响,并体现在多种形式。
一方面,一些公司和项目确实推行类似**“首席程序员团队”的模式。比如许多创业公司的早期开发团队常由一个技术大拿带领少数开发者,这位技术负责人几乎包揽了主要架构和核心代码工作,其他人配合实现周边功能。这种情况下,产品的风格和质量往往高度依赖那位“首席”的能力与视野。又例如,开放源码项目里经常有“BDFL(仁慈的终身独裁者)”的治理结构——如Linux的林纳斯、Python的吉多,在很长时间里担任项目的首席架构师角色,做出关键设计决策。社区其他开发者虽平等贡献代码,但大的方向由BDFL拍板。这实际上就是贵族专制式**的设计领导,确保了开源项目在众人协作下仍保持统一愿景。
另一方面,在现代企业中更常见的是架构师团队或技术委员会。他们不是单个人而是一个小组,但人数仍控制很少(两三人),以集体决策方式承担了“首席程序员”的职责。他们制定系统的总体架构和接口标准,各个开发小组按此规范实现。这样兼顾了概念完整性和避免过于依赖一人的弊端。比如一些大公司的平台项目,会设立首席架构师和少数资深架构师组成的委员会,把关所有设计方案。开发团队在编码前需要通过架构评审,以确保符合整体设计哲学。这种做法虽然不是单一的外科医生模式,但精神上延续了统一设计思想的要求。
值得注意的是,敏捷运动兴起后,团队协作的文化更加强调集体所有制和自主性,似乎与“一个人主导”的理念相悖。Scrum中并没有架构师角色,理想状态下架构设计由整个团队演化得出。然而,在实践中,大型敏捷项目依然会指定架构负责人或“系统工程师”负责整体架构演进。敏捷并不否认架构师价值,而是反对事无巨细的集权设计。在很多Scrum团队里,大家默认经验最丰富的工程师为实际上的技术领导,他在code review和设计会议中起更大作用。这有点类似布鲁克斯所说的“首席-副手”形成头脑信任,在具体设计上占据主导。这说明在保持团队协作和创造力的同时,一定程度的“设计独裁”仍然有其价值。布鲁克斯关于概念完整性高于局部最优的观点获得了现代开发者的广泛认同:与其每人各加自己喜欢的功能导致系统臃肿,不如牺牲一些个人创意换取整体简洁一致。
另一方面,如今的软件系统规模远超昔日,完全靠一个人驾驭全局已不现实。于是我们看到微服务架构的流行,将庞大系统拆分为相对独立的小服务,由不同团队负责。这种组织形式恰恰呼应了布鲁克斯在20年回顾中引用的**“附属职能原则”:让小团队自治,给予他们很大自由去完成本职功能。大型企业(如Amazon、Netflix)内部实行所谓“两披萨团队”原则,每个小团队(人数可以用两份披萨喂饱,大约不超过10人)独立负责一个服务,全权决定实现方式和进度。这相当于把系统的每个部分交给一个“小公司”去完成。这种思路下,宏观架构师制定微服务边界和接口规范,各团队像外科队伍一样在自己领域内高效创造。优势在于每个团队有高度主动性和创造力**,正如Jim McCarthy谈到微软的经验:“每个团队拥有自己的任务、进度,甚至自己的定义和发布流程,由团队而非老板仲裁争论。我无法形容赋权的重要性。”。IBM的软件主管Earl Wheeler也分享了把权力下放给项目团队后的奇迹:质量改进、生产率提高、士气高涨。这印证了布鲁克斯“放权的力量”观点——赋予小团队自治反而加强了整体组织的生命力。
由此观之,外科手术队伍的理念在现代演化出两种方向:一种是仍强调由少数精英集中决策(如架构师团队或开源BDFL),另一种是将大项目划分为许多自治小组(微服务团队),用整体架构规范保证一致性,然后让每组内部扮演自己的外科团队。这两者并不矛盾,常常结合使用。在架构层面由小群体制定标准(相当于“大脑”集中),在实现层面由多个自主团队并行开发(相当于“手术团队”在更大规模复制)。
需要平衡的是,布鲁克斯也提醒过,架构师团队不能脱离实现团队太远,否则架构规范可能不切实际而无法实现。他的OS/360经验表明,让150名实现人员自己去写外部规格导致了概念混乱和延期;但若仅由10人架构组闭门造车也不行,因为实现者会闲置抱怨或觉得创造力被剥夺。现代敏捷在这方面提供了启示:架构设计和实现是并行迭代进行的,架构师不会一开始规划全部,而是与团队一起持续演进架构。这可以看作对“外科队伍”模式的一个改良:首席架构师仍指引方向,但他不断倾听一线反馈,允许架构设计在开发过程中调整。这样即保留概念完整性,又照顾实现人员的创造参与感。
总结来说,“外科手术队伍”提供了软件项目组织的一种极端范式:“一人构思,众人辅佐”。它凸显了概念一致性的价值,也承认了个人创造力在软件工程中的不可替代性。在现实运用中,我们很少见到十全十美的首席程序员团队,但我们随处可见架构师的影子:无论是突出个人英雄的开源项目,还是制度化架构治理的大型企业,抑或自治小团队模式下仍需要的跨团队架构协作,布鲁克斯的思想都在其中起作用。信息隐藏和模块化理念(第20年回顾中他承认Parnas是对的)也提醒我们,哪怕由一个人设计,良好的模块边界仍是关键,否则再天才的首席也无法亲力亲为每一行代码。
在团队协作文化高涨的今天,我们或许不再鼓吹个人独裁式的管理风格,但布鲁克斯对**“设计统治”在某阶段的必要性的论述依然适用。软件架构需要有人拍板、有人统一全局,否则民主设计可能陷入平庸妥协。正如他后来所说:“概念完整性要求设计必须由一个人或极少数志同道合者实现”。关键在于找到正确的平衡:既发挥少数顶尖人才的聪明才智,又不压制团队其他成员**的主动性和创造热情。这正是软件工程管理的艺术所在,也是《人月神话》给予我们持续思考的问题。
第四章 贵族专制、民主政治和系统设计 (Aristocracy, Democracy, and System Design)
概念完整性:建筑的启示
布鲁克斯在本章开头通过一组大教堂的对比,强调了概念完整性(Conceptual Integrity)对于系统设计的重要性。欧洲许多中世纪大教堂因历经数代建筑师,风格杂糅各异;而兰斯大教堂由于后任建筑师尊重了初始设计的总体规划,几百年建成后仍保持了惊人的和谐统一。这一类比映射到软件领域:软件系统虽然不像教堂那样建造几个世纪,但大多数系统内部的概念差异和不一致性却比大教堂更甚。这并非因为历时太久,而是由于设计被拆分给多人完成,导致各部分在风格和思路上参差不齐。
布鲁克斯明确主张:在系统设计中,概念完整性应当是最首要的考虑。为了保证一系列连贯的设计思想贯穿产品,他宁可牺牲一些杂凑的特性和改进,也不要一个拼凑而成、缺乏整体性的系统。哪怕那些零散添加的功能点各自看似有价值,如果破坏了整体统一,也应当舍弃。这一观点是对许多项目“功能至上”倾向的矫正:布鲁克斯引用OS/360与另一简洁系统的对比说明,仅追求功能多往往牺牲了易用性,而概念统一才能在功能与简洁间取得平衡。
他进一步指出,易用性取决于功能和复杂度的比率,而非功能多少或界面简单本身。一个系统若提供大量功能但用户需记忆过多选项,易用性并未提升;反之功能少但无法满足需求也不行。最佳状态是在给定功能集合下,用最简洁直接的方式呈现给用户。这就要求系统有高度的内部一致性:每个部分遵循同样的设计原则、使用一致的折衷和命名风格,如此用户才能更容易掌握整个系统的用法。布鲁克斯总结道:“简洁和直截了当来自概念的完整性。每个部分都必须反映相同的原理和一致的折衷,在语法和语义上保持一致。”只有这样,用户才能真正感受到系统设计上的和谐,进而提高可用性。
这一论述奠定了之后几章的核心:大型系统要成功,必须高度重视整体架构的统一性,而不是把所有“看起来不错”的功能胡子眉毛一把抓地塞进去。概念完整性甚至胜过功能丰富性,在布鲁克斯眼中,它就是软件设计的灵魂。
获取概念完整性:设计独裁 vs. 团队民主
如果概念完整性如此重要,那么如何获得它?布鲁克斯提出了一个看似极端的问题:难道要靠“一位卓越精英(结构设计师的贵族专制)”来实现吗?这是否意味着大批富有创造力的实现人员要被压抑为平民,只机械地执行?另一方面,人们担心如果设计与实现脱节,会出现架构师闭门造车,产出无法实现或代价高昂的设计,让整个团队陷入困境。还有一个实际问题:如何在详细技术规格上充分沟通,让实现人员准确理解设计并正确实现?
为了解决这些矛盾,布鲁克斯提出了两种方法:
方法一是严格区分“设计/架构工作”和“具体实现工作”,将系统的总体设计和架构任务交给少数精英(架构师),实现则交由较多的人力来完成。这样可以保证架构由一小群有默契的人制定,从而概念统一,同时又能利用大团队来并行实现、缩短工期。这就是他在上一章通过“外科手术队伍”提出的方案之一,以及许多成功经验(IBM 360硬件/软件开发的对比)所证明的路径。关键在于架构师队伍与实现队伍明确分工,各司其职。
方法二正是第三章讨论的Mills的方案,即通过崭新的团队组织方式(外科手术队伍)使少数能手带领多人工作。这个方法其实也是在贯彻理念:由一个人(首席)思考设计,其他人支持,从团队结构上实现架构思想的集中统一。
本章聚焦第一种方法,即架构师—实现者模式。对于非常大型的项目,布鲁克斯认为必须采用这种垂直分离:少数顶尖设计师负责体系结构和规范,多数程序员负责编码实现。这样既满足概念完整性,又能调动大量人力加快进度。
不过,正如前文提到的疑虑,这种模式带来一个问题:实现人员在架构师完成设计前会闲着吗?他们如何保持参与感?布鲁克斯以他管理OS/360时的亲身教训回答了这些问题。
回顾OS/360项目,当时他和架构经理、实现经理一起规划了系统的外部规格撰写任务:架构组10人需要10个月完成,但进度只允许7个月;实现队150人则主张由他们辅助编写规格,这样能按时完成且大家有事做。架构经理警告说让实现队写规格会出问题,但考虑到不让150人闲等和赶进度压力,布鲁克斯把工作分派给实现队伍。结果不出所料:规格既延迟3个月,又质量低劣;概念完整性缺失导致系统调试额外花了一年时间去弥补。这个惨痛经验证明了架构经理最初的判断:概念设计工作如果分散给大团队,必然拖期且缺乏统一,反而更糟。
为此,布鲁克斯总结出一条铁律:“在规格完成时才雇用实现人员”。就像建筑行业都是等图纸完善再招施工队一样,软件也应先完成外部设计说明再开始大规模编码。这样就不存在让程序员干等之虞——因为他们根本还没上岗。当然,他也承认计算机行业节奏更快,不可能真的停下实现那么久,所以需要考虑并行搭建。
布鲁克斯援引了同事Blaauw的观点,将创造性过程分为架构(概念抽象)、设计实现(具体设计)和物理实现(最终构建)三个阶段。在现实中,这三可有所重叠。对于软件项目,这意味着在外部规格说明(架构)尚未100%完成时,实现人员可以着手一些前期工作:
- 原型开发:只要有一些初步的外部功能雏形,开发人员就可以开始构建原型或验证性模块。这与敏捷的思想不谋而合——提早做原型,验证架构设想。
- 设定性能目标:实现团队可以针对系统的时间、空间目标进行研究,了解运行平台的限制。比如弄清硬件配置、预估吞吐需求等,为后续实现定调。
- 模块边界和算法预研:在架构师给出整体框架后,设计实现人员可以开始划分模块边界、设计数据结构和关键算法。虽然外部接口可能还在细化,但内部实现的准备工作可先展开。这相当于让实现团队提前介入详细设计。
- 工具和环境准备:实现组可以花时间搭建开发环境,如配置版本控制、制定编程规范、开发存档系统和设计自动化工具等。这些工作不依赖最终规格,但对提高效率很重要。
- 学习和培训:如果项目涉及新机器或新技术,团队可以利用等待时间进行培训、熟悉库和算法,对未来工作打基础。
通过这些并行活动,布鲁克斯认为即使采用架构/实现分离模式,也未必会拖慢开发。事实上,经验显示由于沟通简化和错误减少,整体进度可能更快。垂直划分(架构-实现分离)大幅减少了劳动量的重复和交流摩擦,从而测试时间也减少,最终系统更快完成。
归根结底,概念完整性要求系统只反映一个统一的设计思想,对用户呈现出少数人头脑中的愿景。通过将工作划分为架构、设计实现、物理实现三层并纵向并行,我们既可以让小团队把控架构一致性,又不会显著拉长总工期。这可谓对“既要马儿跑,又要马儿不吃草”的一次成功权衡。当然,这也需要组织和管理上的支持:公司必须授权架构师在设计阶段拥有足够的话语权,而不是时时被市场或领导插手改来改去;同时,架构完成后,实施团队也必须有充分自由度在细节上创新,不能变成纯粹的码农。布鲁克斯在20年回顾中特别引用经济学家Schumacher的观点,强调**“放权”对激发创造力的重要**。这其实与架构师-实现者模式并不矛盾:高层架构集权决策,小团队实现自主负责,正是宏观专制与微观民主的结合。
等待中的实现者:创造力与公平
在实践架构师统揽设计的过程中,一个管理挑战是实现人员可能觉得创造力被剥夺或者被闲置冷落。布鲁克斯记录了当年OS/360实现团队对架构师单独写规格的三点反对意见:
- 架构师设计的功能可能过多,没考虑实现成本(膨胀风险)。
- 架构师享受了所有创造发明的乐趣,剥夺了实现者的创造力(心理不平衡)。
- 架构师工作慢时,实现人员只能干等(资源闲置)。
第一点风险(过度设计)在下一章“第二系统效应”详细讨论,确实是架构独裁可能带来的弊端。但后两点,布鲁克斯认为是误解。他指出,实现其实也是高度创造性的活动,制定了规范也不会减少写代码时的创新,反而因为有清晰框架,程序员可以专注于实现细节的创造(如更佳的数据结构、算法优化),创造力会因为明确目标而被增强。至于等待问题,他的答案如前所述,可以通过分阶段雇用和并行准备工作来解决。
事实上,现代大型项目经常采用类似方式:早期先由小型架构团队工作几个月出方案,然后逐步扩充团队进入实现阶段。在这个过程中,也会面对相似的人心管理。经验表明,如果架构师能够多与开发团队沟通,让他们理解设计理念,并欢迎对细节提改进建议,那么实现人员的参与感会增强,对规范也更认同,不会感到被剥夺创造力。布鲁克斯也强调了沟通的重要性:架构师必须与实现者充分沟通细节,确保大家正确理解设计并精确整合进产品。这可以通过组织评审会、原型验证、编写阐释性的设计文档等方式实现。
另外,随着软件行业的发展,有一种角色崛起,类似架构师与开发者之间的桥梁,即技术领导(Tech Lead)或开发经理,他们通常既参与一定编码又负责部分架构决策。这种角色可以在架构师不多的情况下承担子系统的设计协调,从而分散架构工作降低瓶颈。这有点像大的项目里,每个子系统都有自己的“小架构师”,而总架构师关注整体一致性。这种分层架构团队,使得实现人员有机会成长为子架构师,发挥创造力,同时仍保持总体架构统一。这相当于将贵族专制延展为由一组贵族组成的共和制,在民主与独裁之间找平衡。
现代视角:架构治理与微服务时代
当今的软件架构实践比布鲁克斯时代丰富复杂得多,但“概念完整性”的原则依然被奉为圭臬。无论是面向对象设计中的一致性和单一责任,还是微服务设计中的边界清晰和契约管理,其精神都是维持整个系统的一致协调,从而降低复杂度、提高可理解性。这些都可以看作“概念完整性”在不同层面的体现。
架构治理方面,大型互联网公司普遍设立了架构评审委员会或首席架构师办公室,确保各团队的设计符合公司整体架构战略。例如Google有Architecture Review,阿里有中台架构委员会。这些机制承袭了布鲁克斯提倡的“精英把控设计”思路。虽然流程上可能更加开放(提案可来自任何工程师),但最终拍板的人通常是资深架构师或委员会,以保证重要系统设计决策的质量和一致性。这类似于现代版的“结构设计师的贵族专制”,只是比个人独裁多了一点集体智慧色彩。
信息隐藏理念如今深入人心,也是概念完整性的基石之一。布鲁克斯在20年后反思中承认,Parnas关于信息隐藏的主张是正确的,他自己当年忽视了。信息隐藏指模块内部实现细节不为外界所知,只通过接口交流。这使各部分可独立演化而不破坏整体。这与概念完整性并不矛盾,反而是实现它的手段:架构师定义清晰的模块接口(统一的概念规则),实现人员在各模块内部可以灵活创新而不影响别人。今天的微服务就是极端的信息隐藏,每个服务对外只暴露API,内部如何实现他人无需关心。这带来整个系统在演进时的一致性和自治性的平衡。这也是布鲁克斯所追求的目标,只不过在他那时软件模块化刚兴起,他本人未充分重视。但现在我们知道,良好的模块边界设计+集中的架构原则是既保证一致性又保证并行工作的关键。
在敏捷和DevOps的环境下,我们更加注重团队的自组织和快速响应变化。这似乎与架构师中心制有张力。然而,敏捷并不排斥“大设计”,只是反对一开始设计过度和拒绝适应变化。很多敏捷团队采用**“架构演进”方式:开始只做最小架构决策,然后随着对问题理解加深不断重构架构。这相当于把架构设计分散在整个开发过程中完成。如果团队经验丰富且边界明确,这种方法能保持概念完整性,同时适应变化。但如果团队缺少统一愿景指引,演进过程中可能走向割裂。为此,一些大规模敏捷框架(如Scaled Agile Framework, SAFe)仍然主张在团队级别之上保留系统架构师角色,制定Architectural Runway等全局指引。可见架构师并未因敏捷而消失**,只是需要与团队协作融为一体。这与布鲁克斯强调架构师与实现者充分沟通的观点不谋而合。
在微服务和云原生时代,布鲁克斯的思想得到了新的诠释:过去一个大系统里维持概念完整性很难,但现在我们把系统拆成许多服务,每个服务内部保持高度一致性,而服务之间通过API契约衔接。某种意义上,微服务将概念完整性的难题缩小到每个服务内部,从而更易管理。同时通过统一的规范(比如统一的认证授权、日志格式、监控指标)来保证全局的一致性体验。这相当于架构师制定了“一系列准则”,然后各团队在准则内自由实现各自服务。这种标准化+自治的模型在业界非常流行,因为兼顾了灵活性和一致性。例如,一个电商平台的不同微服务团队各自选择具体技术栈实现,但架构委员会规定所有服务都必须提供某种健康检查接口、日志格式、错误码规范,于是整体运营和使用体验是一致的。这种实践体现了概念完整性的另一个层面:用户在界面上的一致体验。布鲁克斯在回顾中提及,要让用户在各种应用中获得统一的概念模型,而不仅仅每个程序内部一致。现代设计系统、统一UX规范等都是类似思想,把一致性扩展到产品层面。
最后,值得一提的是**“第二组织规律”(有时称Conway定律的反面):组织结构会影响系统设计结构。外科手术队伍建议用特殊组织来得到特殊架构效果。而微服务、DevOps等也发现组织团队方式和系统架构是互相作用的。例如两披萨团队模式自然导向微服务架构,因为每队自主决策自己的服务;反过来,推行微服务也要求组织改为小团队负责业务全栈。这验证了布鲁克斯对于架构和组织必须匹配**的见解:他在第7章也探讨了大型项目组织架构。现代管理者认识到,要获得某种架构特性,也许需要相应调整团队组织。SAFe框架中,有时会组建系统团队专门保证跨团队架构一致,这可以看作外科队伍概念在大组织下的应用。
综上,本章强调的概念完整性和架构师集中设计,依然是今天成功软件项目的关键成功因素之一。无论通过精英架构师制度,还是通过良好的模块化+自治团队,我们都力图实现**“整体统一而局部灵活”**。对比布鲁克斯当年的主张,现代实践更多了一些软性的技巧,如迭代架构、充分沟通、赋能团队等,但核心逻辑未变:优秀的系统需要清晰一致的设计思想,而这通常源自少数人的统筹,而非设计委员会式的大众投票。开发方法可以民主,但是设计决策必须有人负责和把关,否则就会“四海之内皆不同调”。布鲁克斯用充满远见的文字告诫我们:在软件这个复杂艺术中,“少即是多”,宁要协调有序的简单,也不要杂乱无章的丰富。现代架构师和团队在读到此章时,应能会心一笑:许多踩过的坑,书中早已指明。
第五章 画蛇添足 (The Second-System Effect)
第二系统效应:过犹不及的诱惑
布鲁克斯将开发者在第二个系统上常犯的错误比喻为“画蛇添足”,也即第二系统效应。他断言:“第二个系统是设计者们所设计的最危险的系统。”原因在于,初次设计时由于经验不足和谨慎克制,往往倾向于保持简洁;但一旦有了成功经验,开发者在第二个系统会倾向于过度设计,把第一个系统中为了保险而未加入的各种想法和特性一股脑儿都塞进去。结果,第二个系统常常变成功能繁杂、结构臃肿的“大杂烩”。正如古罗马诗人奥维德所讽刺的,那样的产品就像一个“大馅饼”,掺杂了太多配料反而失去了原有的美味。
举例来说,布鲁克斯提到IBM的OS/360操作系统对许多设计者而言都是他们的第二个系统。参与OS/360的架构成员大多来自之前较简单的系统项目(1401磁盘系统、Stretch、7090 IBSYS等)。在那些第一代系统中,他们学到很多经验,同时也因为时间和技术限制推迟或简化了一些功能。到了OS/360这个第二系统,他们倾向于把之前“谨慎搁置”的种种功能都实现出来。这导致OS/360的设计变得过于庞杂,印证了第二系统效应。
第二系统效应不仅体现在添加不必要的功能,还表现为对某些技术过度精雕细琢。布鲁克斯指出,有一种倾向是在第二系统里过度细化和增强技术点。也许基本的系统构想已经变了,或需求发生了变化,但设计师仍执着于把某些上一版没做足的技术部分做到极致,结果反而与整体需求脱节。简言之,第二系统往往充斥着非核心的改进,满足了设计者的自我实现,却让系统变得复杂笨重。
布鲁克斯将这种现象归因于人性的弱点:初尝胜果后容易自信膨胀,对新系统的难度估计不足,想展示才华、纠正上次的遗憾,结果矫枉过正。因此,他将第二系统效应称作设计师的“幼稚病”和“骄傲病”。
规避“画蛇添足”:自律与克制
认清了第二系统效应的危害,布鲁克斯接下来讨论如何让架构师避免重蹈覆辙。他直言:“一个结构师无法跳过自己的第二系统,但他可以有意识地警惕那些特殊危险,并运用某些自我约束的准则来避免功能修饰的诱惑。”换言之,认识到第二系统效应不可避免后,设计者需要自律地控制自己的欲望,在第二个系统上强行压制住“画蛇添足”的冲动。
布鲁克斯给出的建议包括:
- 延迟附加的功能:再次审视系统的基本目的和概念,对于不符合核心目标的功能,即使很有吸引力,也要果断舍弃。要敢于砍需求,拒绝上一版的遗憾清单里那些其实无关紧要的特性。
- 保留简洁性:提醒自己第一个系统成功的要素之一就是简洁。即使现在有更多资源和经验,也不要让设计偏离简洁原则。布鲁克斯甚至建议在第二系统上故意“克制想象力”,坚持只做那些真正必要且经过充分论证的增强。
- 收集第一系统反馈:利用第一版用户的实际反馈,判断哪些缺失功能是真的痛点,哪些只是nice-to-have。如果用户并不热切需要,不要仅因为自己想实现就硬塞进去。这将帮助设计者以用户视角取舍改进。
- 同行审查:让别的没参与第一系统的人来评审第二系统设计。他们没有“前一版遗憾”的包袱,可以客观质疑某些新增功能的必要性,从而抑制过度设计。
- 模块化演进:将想增加的功能尽量设计为可插拔的模块或扩展,将它们隔离于核心系统之外。这么做即使加入很多功能,也不会弄乱主干架构,而且可以在后来发现问题时相对容易地删除或优化。
- 版本计划:把一些想法放到未来版本实现,而非都堆在第二版。一个有远见的规划可以告诉自己:“这个点不错,但不用急于在这版做,我们放到第三版,以后再说。”以此给自己降温。
布鲁克斯还举例说明了第二系统效应的教训在业界多次发生:比如早年的某语言编译器团队,在开发第二版编译器时为了支持所有新花哨语法,耗费大量时间,最后无奈地回退只实现基本功能,因为争论语言特性已经消耗了全部精力。他也提到OS/360中某些功能完全可以交由人工操作来处理,却被设计者执意编程实现,导致增加了过多复杂度(例如一个26字节的特殊常驻程序仅为处理极罕见的闰年12月31日问题)。这些例子说明,设计者在第二系统上往往容易“炫技”,实现一些在布鲁克斯看来完全没有必要自动化的细枝末节。
因此,他的忠告可归结为八个字:谨记初衷,自律克制。在第二系统设计时,不妨把“Keep It Simple, Stupid”贴在墙上,时刻警醒自己不要因为手头资源充裕就恣意挥霍复杂性预算。
OS/360与Windows NT:现实例证
布鲁克斯自己亲历的OS/360正是第二系统效应的一个经典案例。他在此不讳言OS/360因为设计团队的膨胀而变得庞大复杂,甚至在书的附录命题里直接指出:“OS/360是典型的画蛇添足例子。”并括注“Windows NT似乎也是”。这说明,在1995年第二版序言时,他观察到微软的Windows NT操作系统也呈现出类似迹象。事实上,Windows NT可以看作Windows的一个“大重构第二版”,当时功能上试图包罗万象(兼容Posix、OS/2子系统、安全架构等),其体积和复杂度都远超前代DOS/Windows 3.x,开发周期也一再拉长。这与OS/360时代的故事颇有相似之处。
提及Windows NT的例子表明,第二系统效应并非过去年代的传说,在软件业迭代演进中反复上演。甚至可以说,每当一个成功产品进入下一代时,就要提防“画蛇添足”。微软后来开发Longhorn(Windows Vista前身)时增加过多炫目功能以致项目几近失败,不得不砍掉很多特性重新回到基本,这又是一次现代表演。
在20年回顾版中,布鲁克斯针对第二系统效应再次提醒架构师要自律。他指出,越是有成功经验的设计者,越容易陷入自信陷阱,从而忽视克制。因此持续的谦卑心态和学习态度尤为可贵。
现代视角:重写与升级的陷阱
现代软件行业关于**“第二系统”**的教训更是屡见不鲜。尤其在互联网产品中,当第一个版本取得一定成功后,团队往往倾向于“推倒重写”或进行架构大升级,认为趁早打造一个更完美的第二版以迎接未来挑战。然而,据统计,大规模重写项目的失败率很高,其中一个主要原因正是第二系统效应导致的新系统超出控制范围。
例如Netscape浏览器在90年代末决定重写为Communicator 5.0,结果添加了大量新特性、新架构,开发数年未能完成,市场份额拱手让给后来者IE。这是一个著名案例,被Joel Spolsky等业界人士引用来告诫开发者不要轻易重写成功产品,因为重写时很容易陷入第二系统综合症。
还有无数创业公司在产品初步成功后,为追求“企业级”“可扩展”而进行架构豪华升级,结果因为系统过度复杂、新bug丛生,反而错失了市场窗口。
为了对抗这种倾向,现代工程实践提倡增量重构而非推倒重来,以尽量避免设计者在真空中臆想大而全的新系统。当然,有时大版本跃迁确实必要,这时团队必须有强大的技术领导力来审核每一个拟加入的特性,确保不因“可以做”就“都去做”。许多公司在新版本开发中采用**“红队审查”或Architectural Fitness Functions**来防范第二系统效应:由独立小组质疑新系统每个复杂决定,或制定自动度量指标监控复杂性指标,一旦超标及时纠偏。这类似于布鲁克斯所说引入“他人视角”帮助克制设计者的冲动。
值得注意的是,现代强调用户驱动开发在一定程度上也抑制了第二系统效应。布鲁克斯时代,设计者容易自说自话加特性;现在产品经理和用户反馈在很多团队有更大话语权,新版本功能往往以用户需求为依据排列优先级。这样开发者不容易为了自己理想而肆意加功能。但即便如此,**“技术炫技”**仍可能以性能优化、安全加固等名义出现,让系统复杂性激增。因此产品和技术团队都需要有共同意识去保持产品的简洁与均衡。
第二系统效应在开源社区中也能观察到。有的开源项目作者在1.0版后推翻重构,试图做得“更优雅”导致进度迟缓、社区流失。而成功的开源项目往往是小步迭代,核心保持稳定,只逐步演进。Linux内核在Linus领导下几十年持续发展,每次新版本改进谨慎克制,就是反面典型的例子:它没有某个明显的“第二系统”翻车点,因为Linus本人极其反对“一次性重写”,提倡平滑改进。
在架构层面,微服务拆分其实也可以视为重构第二系统的一种。当一个单体应用拆分为微服务,团队常有诱惑为每个服务引入新的技术栈、新的基础设施,从而整个系统复杂度骤增。经验表明,微服务化成功的团队通常控制节奏,逐步拆分,并限制新技术数量,避免架构第二版变成一个炫技实验场。
布鲁克斯当年的忠告“Plan to throw one away; you will, anyhow.(计划扔掉一个,你无论如何会这么做)”鼓励做原型,但也常被误解为赞成重写第二版。然而他自己后来澄清这是过于简单的表述。现代解读应该是:做好扔掉原型的准备,但别让正式第二版也变成需要扔掉的灾难。要通过前期原型和小步试错,把第二版的风险降低。
总的来说,第二系统效应教会现代软件人两个字:克制。在享受了第一次成功后,更需要冷静审视什么是用户真正需要的,什么是架构真正有益的,然后果断地对很多“好主意”说不。正如《UNIX哲学》强调的,“保持简单”,在版本演进中这点尤为难守却尤为重要。
布鲁克斯以他丰富的经历,为我们总结出了这个深刻的人性教训。在技术日新月异的今天,它仍然适用。当我们规划下一个重大版本、重构或技术升级时,都应翻开《人月神话》第五章,提醒自己别做那个给蛇画脚的匠人。必要时,找一个在第一版未参与的人,来当你的“节制师”。因为过犹不及的道理在软件世界从未改变。
第六章 贯彻执行 (Passing the Word)
沟通规范:将设计思想传达给团队
当架构师或小团队完成了总体设计,如何贯彻执行,让设计思想准确传达到每一位实现人员,并真正落实在代码中?第六章聚焦于沟通和规格说明的问题。布鲁克斯指出,大型编程项目常常失败于沟通不畅——即使有了出色的架构思路,如果不能有效传递给整个团队,最终产品也会变形走样。他提出,要将设计的“话语”传递下去,需要借助文档、定义和机制等多种手段。
本章首先强调了文档化规格说明(the Manual)的重要性。布鲁克斯建议将系统的外部行为写成类似使用手册的规格说明书。这份手册不仅是给最终用户的,也是给实现团队的:它详述系统应具备的功能、接口、约束,充当每个人理解系统的一致蓝图。好的规格说明应当清晰、无二义性并且全面覆盖需求。它相当于建筑的图纸,有了它,实现者才能各就其位施工。布鲁克斯认为,规格说明的写作是架构师最核心的工作成果之一。在OS/360项目中,他非常重视编写完整的外部规格手册。
然而,文字规格往往难免含糊其辞或无法精确定义复杂行为。对此,布鲁克斯讨论了形式化定义的作用。形式化定义使用数学或逻辑的严格语言描述系统特性,例如用BNF定义语法、用数学函数定义输出等。当时一些项目尝试用形式语言描述编译器和操作系统的行为。这有助于消除歧义并可被验证。但布鲁克斯也意识到,形式化过于复杂,编程人员未必都看得懂,因此他主张文档化规格与形式定义并用:先用易懂的文字阐述,再辅以关键部分的形式化定义,以确保准确和完整。
除了静态文档,直接整合(Direct Incorporation)也是传达设计思想的方法之一。所谓直接整合,即架构师本人动手实现一些关键框架或核心模块,把设计思想直接写入代码。通过这部分代码,其余程序员可参照风格,理解意图。布鲁克斯提到,架构师可以提供示范代码或框架代码供大家填充(类似今天的框架搭建)。这种亲力亲为的做法在一定程度上保证了概念完整性的落地。当然,它需要架构师有足够时间和精力写一些基础代码,而非仅写文档。
项目内的会议和大会也是沟通管道。布鲁克斯认可一定频率的设计讨论会议,让架构师与实现人员面对面交流。尤其在多人实现的模块上,集体讨论可以统一理解。项目大会则用于全员同步重要决策和状态。这些当面沟通渠道可以解释文档无法涵盖的细节、澄清误解,并提供反馈渠道。
此外,他提出了多重实现作为保证理解的一种机制。即让不同的人独立实现同一个模块或功能,然后对比他们的实现差异,以发现规范中可能的模糊点。这个做法相当昂贵,但在一些关键子系统上可以考虑。比如OS/360曾让两个团队分别开发两个PL/I编译器,以确保语言规范的精确和编译器质量一致。这种冗余实现如果两者结果不一致,一定有一方不符合规范,进而可以改进规范或实现。
通过文档、形式化、示范代码、会议和多重实现等手段,布鲁克斯力图将架构师脑中的概念准确传递到实现者手中。他坦言,沟通是一件艰巨的任务,且不可能完全避免误解。但多管齐下可以把风险降到最低。
项目工作手册:集体记忆库
布鲁克斯提出一个实用工具——项目工作手册(Project Workbook)。这是一套有组织的文档集合,涵盖项目的各个方面,包括规格说明、设计决策记录、进度报告、变更请求、测试结果等等。它充当整个团队的“集体记忆”,让每个人都能方便地获取项目当前状态和历史记录。
项目工作手册的意义在于:当团队规模很大时,信息容易散落和遗忘。一本结构良好的工作手册确保信息共享和一致。布鲁克斯建议手册应采用松散页(binder)形式,方便更新和插入新内容。它应该包含目录和索引,模块化分卷,如需求、架构、详细设计、测试、管理等等,每部分由专人维护更新。这样团队成员在执行任务时,可以随时查阅相关部分的最新内容,减少口耳相传的失真。
更重要的是,准备工作手册的过程本身就强制团队思考和明确许多事项。每份文档的撰写是一次集中讨论和澄清的机会,没写下来的东西往往就被忽略或引发混乱。手册的维护也是项目监督和预警机制:通过跟踪文档的更新,管理者可以监控项目进展和发现遗漏。
布鲁克斯借鉴了其他行业的文档实践来说明这一点。例如,要制造一台计算机,需要目标说明、技术规格、进度表、预算、组织结构、空间分配、市场预测等关键文档。这些关键文档成为管理者的主要工具。软件项目同样需要类似文档支柱来运转。所以他把项目手册比喻为一套关键枢纽文档集合——在纷繁的纸堆中,有少数文档是核心枢纽,每项管理工作围绕它们展开。项目手册正是这些枢纽的载体。
手册内容也不是凭空定义的,而是往往由行业传统和技术因素决定。例如在军事软件项目里,手册通常包含需求、系统规格、接口控制文档、测试计划等,这些都是约定俗成的。当然,每个项目也可以根据需要增删项,但重要的是事先设计好手册的结构,以免后来文档杂乱无章难以管理。一旦结构确定,后续所有文档都按此分类归档,保证增补时有序放入对应章节。
布鲁克斯强调,维护手册是项目经理职责之一,不要视之为浪费时间。他本人在OS/360时颇为重视这项工作,事实证明帮助很大。通过项目手册,IBM管理层能够了解项目状态,新成员加入能快速上手,用户文档编写也有依据等等。
沟通组织架构:塔式团队 vs. 协作网络
沟通不仅发生在文档和规范层面,组织架构本身就是沟通的渠道安排。布鲁克斯分析了大型项目的组织结构问题。他再次引用了巴别塔寓言(下一章会详细论述)说明,组织不当和沟通混乱足以毁掉项目。
在本章末尾,布鲁克斯提出要根据系统设计来调整组织架构:“初始反映系统设计的组织架构图肯定不会是正确的。如果系统设计可以自由变化,项目组织架构也必须为变化做好准备。”。这句话揭示了Conway定律的思想:系统的结构受组织结构影响,反之亦然。如果发现系统设计需要改变模块划分,就应该相应调整团队职责和沟通关系,以匹配新的架构。
他建议大型项目常用的组织结构是一种树状层级:顶层是体系结构师团队,然后下面分若干实现小组,每组负责一个大模块,再往下分子模块组等等。这种层次结构对应系统模块的分层,确保沟通在需要的范围内进行。然而,他也警告组织架构不能一成不变,要随设计演化动态变化。如果架构师发现原先划分的模块不合理,需要拆分或合并,就应该重新划定团队边界,否则团队将按旧分工工作,系统改进难以真正落实。
实际上,他在第7章中将进一步阐述如何组织大项目,包括沟通策略等。但在此可以窥见他的思想:让组织结构服务于技术设计,而不是相反。沟通路径和责任划分要与系统模块图一致,这样才能保证信息在正确的人之间流动。例如,把相互依赖紧密的两个模块交给同一团队,则团队内部沟通顺畅;若模块间接口清晰且稳定,可分别交给不同团队,中间由接口控制文档沟通即可。
现代视角:协同开发的工具与文化
在数字化时代,布鲁克斯关于沟通和文档的指导显得越发宝贵,同时也被更先进的手段所扩展和强化。现代团队规模动辄上百乃至上千,如何将设计思想贯彻,依赖的不仅是纸质手册,还包括电子化协作平台和开放的交流文化。
首先,几乎每个大型项目都会建立知识库或协作网站(如Wiki、Confluence)。这正是项目工作手册的演化形式。Wiki允许多人实时编辑,版本控制方便,搜索快捷,可以看作动态的项目手册。而且多媒体内容(图表、代码片段)也能轻松纳入,使规格说明更丰富直观。维护Wiki也成为敏捷团队的常规活动之一,被称为“记录活文档”。敏捷虽强调“工作软件高于详尽文档”,但并非不要文档,而是要求文档与代码同步演进。Wiki的易更新性满足了这一要求,确保团队知识不过期。可以说,Wiki是现代贯彻设计思想的利器,延续了布鲁克斯项目手册的思想,即提供团队共享的单一信息源,让每个人都“从同一本剧本上读”。这极大减少了误解和重复沟通。
其次,自动化工具加强了规格的一致传达。例如今天广泛使用的API接口规范(Swagger、OpenAPI等),用机器可读的格式定义接口,然后自动生成文档、客户端代码、测试桩。这就等于把形式化定义和文档结合起来,让前后端团队对接口的理解完全一致,不会有人因手工文档更新不及时而掉队。这与布鲁克斯强调的形式化定义异曲同工,只是现代工具让形式规范融入了工作流程。
再如设计评审工具、**架构决策记录(ADR)**等,帮助架构师将设计决策历史透明化保存。ADR是一种轻量方式,团队为每个重大设计决策写一篇记录,包含背景、权衡、结论。它类似架构师在项目手册中写下设计过程的注解。当新人加入或回顾历史时,可以了解为何当初这么做。这避免了架构理念在人员流动中丢失。
布鲁克斯当年受限于技术,只能依靠手工文档。而现在的DevOps工具链把许多规范直接编码进流程中。例如CI/CD流水线中定义的测试用例、代码规范 lint 规则等,本身就是对“正确实现设计”的保障机制。这些工具自动检查代码是否符合某些架构约束,某种意义上替代了一部分人工文档沟通。比如微服务在上线前通过契约测试验证接口符合规范,这相当于机器在帮助团队贯彻接口设计思想,而无需无休止的人为确认。
会议沟通在敏捷中也演变为例行机制:每日站会、每迭代评审和Retrospective都是沟通的渠道,只是比起过去漫长的大会议更加频繁短小。这种节奏更像持续的“Passing the Word”。尤其评审会上,团队向利益相关者展示工作产出,架构师可以在会上检查实现是否违背初衷。敏捷主张面对面交流是最高效的沟通方式,这跟布鲁克斯强调当面澄清误解的作用一致。
组织结构方面,现代的大型软件组织常采用矩阵或网络结构,沟通不像过去那样自上而下垂直分层,而是更加网络化灵活。虽然这样容易造成信息过载,但配合敏捷的Scrum of Scrums、多团队PI规划会议等,也能让正确的信息跨团队扩散。Netflix等公司甚至采取极度扁平的管理,靠文化和原则而非层级命令来“贯彻执行”。他们发布技术白皮书、架构原则文档给所有工程师,鼓励工程师自治。这某种程度上把每个人都变成手册读者和维护者。内部技术会议、工程师交流大会也是大企业传播统一理念的方法,相当于布鲁克斯所说项目大会的升级版,甚至跨项目进行。
总而言之,沟通仍然是软件项目成败的关键。正如腾讯云的一篇文章所言,在软件吞噬世界的当下,开发成本不再是简单预算,而是一场关于复杂性和人性的永恒博弈,尤其是一场关于时间与沟通的骗局。管理者若用“人月”衡量成本而忽视沟通投入,就会掉入陷阱。今天我们投入大量精力在所谓“DevRel”(开发者关系)、团队文化建设、知识管理上,都是为了解决沟通问题。这印证了布鲁克斯的远见。
值得注意的是,开源社区作为一种特殊的大规模协作,也非常依赖清晰的规范和沟通渠道。成功的开源项目通常有详细的贡献指南、代码规范文档、issue模板等,让全球开发者能理解项目愿景并协同编码。这可以看作《人月神话》思想在无领导组织中的验证:即使没有强制性上下级关系,共享的文档和文化也能使设计思想传遍社区。如果沟通一旦断裂,开源项目就会分叉(fork),这就像架构思想不统一时组织也分裂一样。
在人工智能辅助沟通方面,我们看到一些智能文档工具开始涌现。比如利用自然语言处理为复杂技术文档生成概要、Q&A,帮助新人更快吸收信息。虽然这只是早期尝试,但将来AI也许能自动检查文档与代码一致性,甚至充当实时问答的架构助手。这些都将提升“贯彻执行”的效率,但不能改变根本:软件设计需要以人为中心的沟通。布鲁克斯曾说过,软件本质复杂性很大部分来自于需要让不同人共同理解同一套抽象。AI可以辅助理解,但最终决策仍然在人。
总结来说,现代软件工程在沟通传达设计上较布鲁克斯时代有了更多样的工具(Wiki、自动化、协议契约)和更敏捷的文化(自组织、持续交流)。但这些都是实现他当年目标的方法补充,并没有颠覆那一目标:保证每个项目成员对系统的目标、规格和设计有统一的理解。达到这一状态,我们才能说“贯彻执行”了架构思想。否则,即使有再好的架构蓝图,也可能在实现中走形,产出一座翻版的“巴别塔”。对此,我们在下一章将进一步探讨沟通失灵的悲剧,以及如何避免。
第七章 为什么巴比伦塔会失败? (Why Did the Tower of Babel Fail?)
巴别塔的教训:沟通失灵的代价
圣经中建造巴别塔的故事是人类协作失败的著名寓言:人们试图建一座通天高塔,但上帝变乱了他们的语言,使他们彼此无法沟通,工程因而半途而废。布鲁克斯借此说明交流在大型项目中的关键作用:没有有效的沟通,庞大工程注定崩溃。
他指出,巴别塔失败的直接原因是沟通受阻,但深层次原因在于组织规模过大且缺乏统一。现代大型软件项目就像建塔,需要众多人协同工作,如果没有恰当的沟通渠道和组织纪律,也会陷入混乱。所以巴别塔故事对软件工程管理者而言,提供了负面教材:告诉我们不应重蹈那些古人的覆辙。
布鲁克斯将巴别塔教训概括为两点管理原则:
- 沟通基础设施:必须为大团队建立良好的信息传递机制,让每个人都能理解项目目标和自己的任务。不应让任何成员陷入语言不通、信息孤岛的状态。
- 组织纪律:需要在规模扩张时保持组织的一致性和凝聚力。避免团队内部各行其是,各说各话。
他特别强调“大型编程项目的组织架构”应围绕沟通展开设计。通过精心的组织和过程,可以避免巴别塔式的结局。
大型项目的沟通方法
在具体策略上,布鲁克斯提出了几种提高沟通效率的方法:
明确领导与报告机制:项目应有清晰的层级,谁向谁报告,谁对哪个部分负责,这样信息才能在正确路径上传递。如果关系模糊,消息就可能遗漏或重复传达。这并不意味僵化官僚,而是说沟通应该“有人跑腿”。他提到**“电话日志”**等做法,让管理者及时记录沟通事项并跟进,有条理地向团队广播重要信息。
关键问题集会:对于项目中的重大问题或接口,需要定期召开全体沟通会议或者专题研讨,让所有相关者面对面达成共识。就像建巴别塔时应该开全员动员会统一图纸一样,软件项目在关键里程碑前也应召集全队统一思想。布鲁克斯建议在项目的主要阶段转折点召开大型会议,确保人人清楚下一步方向。
文档公开共享:上一章提到的项目手册在这里再次发挥作用。布鲁克斯主张项目文档应该公开透明地让团队成员查阅。当文档成为公共资源,沟通就不再依赖层层口头传递(那容易失真),每个人都可以直接获取权威信息源。好的项目会建立共享文件库或公告板,让信息对称。
丰富沟通渠道:不要只依赖单一的正式报告链。布鲁克斯认识到非正式沟通同样重要,比如走廊聊天、技术讨论会等,可以弥补正式渠道的不足。他甚至鼓励项目团队建立一定的社交关系,这样工程师们彼此熟悉,更愿意主动沟通。在OS/360项目中,他推动建立跨部门技术交流,让不同子系统的人员也有机会交换信息。
减少人员变动:沟通成本与团队成员数量的平方成正比(人与人之间要互相沟通)。所以保持团队稳定、精简,可以减轻沟通压力。如果中途换人或频繁新增人手,就像巴别塔里突然语言变了,会严重干扰沟通。布鲁克斯建议大型项目尽量避免晚期换员,并在必要时做好知识传递。
本章还讨论了**“项目工作手册”如何在沟通中帮助项目经理预警**问题。正如一座塔的建造如果材料清单或者进度表不更新,塔就会断货或停工。同理,项目管理通过监控文档更新可以发现哪些团队落后或存在分歧。例如,如果某接口规范文档迟迟不完善,就说明相关模块负责人没沟通好,可以及早干预。工作手册因此是沟通的一面镜子,反映项目协作的健康度。
组织架构与沟通效率
布鲁克斯在本章最后,给出了大型项目组织的一种建议架构(实际上在第9章中更详细展开)。简而言之,他倾向于**“树状层级”组织(类似前述外科队伍的扩展):顶层架构师团队负责总体设计,下设若干功能小组,各小组内部再细分。每个节点有清晰的上下沟通接口。如此一来,沟通网络被简化为分层树**而不是任意网状,大大减少了路径数。正如前文计算,两个人一条线,三个人三条线,十个人45条线,而采用层级,每个人只与其上下级沟通,则沟通量控制在线性级别。
他还建议项目应该指派专职的通信联络员(可能就是前章提到的管理员、程序职员等),确保各组间信息流动畅通。例如跨团队依赖点,应有专人协调沟通,不能等问题爆发才发现信息没同步。
Conway定律告诉我们:软件结构会趋向于组织结构。布鲁克斯在此反其道而用之:设计组织结构来匹配理想的软件结构。一旦组织结构正确,沟通自然围绕模块边界展开,就不会发生跨界混乱。巴别塔失败部分原因就是组织无序,大家各说各话。而现代项目如果各团队各自为政,不遵守统一规范,也会造出一座“沟通乱塔”。
现代视角:规模协作与组织沟通
在当今超大规模的软件项目(例如数千开发者参与的开源项目或大型互联网平台)中,沟通和组织成为决定成败的首要因素之一。布鲁克斯关于巴别塔的比喻更显现实。我们可以看到几种趋势和实践:
微服务与团队自治:大型互联网公司往往以微服务架构应对复杂性,每个微服务由独立团队负责。这类似于把一座高塔拆成许多独立的小塔,各建各的。然而,为了系统不分崩离析,他们制定平台和中台来统一基础设施(通信协议、监控、配置等),并通过架构委员会和技术规范来统一横切关注点。这相当于给各团队构建了共同的“语言”和“度量衡”,避免巴别塔式的鸡同鸭讲。举例,所有服务用统一的RPC框架,这样团队间沟通调用就没障碍。微服务成功的关键不在于拆分,而在于拆分后仍有统一的治理。
API经济和契约测试:现代分布式开发中,各团队通过API接口协作。为防止沟通不良导致集成失败,大家约定契约,并以自动化测试来验证对方是否满足契约。可以说**“契约”就是现代的通信语言**。有了契约,即使团队分别工作,也能确保对接。这很像布鲁克斯强调的接口控制文档,只是自动化程度更高。契约测试的通过,表明沟通达到了一致,否则测试失败就逼迫双方坐下来对齐。
规模敏捷(Scaled Agile):近年各种Scaled Agile框架(SAFe、LeSS等)都是在解决多团队敏捷协作的问题。本质上它们提供了大量同步沟通的仪式和角色。比如SAFe有Release Train Engineer作为跨团队“列车长”,负责协调几十个团队的沟通;PI Planning大会聚集所有团队策划下季度目标,确保大家目标一致。这就像巴别塔建造前的誓师大会。敏捷强调的“面对面交谈”在大规模下就变成“全员大会”或“跨团队Stand-up”。这些都是为了同步上下文,避免团队间误解。有趣的是,SAFe借用了军事调度和产业工程的一些理念,与布鲁克斯借鉴传统工程管理的思路不谋而合。
DevOps与ChatOps:当开发和运维融为一体时,沟通包括了更多角色。DevOps实践中流行ChatOps,即用IM(如Slack、Teams)作为团队沟通中枢,所有部署、告警信息实时共享在频道里,大家即时响应。这营造了一种持续沟通的氛围,让团队对系统状态有共同了解,减少信息不对称。ChatOps频道有点像布鲁克斯的“电话日志”升级版,只是如今的电话变成聊天机器人发消息,日志变成群组记录,领导和成员都在里面。这使信息扩散更快,沟通壁垒降低。
文档驱动开发:很多团队推行“Docs as Code”,将文档纳入版本控制,与代码同流程管理。这保证文档与实现同步演进,不会文档说一套代码做一套。更进一步,一些采用Documentation-First(文档优先)的方法,例如先写API文档再实现API,让文档成为沟通和设计契约。这就像提前写好塔的设计图,再去施工。亚马逊闻名的“6页文档会议”传统(每个提案先写6页文档,会议先15分钟默读文档再讨论)也是强调用文字统一思想的有力例子。文字强迫思考周全,也让沟通基于书面更精确。这些做法都是为了破解沟通含糊和不到位的问题。
组织文化:技术之外,很多公司注重培养一种开放沟通的文化,鼓励工程师主动跨团队联系、分享经验。这可以说是建立“统一语言”的文化基础。当不同团队有共同的价值观和术语,合作起来障碍少。很多大厂内部有工程师社区、黑客松、技术论坛,这无形中增进了人员间联系和理解。当需要协作时,就不至于像巴别塔那样互不认识、各说各话。
布鲁克斯的洞见之一是:沟通开销随团队规模急剧上升,而许多组织低估了这一点。现代项目管理开始重视“沟通成本”的量化,比如用团队热图、协作网络分析等方法来评估沟通效率。某些工具甚至可分析代码库中的协作关系,提示哪些团队经常改动同一模块,需要加强联系。这些技术手段帮助识别潜在的“沟通断层”,有助于组织调整。比如发现模块X被三个团队频繁修改但彼此没有定期sync,那么就安排接口人或合并团队。
可以说,巴别塔失败的场景每天都可能在大型软件项目中上演——如果沟通不到位,语言不统一,就会浪费大量人力物力。布鲁克斯当年痛陈这一点,如今我们有更多方法避免悲剧,但前提是始终绷紧沟通这根弦。正如有句业界玩笑:“软件项目90%的问题都是沟通问题,剩下的10%是我们还没意识到是沟通问题。” 夸张但发人深省。
尤其在多地域、多文化的团队协作中(比如跨国分布式开发),语言和文化差异就真的是现代巴别塔难题。解决方案除了制定共同语言(如统一英语沟通、代码注释)外,还要投入更多沟通缓冲时间,进行文化融合活动。这些额外努力都是沟通成本,但回报是避免低效和冲突。从管理角度看,这些投入是必须的,不应吝惜,否则就要付出项目延期甚至失败的代价。
总而言之,沟通和组织架构在软件工程中的地位,布鲁克斯已经讲得非常透彻。现代的工程理论和管理实践不断验证他的观点。我们虽有更先进的工具和方法,但仍需警惕“塔未成、人心散”的情况。技术越进步,协作的规模和复杂度越大,沟通越成为瓶颈。唯有像建造巴别塔教训的反面那样——制定共同语言,保持信息畅通,组织有序分工——软件这座“通天塔”才能真正建成而不崩毁。
第八章 胸有成竹 (Calling the Shot)
估计与数据:掌握进度的艺术
“胸有成竹”一章讨论了软件项目中预估工期和衡量进展的问题。标题取自射击运动中的术语 Calling the Shot,意为射手在子弹击中靶子前就能判断自己这一枪的命中情况,比喻项目经理能够提前判断项目走势。布鲁克斯认识到,当年很多软件项目的延期并非突然发生,而是因为经理人缺乏手段量化地跟踪工作进展、预测结局,导致问题积累到最后集中爆发。
本章提供了一组真实项目数据来说明典型软件工程的进度规律,包括Portman的数据、Aron的数据、Harr的数据、OS/360的数据、Corbató的数据等。这些数据涵盖了不同机构的大项目进度分布。布鲁克斯借此寻找共同模式,以便项目经理在自己项目中应用。
例如,Portman的数据展示了某大型项目各阶段实际耗时与预计值对比;Harr的数据给出了若干类似项目的人月消耗曲线;Corbató的数据则是MIT的一个系统开发效率度量。OS/360自身的数据更是宝贵的实例。通过对比,他发现许多项目共有的特性:
- 实际进度拖后:几乎所有案例中,初期进度看似正常,到了后期系统测试阶段都远超计划。正如他在第2章经验法则里提过,大多数项目在系统集成之前都还能跟上计划,一到整体测试就延宕。数据验证了这一点。比如Aron的数据或许显示编码阶段达到里程碑,但最终交付依然推迟。
- 人月效率差异:不同项目的单位人月产出差异很大,有的团队一年一个人完成1000行有效代码,有的5000行。这和第三章提到的个人能力差异契合。因此,历史数据必须考虑团队具体情况才能套用。
- 投人成本曲线:Harr的数据或者别的模型显示,当人力投入超过某个点后,产出率不增反降(通信开销抵消收益)。这印证了人月不可线性替换理论。
基于这些观察,布鲁克斯强调项目经理要建立自己组织的历史数据库,以便在新项目上做更准确的估计。他建议跟踪每个项目的投入、输出和时间,积累经验规律。同时在项目进行中记录偏差,及时调整估计。
靶心与里程碑:掌控项目过程
如何避免最后才发现跑偏?布鲁克斯倡导设定里程碑并严格监控。但是他不满意于传统里程碑往往**“不切实际”:比如一个里程碑是“设计完成”,但设计概念模糊、完成度难判断,结果名义完成却漏洞百出。布鲁克斯要求里程碑必须客观可验证**。理想的里程碑有明确交付物或指标,如“通过所有单元测试”的测试里程碑就比“代码完成”更实在。指出,不准确的里程碑会误导团队,带来灾难性士气打击。
为掌控进度,他提出一些具体方法:
分阶段交付:将项目划分多个里程碑节点,每个节点产出一个可运行或可验证的部分产品。这与现在迭代开发概念类似。这样能及早暴露问题,而不是最后才发现无法接受的性能或缺陷。
充分的系统测试时间:在计划中加入缓冲。第2章给的1/2时间用于测试经验就是一个依据。布鲁克斯反复提醒,为系统测试安排不足时间几乎必然灾难。因此进度表中要留富余,且测试阶段绝不能再压缩。
独立测试小组:第3章末尾有提到Baker的文章,建议专门的测试队伍。布鲁克斯赞同建立独立测试组,在项目开发并行进行。这些测试专家的进度也能作为里程碑,如某日期前完成某百分比的测试用例运行。这可以提供额外监控视角,因为开发自测不可靠。
状态报告真实:项目经理应该营造氛围让工程师报告真实状态,不隐瞒延期风险。很多失败项目里,基层工程师早已知道进度不可能按期,但层层上报时被过分乐观掩盖。布鲁克斯崇尚“Bad News Early”原则:坏消息越早说越好,可以调整。要避免顾客和高层在发布日期才第一次听说延期。
使用度量:度量项目进展的指标可以有:已完成模块数、通过测试用例数、已实现功能点比例等。度量帮助客观跟踪,而不靠主观感觉。布鲁克斯在第16章里谈到缺陷率、生产率图表等。这些都应该在项目管理中使用。今天我们常用Burn-down Chart(燃尽图)等,就是度量思路的体现。
Calling the Shot的精髓在于:项目经理应该像神枪手一样,在项目过程中就“看见子弹弹道”,预知命中与否。不要等到最后验收那一刻才知道自己偏离靶心。布鲁克斯以丰富数据和经验规则,帮经理们校准自己的直觉。只要对照历史和正在发生的事实,一个经验老到的经理是可以提前判断项目健康度的——这正是“胸有成竹”的来源。
现代视角:量化管理与敏捷度量
当代软件项目管理在进度度量和预测方面有了更多工具和方法。布鲁克斯当年的理念在敏捷和DevOps中继续发扬光大:
首先,敏捷开发本质上就是把项目切成许多短周期可交付的里程碑(迭代),并通过燃尽图、速度等指标持续度量进展。Scrum中的燃尽图就像一个动态靶盘,每天可以看剩余工作量的斜率,预测能否按时完成。团队的速度(每迭代完成故事点)积累起来,可以用于未来估算,这类似布鲁克斯提的历史数据经验法。敏捷还有每日站会,确保问题早发现、早汇报。可以说敏捷使“Bad News Early”成为一种制度化。
其次,看板方法提供了直观的进度可视化管理。WIP(在制品)限制、循环时间等度量可以帮助发现瓶颈。当看板上某任务停滞过久,或累计流图显示队列堆积,管理者便能提前干预。这比传统甘特图更灵敏,是另一种“Calling the Shot”。Lean Kanban强调Lead Time的稳定,通过监控Lead Time的变化可以预测交付能力是否异常。比如突然某阶段Lead Time延长,则意味着潜在问题。
度量指标方面,现在的项目可以自动收集大量数据:代码提交频率、构建通过率、测试覆盖率、缺陷修复率等等。工程团队常用的DORA指标(部署频率、变更前置时间、变更失败率、恢复时间)就是一套衡量软件交付性能的指标,可以作为进度健康度的参考。例如变更前置时间反映从开发到部署所需时间,如果这指标逐步上升,表示流程有变慢趋势,项目可能会延期,则需要查找原因(技术债、流程问题)。这些数据驱动的管理方式与布鲁克斯倡导的用生产率图表等度量非常契合,只是手段更丰富了。
风险Burn-down也是现代项目管理一个概念。Scrum of Scrums里会跟踪各团队的风险清单,如果一段时间风险项不减少,说明在消耗缓冲,很可能冲击里程碑。甚至一些组织会给每个项目计算风险指数,根据过去成功失败项目的特征参数来机器学习评估当前项目成功概率,作为预警支持。这是高科技版的“Calling the Shot”。
持续集成/持续交付使项目总有一个可以运行的产品版本,这和布鲁克斯强调阶段交付的思路一致。通过CI结果,我们可以量化进展,比如每天通过多少测试、完成功能多少。CI系统fail了就立即修复,不积压问题。这样不会出现最后集成测试一发不可收拾的状况,因为集成在每天都发生,问题每天都解决一点。DevOps强调Shift-Left(左移)测试,就是把后期活动前移,这也正是布鲁克斯提醒的不要把所有问题留到后期。
预测交付日期的技术也比过去进步,例如蒙特卡罗模拟可以基于过去完成用户故事的速率分布模拟未来可能完成量,从而给出较可靠的完成日期分布。敏捷团队可用这样的方法而非拍脑袋给承诺。这等于用统计“看见未来子弹的落点范围”。布鲁克斯当年靠经验直觉,如今我们可以借助计算机算力更科学地直觉。
然而,即便有这么多工具,真正做到“胸有成竹”仍需要经验和判断。很多度量如果不了解上下文会误导。比如燃尽图下滑停滞也许不是团队无所事事,而是外部依赖阻塞。AI可以报出指标异常,但项目经理要通过沟通了解背后原因,然后做决策。布鲁克斯本身也是强调数据要结合人的智慧使用。他在本章提供数据不是要盲信数据,而是积累直觉:“别人才智使你免于再犯错”。
敏捷有个概念**“信息径流”(Information Radiator),指用可视板、图表让重要信息径流出来给团队。这符合布鲁克斯主张的透明共享文化。但还有另一面,许多组织因KPI压力而隐藏坏消息或粉饰指标,这与布鲁克斯强调的诚实汇报背道而驰。现代管理者必须营造安全氛围**,让团队敢于报告风险,不因进度落后被惩罚,而是一起解决。这也是DevOps文化里“拥抱失败”的内涵。
在更宏观的层次,软件估算依旧是老大难,不精确性正是No Silver Bullet一部分。COCOMO模型、Function Point等方式曾尝试用更精细的数学来估计,但实际使用有限,因影响因子太多。现在流行的是敏捷估算,用相对度量(故事点)和小批量反馈(每迭代校准速度)逐渐逼近整体工期。这种试错纠偏机制倒是非常符合人类认知,不必一开始算准而是在过程中不断修正“瞄准点”。某种意义上,比起瀑布时期,敏捷承认估算的不确定性,把“校枪”融入开发过程,所以更容易“中靶”。
总的说来,布鲁克斯这章的思想在当代项目管理被广泛印证和拓展。无论Scrum Master每日看燃尽图,还是高层用OKR跟踪进展,其目的都是像射手一样及时矫正瞄准。我们甚至可以说,本章是敏捷思想的先声之一——快速反馈、数据驱动、自我校正。这一切是为了让软件项目走在计划轨道上,不偏离目标太远,以至无可挽回。
最后值得一提的是,现代产品开发除了进度外还强调价值交付。可能进度都跟上了但交付的是无用功能,那也是“打靶不中”。因此“Calling the Shot”现在有双重含义:不仅要按时交付,而且要交付正确的东西。为此,Scrum引入“验收标准”确保达成预期价值。布鲁克斯在这里未讨论产品需求价值部分(他的书聚焦工程管理),但现代项目经理必须两手抓:进度对、方向也对。
结合这些,我们看到,成功的软件项目经理必须像个神枪手兼军师:一手拿度量仪表板,掌握团队节奏;一手握调整方向盘,修正偏差指挥资源。半个世纪过去,这个比喻依然生动。而布鲁克斯以深厚的经验告诉我们:虽然没有银弹,但数据加经验可以让我们更接近目标。今天我们拥有前所未有的丰富项目数据和分析手段,理应比前人更“胸有成竹”。只要谨记布鲁克斯的教诲——客观、诚实、持续跟踪并预留余量,我们也能做到发发命中,驯服软件这个常被戏称为“不定时炸弹”的东西。
第九章 削足适履 (Ten Pounds in a Five-Pound Sack)
软件的空间维度:内存与功能的权衡
“削足适履”这一成语比喻为了迎合某种条件而牺牲或改变本质。布鲁克斯用它来讨论软件的空间(内存)约束和功能复杂度之间的矛盾。简单来说,本章关注如何在有限的计算机存储空间内塞下预期的功能——就像把十磅的东西装进五磅重的口袋。在1970年代,硬件资源(特别是内存)非常有限,软件常常不得不精打细算地使用空间。因此开发者面临一个削足适履的抉择:是削减功能以适应内存,还是设法优化程序让其“瘦身”?
布鲁克斯指出,内存空间在当时是一种重要的成本。他给出了一个比例概念:程序空间=成本。一个软件占用内存越大,通常意味着需要更昂贵的硬件或导致性能下降。因此在设计时要像控制其他成本一样控制空间。他甚至将“作为成本的程序空间”列为小节标题。
他介绍了项目中常用的空间度量:比如字节数、存储单元数等。并强调每添加一个功能都会消耗空间,有时额外的空间消耗与功能价值不成比例。因此要把握取舍。在OS/360开发中,他就曾遇到必须裁剪功能以确保系统能运行在预定内存配置上的情况(如那个26字节闰年处理例子就是反思)。
本章核心之一是规模控制:软件的规模(主要指代码和数据量)如果无限膨胀,不但会挤爆内存,还会增大开发和维护复杂度,导致进度问题。因此必须主动控制需求和实现的规模。他类比建筑中,为了预算常要删减一些可有可无的设计。同理,软件经理应敢于砍掉那些锦上添花的特性以保证核心系统可行。
布鲁克斯提出一个有趣的观点:“数据的表现形式是编程的根本”。这句金句意思是,如何表示和组织数据对程序效率至关重要,也往往决定了程序的空间占用和性能表现。他举例说,许多突破性的优化来自于数据结构或表的重新表达。如果给他看一个程序的数据组织,他不一定需要看流程图就能明白程序结构。这说明,巧妙的数据表示可以让程序功能得以紧凑实现,从而在有限空间里容纳更多逻辑。这与后来计算机科学强调算法和数据结构的观念一致。正如本节标题强调的,优秀程序背后往往是对数据的精炼表示。
布鲁克斯在这一章里赞美了一些密集编码技巧:例如一个年轻人通过编写“解释器的解释器”大幅减少控制台解释器的空间占用;Digitek Fortran编译器通过高度密集的自我表达几乎不需要外部存储。这些例子体现出程序员为了节省内存各显神通,用非常规方法换取空间效率。
当然,这些密集方法有时牺牲了部分时间效率,但常常总体得益,因为减少I/O或无需扩展存储。布鲁克斯指出,那位解释器的解释器虽然每次执行略慢,但由于减少了巨量I/O,反而速度提升十倍。这体现了空间-时间权衡的重要性:为给定功能,更多空间通常换来更快速度,反之省空间可能损失速度。项目经理要懂得这种权衡,并根据实际需求(对速度还是空间更敏感)做决定。
空间技巧与模块化
布鲁克斯总结出两种空间技巧来帮助团队:
- 培训与技能:确保程序员掌握低级优化和高效语言。新语言或机器需要培训,以便工程师知道如何写出省空间代码。同时,组织应鼓励经验分享和奖励巧妙的空间优化,让大家重视这件事。
- 公共库:开发一套常用例程库(比如队列、排序等)并提供两个版本:一个追求速度,一个追求短小。这样每个项目可按需求选择快或省空间版本,不用重复造轮子。共享库的开发最好与系统设计并行进行,成为基础设施。这个思路在今天就是标准库或公共模块,减少冗余实现且可高度优化,使整体软件更紧凑高效。
接下来,布鲁克斯再次强调了反思和重构对空间优化的作用。在往往“无空间可挤”的困境下,程序员必须“挣脱自己的代码”,跳出成见重新审视数据和算法,仔细思考常能找到好的结果。**“实际上,数据的表现形式是编程的根本。”**通过这个结论,他呼吁开发者在遇到瓶颈时,应回到根本,思考有没有更优雅的数据结构或者算法可以大幅节省资源,而不是拘泥于已有代码上微调。
在这一章里,“十磅塞进五磅袋”隐含着需求 vs. 资源的永恒冲突。布鲁克斯的态度既现实又灵活:一方面,可以通过精巧设计、优化实现,把看似过多的功能装进有限资源(提高“袋子”利用率);另一方面,必须敢于舍弃不必要的东西,不要硬塞以致袋子胀裂(删减需求)。二者并举才能成功。
现代视角:性能优化与资源冗余时代
布鲁克斯当年对内存的斤斤计较,放在今天硬件资源充裕的环境下似乎没那么尖锐。但其思想精髓在于优化和取舍在任何资源维度上都适用。如今主要焦点从内存转向性能、能效和维护复杂度上,但“十磅vs五磅”的抉择依然存在。
现代系统的内存相对富余,但新的维度如移动设备、IoT设备仍有严苛内存/电量限制,需要类似精打细算。比如手机App开发者会为减小APP尺寸和内存占用而绞尽脑汁——他们非常能理解布鲁克斯的箴言。IoT固件开发甚至比60年代更近似当年,需要将功能塞进极小闪存中。布鲁克斯的数据结构优化经验在嵌入式领域被奉为圭臬:用bit-field、紧凑表等技巧节省每一字节。
在一般应用中,内存不再是头号瓶颈,但CPU计算量和响应时间成为关键。于是算法和数据结构的重要性继续凸显。布鲁克斯那句名言“数据表示是编程的根本”在现代解读为:选对数据结构和算法往往决定了程序性能上限。这在算法工程、LeetCode文化等都有体现。Google等公司很注重工程师的算法功底,就是因为在大规模系统里,一个不好数据结构会耗费数倍资源和成本。
现代面临的新挑战是扩展性,即当用户增加系统能否线性扩展。这里也涉及“十磅vs五磅”:如果单机支持5磅,需求来了10磅,就要扩展多机。但要实现线性扩展,程序必须设计简洁而可分布,否则十磅可能需要二十磅资源。微服务和云计算常以资源冗余换规模,这相当于五磅袋子准备足够多,但过多资源投入也是成本。所以云厂商和大公司会高度优化关键服务的性能,减少每请求资源消耗,因为那会乘以亿万规模。近年Netflix、Google不断优化RPC框架、存储引擎的效率,其实与布鲁克斯当年改良OS/360的目标相似:在有限资源基础上服务更多功能或用户。
空间-时间权衡在今天尤其显著。硬件廉价让人们倾向用空间换时间:比如用缓存、冗余存储提升速度。但过度则资源浪费。一个教训是早期一些JS前端框架为了性能将数据重复存储数份,后来发现内存占用飙升,移动设备吃不消。现在的Rust等系统编程语言,重新强调内存控制和零成本抽象,就是对资源精打细算思想的回归,只不过目标换成效率和安全。Rust开发者常会说:选用正确的数据结构(如用Vec还是HashMap)对性能和内存有重大影响。这与布鲁克斯如出一辙,只是背景变化。
信息隐藏使模块彼此隔离实现,但是也可能导致数据冗余和性能损耗,要平衡。微服务架构下,每服务独立数据库,跨服务需要通过网络通信,这牺牲了一些效率以换系统可扩展。如果性能压力大,就得考虑合并服务或在服务间引入缓存机制(其实是破一点信息隐藏)。布鲁克斯“精炼概念”的理念在这里引导我们:尽量设计统一的抽象避免重复数据转换,让数据以最具表现力的形式在系统中流通。
进化式架构理念也跟“削足适履”类似:开始刻意省略某些特性(瘦身),后续看需要再加,不然不加。这对应敏捷的YAGNI原则(You Ain't Gonna Need It),避免因为预留太多扩展而臃肿。布鲁克斯本章强调砍掉不必要功能,就是YAGNI的先声。
如今的技术栈高度抽象、库丰富,一些程序员忽视了底层消耗。结果应用无端臃肿——比如Electron应用内置整个浏览器导致简单应用几百MB内存。对此业内开始反思,出现**“瘦客户端”**回潮,用Rust/Go重写高效本地应用替代Electron。可以说,布鲁克斯的“十磅五磅”警钟再次敲响,因为不顾资源的堆砌会带来用户体验变差、成本上升。Chrome浏览器曾被戏称为“内存吞噬者”,Google也在持续优化Chrome的内存占用以提升移动体验。
一个有趣领域是竞赛级优化:顶尖程序员在极限条件下优化程序,如ACM竞赛或IOI。这些选手深谙数据结构与算法调整之道,可在Given Memory Limit内求最大性能。这几乎就是“Ten Pounds in a Five-Pound Sack”的竞技实践。
不可忽视的是,现代大多数应用的资源瓶颈常常不在单机内存,而在网络带宽、I/O等。比如视频流服务,如何在有限带宽下提供高质量?通过更高效编码(类似压缩数据表示)和CDN等手段。又如区块链智能合约,有严格的Gas限制(执行步数限制),开发者必须优化算法在Gas预算内完成,这是另一种“削足适履”。
**能效(Green Computing)**也成为考虑因素。在数据中心里,省一份算力就省电、省钱还有益环保。谷歌和FB都有团队致力于把软件优化更高能效。这延续了布鲁克斯对于“成本”的关注,只不过成本从内存硬件变成能源账单。
综合来看,虽然普通开发者不再需要用汇编挤每个字节,但优秀架构师依然需要具备优化意识。面对业务需求猛增或成本压力,他们要懂得何时**“削功能适资源”(降级服务、阉割次要功能),何时“巧优化以兼顾”**(改进算法压缩数据)。像当年IBM那样严控OS/360在规定内存内运行的场景在某些Embedded和Realtime系统依然每天发生。
最后值得注意,布鲁克斯倡导的培训与技术共享今天也体现在各种工程师培训计划、内部TechTalk中。培养开发者写高效代码,掌握性能调优工具,是各大公司确保软件不胡乱耗资源的举措。可以看到Google有专门性能小组指导团队;Mozilla对Rust社区推广内存安全同时不牺牲性能这些都是类似努力。
结论:本章思想虽然出于资源匮乏年代,但节俭与精巧的理念在现代有不同映射。工程趋势往往在“资源浪费 vs. 精益求精”之间摆荡,当浪费过度,精益思想便重回中心。软件如生活,富足时可以奢侈些,但终究会回到效率。布鲁克斯的教诲让我们始终记得去寻求**“更聪明而不是更臃肿”**的解决方案。这不仅关乎性能,也关乎软件的优雅与可持续。正如他所说:“分析实际,仔细思考数据,就能得到非常好的结果”。在今天的大数据、AI喧嚣中,这种冷静分析和精心设计的工匠精神尤其可贵。
第十章 提纲挈领 (The Documentary Hypothesis)
关键文档:项目管理的支柱
“提纲挈领”一章扩展了第六章关于文档的讨论,从更广泛的项目管理视角论述文档体系的重要性。标题中的“假说”是指:在大量项目文书中,少数关键文档起到了纲举目张的作用,每项管理工作都围绕它们运转。这个假说强调文档对项目管理的核心地位。
布鲁克斯先借鉴其他行业来说明文档对工程的重要。比如制造计算机硬件,需要目标说明、技术规格、进度表、预算、组织架构图、空间分配、市场预测、价格等关键文档。这些文档像车之两轮,缺一不可。管理者用它们做决策、跟踪进展。如果没有文档,项目会陷入混乱。
他把这些文档比作支点:项目管理各项工作都以它们为中心。例如目标说明文档支撑着需求管理;进度表支撑着时间管理;组织架构图支撑着人员分工。文档本身成为管理工具,不仅仅记录信息,还促使团队在各阶段做出明确决定(如制定预算迫使做出技术选择)。这一论点堪称将文档提升到了战略高度:不是为文档而文档,而是通过编写文档来推动思考和沟通。
布鲁克斯列举了软件项目常见的文档种类:
- 外部规格说明(用户手册/性能规格):最先产生,最后完成。定义系统要实现的功能、性能。
- 目标/需求文档:定义项目需要满足的目标、资源、约束、优先级。
- 进度表:划分里程碑和时间计划。
- 预算:资金和人力预算,不只是约束,也是重要工具,因为预算会促使技术决策明确化。
- 组织架构图:定义团队结构。
- 工作空间分配:在硬件项目中重要,软件里或体现为计算资源分配。
- 报价、预测、价格:这三者连动决定商业成功。软件中可能体现为成本估算、市场预期、定价策略。
对于软件项目,这些可以类比为:需求文档(目标)、架构/设计规格(技术说明)、项目计划(进度)、成本估算(预算)、团队分工文档(组织架构)、环境配置文档(空间/资源分配)、商业计划书等。
布鲁克斯强调,新上任的项目经理往往讨厌写这些文档,觉得繁琐分心。但随着经验增长,他会发现这些文档各部分其实浓缩了管理工作的要点。在编写它们时,项目中各种模糊或有争议的问题必须弄清,否则文档写不出来。文档准备过程是集中思考和澄清讨论的时刻。如果不这样做,项目就可能无休止混乱。
文档还有跟踪维护作用:它可以作为清单检查、状态控制、汇报数据基础。比如版本发布检查文档,看哪些任务未完成;状态报告依据进度文档填制;依赖关系通过文档更新监视。总之,文档维护机制成为项目监督和预警的手段,如第6章所述。
学习他山之石,设计文档体系
为指导软件项目如何建立文档体系,布鲁克斯建议先借鉴其他学科的文档实践。他引用硬件产品开发的文档经验,然后试图归纳通用模式。文档体系由技术、组织、行业传统等因素决定。他说,对于刚从技术人员升任经理的人,文档工作往往是厌烦且不理解的。但他相信,跨学科看,我们能总结出项目必须准备的几类文档,这些就是管理的关键枢纽。
具体对软件,他接下来讨论了计算机产品的文档(上面提及)、大学科系的文档(或许指大学计算机系项目? PDF未显示细节)、软件项目的文档。从段落分布猜测,**“软件项目的文档”**部分可能讲需要准备哪些。如果结合1970年代背景,一般包括:
- 软件需求规格(SRS)
- 软件设计说明(SDS)
- 测试计划和报告
- 用户手册
- 安装指南
- 项目计划与进度报告
- 问题跟踪报告
- 变更日志
以及最后一节讨论正式文档的意义。布鲁克斯问:“为什么要有正式的文档?”。他的回答在第6章有伏笔:因为没有文档的项目会陷入无穷扯皮和混乱。正式文档起到共识和基线的作用,每个人据此开展工作,不至于各执一词。特别是在人员交接和维护阶段,文档更是不可或缺,否则新手没法接管。后文(第15章)也谈及自文档化代码的问题,但对于全局设计、接口合同、决策记录等,离不开正式书面资料。
现代视角:文档作为协作载体
在敏捷开发盛行的今天,文档的重要性没有降低,只是形态变化。敏捷宣言有条“工作软件高于详尽文档”,有人误解为敏捷不需要文档。其实真正含义是:简洁、关键的文档仍然很重要,只是不崇尚官僚繁琐的文档。现代的DocumentationOps理念更是证明文档和代码一样需要工程实践支持。
布鲁克斯的**“关键文档枢纽”**假说依然成立。当今项目管理也围绕几类核心文档/Artifact进行:
- 产出文档:需求规范、架构设计文档、测试计划等,依然是评审、开发依据的核心。即使以User Story形式,还是要有聚合视图来检查覆盖。Scrum产品Backlog就相当于需求文档数据库。
- 计划和进度:敏捷用Release Plan、Sprint Plan等,体现为Burndown Chart、Roadmap的形式。它们对齐着布鲁克斯的进度表文件。
- 组织职责:敏捷团队虽扁平,但RACI(责任矩阵)等文档在大规模协作时有用。许多项目Wiki记录谁负责哪部分模块,这实际就是组织图/责任分配文件。
- 预算/资源:在云环境,每月的Cloud Bill、资源Quota配置可以视为“预算文档”。Infra团队配置Terraform脚本定义资源,也是一种文档形式。PMO仍会跟踪成本。
- 风险和决策:现在项目喜欢使用决策记录(ADR)和风险清单。这些对应布鲁克斯所说管理决策澄清文档。
- 发布与运维:今天可能多了Deployment Playbook、OpsRunbook等关键文档。
现代敏捷实践引入**“Definition of Done”**文档,它规定了故事完成的标准。它成了团队质量和验收的共同协议,也是一种关键文档,指导开发和测试工作。
另外,监管要求在某些领域使文档更关键(医疗、金融软件需合规文档)。DevOps则强调Everything as Code,把配置、基础设施都文档化(代码即文档)。CI流水线配置(Jenkinsfile等)就是版本化的过程文档。
布鲁克斯提出写文档促决策在敏捷有佐证:亚马逊6页文档原则就是逼着提案人深思熟虑。Google提倡RFC(Request for Comment)文档流程,大项目前先写设计文档征求团队意见,也印证了“写文档理思路”的力量。
文档共享在今天非常便利,Wiki/Sharepoint/Jira形成知识库。Confluence按模板组织多个“枢纽文档”(需求、设计、测试),并可相互引用,建立关联网络,比以前孤立手册更强大。关键是要设计好信息架构(就像布鲁克斯说事先设计好手册结构)。否则Wiki容易乱成一锅粥,和没人维护的老手册无异。
敏捷团队注重轻量文档但文档实时更新。CI/CD pipeline often integrate doc generation (e.g. code documentation, API docs). Kubernetes这样复杂系统,官方文档和版本同步,是确保用户和开发者都对系统理解一致的关键。其实相当于项目工作手册公开化。
最近兴起Docs as Code说明将文档纳入版本管理,与代码一起评审发布。布鲁克斯假如在世,想必欣慰现代工具让文档维护如此高效。他强调文档版本控制在当时只能人工协调,“Wiki+Git”将它自动化。
文档Ops也强调文档枢纽:比如给项目设README(概览)、CONTRIBUTING(流程)、CODE_OF_CONDUCT(文化)、CHANGELOG(变更),这些核心文档帮助参与者快速理解项目关键点,符合枢纽文档概念。
总的来说,现代趋势没有否认文档,反而文档复兴。Stack Overflow等调查显示优秀团队往往文档也优秀。开源项目凭文档吸引贡献者,企业通过知识库积累组织智慧。这些印证布鲁克斯所说文档的价值。
当然,也有反思:有些敏捷团队文档太少导致知识随人走。DevOps催生的Runbook概念又把知识显性化。SRE(Site Reliability Engineering)要求所有运维步骤都记录于Playbook,这其实也是文档假说在运维领域的体现:当故障发生,SRE按Runbook处理,相当于有了提纲挈领的应对手册,否则每次靠经验临场发挥风险大。
布鲁克斯以假说名之,是因为在70年代软件文档还没标准化。而今这已不是假说,而是常识:文档工作是项目管理不可或缺的一环。学界和业界都有文档标准(IEEE 830需求规格标准等),也证明大家认可关键文档类型。
然而,依然有项目忽视文档,只靠口头或IM沟通,这常导致重复讨论、知识遗失。布鲁克斯书半世纪后,很多团队才痛悟要补文档债,可见人性对文档工作的厌恶仍在,但越成熟的团队越克服它。
AI技术开始应用于文档,如根据代码自动生成文档草稿,用AI assist写架构决策记录、自动摘要会议纪要等。虽无法完全代替人撰写关键决策,但能减轻写作负担,让工程师更愿保持文档更新。
最后值得一提,文档也需管理。Scrum有Sprint Retrospective,会检查是否文档过期或需要更新。DevOps有DocOps pipeline,每次合入代码检查是否需要更新文档(通过commit tag触发)。这些确保文档不变成二等公民。
总结:布鲁克斯文档假说在今天变为文档实践原则:Identify your key docs, maintain them well, use them to drive consensus and track progress. 现代工具和文化都在尽力践行这一点。
第十一章 未雨绸缪 (Plan to Throw One Away)
第一次总是作废:原型的重要性
“未雨绸缪”是布鲁克斯极为著名的观点——“Plan to throw one away; you will, anyhow.”,中文即**“计划扔掉一个;反正你无论如何会那么做。”。布鲁克斯断言:第一个系统原型几乎注定不够完善,应当及早舍弃,在此基础上设计出真正成功的第二版本。他主张将原型的报废纳入计划**,而不是事后才不得不推倒重来。
这个论断当年非常惊世骇俗,但也影响深远。在第11章开头,他甚至引用了一幅著名的照片:倒塌的塔科马海峡大桥,作为劈头警示。那座桥因为设计缺陷在强风中塌毁,后来重建时吸取教训成功。这类工程案例暗示:第一个成品失败了,第二次才做好。布鲁克斯将此类比软件——与其等第一个版本失败,不如提前做好“要有废品”的心理和计划准备。
他强烈建议:“为舍弃而计划。无论如何,你一定要这样做。”。也就是说,项目伊始就应该规划一次原型实现及其废弃过程。当时软件工程界过于追求“一步到位”的瀑布方法,而布鲁克斯强调试制原型才能发现隐藏的问题。他甚至认为不只是第11章提到的原型,第2章的进度法则、贯穿全书的经验都表明瀑布模型假设一次性完成是谬误。
他给出原型的几大作用:
- 验证需求:通过原型,让用户或开发者更明确需求的可行性和真需求。例如IBM在System/360开发早期做过“模拟器”原型,帮助理解新系统行为。
- 发现设计缺陷:纸上设计再周详,实践才能暴露错误和性能瓶颈。原型提供一个低成本试错机会。布鲁克斯在OS/360时吃过没有原型的亏,这里汲取经验。
- 培养实现经验:开发者在原型阶段积累了对新技术、新问题领域的理解,提高了随后正式开发的成功率。
- 心理准备:团队知道原型会扔掉,就不会对原型代码恋恋不舍,更愿重构或推翻不良部分。否则大家往往沉没成本心态,不愿放弃已经写出的代码,导致将缺陷继续带入成品。
在计划角度,他建议留出资源(时间和预算)专做原型,即**“试验性工厂”,然后扩大规模**。也就是先用一小部分人快速做个样品,再在那基础上扩展成正式产品。他强调这一点贯穿许多行业:造汽车先出概念车,造大桥先建模型吹风测试等等。同理软件应**“先扔掉一个”**。在第6章架构管理里,他也引用了Corbató的话:两次构建往往必要,比一上来完善好得多。
布鲁克斯特别指出,变化不变——需求永远在变,如果没有原型快速响应变化,瀑布那种静态计划就跟不上。因此原型与增量开发思想一致:拥抱变化。他说过唯一不变的是变化本身,这后来成为软件工程经典格言。
他还提出为变更制定计划系统和计划组织架构。Meaning: 组织结构也要适应原型->成品的流程,不能死板分工。这在第7章提及当架构团队写规格时实现人空闲,要灵活调整安排。团队应分阶段组织:原型期小团队敏捷,正式期扩充团队线性实施。这与后来的“Skunk Works”创新小组概念相似。
二次开发陷阱与银弹之争
当然,布鲁克斯清醒地认识到扔掉一次不是代价为零的。实际上很多项目经理怕浪费而不愿做原型。他强调,不做原型往往最终更浪费:因为最终产品会充满缺陷,需要花更多代价修补。不如一开始花较小成本试错一次,确保最终产品健壮。
值得注意的是,布鲁克斯在20年后的回顾中对“Plan to Throw One Away”做了修正:他认为原话“太过简单”。他意识到并非所有部分都要重写两次,更多时候增量迭代比完全舍弃更好。于是他在新版第19章提出**“增量开发模型更佳——渐进地精化”。即,与其全盘推倒,不如逐步完善原型直到满足质量。这是部分吸收了后来的敏捷思想。不过那已经超出第11章当时的论述。在1975年版中,他依然坚定建议做扔掉原型**。
“No Silver Bullet”论战中,有人把原型化当作银弹候选。布鲁克斯认为原型不能十倍提高生产率,但确能减小项目风险。原型化其实针对的是本质复杂性(需求理解等),不能省人月却能省痛苦。反对他的人如Harel、Boehm也探讨了原型的作用。后来的Spiral模型(1988年Boehm提出)把原型化融入开发周期,可视为布鲁克斯原型思想的具体实现。
现代视角:迭代开发与MVP
布鲁克斯“扔掉原型”的思想与现代软件工程的迭代增量开发、**MVP(最简可行产品)**高度一致,并被广泛证实有效。其实他预见的趋势正是敏捷。
当今普遍的Scrum方法,本质是构建-反馈-改进的循环,连续原型化。Scrum不称之为“扔掉”,而是在每次Sprint末交付可用增量,但不断演进,相当于频繁“把旧版扔掉换新版”。CI/CD流水线也是快速验证想法的机制。
MVP概念(Eric Ries等提)提出产品应先做最小可行原型上线试水,然后根据用户反馈调整。这和布鲁克斯讲的概念验证原型完全一致。许多创业团队成功经验是快速推出原型获得真需求,而失败例子常是闭门一年做完发现不符市场(验证了布鲁克斯那句第一个几乎肯定要废)。
Refactoring重构概念可看做微观层面的“Plan to throw away code”。极限编程的实践指出,不要害怕重写坏代码,通过持续重构,代码base不断淘汰原始实现换上更好实现。就像不断扔掉小块原型。Jettison old design and refine. 这显然跟布鲁克斯理念一致,只是敏捷把它纳入日常小步。
DevOps强调“Shift left”,把测试、集成等提前。这也是在说:早点搞原型试跑,总好过最后一次出问题。Auto-scaling architecture often built with initial simplified version, then improved after witnessing real traffic.
Microservices战略也体现先原型再完善。许多企业从单体应用切换到微服务,不是一蹴而就,而是拆一部分服务试点跑,然后逐步推广。期间可能“扔掉”一些错误的拆分方案。Netflix早年微服务转型中,有多次实验失败的服务架构,但他们都及时废弃,再重构直至OK。正是“Plan to throw one away”在架构层面的实践。
A/B 测试也是原型思想的延伸:同时发布两种实现,看哪个效果更好,不佳的那个就舍弃。大规模的 Web 开发通常使用功能开关将原型发布给子集用户,收集指标,然后决定保留还是放弃。可以说这是一种自动化的“如有必要就丢弃”的计划。
在开源领域,“尽早发布,频繁发布”的口号正是意味着不要等到完美,尽早推出初版(原型),再进行迭代。Linux 就是著名的例子,通过社区反馈不断迭代演进。
但需要指出一个区别:布鲁克斯说“扔掉一个”暗示第一个版本基本就是废稿。而敏捷试图通过持续改进尽量保留已有成果,而不是完全丢弃。这也符合他后来的修正观点,即增量精化更好。实践中我们很少彻底丢掉整个代码库(虽然架构转向或技术栈变更时偶尔会),更多时候是“部分丢弃”:我们舍弃糟糕的模块,但保留核心经验。
案例研究:Netscape 在 90 年代末进行了彻底重写(Mozilla),有效地丢弃了原始代码。这确实带来了更好的架构,但也让他们错过了关键的市场时机。许多人认为这是一个警示故事:如果处理不当,“一次性大丢弃”可能失败。更好的方式是在维护第一个系统的同时并行构建第二个系统。这正是当时发生的事:IE 趁 Netscape 从头重写时占领了市场。所以重点在于:做好“丢弃”的准备,但要控制其影响。
持续交付的讽刺之处在于它努力避免必须丢弃整个版本的问题,而是通过小幅增量发现问题。如果每个增量都通过验证,也许你永远不需要“完全丢弃”。但也可以说,如果最终设计与初版差异极大,初版其实是被逐步替代了——总体效果与“丢弃”相似。
一个方面是:人们容易对第一版代码产生情感依附,不愿丢弃(这与“第二系统效应”有所交集)。现代开发试图通过临时环境和频繁变更来避免情感依赖。或许因为变更成为日常事务,丢弃部分内容变得更容易。
在 UI/UX 设计中,原型设计是标准流程:设计师使用 Figma 或其他工具创建线框图、交互原型,供用户测试,然后定稿。这恰好实现了“先构建一个原型,有可能被丢弃”。实际上,往往要丢弃多个原型,才选定最终方案。
测试和质量保证也经常进行“试探性方案”或“试验项目”,用以探索新技术,这些方案在知识获取后通常被丢弃。
AI/ML 项目可能会先用小数据训练一个初步模型(原型),以评估可行性,如果没有前景就丢弃,否则再进入大规模训练。
有趣的是,现代管理几乎不再使用“丢弃一个”的说法,但其概念仍然存在,例如微服务团队中有说法是“先构建一个用来丢弃的”。所以这个理念显然仍然相关。
一个显著变化是:早期布鲁克斯说“10% 资源应该用于原型开发”(计划上)。而现在虽然不再明确分配,但在敏捷实践中,每个冲刺中约 10% 的时间用于试探性任务或技术债任务,这等同于构建/丢弃小型原型。
边缘情况:某些领域(如航空电子、医疗)历史上不允许“原型然后丢弃”,因为合规负担沉重。但即使在这些行业,也开始采用基于模型的设计和仿真原型。
AI 的使用:我们现在会大量模拟(如数字孪生),这些都是用来测试场景的原型,然后才真正实现。这正是多做一步以确保正确。
但要谨慎:正如他后来承认的,向管理层说“我们计划废掉第一版”很难被接受。许多人依然不采纳这个建议,结果吃了苦头。但迭代式方法给了我们一个更易接受的叙述方式:不是“浪费”,而是“渐进细化”。
银弹:有人曾认为生成式 AI 可能一次性生成完美软件(银弹?),但早期尝试表明它通常只是一个初稿,需要迭代——再次验证“丢弃一个”的理念仍然成立:AI 的初稿可能会被丢弃,改为更完善的版本。
所以,总结来说,“未雨绸缪”(原型)已完全融入现代方法论,虽然说法不同,但都体现为“敏捷迭代、持续改进、快速失败”。
无论如何,现代对“迭代”的接受,都是建立在布鲁克斯提出的“第一次尝试很难成功”的基础之上。明智的管理做法是将失败预期纳入计划中,从而掌控它,而不是被它击溃。
未雨绸缪:扔掉一个来练手
布鲁克斯在第十一章提出了软件工程中惊世骇俗但极具先见的观点:“Plan to throw one away; you will, anyhow.”,意即**“预先计划扔掉一个系统;反正你迟早都会这么做。”。他断言,首版系统几乎注定无法完善满足需求,因此项目管理应该预留一次推倒重来的机会**。与其到头来因为第一个版本问题重重而被迫重写,不如一开始就把原型试作和废弃纳入计划,从容地以第一次试错来换取最终成功的版本。
这一思想源于他在大型项目中的惨痛教训:OS/360由于没有原型验证,许多设计缺陷到后期才暴露,导致延期和性能问题。布鲁克斯痛感如果当初做一个可运行的试验系统来验证思想,再在此基础上增大规模,就能未雨绸缪地避免许多问题。因此他大声疾呼:在项目伊始就建立“试验性工厂”,做一个小规模原型,然后再扩大规模投入正式开发。
经典名句:“为舍弃而计划。无论如何,你一定要这样做。”布鲁克斯强烈建议经理人将原型的报废视作计划内事项,而非意外开销。这句名言后来被奉为软件工程至理。
为什么要主动“扔掉”第一个系统?布鲁克斯给出了多方面理由:
- 验证需求假设:书面需求往往不完整或有误解,通过原型让用户实际体验,可以发现哪些功能真正需要、哪些可以删减。原型是探索未知需求的实验田。
- 暴露设计缺陷:再周全的设计在现实运行中都会碰到意料之外的问题(性能瓶颈、边界条件等)。早期原型提供一个低成本环境来发现和纠正这些缺陷。唯一不变的是变化本身,项目过程中需求和环境总在变化,而原型让团队及时适应变化。
- 累积实现经验:开发原型的过程本身让团队熟悉了新技术和复杂领域,提高了第二次开发的效率和质量。第一次写的代码也许要丢弃,但开发者头脑中收获的理解将沉淀下来。
- 心理放下包袱:有了“反正这个要扔”的心理预期,工程师在原型阶段更勇于尝试创新方案,同时对原型代码不会过度留恋。一旦需要重构或推翻,大家心态上容易接受,不至于陷入“明知不好却舍不得换”的窘境。
布鲁克斯戏称这种原型就像塔科马海峡大桥的首次坍塌:那座桥因设计错误在强风中倒塌,重建的新桥才成功。他以此警示软件经理不要企图一次造就通天塔,而应允许第一次倒塌来换取宝贵教训。
在计划实践上,他建议为原型预留约十分之一的开发资源和时间。这一原型尽可能快速搭建,验证关键的架构和概念,然后果断舍弃。接着根据原型反馈重新制定更加靠谱的规格和计划,投入全力开发第二版。他形象地比喻:“一步到位”的瀑布模型是假设项目只经历一次实现过程,而且设计完美无需大改,这是基本谬误。正确的做法是**“前进两步,后退一步”**:在交付最终系统前,先做一步尝试(原型),然后退回来修正,再大踏步前进。
需要强调,计划扔掉原型并不意味浪费,相反是为了节约整体成本和时间。布鲁克斯指出,没有原型的项目,往往最后调试阶段返工巨大、甚至推倒重来,代价更高。与其被动推翻,不如主动规划好原型和重构的节奏,把风险控制在早期。他以自己的管理经验保证:“扔掉一个原型只会让你的成品来得更快、更好。”
布鲁克斯的“未雨绸缪”理念可以说是对当时流行的严格瀑布方法的一次冲击。20世纪70年代的软件工程偏好于预先详尽规划,然后一步实现,但布鲁克斯看到了这种方式难以应对软件的复杂多变。他的呼吁后来得到了极大的印证:迭代式增量开发成为业界主流做法之一。
现代敏捷开发、DevOps文化都渊源于此思想:强调快速迭代、不断试错。例如Scrum方法每个Sprint产出可用增量并根据反馈改进,其精神正是“扔掉部分上一个迭代的假设,持续改进”。又如创业圈奉行的MVP(最简可行产品)战略,也与布鲁克斯如出一辙:先开发一个核心功能的原型投放市场收集反馈,然后基于验证结果调整方向,哪怕推翻初始设想也在所不惜。敏捷实践者常说“尽早失败,尽早修正”(Fail Fast, Learn Fast),这正是对“Plan to Throw One Away”的精炼表述。
值得注意的是,布鲁克斯在20年后(1995年)的新版前言中对这句话进行了反思。他坦承“为舍弃而计划”过于简单粗率,容易被误解为必须完全废弃第一次实现。实际上,通过增量迭代的方法,可以在保留部分有效成果的同时逐步演进系统,而不一定要彻底抛弃初版。他修正道:“增量开发模型更佳——渐进地精化”。也就是说,比起真的把整个原型扔掉,现代更提倡持续重构、渐进改良,让系统随原型一起演化到最终形态。这与敏捷和DevOps的持续交付思想完全吻合。
然而,无论是“一次扔掉”还是“多次渐进”,核心宗旨没有变:不要指望第一个实现就是正确的。要么提前计划一次大的重构(扔掉重做),要么通过不断的小重构来迭代优化。总之,唯有主动地拥抱重来,才能避免被动地推倒。
在今天的实践中,我们随处可见布鲁克斯理念的身影:
- 迭代开发:主流软件项目都采用迭代方式,经过多个内部版本逐步完善。每个版本就像小原型,不断根据反馈调整。最终上线的产品可能与最初原型差异巨大,但正是通过多轮“丢弃原来的想法”才达到优化。
- 重构重写:开源社区和企业内,时常有大规模重构或重写项目的决策。这正是认识到初版架构已不适应,需要舍弃一部分/全部来重建。例如苹果用SwiftUI重构UIKit、Google多次推倒重做社交产品等,都是敢于承认第一个实现不理想而另起炉灶的体现。虽然有些案例(如Netscape浏览器重写)失败了,但更多时候,小心规划的重构让产品焕发新生。
- 实验特性:很多大型系统通过Feature Toggle发布实验性功能给部分用户测试,视结果决定保留还是移除。那被移除的功能代码就是计划内“扔掉”的原型。这种“试点-弃用”在持续交付环境中非常常见。
- 原型驱动设计:UX设计中早已标准化:先做线框图/点 click 原型→用户测试→推翻改进→再原型……直到设计成熟,再进入开发。同样地,AI模型开发中,也常训练一个粗模型验证思路,再决定是否花资源训练最终模型。第一版模型不理想就弃用方案,也是“扔掉原型”的策略。
增量开发与原型验证已被证明是降低软件风险、提高成功率的最佳途径之一。许多实践经验和数据支持布鲁克斯当年的洞见。现在的工程师几乎是本能地先写个“hello world”版来试验,然后不断迭代逼近目标。这种工作方式在半个世纪前由《人月神话》率先倡导,可谓高瞻远瞩。
综上,“未雨绸缪”就是要在天空放晴时就准备好雨伞。软件开发的雨伞,就是原型和迭代。我们必须接受这样一个现实:第一个产品版本只是一次练习。只有敢于推倒重来、持续磨炼,我们才能最终打造出令人满意的系统。这一思想从当年的大型机操作系统、一以贯之地影响到今天的移动互联网应用和人工智能服务,历久弥新。对每一位软件工程师与经理来说,布鲁克斯的忠告依然敲响警钟:**“请提前规划你的原型和重构,否则将来会迫使你这么做。”**未雨绸缪,方能胜券在握。