左耳朵耗子:拖累开发团队效率的困局与解决之道

2023-11-11

作者|陈皓编辑|小智影响软件开发团队效率的因素有许多,产品和业务上的效率问题固然是根本,但很多时候,这种问题并没有解。如果只从软件开发的过程出发,哪些开发方式是典型?又该怎么解呢? 写在前面

我之前写过一篇叫《加班与效率》的文章,从概念上说了一些我对“效率”的认识,但是那篇文章趋于概念化,对于一些没有经历过这样的环境的同学来说,可能会觉得太抽象了。很早以前就想写一篇更具体一点的,可执行的文章与《加班与效率》这篇文章相辉映,并再把我几年前在杭州QCon上的那个“鼓吹工程师文化”的《建一支强大的小团队》的观点再加强一下。

《加班与效率》链接:http://coolshell.cn/articles/10217.html

本文不讨论任何业务上的效率问题,只讨论软件开发或是软件工程中的效率问题。虽然产品和业务上的效率问题是根本,但是因为本文不是拉仇恨的,我也不想混在一起谈,所以请原谅我在这里先说开发团队的,以后重新开篇文章专门谈产品和业务的。

我下面会罗列几个非常典型的开发方式——软件开发中的“锁”,接力棒式软件开发,保姆式软件开发,WatchDog软件开发,故障驱动式软件开发。

软件开发中的“锁”

如果你搞过并发编程,你一定知道什么是“锁”,锁就是用来同步和互斥。我发现有好些开发部门里的各个开发团队间存在很多锁。比如:

  • 技术能力上的锁。有一个项目需要在不同的地方做开发,这些模块用到不同的技术,比如:Java, C/C++, Python,Javascript,但是,这个团队里的每一个开发人员就只懂一门语言,于是,需要配合,需要任务排期,同步互斥锁就很多,于是,一个本来只需要2个人干3周的的工作变成了8个人干两个月。

  • 负责模块上的锁。同理,不同的人负责不同的模块,于是一个项目要动好多模块,那么你就需要把这些模块的人找过来,和上面一样。每个人都有自己的时间安排,人越多,锁越多。于是,一个来来只需要2个人干2两周的事,变成了7、8个人干一个多月。

我上面并非瞎扯,这都是事实。我们可以看到,

  • 时间锁、进度锁。这堆有不同技能或是负责不同模块的开发人员有锁,有锁你就要等,他们有自己的安排,所以,要协作起来,你就需要排期,去同步。而参与的人越多,你的锁就越多。你协调他们的时间就更复杂。

  • 沟通锁、利益锁。而且,最恐怖的事情是,他们之间的沟通成本巨大。他们会花大量的时间在讨论,一个功能是实现在你那边,还是我这边,每个人都有自己的利益和算盘。无形中增加了很多推诿、官僚和政治上的东西。

有时候,我们会觉得分工和分模块是产生效率的前提,但是实际情况并不是这样。我们也可以看到,所谓的“分工”被彻彻底底的滥用了。他们把“分工”当成了永远只干一件事的借口。

解决方案

一个程序员应该能够掌握多个语言,也能够负责多个模块甚至不同的职责。如果一个程序员觉得多学习一门语言,多掌握一个模块是件很困难的事,那么这个程序员本质上是不合格的。

“接力棒式”软件开发

在有各种“工作锁”的软件开发团队里,一般都无法避免“接力棒式”的开发。也就是说,底层的C程序员干完了,交给上层的Java程序员,然后再交给更上层的前端程序员,最后再交给运维人员。这就是接力棒式的开发。

而且,更糟糕的是,如果在引入了软件流程下,这种“接力棒的方式”真是会把你搞崩溃的。比如下游团队开发一个月,交给QA测试一个月,再交给运维分步上线一个月,然后,上游团队拿到下游开发的API后开发一个月,再交给自己的QA测试一个月,然后再交给自己的运维上线一个月,于是,半年就这样过去了。这是一个由一个一个小瀑布叠出来的一个大瀑布

