研发效能-加速

Posted by 汤键|兔子队列 on January 25, 2024 禁止转载
本文总共 7382 字 · 阅读全文大约需要 22 分钟

前言

  • 实现DevOps最困难的部分是与管理层交流,转变他们的思想
  • 管理层习惯于询问如下问题:
  • 1-实施这个方案的成本是多少?
  • 2-实施这个方案后我们能收益多少?
  • 从管理者的角度来看,这些都是很合理的问题
  • 但是在DevOps的世界中,如果在错误的时间以错误的方式回答这些问题
  • 那么这些问题对于组织而言可能是有害的,并可能导致产生大量的预备工作
  • 本文将介绍一些指标,这些指标可以使开发者与管理层的讨论不再局限于努力提高工程效率和开发者生产力
  • 注意
  • 本文尽量保持用词与开发模式无关
  • 见过采用DevOps实践的企业
  • 包括敏捷模型、Scrum模型、规模化敏捷框架(SAFe)、看板和瀑布法等
  • 但每个系统都有自己的术语,我尽量保持中立
  • 例如,本文使用”需求”​,而不是”用户故事”或”产品待办事项”​
  • 但本文使用的大多数示例都是基于Scrum的

工程效率-工作量

  • 企业是如何衡量开发者效率的?
  • 最常见的方法是通过工作量来衡量
  • 曾经有一些企业使用诸如代码行数或代码测试覆盖率之类的度量标准
  • 但这些显然是不好的选择,不知道现在还有哪些企业在这么做
  • 如果可以通过1行代码或100行代码解决一个问题,那么1行显然更可取
  • 因为每一行代码都有维护成本,代码测试覆盖率也是如此
  • 覆盖率本身并没有说明测试的质量,而且糟糕的测试还会带来额外的维护成本

工程效率-无须评估

  • 评估并不是一件坏事,如果应用在正确的场合,它会很有价值
  • 如果开发团队和产品经理讨论下一个用户故事,评估可以有助于推动交流
  • 例如,如果团队使用“扑克规划”评估用户故事但是评估结果不一致,表明人们对如何实现它有不同的想法
  • 这可能引发有价值的讨论,并可能更有成效,因为可以跳过一些具有共识的讨论
  • 对于业务价值来说也是如此
  • 如果团队不理解为什么产品经理分配了一个非常高或者非常低的估计数字,这也可能引起重要的讨论
  • 但是很多团队在完全不评估需求的情况下会感觉更合适
  • 特别是在高度实践化的环境中,评估通常被认为是浪费时间的行为
  • 远程和分布式团队通常也不喜欢进行评估
  • 他们经常会进行面对面的会议或发起关于问题和Pull Request的讨论
  • 这有助于记录交流过程,并帮助团队以更异步的方式工作
  • 这也有利于跨越不同时区的开发者进行协作
  • 不讨论开发者开发效率的情况下,开发团队应该被允许自行决定是否需要进行评估

部署到任何平台

  • CI/CD
  • CI意味着每次把代码修改推送到存储库时,代码都会被构建和测试,输出打包成一个构建工件
  • 在CD中,每当创建一个新的构建工件时,会自动将该工件部署到指定环境中
  • 当执行CI/CD时,开发和交付阶段是完全自动化的,代码随时准备好部署到生产环境
  • 分阶段部署
  • 阶段或层是部署和执行软件的环境
  • 典型的阶段包括开发、测试、暂存(或预生产)和生产
  • 通常,暂存(或预生产)阶段是生产环境的完全镜像,有时它用于通过使用负载均衡切换两个环境的零停机部署
  • 而接近生产的阶段需要手动批准才能部署
  • 如果一个企业使用功能标志和CD,则阶段的数量会减少

