软件工程的最大难题

作者: 阮一峰

日期: 2021年5月10日

一、引言

大学有一门课程《软件工程》,研究如何组织和管理软件项目。

说实话,这门课不适合本科生,因为学生可能体会不到,课程到底要解决什么问题。只有亲身参与过大项目的开发,经历过大团队,才能感受为什么软件工程很重要,又很难做对。

软件开发有一个难题,叫做"扩展"(scaling),即怎样服务更多的用户。 你有10000个并发用户,跟你有10个并发用户,这是完全不同的概念,哪怕功能完全相同,背后的实现是完全不一样的。并发用户数上升一个数量级,软件就必须重构,大量问题随之产生。

大项目的技术难度高,管理难度更高,而且大团队的生产率往往很低,行动缓慢。 《软件工程》就是研究,如何扩展软件和团队,适应大项目的需要。

国外有很多专著,研究这个问题。前些日子,我读到一篇文章,推荐了两本书。第一本叫做《加速:构建和扩展高性能技术组织》,第二本叫做《规模:生物,城市和公司的普遍法则》。

我看了这两本书的介绍,觉得很有启发,下面就做一些摘录。

二、大项目的困境

一个典型的大型软件项目,开发过程通常是下面这样。

最开始的时候,它是一个小项目,开发人员就是两三个人,甚至可能只有一个人。产品比较简单,功能很有限。

第一版发布后,拿给客户使用,反响不错。客户要求的新功能,能够很快开发出来,Bug 修补也很快,因为早期客户往往可以与开发人员直接沟通,快速反馈。

公司于是决定投入更多人员,开发这个项目。团队慢慢变大了,软件开始变得复杂,开发速度逐渐变慢了,2.0 版花费的时间比预期要长一点。Bug 的修复难度开始增加。总之,新功能的开发日程变久了。

公司的自然反应是进一步扩充团队。但是更多的新成员其实会降低其他人的生产率,一个普遍现象是团队规模越大,每个人的平均生产率越低。

几年以后,代码逐渐老化,复杂性不断增加,项目开始停滞不前。某些极端的情况下,软件的维护成本变得非常高昂,并且几乎不可能进行更改。

最终,这个项目成为技术债务,等待被新项目替换。

三、为什么大项目的开发效率低?

大项目就像一头大象,让人望而生畏。可是一旦需要奔跑,大象就会步履蹒跚,被猎犬远远甩在后头。它快不起来的原因有两个。

(1)代码复杂度

随着代码量的增长,单个开发者想要理解整个代码库,变得越来越困难。如果团队超过五个人,每个人负责一个功能,那么单个人几乎不可能追踪系统的所有工作进度。

当你中途加入团队,整个项目是一个紧密耦合的大型系统,你又不理解系统的每一个工作细节。这时,你就不太敢修改以前的代码,因为不知道随之而来的全部影响。

你不真正理解系统,也就不会感到自己是代码的主人。你会很犹豫要不要重构,过时的代码开始累积,技术债务就这样出现了。长此以往,开发变得越来越不愉快和令人无法满意,最终导致人才流失。后面接手的新人,更难于重构那些遗留下来的代码。

(2)团队原因

随着团队成员的增加,交流成本开始指数式上升。如果整个团队有 n 个程序员,为了了解其他人的工作,你需要跟 n - 1 个人逐一交流(口头或者书面),那么整个团队的交流路径总数就是 n * (n - 1) / 2。这意味着,交流成本的增长速度是人员增长速度的平方,团队人数越多,协同的难度就越大。

大团队保持扁平化管理,也会越来越困难,必须拆分成较小的群体。这时,对等的交流会被自上而下的交流所取代。团队成员会感觉,自己从平等的利益相关者,转变为普通的工作人员,工作动机受到了影响,责任感和主人翁意识都会淡漠。

管理层为了解决问题,会尝试组建新团队和新的管理架构。但是,不管怎么做,大型组织都很难保持所有成员的积极参与。

四、解决方法:代码解耦

大项目的开发效率不高,把这个问题归咎于程序员的技术水平低和管理不善,都是没用的。 根本原因是软件规模的增长,必然使得代码和团队变得笨重。 除非很早就认识到问题的根据,采取缓解对策,否则前面描述的情况,迟早都会出现。