哦,你会说,这个好办啊,上下游不会先商定好接口么?然后做并行开发么?是的,这是其中的一个优化方式,但是需要很好的接口设计。但是,在实际过程中,你会发现(这时我并非信口开河,我说的都是事实),

  • 如果这两个上下游团队在一起还好办,要是不在一起,那么,实际情况是,后面的团队会等到前面的团队提测了,才开始开发,本质上就是串行开发的。

  • 如果有更多的团队呢?比如:A团队 -> B团队 -> C团队 ->D团队呢。接口就变得非常地关键了。而在实际情况下,因为没有好的接口设计人员,所以,在开发过程经常性地修改接口,或者是因为接口不好用也只得忍着。

解决方案

我以前写过一篇叫《IoC/DIP其实是一种管理思想》,对于这种接力棒的方式,应该反过来,如果业务应用团队是A团队,那B/C/D团队应该把自己的做成一个开发框架也好,服务化也好,让应用团队自己来接入。比如:前端做好一个前端开发框架,PE做好一个运维开发框架、各种工具,共享模块团队做好开发框架,让应用团队自己来接入,而不是帮他做。你会发现,在这么多团队各自P2P勾兑出来的很随意的接口的所带来的成本已经远超过一个统一标准的协议

“保姆式”软件开发

所谓“保姆式”软件开发就是——我只管吃饭,不管做菜洗碗,就像——衣来伸手,饭来张口的“小皇帝”一样,身边有一堆太监或宫女,不然生活不能自理。这种情况经常见于开发和测试,开发和运维间的关系。很多公司,测试和运维都成了开发的保姆。

我就能看到,很多开发快速写完代码后基本上都不怎么测试就交给QA去测试了,QA一测,我草,各种问题,而只会做黑盒的QA并不能马上就能确定是代码的问题还是环境的问题,所以还要花大量时间排除不是环境问题,才给开发报BUG。很多问题,可能只需要做个Code Review,做个单测就可以发现了,硬要交给QA。运维也是一样的,开发出来的软件根本就没有考虑什么运维的东西,因为有运维人员,所以我才不考虑呢。

这和我们带孩子的道理是一样的,对于孩子来说,如果父母帮孩子做得越多,孩子就越觉得理所应当,就越不会去做。

“保姆式”开发一般会进化成“保安式”开发。

  • 因为你的团队开发人员的能力不行,设计不行,Code Reivew/UT不做,你就只能找堆QA看着他。

  • 因为Dev/QA只管功能不管运维,所以,还得找堆运维人员看着他们。

  • 因为你的技术人员不懂业务,不懂需求,需要再找个BA,找个产品经理来指挥他。

  • 因为你的技术人员不会管理项目,所以,再搞个项目经理,找个敏捷教练、以及SQA来管着他。

就这样,你不行,我找人来看着你,看你的人不行,我再找人来看着看你的人……层层保姆,层层保安。于是,你就会发现,团队或部门里的人员越来越多,你整天都在开会,整天都在互相解释,互相争吵,会扯淡的人越来越多。那还有个屁的效率。

网络上一个非常经典的图片,来源不详,程序员在挖坑,其它人站在当监工

解决方案
  1. 不要招只会写代码的“码农”,要招懂“需求”,注重“软件工程”和“软件质量”和“软件维护”的“工程师”。

  2. 最好的管理,不是找人来管人,而是自己管自己。

  3. 组织和团队中支持性工作的人越少越好,最好不要。

  4. 服务化。我服务于你并不代表我要帮你干活,而是代表——我要让你干活干得更爽。

我在微博上说过下面的话,(大家可以体会一下保姆和服务的差别)

运维要用“云服务”的思路去做。如果一个公司内的运维团队开发出一堆工具,让做应用开发团队可以很容易地申请机器、存储、网络、中间件、安全等资源,并很容易管理、监控和部署应用,并提供运维资询。而不是帮应用开发团队干活擦屁股当保姆。那么,这个公司就会不经意地做出一个云计算平台来了。

“WatchDog式”软件开发

什么是WatchDog?就是说——为了解决某个系统的问题,我要用一个新的系统去看着它。

  • 我的系统架构太复杂,出了问题不好查找。咋办?那就搞个专门的特殊的监控系统吧……

  • 我的系统配置太复杂,容易配错了。咋办?那就加一个配置校验系统吧……

  • 我的系统配置和真实的情况有时候可能会不一性。咋办?那就加一个巡检系统吧……

  • 我的系统测试环境和线上环境有时候会搞混了。咋办?那就为线上环境加一个权限控制系统吧……

  • 我的系统有单点,那就加个负载均衡器吧,负载均衡器的单点呢?那就再加个等价路由器吧……

做加法谁不会?就不想去简化一样系统吗?就不能不拆东墙补西墙么?这些了系统加的越来越多,我看你以后怎么运维。

一开始没有想清楚就放到线上,然后,出了故障后,也无法重新设计和重新架构,只能以打补丁地方式往上打,这就好像一个本来就有缺陷的楼没有盖好,你要拆了重盖是不可能的,也只能不停地打补丁了。字是一只狗,越描越丑。

解决方案
  1. 设计想好了再做,多评估几个设计没坏处,简化,简化,简化。

  2. 残酷无情地还债,就算是CEO来了,也无法阻止我还债的脚步。

“故障驱动式”软件开发

WatchDog式的软件开发通常来说都是“故障驱动式”软件开发的产物。这种开发方式其实就是在表明自己智力和能力的不足。以上线为目的,上了线再说,有什么问题出了再改。

上面的老大或是业务方基本上会说,没关系,我们不一开始并不需要一个完美的系统,你先上了再说,先解业务的渴,我们后面有时间再重构再完善。而有的技术人员也会用“架构和设计是逐步演化出来的”这句话来证明“故障驱动”开发是值得的。

我同意逐步迭代以及架构演化论,但是,我觉得“系统迭代说”和“架构演化论”被彻彻底底地成为那些能力有限甚至不学无术的人的超级借口

你们有没有搞错啊?你们知道什么叫迭代,什么叫演化吗?你们知道,要定位一个线上的故障需要花多大的力气吗?你们知道,随随便便去应付局部上你会快,但总体上来说你会慢。

虽然,我看到那些系统在一个又一个的故障后得到一点又一点的改善,但是我想说,为什么一开始不认真不严谨一点呢?我从来就没有见过一个精良的系统是靠一个一个的故障和失败案例给堆出来的,就算是Windows 95/98这样史上最烂的操作系统,如果没有设计精良Windows NT的补位,Windows也早玩完了(看看IE的下场就知道了)。

解决方案
  1. 基础知识和理论知识非常重要。多多使用已有的成熟的方案是关键。

  2. 对技术要有一颗严谨和敬畏的心。想清楚了再干,坚持高标准,Design for failure! 很多事情都急不得。

其它开发方式

其实,这样的事情还有很多。比如:

1.配置管理上的问题。对于源代码的配置管理,其实并不是一件简单的事情。配置管理和软件和团队的组构的结构非常有关系。我看到过两种非常没有效率的配置管理,一种是以开项目分支的方式来做项目,同时开很多分支,分支开的时间还很长,导致merge回主干要花大量的时间去解决各种冲突,另一种是N多的团队都在一个代码库中做修改,导致出现很多复杂的问题,比如某团队的改动出现了一个bug,要么所有的团队的功能都得等这个bug被修复才能被发布,要么就是把所有的改动回滚到上一个版本,包括其它团队开发的功能。很明显,软件模块的结构,软件的架构,以及团队的组织形式都会严重影响开发效率。

2.人肉式的软件开发。大多数的软件团队和主管都会用“人手不够”做为自己开发效率不够的借口,而大多数故障发生的时候,都会使用更重的“人肉流程”来弥补自己能力的不足。他们从来没有想过使用“技术”,使用更“聪明”的方式来解决问题。

3.会议驱动式开发。人多了,团队多了,想法也就多了,沟通也就多了,于是需要不停得开会开会开会。