功能标记

  • 功能标记(Feature Flag)是一种软件开发技术,允许在不改变代码的情况下修改运行时行为
  • 是一种条件性编码工具,它允许在不部署新代码的情况下启用或禁用软件功能
  • 它把最终用户功能的发布与二进制文件的推出分离开来
  • 功能标记的工作原理类似于开关或切换,由于具有布尔性质,因此通常被称为功能切换或功能开关
  • 但是功能标记可以有许多不同的用例,并且可能比开关更复杂,因此功能标记这个术语更加适合
  • 功能标记允许开发者把新代码封装在功能标记后,将其推出到生产系统,然后可以根据给定目标受众的上下文启用该功能
  • 800
  • 800
  • 800
  • 800
  • 对开发者来说,如果具有持续交付和负责基础架构的独立团队
  • 功能标记是一种非常地道实用的技术
  • 将标记添加到代码比更改基础架构更容易,因此开发者通常会使用标记允许测试人员执行与普通用户不同的操作
  • 或者允许某些测试版用户测试某些内容
  • 如果没有明确指定功能标记,配置通常会分散在不同的位置(配置文件、组成员身份和应用程序数据库)
  • 明确使用功能标记有助于提高团队的透明度,并确保方法统一,可以启用更高级别的用例并确保安全性和可扩展性
  • 这种将代码部署与功能激活分离的做法带来了诸多好处
  • 如在生产环境中更有效地进行测试、分阶段推出新功能、迅速回滚更新以及提供定制化的用户体验
  • 功能标记代表了一种从传统、固定的发布流程向更加灵活、可控制的策略的转变
  • 随着 DevOps 的兴起,功能标记的发展与对敏捷和更少干扰的发布机制的需求相吻合,特别是在分布式系统中
  • 通过功能标记,可以将新代码以休眠状态加入到生产环境中,在适当的条件下激活
  • 从而最小化大规模部署和潜在的系统中断风险
  • 部分使用案例:
  • 0-发布标记
  • 这些标记用于在标记后发布代码
  • 发布标记通常在功能完全发布之前保留在代码中,可能需要保留数周或数月的时间
  • 发布标记可以随着每个部署或系统配置的更改而变化
  • 可以与金丝雀发布(Canary release)(逐渐向更多用户暴露该功能)或蓝绿部署(blue-greendeployment)(交换缓存和生产环境)一起使用
  • 1-实验标记
  • 如果开发者发布多个版本的同一功能并将其暴露给不同的受众,称为A/B测试或实验
  • 实验标记通常用于通过测量用户如何与功能版本交互的某些指标来确认或排除假设
  • 实验标记高度动态,并依赖于许多上下文来使用它们以针对不同的目标受众
  • 2-权限标记
  • 功能标记的一个常见用例是控制用户可以访问的内容
  • 这可以是仅向特定受众暴露的管理功能或测试功能
  • 或仅向付费客户暴露的高级功能
  • 3-操作标记
  • 有些标记用于应用程序的运营方面
  • 例如用于禁用某些功能的故障开关(也称为断路器)​
  • 这些功能可能会成为其他功能的瓶颈
  • 控制后端系统不同版本的标记也被认为是操作标记
  • 4-简化的分支策略
  • 功能标记和基于主干的开发往往是相辅相成的
  • 因为不同的开发人员可在同一个分支上开发不同的功能
  • 并通过启用或关闭特定功能来避免出现冲突的变更
  • 这种简化的分支策略可有效避免以下情况发生:
  • 即个别开发人员在独立的分支中工作,并且没有足够频繁地集成他们的更改,从而可能导致不必要的代码发布延迟
  • 它还允许持续交付代码,因为开发团队可以轻松关闭不完善的功能,直至功能完善为止
  • 5-持续部署
  • 通过使用功能标记,开发团队可以使用安全的方式更快捷地为用户部署软件
  • 功能标记是持续部署流程的关键组件
  • 代码发布与代码部署是分开进行的
  • 也就是说,代码可以逐步进行发布,如果发现缺陷就立即关闭功能标记
  • 修复后再重新启用,从而确保不会影响代码部署周期
  • 6-降低风险
  • 因为功能缺陷造成问题?仅需关闭功能标记即可
  • 这在测试期间很有帮助,必要时,可以通过快速关闭功能标记来减少该类问题所造成的影响
  • 因为系统性能造成问题?
  • 只需在一段时间内关闭不太重要的功能,直到该系统性能问题得到解决
  • 担心如何接收新功能或修改过的功能?
  • 只需为一小部分用户提供此类功能,并征求他们的反馈意见

