You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 37 | 过程:规模与协作——规模化的过程方法
在学校时,你学习编程,写写课程作业的代码,但你想过真正的行业里,公司中的规模化开发方式是怎样的吗?在上一篇[《核心:安全与效率》](https://time.geekbang.org/column/article/42517?utm_source=weibo&utm_medium=xuxiaoping&utm_campaign=promotion&utm_content=columns)的文中,你应该还记得我讲的那个电站的例子,那么编写课程作业的代码就像搭建的 “酒精灯电站”,而工业级的规模化开发才是建设 “真实电站” 的方式。
工业级规模化的程序系统开发包括了一系列的过程,而这一系列过程的起点是:需求。
## 需求与调度
需求,有时会有很多不同的表达形式,包括:客户的诉求、用户的请求、老板的要求,但这些不同的表达形式,不一定是真正的需求。
客户的诉求,更多来自传统甲、乙方关系的场景,在软件工程过程中有一个子过程——需求工程——去对客户的诉求进行分析和提炼,并转化为需求文档。用户的请求,更多来自互联网 toC 的场景,通过洞察大量用户请求中的共性去提炼并转化为真正的产品需求。老板的要求,更多是因为老板也可能是你的产品用户之一,但这个用户的特殊之处在于,他为这个产品买单。所以,他的要求无论合理与否都能很容易地变成需要开发的需求。
正因为需求的来源多,表达形式也多,因而真实情况是 “需求” 似乎总是源源不绝,但是真正的需求往往隐藏在这些诉求、请求与要求的表象之下。这是关于 “需求” 的第一个困难点。如果我们总是能找出真正的需求,那么也许需求也就没那么多了。但现实往往是我们不能,那么需求过载的场景就会常常发生。
这时,你就会面临第二个困难,如何对过多的需求进行排序?
为什么需要排序?我们进行需求排序的原因是,在有限的资源下我们想要达到如下目标:
* 最大化用户、客户和老板的整体满意度;
* 最大化价值与产出,让最多的资源投入到最有价值的需求上。
只有当用户需求被快速地满足时,他们才会感到满意。但在有限资源限制的条件下,我们不可能让所有用户的需求都能被快速满足。面对这样的矛盾,我们也许可以学习、借鉴下**操作系统的资源调度策略**。
我用了好多年的 Mac 和 iPhone发现它们相对同等资源配置的 Windows 和 Android 机,在满足用户使用的响应性方面要好得多,特别是在使用了几年之后,这种差距会更明显。
在同等硬件资源配置的情况下Mac 和 iPhone 表现得更好,只可能是其操作系统的资源调度策略实现更优秀。通常,操作系统需要同时执行多个应用程序时,它的执行调度策略就会在多个应用程序间不停切换,有如下几种常见的调度策略:
1. **先来**先执行
2. 执行起来**最快**的先执行
3. 占用资源**最少**的先执行
4. 释放资源**最多**的先执行
5. **高优先级**的先执行
当资源充足,只用策略 1 就足够了,但更多情形下需要综合后 4 种策略。比如:老板的要求天生自带高优先级,需要先执行;而一些小需求,优先级不高,但执行快,占用资源少,随着它们排队的等待时间延长,优先级可以逐步提升,以免消耗完用户的等待耐心,形成负面评价。
当用户同时运行的应用程序确实太多时,操作系统发现资源无论如何调度都不够了,它有一个选项是提供了资源消耗的监视器,来提示用户主动停掉一些同时运行的应用,而最后的底线是操作系统主动杀掉一些应用程序以释放资源,以保障系统还能正常地运转下去。那么我们在调度需求时,是否也能以透明的资源消耗监视提示用户主动控制需求或选择 “杀” 掉需求,然后还不降低用户的满意度呢?
需求调度,可以像操作系统一样运转,形成一个规模化的需求调度体系,并通过多种调度策略来平衡需求的响应性和投入产出的价值最大化。
## 设计与开发
紧接需求之后的过程是:设计与开发。
成为程序员后,你一开始可能会习惯于一个人完成系统开发,自己做架构设计、技术选型、代码调测,最后发布上线,但这只适合代码量在一定范围内的系统开发模式。在一定范围内,你可以实现从头到尾“一条龙”做完,并对系统的每一处细节都了如指掌,但当系统规模变大后,需要更多的人共同参与时,整个设计和开发的模式就完全不一样了。
一定规模的系统,下面又会划分子系统,子系统又可能由多个服务构成,而每个服务又有多个模块,每个模块包含多个对象。比如,我现在所在团队负责的产品,就由数个系统、十数个子系统、上百个服务构成,这样规模的系统就不太可能光靠一个人来设计的,而是在不同的层次上都由不同的人来共同参与设计并开发完成的。
**规模化的设计思路,一方面是自顶向下去完成顶层设计**。顶层设计主要做两件事:
* 一是去建立系统的边界。系统提供什么?不提供什么?以怎样的形式提供?
* 二是去划定系统的区域。也就是系统的层次与划分,以及它们之间的通信路径。
今年世界杯期间,读到万维钢一些关于 “足球与系统” 的文章,感慨原来系统的顶层设计和足球运动十分类似。按文中所说,足球的科学踢法是:“球员必须建立 区域zone 的观念,每个球员都有一个自己的专属区域”,通过区域的跑位来形成多样化的传球路线配合。
而系统的区域划分,也是为了让系统内部各部分之间的耦合降低,从而让开发人员在属于自己的区域内更自由地发挥。而在区域内的 “控球”“传球” 与 “跑位”,就更多属于开发人员个体能力的发挥,这个过程中区域的大小、边界都可能发生变化,进而导致区域之间的通信路径也跟随变化。这样的变化,就属于自底向上的演化过程。
所以,**规模化设计思路的另一面,就是要让系统具备自底向上的演化机能**。因为,自顶向下的设计是前瞻性的设计,但没有人能做到完美的前瞻性设计;而自底向上的演化机能,是后验性的反应,它能调整修复前瞻性设计中可能的盲点缺陷。
记得,我曾经看过一个视频名字大概是 “梅西的十大不可思议进球”,视频里的每一个进球都体现了梅西作为超级明星球员的价值,而在前面提及的万维钢的文章中,有一个核心观点:“普通的团队指望明星,最厉害的球队依靠系统”。其实二者并不矛盾,好的系统不仅依靠 “明星” 级的前瞻顶层设计,也指望 “明星” 级的底层演化突破能力。
所以,一个规模化的系统既要靠前瞻的设计视野,也依赖后验的演化机能,这样才可能将前瞻蓝图变成美好现实。
## 测试与运维
完成了设计与开发之后,系统将进入测试,最后发布上线进入运行与维护阶段。
在前面需求、设计与开发阶段的规模化过程中,都存在一个刚性扩展的问题,也就是说,如果提出的需求数量扩大一倍,那么需要去对接、分析和提炼需求的人员理论上也要扩大一倍;如果提炼出的需要进入开发的需求也翻倍,相应开发人员只增长一倍那已经算是理想情况了,这说明系统的正交与解耦性做得相当完美了,所有的开发都能并行工作,不产生沟通协调的消耗。
但真实的情况不会那么完美,需求的产生与爆发很可能是一种脉冲式的,而企业一般会维持满足需求平均数量的开发人员,当需求进入脉冲高峰时,开发资源总是不够,然后就会过载,进入疯狂加班模式。
开发完成后,进入测试与线上运维的循环阶段,这个阶段与前面阶段的不同之处在于:需求提炼、设计开发基本都只能由人来完成,而测试、运维的很多步骤可以通过制作工具来完成自动化过程。所以,这个阶段随着规模化过程实际可以是一个柔性扩展的阶段。
但它从来不是一开始就是柔性的,因为制作工具也有一个成本考虑。比如,在我做的这个系统发展的早期,系统架构简单、部署规模也很小,基本所有的测试与运维工作都是通过人肉来完成的,这样的效率也不算低,唯一的问题是对测试人员而言,大量的工作都是低水平的重复,不利于个人成长。
随着后来的业务快速增长,系统增长越过某个规模临界点,这时人肉负载基本饱和,效率已没法提升,制作工具是唯一的出路。你可能或多或少都知道一些现代化的工业流水线,而在软件开发过程中,“测试与运维” 的运转体系是最可能接近工业流水线方式的。
因此,以测试为例进行规模化的最佳方式,就是打造一条 “测试机器” 流水线,而我在[《转化:能力与输出》](https://time.geekbang.org/column/article/40371?utm_source=weibo&utm_medium=xuxiaoping&utm_campaign=promotion&utm_content=columns)一文中写到了关于打造 “机器” 的三个核心点,这里再强调一次:
* 流程规则
* 工具系统
* 规范共识
围绕这三个核心点,我们再来看看 “测试机器” 如何打造?
从开发提测,机器自动下载提测版本分支代码,进行构建编译打包,实施代码规范性检查测试,通过后发布测试环境,进行分层次的各类自动化专项测试。如:用户终端层、通信协议层、服务接口层、数据存储层的各项测试,全部通过后,生成相应的测试报告,进入下一步发布流程。这就是测试体系的“流程”,而“规则”就是其中定义的各种测试项检查约束。
上述流程中涉及的“工具系统”包括:代码规范检查工具、终端 UI 的自动化测试工具、通信协议与服务端接口调用的模拟工具、数据一致性校验工具、测试报告生成工具、测试 Bug 统计分析与收敛趋势等可视化展现工具,等等。
最后,“规范共识” 是整个团队对这个流程环节、里面具体规则的定义以及 Bug 分类等方面达成的共识,这决定了这台 “测试机器” 运转的协调性与效率。
测试通过后,发布到线上就进入了运维阶段,行业里已经有大量的关于 DevOps 的分享内容,而它的本质也就是打造了一台 “运维机器” 流水线,这和我上面描述的 “测试机器” 运转类同,只是有不同的规范共识、流程规则和工具系统,便不再赘述了。
到了规模化的测试与运维阶段,看一个团队的水平,就是看这台 “机器” 的制作水准与运转效率。
在程序系统的开发过程中,当系统的大小和复杂度到了一定的规模临界点,就会发生从量到质的转变,规模不同,相应的需求调度、设计开发、测试运维的过程也都不同了。
量级变了,逻辑就不一样了。
每一个具备一定规模的组织都有对应的规模化工程过程,而且这个过程的形成受公司文化、团队构成、组织架构,甚至业务特性共同决定。那你所在组织的规模化过程是怎样的?这个过程系统如何运作的?欢迎你在留言区分享。
* * *