总结一下

综上所述,我有如下总结:

1. 软件工程师分工分得越细这个团队就越没效率,团队间的服务化是关键的关键。不管是从语言上还是从软件模块上的人员分工,越细越糟糕。服务化不是我要帮你做事,而是我让你做起事来更容易。

2. 你总需要在一个环节上认真,这个环节越往前就越有效率,越往后你就越没效率。要么你设计和编码认真点,不然,你就得在测试上认真点。要是你设计、编码、测试都不认真,那你就得在运维上认真,就得在处理故障上认真。你总需要在一个地方认真。另外一篇文章你可以看一下——《多些时间少写些代码》

3. “小而精的团队”+“条件和资源受限”是效率的根本。只有团队小,内耗才会小,只有条件或资源受限,才会逼着你去用最经济的手段做最有价值的事,才会逼着你喜欢简单和简化。

4. 技术债是不能欠的,要残酷无情地还债。很多事情,一开始不会有,那么就永远不会有。一旦一个事情烂了,后面只能跟着一起烂,烂得越多,就越没有人敢去还债。

5. 软件架构上要松耦合,团队组织上要紧耦合

6. 工程师文化是关键,重视过程就是重视结果。只重视结果的KPI等同于“竭泽而渔”和“饮鸩止渴”。

本文系酷壳博主陈皓原创文章,已授权InfoQ公众号转发传播。原文链接见:

http://coolshell.cn/articles/11656.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

左耳朵耗子:拖累开发团队效率的困局与解决之道 的相关文章

  • Unity开发(2)建片草地

    文章目录 1 简述 2 创建 2 1 创建项目 2 2 进入开发窗体 3 建个地面 3 1 新建地面 3 2 调整地面大小 3 3 添加草地 3 3 1 初识Unity图片资源 3 3 2 添加图片资源 3 3 3 修改图片在场景中大小 1