主干开发

  • 主干开发是一种源代码控制分支模型
  • 开发者将小而频繁的更新合并到单个分支
  • 并抵制创建其他长期开发分支
  • 基本思想是主分支始终处于干净状态
  • 以便任何开发者在任何时候都可以基于成功构建的主分支创建新分支
  • 为了保持分支处于干净状态,开发者必须采取多种措施来确保只有不会破坏任何内容的代码才能被合并到主分支
  • 如下所述:
  • 1-从主分支获取最新的变更
  • 2-执行清洁测试
  • 3-运行所有测试
  • 4-与团队有高度的凝聚力(结对编程或代码审阅)
  • 合并的复杂度在很大程度上取决于修改的代码
  • 这需要考虑以下几点:
  • 1-是使用现有代码还是新代码
  • 2-是有很多依赖关系的复杂代码,还是简单代码
  • 3-使用的是孤立的代码还是高耦合的代码
  • 4-有多少人在同时更改代码
  • 5-是否同时对很多代码进行重构
  • 人们倾向于使用时间而非复杂度来进行衡量
  • 原因是复杂度没有好的衡量标准
  • 所以,如果开发者在完成一个更为复杂的功能,则至少应该以每天一次的频率将变更合并到主分支
  • 但如果变更很简单,那么让分支/PR长时间开放是没有问题的
  • 请记住,这与时间无关,而与复杂度有关!

主干开发-Git工作流

  • 1-Gitflow
  • Gitflow仍然是最流行的工作流之一
  • 它对如何解决git中的问题进行了描述性的介绍
  • 例如,如何使用标签发布和处理合并后删除的分支
  • 800
  • 如果企业每隔几个月将软件发送给不同的客户,想要将一些功能绑定到单独许可的新主要版本
  • 并且需要持续多年地维护多个版本,那么Gitflow非常有
  • 但在复杂的环境中,该工作流会引发一些问题
  • 它不是基于主干的,并且有多个长期存在的分支
  • 在复杂环境中,这些分支之间的集成可能导致合并困境
  • 随着DevOps和CI/CD实践的兴起,Gitflow名声渐差
  • 2-GitHub flow
  • GitHub flow非常注重与PR的结合
  • 首先开发者创建一个具有描述性名称的分支并进行第一次变更
  • 然后创建一个PR并通过代码的审阅意见与审阅者合作
  • 一旦PR准备就绪,它会在合并到主分支之前被传送到生产环境
  • 800
  • GitHub flow是基于主干的,且非常流行,不包含部署PR的基本部分是大多数其他工作流的基础
  • 问题就在于部署,将每个PR部署到生产中会造成瓶颈,并且不易扩展
  • GitHub本身使用ChatOps和部署队列来解决这个问题,但这似乎有点矫枉过正
  • 只有在生产环境中被证实有效的更改才被合并到主分支中,这个观点是有说服力的
  • 但是在复杂的环境下,这个目标基本无法达成
  • 企业将需要相当长的时间来查看在生产环境中隔离运行的变更,以真正确保它们不具有破坏性
  • 但在这段时间内,该瓶颈会阻止其他团队或团队成员合并他们的更改
  • 在具有快速失败和前滚原则的DevOps世界中,最好在隔离环境中验证PR
  • 并在使用主分支的push触发器合并PR后将它们部署到生产环境中
  • 如果这些变更对生产环境产生了破坏,仍然可以部署上一个有效的版本(回滚)​,或者修复错误并立即部署修复(前滚)​
  • 不推荐GitHub flow的另一个原因是它对用户、分支和PR的数量不是很明确
  • 一个功能分支可能意味着多人向同一个功能分支提交代码
  • 虽然这种情况较少发生,但仅从文档来看它并没有明确说明
  • 3-Release flow
  • Release flow(发布流)基于GitHub flow
  • 但它不是连续部署PR,而是添加单向发布分支
  • 分支不会合并
  • 并且修复遵循上游优先原则:错误在基于主分支的一个分支中修复后,更改被拣选到发布分支中的一个分支里
  • 这样,就不可能忘记对主分支进行错误修复了
  • 800
  • 发布流不是持续部署
  • 创建发布仍然是一个必须单独触发的过程
  • 如果必须维护软件的不同版本,发布流是一种很好的方式
  • 条件允许的情况下,应该尽量做到持续部署
  • 4-GitLab flow
  • GitLab flow也基于GitHub flow
  • 它添加了环境分支(例如开发、暂存、预生产以及生产)​
  • 并且每次部署都是在合并到这些环境时发生的
  • 800
  • 由于变更仅流向下游,因此用户可以确保所有变更在所有环境中都进行了测试
  • GitLab flow也遵循上游优先的原则
  • 如果在其中一个环境中发现错误,可以创建一个基于主分支的功能分支,并拣选该更改,使其对所有环境生效
  • 错误修复在GitLabflow中的工作方式与在发布流中的工作方式相同