解决这个问题,要从代码和团队两方面着手。

代码层面的解决方法是,将软件解耦,拆分成组件或者模块,防止各个部分紧密地耦合在一起。每个组件和模块,都可以独立开发,通过公开的接口被其它部分调用。

这样的话,就大大减轻了开发者的负担,只需要负责自己的代码即可,不需要关心其他部分的实现。每个部分都可以单独重构,不担心影响到其他部分。

五、解决方法:团队解耦

除了代码解耦,团队层面也需要解耦,要把人员分开。

这可以参考互联网的架构。互联网是迄今为止最成功的大型软件解耦实例,它之所以能够扩展,是因为它由一个个独立的节点组成。为了防止节点之间互相依赖,各个节点都遵循以下规则。

  • 遵守公开的通信协议。
  • 不需要了解其它节点的内部实现,就可以与之通信。
  • 节点之间不直接共享状态,只通过通信交换数据。
  • 每个节点单独开发和部署。

大团队也应该遵循类似的原则,进行解耦。

  • 每个子团队都可以独立运作,不依赖外部人员。
  • 子团队内部的运作,不需要被外部知道。
  • 子团队之间的协调,应该按照公开的协议和规则,最好能够自动完成,避免私下协商。

六、团队解耦的注意点

团队解耦有一些注意点。

(1)子团队的人数不宜过多,每个子团队最好不要超过5个人。

(2)子团队应该是一个小型的全功能软件开发组织。

很多大团队按照人员角色分组,比如架构组、开发组、DBA 组、测试组、工程组等等,这是错误的。这样完全没有解耦,还是瀑布式流程,各组之间依然互相依赖,工作时必须与别组单独协商。而且,可能会产生利益冲突,比如,开发组希望尽快交付,而测试组希望多一点时间测试。

正确做法是按照软件的业务功能分组,每组负责一个全流程的软件大功能,设计、编码、测试、部署、支持等人员都在同一组。这样才能做到解耦,以及独立的交付和重构。每组内部使用什么工具、如何实现某个功能,都是自己决定,各组之间不需要共享内部细节,也不依赖别组的工作。

(3)大团队应该保障子团队的自主权,按照子团队提供的功能和商业价值,进行资源分配。

(4)软件架构师的角色很重要。

软件架构师的关注点,不应该是团队使用的工具和技术,而是各种服务与整个系统运行状况之间的协议和通信,保证代码和团队可以正确解耦。

(5)代码解耦和团队解耦的关系。

理想情况下,代码解耦与团队解耦保持一致,形成一对一的关系,一个子团队负责一个独立的模块。实际运作中,一个子团队负责几个模块也可以,但是一个模块不能由多个子团队来参与。

(6)通信(模块之间的、子团队之间的)尽量规范化,争取做到过程简单、文档充分,最好有规范的 API,这样无需任何人员交流,就能建立通信。

(完)

留言(34条)

感谢老师的文章。大学中学的理论知识比如《软件工程概论》在当时看来确实很难知道它具体的用处,后面工作了一段时间后发现有时候确实需要理论知识支撑才能走得更长远。

野蛮生长的情况下,多少能做到这个层次呢。

微服务:的确适合大项目。
大项目=》小团队=》维护好自己的接口

需要共同的服务中心、底层协议。

确实,以前我做的项目少没什么感受

随着项目越做越多,越做越大,我发现还是那句话

More is different

维持一个团队的成本已经非同一般的巨大

阮老师说的这些有深切体会啊,好的架构师一般只有编码的份,混子往往还能当你上级,你说气不。

1. 随着项目的增加,模块的增加。模块之间的调用链过长是不是也会有问题。
2. 出现问题溯源这个问题
3. 基础架构的耦合,比如和基础设施团队的协作,资源、运维、升级、灰度、部署、CI/CD、等团队的协作是不是会增加。

我感觉最大的核心在于软件的架构设计及拆分,但是最终的成效确是依赖于每个团队模块开放接口的完善程度,因此如何平衡二者间的关系成为软件工程能否成功的重点

推荐一本 邹欣老师写的 《构建之法》