随机推荐

  • C语言入门知识1(零基础新手适用)

    C语言入门知识1 零基础新手适用 程序语言 1 机器语言 机器语言是低级语言 是用01码来编写的二进制代码语言 2 汇编语言 汇编语言也是低级语言 是用英文字母和符号串编写的 3 高级语言 由于汇编语言依赖于硬件体系且符合较多 为了方便高级
  • Go中 defer的使用

    文章目录 简介 示例 使用场景 捕获异常 文件操作 简介 defer 是 Golang 中的一个非常有用的关键字 它用于注册延迟调用 也就是一个函数的执行被延迟到调用它的函数返回之后 常用于资源清理 异常处理等场景 示例 defer 是注册
  • python实现电子邮件编程

    一 几个专业名词 MUA MTA MDA 假设我们自己的电子邮件地址是me 163 com 对方的电子邮件地址是friend sina com 注意地址都是虚构的哈 现在我们用Outlook或者Foxmail之类的软件写好邮件 填上对方的E
  • C++提高8: 类模板成员函数类外实现和类模板分文件编写

    1 类模板成员函数类外实现 类外实现主要有三个关键点 作用域 识别T的数据类型 告诉编译器这是一个类模板 剩下的 就还是基础的类内声明类外定义实现了 直接上代码观察一下 include
  • redis后台实现投票功能

    原创文章 转载请注明出处https blog csdn net qq 41969845 article details 108406059 一 前言 本文以投票功能为例 从实际例子中熟练掌握redis的应用 阅读本文需要有一定的Java基础
  • SparkStreaming与Kafka010之05之01 Consumer

    package Kafka010 import Kafka010 Utils MyKafkaUtils import org apache kafka clients consumer ConsumerRecord import org a
  • 常用网络数据帧格式

    常用网络数据帧格式 1 ARP帧格式 2 ICMP帧格式 3 UDP帧格式 4 TCP帧格式 本文主要介绍ARP ICMP UDP TCP等常用网络数据帧格式 1 ARP帧格式 当一个应用层的数据在网络中传输时 会被逐步封装成链路层的帧 而
  • ffplay源码解析-main入口函数

    main入口函数 初始化 变量 缓存区 SDL窗口初始化等 int main int argc char argv int flags VideoState is av log set level AV LOG TRACE init dyn
  • L1-086 斯德哥尔摩火车上的题(15分) Python

    上图是新浪微博上的一则趣闻 是瑞典斯德哥尔摩火车上的一道题 看上去是段伪代码 s a 1112031584 for i 1 i lt length a i if a i 2 a i 1 2 s max a i a i 1 goto url
  • 2020-11-24-ElasticSearch7.x学习笔记

    笔记记录 B站狂神说Java的ElasticSearch课程 https www bilibili com video BV17a4y1x7zq 在学习ElasticSearch之前 先简单了解一下Lucene Doug Cutting开发
  • 根据PV或者QPS来计算需要多少台机器

    QPS 单个进程每秒请求服务器成功的次数 req sec 总请求数 进程总数 请求时间 一般使用http load进行统计 每台服务器每天的PV QPS x 3600 x 6 或者乘以8小时 一天按照6或者8小时计算 晚上可能没人访问 服务
  • Conda环境 下载Jupyter Lab并使用

    1 下载Jupyter Lab conda 安装方式 conda install jupyterlab conda install c conda forge jupyterlab python 安装方式 pip install jupyt
  • python waitress_python 角度理解web服务器

    概述 web服务器实际上就是一个运行在物理机上的网络服务器 它等待客户端给他发送请求 成功接收后将客户端请求的资源响应给它 客户端与服务端的通信通过http协议实现 客户端可以是浏览器或者可以发送请求的一段程序 一 一个简单的web服务器
  • Android11 热点设置永不关闭

    Android11 热点设置永不关闭 文章目录 Android11 热点设置永不关闭 一 前言 二 framework设置热点永不超时关闭 三 基于 SoftApManager java 研究超时逻辑 三 总结 1 设置热点不关闭的方法 1
  • cutlass入门: 调用cutlass做通用矩阵乘法Gemm(附代码)

    cutlass是CUDA C 模板抽象的集合 用于实现CUDA中所有级别和规模的高性能矩阵乘法 GEMM 和相关计算 相较于cuBLAS和cuDNN cutlass中包含了更多可重用的模块化软件组件 这使得cutlass相较于前两者更为灵活
  • 详细介绍InnoDB数据存储结构

    InnoDB数据存储结构 1 数据库的存储结构 页 索引结构给我们提供了高效的索引方式 不过索引信息以及数据记录都是保存在文件上的 确切说是存储在页结构中 另一方面 索引是在存储引擎中实现的 MySQL服务器上的存储引繁负责对表中数据的读取
  • 接口测试简介以及接口测试用例设计思路

    接口测试简介 1 什么是接口 接口就是内部模块对模块 外部系统对其他服务提供的一种可调用或者连接的能力的标准 就好比usb接口 他是系统向外接提供的一种用于物理数据传输的一个接口 当然仅仅是一个接口是不能进行传输的 我们还的对这个接口怎么进
  • OpenCV读取图像_显示图像和保存图像

    配置好 OpenCV 以后 包含以下两个头文件 include cv h include highgui h IplImage image cvLoadImage D 123 jpg 1 函数cvLoadImage 的第1 个参数是图像文件
  • C++中插件使用举例

    插件并不是在构建时链接的 而是在运行时发现并加载的 因此 用户可以利用你定义好的插件API来编写自己的插件 这样他们就能以指定方式扩展API的功能 插件库是一个动态库 它可以独立于核心API编译 在运行时根据需要显示加载 不过插件也可以使用
  • 左耳朵耗子:拖累开发团队效率的困局与解决之道

    作者 陈皓编辑 小智影响软件开发团队效率的因素有许多 产品和业务上的效率问题固然是根本 但很多时候 这种问题并没有解 如果只从软件开发的过程出发 哪些开发方式是典型 又该怎么解呢 写在前面 我之前写过一篇叫 加班与效率 的文章 从概念上说了