软件架构-松散耦合系统

  • 所有曾经在紧耦合的单体应用上工作过的开发者都知道它所引起的问题:
  • 沟通的开销和执行更大变化所需的会议;
  • 在修复了应用程序的另一部分的错误之后,又出现了新的错误;
  • 其他开发者的改变破坏了原有的功能
  • 所有这些问题都会导致开发者对集成和部署的恐惧,并减慢开发速度
  • 在设计系统和软件时,开发者应该关注以下特点:
  • 可部署性:每个团队能否独立于其他应用或团队发布他们的应用
  • 可测试性:每个团队能否在不需要必须同时部署来自其他团队的多个独立解决方案的测试环境的情况下完成大部分测试

软件架构-微服务

  • 松散耦合系统最常见的架构模式是微服务模式
  • 这是一种将单个应用开发为小型服务套件的方法
  • 每个服务都在自己的进程中运行,并通过轻量级的机制进行通信,通常是HTTP API资源
  • 微服务从面向服务的架构(SOA)中演变而来,具有一些额外的特征
  • 微服务具有去中心化数据管理的特点——意味着每个服务都完全拥有自己的数据
  • 此外,微服务支持轻量级的消息传递,而不是用于服务间通信的复杂协议或中央编排——智能端点和傻瓜式管道
  • 微服务的一个重要特征经常被忽略——它们是围绕着业务能力建立的
  • 这也定义了一个服务应该有多小
  • 为了定义服务的范围,开发者必须了解业务领域
  • 在领域驱动设计中,一个微服务与一个限界的上下文相匹配
  • 微服务的另一个特点是完全独立部署和可测试的
  • 这就是为什么它们与高工程速度有关的原因
  • 微服务有很多优点
  • 它们的扩展性非常好,因为开发者可以独立地扩展每个服务
  • 它们还允许每个团队用自己的编程语言和数据存储解决方案工作,以最好地满足其需求
  • 最重要的是,它们允许开发大型复杂应用的团队在不干扰其他团队的情况下快速开发
  • 但这些优势是有代价的
  • 基于微服务的应用程序很复杂,很难操作和排除故障
  • 有许多著名的基于微服务的解决方案——例如,Netflix和Amazon
  • 它们运行全球级规模的服务,并有一个允许他们每天部署数千次的架构
  • 但也有很多企业试图实施微服务,最终都失败了
  • 失败的新兴项目的数量尤其多,其原因往往是缺乏对业务领域的了解,以及对每个服务的界限上下文的错误定义
  • 特别是当应用程序是由外部企业开发的时候,他们还没有学会该领域的适用语言
  • 另一个原因是,他们低估了操作服务的复杂性
  • 因此,与其实施微服务,不如关注架构的可部署性和可测试性特征,并根据需求调整方案设计
  • 需求不是一成不变的,它会随着时间的推移而变化——开发者的架构也应该如此

软件架构-进化式设计

  • 某些架构风格的优势和劣势因各种原因而转变
  • 一个是开发者的应用程序的规模,另一个是对开发者的领域和客户的了解以及规模化运作的能力
  • 根据这些因素,不同的架构风格更适合开发者
  • 800
  • 进化式设计即不断调整架构和系统设计以适应当前的需求
  • 要启动全新产品,最好从单体方法和一个团队开始
  • 这可以让开发者在没有太多开销的情况下快速行动
  • 如果规模扩大并对这个领域有了更多的了解,就可以开始使用编程语言的功能来模块化应用程序了
  • 在某一时刻,复杂性和规模将变得非常高,以至于需要微服务来帮助开发者保持产品的可测试性和可部署性
  • 那么如何从已有的架构中得出需要的架构
  • 完全重写是非常昂贵并且有风险的
  • 更好的方法是逐步演化设计
  • 开发者不应重写自己的应用程序,而是在它周围长出一个新的“绞杀植物”的应用程序
  • 让它逐渐成长,直到旧系统被扼杀并可以关闭

