为啥有流程
为什么要有流程?
- 个人开发者是不需要流程的
- 超过一个人的团队就需要协作
- 随着团队规模上升,会出现全新的问题
复杂项目没有流程会有什么问题?
- 需求阶段:每个人都有自己的想法,团队决策需要有一个过程
- 开发阶段:多人/多端协作开发,每个人有自己的安排,相互配合需要有一个流程
- 测试阶段:产物怎样交付,测试如何开展,BUG怎么修都需要流程
- 发布阶段:怎样确保发布过程平稳丝滑,版本和流量如何控制,需要有规范
- 运维阶段:线上问题如何应急响应,处理用户反馈和线上问题需要有流程
瀑布模型
传统的瀑布模型:
优点是安全稳定,缺点是效率低。
敏捷开发
敏捷开发宣言如下:
敏捷开发简单来说就是以更小的团队,更快速的进行迭代。因为团队小,所以大家可以围绕着一个很具体的目标开展工作,大家的合作也更加紧密。在敏捷开发的概念体系里,有很多具体的方法,比如: scrum,kanban等等,scrum这个词的来源,就是橄榄球中的争球,大家肩并肩,共同围绕着—个目标前进。
敏捷开发优点:
- 以小团队快速迭代
- 团队成员之间的合作更加紧密
- 以人为本,和用户沟通
Scaled Agile Framework
在实践当中,现在敏捷已经发展出了—套规模化的管理的框架,也就说所谓的SAFe(The Scaled Agile Framework),这套框架是为企业中实施敏捷开发提供—套方法论。如果说敏捷开发是一个团队内部的协作方式,那么SAFe就是在企业中,多个敏捷团队之间怎样配合。
现在软件开发的阵型已经不是冷兵器时代的方阵了,大家更像是特种部队,每个人都是身怀绝技,虽然大家会分工,但是并不是说产品设计只由产品经理负责,领导负责分配任务,大家的决策和配合都是非常有凝聚力的行动。
如果—个scrum就是—个战术小队,敏捷教练就好比是小队的队长,产品负责人是负责联络指挥部和发布任务的人,其他团队成员就是特种兵。大家也不是按照一个方阵去前进,而是用更敏捷的方式去前进,也就说敏捷发布火车。
有哪些流程
需求阶段
不要浪费时间讨论不应该存在的问题。 特斯拉进行生产线自动化的时候,有一个零件的安装自动化总是出问题,特斯拉的工程师为了优化这个自动化流程,投入了大量的资金和精力。后来马斯克问他们的技术人员,为什么需要这个零件,结果发现大家居然并不清楚。最后证明其实在电动车上,根本不需要这个零件。所以围绕着这个不应该存在的问题,进行了大量投入,造成了很多浪费。
需求究竟应该怎么评估?这里教大家一个MVP(minimum viable product,最小化可行产品) 的思维方式,如果我们要给用户造一辆车,我们不应该第一天给他一个轮子,第二天给两个轮子,第三天给他一个底盘,第四天才让他开上车。我们应该先给用户一个简单能用的产品,比如一个滑板车,一个自行车,根据用户的反馈我们再逐步把车的功能升级,最终变成用户想要的产品。
另外一个评估需求的方法,也是我们日常工作中安排自己任务的一个方法,就是四象限法:当你很多任务的时候,可以按照这个坐标,把他们按照重要性和紧急程度分类,有些事情既重要又紧急,那么我们就应该先去做;不重要又不紧急的可以最后做;而有些事情虽然重要但是不紧急他们的优先级应该比那些紧急但是不重要的事情高,因为如果我们不去处理,后面他们就会变得又重要又紧急;这个理论的原则是先判断事情的重要性,再判断紧急程度。
开发阶段
首先大家正在一个时代的变革中,因为云原生的发展,现在的后端开发工作跟几年前已经有了很大的区别。
什么是云原生?
作为后端开发同学,大家应该把Kubernetes和容器技术作为一个基础知识,如果过去在虚拟主机的开发是用剑决斗
那在云原生技术爆发的当下,就是能在决斗中用枪了。所以就像这张图—样,大人,时代变了。
云原生下的开发,—个最大的区别就是部署的形式不同,传统虚拟机上的服务开发,是在物理机或者是更底层上虚拟出多个虚拟主机,然后在每个虚拟主机中安装软件和依赖虚拟机需要有专门的运维人员维护,本地开发的时候也大多是直接在电脑上运行程序。
而开发云原生的后端程序,容器是从操作系统中虚拟出来的,所有容器共享宿主机的系统,通过cgroup,namespace和union mount实现了容器之间的隔离。因此在部署的时候,应用和其依赖的系统是整体打包成一个镜像的后端开发不再依赖运维人员创建程序的运行时环境。
云原生带来的另外—个改变就是微服务,在之前的几年,web应用的主要架构是SOA架构,在一个服务中多个不同的模块构成了一个部署单元,各个模块作为一个整体部署和伸缩这种架构下往往服务会形成一个很大的代码仓库,大家共同维护一个大型的系统。好处是模块之间的调用不需要通过RPC,但是坏处就是加减机器的时候不能按模块处理,开发的工作也需要多人共同进行充分的集成测试,保证不会把别人的东西改坏。
单体架构:
- 多个模块共同组成一个服务,服务体量较大
- 模块之间直接调用,不需要RPC通信
- 服务整体扩缩容量
- 多人开发一个代码仓库,需要充分集成测试
微服务架构:
- 各个功能在不同的服务中不同模块
- 需要进行RPC通信
- 不同模块可以独立扩缩容
- 每个服务的代码仓库仅由少部分人维护
Martin Fowler在《Microservices》中描述单体架构和微服务的区别:
云原生让我们的开发环境也逐渐云化,新人入职往往前面几天导师安排的工作就是搭建开发环境。—旦开发环境出了问题,排查和解决起来都非常痛苦,而云原生的IDE就可以很好的解决这个问题,借助容器技术,可以轻松创建─个模拟线上的开发环境。
讲完了云原生,接下来影响我们开发的最大因素就是团队的分支策略不同的分支策略往往影响团队的开发流程。
这里假设大家没有深入使用过git,简单介绍一下,我们日常工作中写代码都是基于主干的某个版本进行修改,改完之后再把代码合并回主干形成新的版本这里就会有一些协作上的问题:
- 多个团队成员之间各自用什么分支开发?
- 修改之间有冲突怎样解决?
- 出了问题的代码如何回退到之前版本?
最后,在开发阶段,代码规范,自测和文档也是非常重要的。一个有经验的开发和新人写的代码,往往最大的差别不是功能的实现,而是在于代码的风格和规范。
比如有一些原则可以很容易遵循:良好的注释习惯,有复杂的地方时间长了自己都会忘,不要有魔法数字和魔法字符串,比如在判断条件里判断某个变量等于2,2代表什么?可以用常量来定义。
代码规范:
- 养成良好的注释习惯,超过三个月的代码,自己都会忘了当时在想什么
- 不要有魔法数字,魔法字符串
- 重复的逻辑抽象成公共的方法,不要copy代码
- 正确使用IDE的重构功能,防止修改错误
自测:
- 单元测试
- 功能环境测试
- 测试数据构造
文档:
- 大型改造需要有技术设计文档,方案评审
- 好的接口文档能更方便的和前端进行沟通
测试阶段
开发阶段占据我们日常工作的比例其实可能只有30%,还有大量的时间我们其实是在进行测试。这里向大家推荐—本书《google软件测试之道》。测试应该是伴随着开发的全部过程的,每写一段代码之后就要想办法测试这段代码。有的人可能以为测试负责保证代码的质量,但是这是—个很常见的误区,质量不是测出来的,BUG也不是因为没有测试产生的,希望大家在以后的工作中,能够为自己的代码负责。
你需要在写完每一段代码之后立刻测试这段代码,当完成了更多的代码时,就要做更多的测试。测试不是独立隔离的活动,它本身就是开发过程的一部分,质量不等于测试,当你把开发过程和测试放到一起,就像在搅拌机里混合搅拌那样,直到不能区分彼此的时候,你就得到了质量。
——《Google 软件测试之道》
上边这个图是传统的测试金字塔模型,越底层的测试粒度越细,就需要越多的数量去覆盖所有场景,越顶层的测试越能用少量的case覆盖大多数场景。
但是有一个软件开发中的常识:越早发现的缺陷解决成本越低,因为85%的缺陷是在开发阶段引入的,而如果要在上线之后修复他们,花费的成本可能是一开始就解决他们的数百倍,所以我们还是要尽可能早的发现bug,进行充分的单元测试。
我们日常开发的应用,可以简化成图中的模型:
由客户端发送请求到网关,网关请求到后端服务器。比如我们抖音的测试同学,可能每人手里会有好几个手机,分别用来测试不同的功能。对于后端的服务,也可能有不同的版本,因此在测试中会有一个虚拟的环境概念,我们用特定的设备可以通过某些设置,让他请求到对应的后端服务器,从而达到测试对应的后端服务的目的。
功能环境:
- 需要一个能模拟线上的环境进行开发和测试
- 环境和环境之间能够隔离,不影响其他功能的开发和测试
集成环境:
- 不同人开发的功能合并在一起测试,相互之间的影响可能产生缺陷
- 迭代发布的所有功能合并在一起测试,确保发布的所有功能之间的影响不产生缺陷
回归环境:
- 确保新的功能不对老的功能产生影响
- 回归测试一般会借助自动化测试脚本
发布阶段
根据国际航空事故数据库中统计波音737系列飞机飞行事故数据,80%的事故发生在起飞和着陆阶段,按事故主要原因分类,人为因素占48%,机械原因只占20%。
飞行员起飞前的检查项,很多是有血淋淋的教训的。而在互联网公司制定的发布规范中,许多规范的背后也 是从严重事故总结而来的经验。
实际上在发布过程中,除了出问题要快速定位,根据角色的不同,还需要做一些常规的检查:
发布负责人:
- 负责按照计划执行发布
- 需要通知各个相关人员发布进展
- 观察各个服务的发布状态,及时处理异常
变更服务的相关RD:
- 按照上线checklist检查服务的日志,监控,响应上线过程中的告警
- 对于自己负责的改动,在小流量或者是预览环境进行功能验证
- 执行发布计划中的其他操作(如唯戈上配置,数据处理等)
值班同学:
- 发布过程中的监控和告警需要特别关注,如果有异常需要立刻判断是否由变更引起
- 如果有变更引起的告警或者用户反馈,需要及时中止发布
各种发布模式:
| 发布模式 | 描述 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| 暴力发布 | 简单粗暴,直接用新版本覆盖老版本 | 简单、成本低 | 发布过程中服务会中断、出了问题会影响全部用户 | 测试环境部署、小公司或者非核心的业务服务 |
| 金丝雀发布 | 由于金丝雀对瓦斯极其敏感,因此以前矿工开矿下矿洞前,先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。 | 相对简单、能够用少量用户验证新版本功能 | 发布过程中服务会中断、发现不了随用户量增大才会暴露的问题 | 测试环境部署、小公司或者非核心的业务服务 |
| 滚动发布 | 每个实例都通过金丝雀的方式逐步放大流量,对用户影响小,体验平滑。 | 发布过程中用户体验不会中断、可以充分验证服务功能 | 流程较复杂,对发布系统有比较高的要求发布速度较慢、新老版本不兼容的情况不能使用 | 发布系统能力较强,可以平滑切换流量、发布自动化程度高,可以自动滚动 |
| 蓝绿发布 | 把服务分成蓝绿两组,先把蓝组流量摘掉然后升级,只用绿组提供服务,之后切换全部流量,只用蓝组提供服务,然后升级绿组服务,最终两组全部升级。 | 发布速度快、流程相对简单 | 需要有一半机器承担所有流量的能力、出问题会影响全部用户 | 服务器资源丰富、新老版本不能兼容的情况,需要一次性升级到新版 |
其实发布的模式还不止这几种,实际工作中,我们的发布使用的是滚动发布,发布的负责人需要关注滚动的粒度和时间,以及具体执行的进度因为这种方式对用户的体验最平滑,同时公司也有强大的流量控制能力,能够平滑的切换流量能够支持滚动但是仍然有一些场景需要使用蓝绿发布。
可能有些公司因为发布需要在用户低峰期进行,所以在那些公司发布的时候,往往都是在夜深人静的时候,这也就是程序员经常要晚上加班的很大一个原因。
运维阶段
服务运行时也可能产生各种各样的问题:
- 用户量增加引起流量洪峰(12306抢票)
- 数据库表的数据量增长导致查询速度变慢
- 内存/进程泄漏导致服务资源不足
- 光缆被挖断
当故障发生之后,我们是有几个关键的动作的:
首先是止损,尽快去让服务恢复功能,其次是要让服务的上下游感知到出了问题。当上面两个动作做了之后大家才需要去定位和修复问题。所以大家不要反过来,线上页面都打不开了,第一时间打开IDE开始看代码,这个其实是有问题的。
公司在发展过程中,逐渐形成了十分复杂的超大规模微服务体系。为了实现对这些复杂微服务的监控,我们往往会在微服务中添加埋点采集Metrics、Logging、分布式Trace等多种数据。
流程怎样优化
我们在之前的流程里面有很多的繁琐的规范和操作,这些操作无疑能让我们的服务质量提升。但是另—方面,又会导致我们工作的效率下降,所以往往大家会在质量和效率之间取—个平衡。
但是,时代又变了。我们看到技术的发展,让质量和效率同时得到了提高,把规范融入流程,把流程自动化
从需求到上线全流程自动化,我们就同时提高了质量和效率。
DevOps
DevOps解决方案:
- 代码管理
- 自动化测试
- 持续集成
- 持续交付
这就是现在的DevOps理念,所谓DevOps,就是由左侧的Dev和右侧的Ops组成。从需求开始,写代码,编译,测试,发布,运维,监控,形成了—个闭环。于是我们可以进行持续集成,持续交付。
在DevOps的基础之上,我们还可以怎样优化?这里就引出了—个效率竖井的概念,可以看到,从需求到交付的过程中,我们真正产生价值的开发,测试等等动作占比是很低的,大量的时间可能是在等待和传递。比如测试—直在等开发把环境部署好,另外人和人之间的沟通,也很慢。
全自动流程化
通过效能平台串联各个阶段:
- 需求发起研发流程的自动化
- 写代码,测试环境部署的自动化
- 自动化测试触发和报告分析
- 发布过程可观测融入流程
减少无价值的等待:
- 分析整个流程的耗时,计算真正产生价值的时间
- 不断优化流程,让有价值的流程时间占比上升
价值流模型图: