软件工程的最大难题

作者: 阮一峰

日期: 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,这样无需任何人员交流,就能建立通信。

(完)

留言(58条)

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

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

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

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

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

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

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的发言:

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

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

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

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

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

这说的不就是敏捷团队、微服务么

参加工作两年了,对于软件工程只想说:

初学不知书中意,再看已是书中人

我之前公司的项目一点都没有维护的欲望,更别说在项目的基础上进行开发了

看完很有共鸣:
上学和工作时我的老师一直和我们讲,软件工程的关键就是规模问题。
任何脱离场景去讲xxx好,基本都是耍流氓……
现在团队里说先这样,之后再考虑扩展的,基本都要被我打回。

阮老师的文章和配图都非常棒,形象生动,一听就懂。
开发软件就像盖房子或者建桥,一开始的框架、布局非常重要,不然房子盖得再漂亮,最后可能都是一堆废砖。
有时候对系统架构的构思所花费的精力、时间要大于编码的过程:
* 一个健壮的程序应该是各方面逻辑性都非常强的组合。
* 一个合格的团队、架构师或者程序员,应该是善于在这方面思考的,这也是合格的程序员和码农的本质区别,也是程序员进阶的必经之路。

引用wuwao_1的发言:

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

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

人力是可以复用的。

说的很对,我们原来是按业务功能分组,现在反而退化成了按角色分组,管理一塌糊涂

@wuwao_1:

小团队一个人不会只做一件事。

引用PeterYuan的发言:

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

合理解耦,一般一个团队不会有前端,又有后端。
产品应该不会归在小团队内。而是以产品为单位进行配置。
DBA也是类似。
如何实现好的解耦,是需要一定的能力的。

引用sunmoon的发言:

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

不知道哪里有介绍一下像 Windows 这类巨型工程团队,是怎么沟通协作、协同工作的?
或者网上有找到的资料也分享一下哈。

软件工程:如何高效率低成本,开发出正确的软件。

阮神,你好。
《软件工程》这门学科我的理解就是:在还是大学生的时候,先给一巴掌,让大学生一生记住软件工程这个名词。这样以后在软件开发遇到难题时,可以联想到软件工程这个标尺。如果不开这门课程的话,以后的新新人类可能就更迷茫了。更别提解耦、高并发、高可用这些名词了。
软件工程对我而言,是如何开发一个规范的单体集成软件(规范)、怎么做基础架构(布局),以及启示思维(计算机是一门综合学科,内含了数学、经济学、运筹学等思想)。
随着时间更替,社会发展。基础软件工程可能已经不适用,但却提供了个体研究新框架以及从框架中提取思想的思路(变通)。例如:多线程、事务都是解决并发问题。从初期分布式到大数据、云计算的发展过程,就像是开发程序从面向过程、到面向对象、到面向接口、再到面向服务的思维革新过程。
数量级剧增,解耦。业务复杂,解耦。公司为了扩展业务建立分公司,解耦。厉害了,我的耦。
从管理角度,金子塔模型更合适。从开发整体集成项目角度,初期分布式模型更合适。从极限开发、个体单元项目角度,单一职责(架构者、开发者、DBA 者、测试者、工程者)模型更合适。如果按照量子比特模型,一个个体即属于金字塔模型,又属于初期分布式模型,或许更合理。
依照初期分布式模型,再加上极限开发、单一职责模型。估计就是黑企加外包了。若是如此,我只能说,我想哭。
总之,龙有龙游,虎有虎行。具体境况具体分析吧。
写的不好,可提意见,但求勿喷。

1. 概论是可以向本科生开课的,重点了解软件工程
2. 软件工程重点是软件工程化,不是软件技术,也不是开发团队。
2.1 软件工程中的过程/生命周期的瀑布等多种模型是从长时间多项目总结的模式,不存在过时的瀑布,只要你有经验,瀑布应对多变的项目也是可以驾驭的
2.2 大项目也是由小项目组成,小项目也是麻雀一样五脏俱全,项目规模上的小型、微小型即初创公司都适用软件工程的
2.3 项目团队的组成也有多种模型,同组的成员基本不是同质,无法一个人负责一个小功能到底,尽管我一直实践着这样的纵向分工的实验做法,但是效果一直不好:有的人不会抓取需求,有的人不会写代码,更不用说设计的,测试一直都是演示,这样哪能开发软件项目?相反,横向分工的,所谓错误的公司组织的实际效果要好一些。
3.软件技术的解耦比较好解决,像组件化、插件化、模块化等都是成功成熟的技术,更不要提现在的SOA、微服务和无服务的新技术,关键是一般人学不到这里就死在过期曲线的谷底了——这个原因主要是教开发的老师只教整体设计
4.我自己设计了一个HelloWorld工程,输入一个问候,可以识别不同语言应答。Helloworld简单,这样的要求也简单,采用整体设计,是无法满足的,插件框架设计,各种语言的问候都是一个插件,把这个技术习得,应对千变万化的需求
5.回到软件工程的重点,过程,技术,方法,工具,最后落脚于软件质量,整个流程,从开始到结项采用工程化进行。初学者知道分析、设计、编码、测试、维护,不至于一个简单地址薄,上来就是设计ER图了。
6.如果软件工程概论中介绍的有兴趣,此时再去细读SWEBOK的各课。
向阮老师讨教学习的忠实迷粉

垂直性小团队“每组内部使用什么工具、如何实现某个功能,都是自己决定,各组之间不需要共享内部细节,也不依赖别组的工作。”在很多公司的很多业务发展阶段都是不可行的。

有几个必须要处理的问题非常难解决:1、工程跨团队交接问题 2、团队知识面和专业性问题,如何让每个团队都具备完整的软件技能? 3、高水平资源共享问题,水平高的研发人员嵌入在小团队,难以提供超越小团队的价值 4、鸡肋型业务的持续维护问题,不是每个业务都是非死即好的,不死不好的甚至于是大多数(符合20/80),保留在小团队内会极大消耗小团队的能力,再展开的话还有很多。。

阮老師還可以從項目人員流動的角度展開一下

国内公司喜欢救火式程序员,被认为高级人才。什么架构师,会软件工程的团队管理人才,在公司领导眼里不值钱

微服务和微前端适合这种解耦。

屏蔽复杂度最后只能面向接口编程,无论是接口是什么。如同一个优秀的OOP模式的设计师,合理地设计和迭代一个代码之间,人员之间互动的模式是要相当的天赋,学习思考,以及实践经验的。
为什么有人理解得深,有人理解得浅,可以问自己几个问题
你有天赋吗
你的技术是个什么真实水平
你真的管理过大项目吗,你真的有权力迭代架构来实践过你的设计吗

软件工程导论 UML GOF设计模式 人月神话 代码整洁之道 架构整洁之道 构建之法

devops一定程度上解决了这个问题。敏捷开发+精益管理理念结合
越来越理解devops不仅仅是一个技术工具包,而是技术成熟+工具成熟+管理理念升级的结合

超级APP毕竟是少数,中型APP如果没有把握好,就会提前负技术债!

老师,把康威定律加加进来,用来解释沟通结构解耦

森赛说的是最理想情况,实际上很多公司最大程度也只是做到了某一步骤。测试可以跟着开发。
但是DBA,运维和开发一起不多。通常是运维团队,开发团队分两块,开发再有子团队。而运维是服务于整个大开发团队的。

类似于小世界模型.

我要发表看法

«-必填

«-必填,不公开

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