软件架构-事件驱动架构

  • 除了微服务、单体和多层应用程序之外
  • 还有其他架构风格——例如,事件驱动架构(EDA)
  • 事件驱动架构是一种围绕事件的发布、处理和持久化的模式
  • 主干是消息代理(例如Apache Kafka),而各个服务或组件可以发布事件(发布者)或订阅事件(订阅者)
  • 事件驱动架构可以很好地适应基于微服务的方法,它还可以与其他架构风格一起使用
  • 它可以帮助开发者在松散耦合的组件或服务中保持一致性,由于事件的异步性质,它可以完美地水平扩展
  • 因此非常适合处理大量动态数据的解决方案
  • 例如近乎实时处理传感器数据的物联网解决方案
  • 特别是在云原生环境中,事件驱动架构可以帮助开发者快速移动
  • 在很短的时间内建立松散耦合和全局可扩展的解决方案
  • 一个经常被用于事件驱动架构的模式是事件溯源
  • 事件溯源不是持久化实体,而是将应用状态的所有变化(包括实体)作为一个事件序列来捕获
  • 要检索一个实体,应用程序必须重放所有的事件以获得最新的状态
  • 由于事件是不可改变的,这提供了一个完美的审计跟踪
  • 开发者可以把事件流看作是一个不可变的事实流,它可以被看作是唯一的真相来源
  • 除了可审计性之外,事件源在可扩展性和可测试性方面也有很多好处
  • 如果开发者需要捕获数据的意图、目的或原因,需要避免更新冲突,以及必须保留历史记录并经常回滚更改时
  • 事件溯源是一种合适的模式
  • 事件溯源与命令和查询责任隔离(CQRS)配合得非常好
  • CQRS是一种读写分离的模式
  • 但要注意的是,事件溯源非常复杂,大多数开发人员都不会自然而然地在事件中对域进行建模
  • 如果上述标准不适合企业产品,那么事件溯源可能不是一个好模式
  • 一种更适合于简单领域的架构风格是Web-Queue-Worker
  • 这是一种主要用于无服务器PaaS组件的模式
  • 它由一个服务于客户端请求的Web前端和一个在后台执行长时间运行任务的工作程序组成
  • 前端和后端是无状态的,并使用消息队列进行通信
  • 该模式通常与其他云服务相结合,如身份提供者、数据库、Redis缓存和内容分发网络(CDN)
  • Web-Queue-Worker是开始使用云原生应用程序的良好模式
  • 无论选择什么样的架构风格,都要尽可能地保持简单
  • 从简单的开始,并随着时间的推移和需求的增加而不断发展设计
  • 而不是过度设计,最终得到一个复杂的解决方案

团队赋权-康威法则

  • 一种组织结构和软件架构之间的关联性被称为康威法则
  • 康威法则可以追溯到1968年的一篇文章:
  • “设计系统的组织被限制在生产设计上,这些设计是组织的通信结构的副本”
  • 该法则并非专门针对软件或系统架构,而是针对任何系统的设计
  • 请注意:它不是指一个组织的管理结构,而是指其沟通结构
  • 这两者可能是相同的,但在某些情况下却并非如此
  • 通常情况下,如果组织结构图与软件设计不匹配,可以寻找沟通流,它与组织结构图是不同的
  • 例如
  • 如果企业有许多小团队或个人开发者,他们从不同的客户或顾问那里获得需求
  • 可能会没有任何组织界限地相互交谈
  • 他们正在开发的系统将反映这一点,并由许多具有高度内聚力的模块组成
  • 这些模块相互引用——这就是所谓的意大利面式架构
  • 而那些一起工作的团队,通过一个沟通渠道(例如产品负责人)接收他们的需求
  • 将建立一个在团队工作的模块中具有高度内聚力的系统
  • 但其他团队工作的系统部分将有较少的引用
  • 用Eric S.Raymond的话来说,“如果三个团队都在做一个编译器,你会得到一个三通编译器”
  • 800

团队赋权-双比萨团队

  • Amazon的架构是讨论最多的基于微服务的架构之一
  • 它允许每天进行数千次大规模部署
  • 他们在团队设置中使用双比萨规则:
  • 在日常生活中,一张比萨大约可供3~4人食用
  • 这意味着团队规模大约为6~8人
  • 这个规则意味着团队应该是小规模的
  • 大团队的问题是,每增加一个团队成员,团队中的链接数就会迅速增长
  • 一个有6名成员的团队在成员之间有15条链接——而一个有12名成员的团队已经有66条链接
  • 800
  • 如果人们在团队中工作,他们会体验到一种积极的协同效应,多样性和沟通有助于提高质量以及结果
  • 但是,如果团队中增加更多的人,沟通的开销和决策的迟缓就会导致负面的协同效应
  • 800