恰巧最近在看Bob大叔的《架构整洁之道》颇有感触,稳(TDD)就是快,简单(解耦)就是快。

受益

团队解耦还是不太理解,一个全功能软件开发团队,很难做到5个人以下。开发、测试、系统都出两个人就6个了,实际上根本不可能这么少人,光开发可能就要三个了。

难道说要把功能解耦成小模块?那样的话,会不会一个项目分太多组了,人力成本也会上升?

阮老师,配图真棒!

引用Dark的发言:

恰巧最近在看Bob大叔的《架构整洁之道》颇有感触,稳(TDD)就是快,简单(解耦)就是快。


所有软件公司都可以用 TDD 嘛?比如自动驾驶的汽车制造产业链偏软件的供应商。

团队解耦这个对于创业团队来说貌似不太可行。人员资源有限,怎么可能拆分成多个独立团队。大家就是绑在一条绳子上的蚂蚱。

如果有微信公众号的话,有很多人订阅。

推荐的两本书有中文版本,《加速:精益软件和DevOps的科学:如何构建和扩展高性能的技术组织》、《规模: 复杂世界的简单法则》。

(1)子团队的人数不宜过多,每个子团队最好不要超过5个人。

5人精团队,感觉要每个成员都比较强的各方面能力,起码在某个方向上,前端,后端,测试,运维,或者或者业务架构,但国内大多都是一个老兵带几个新人,比较难操作,可能适合一些实力强的组织,确实小组内沟通协助效率最高,跨团队成本高

(2)子团队应该是一个小型的全功能软件开发组织。

有点像全栈工程师团队。

软件工程最终目标是实现软件的工业化生产

大佬写得很到位,体会过一个项目从简单快速上线,到后期维护成本增加的过程,感触颇多,一个大项目中途加入的人力必然会有一定的理解成本,能做到小团队闭环,边界清晰确实有利于快速迭代,软件工程这个真的只能在实践中才能感受其重要程度。

节点之间不直接共享状态,只通过通信交换数据。

这个思想在 Golang 的基于 Channel 的协程间通信策略也能看到。

原来无论是跨协程、跨进程乃至跨节点,许多思想是共通的。

写得很不错,谢谢

文字的力量,文当用时

感同身受,最近入职了新公司,维护老项目,所以的东西都堆叠起来,太多的环境变量,太多的嵌套判断,看的头大,往往改一个需求需要看半天代码,相当难受

难以想象那种几百几千人的项目是做成的,比如windows?

现在就在学软件工程,感觉在画一大堆没有意义的图,写一些自己都看不懂的文档。

你好,很多大团队是按照人员角色分组,还是按照组织架构分组,我个人也偏向后者。但在实际项目管理中也遇到一个问题,如果按照组织架构分组,相同技术栈人员分散、彼此沟通交流少,人员难以管理的问题,老师有什么好的建议吗?

就像各个独立团一样

5人规模的小团队,涵盖了后端、前端、测试、产品以及DBA人员,这几个人之间的协同关系也很难分配,且一旦有人请假,backup又需要向外部其他团队需求协调援助,存在的问题也很难解决。阿米巴组织形式是一个理想的状态,在实际工作过程中,很少有实践的很好的,也因此,大多数企业团队,还是遵循着规矩的至上而下的组织方式和按职能划分的团队协作方式推进工作……

有人推荐点软件团队管理的书 或者 架构师的书吗?

引用James的发言:

团队解耦这个对于创业团队来说貌似不太可行。人员资源有限,怎么可能拆分成多个独立团队。大家就是绑在一条绳子上的蚂蚱。

初创团队都是一个人,两个人一个项目,哪里算得上工程?

引用James的发言:

团队解耦这个对于创业团队来说貌似不太可行。人员资源有限,怎么可能拆分成多个独立团队。大家就是绑在一条绳子上的蚂蚱。

的确, 没有这么多的人员资源, 许多还要兼顾一两个项目

有人推荐点软件团队管理的书 或者 架构师的书吗?

真的很少有公司能做到真正的软件工程论

工作方式,混乱不堪的框架,没有注释的代码,困扰着很多程序员。
个人一直都认为软件工程非常必要,特别是产品类项目

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接