累积的技术债务可能会使您的公司陷入危机。 但是它也可能成为大规模过程变更的强大驱动力,并有助于采用工程实践。 我将以自己的示例向您介绍。

在过去的7年中,
Dodo Pizza IT团队从为一个国家/地区服务的2名开发人员成长为为12个国家/地区服务的60人。 作为Scrum Master和XP教练,我帮助团队建立了流程并采用了工程实践,但是这种采用速度太慢了。 当多个团队在一个产品上工作时,要使团队保持高代码质量对我来说是一个挑战。 我们陷入了对业务功能而不是技术卓越的偏爱陷阱,并且积累了太多的架构技术债务。 当市场营销在2018年发起大规模的广告活动时,我们无法承受重担而倒下。 真可惜。 但是在危机期间,我们意识到我们能够更有效率地工作许多次。 这场危机将我们推向了过程的革命性变化,并迅速引入了著名的工程实践。
背景知识
您可能会认为Dodo Pizza是一个常规的比萨店网络。 但是实际上
Dodo Pizza是一家出售披萨的IT公司 。 我们的业务基于
Dodo IS (基于云的平台),该平台管理所有业务流程,从接订单,烹饪到完成,库存管理,人员管理和决策。 在短短7年中,我们已从2个为单个比萨饼店提供服务的开发人员成长为在12个国家/地区为470个比萨饼店提供服务的70多个开发人员。
当我两年前加入公司时,我们有6个团队和大约30个开发人员。 Dodo IS代码库具有大约100万行代码。 它具有整体架构,几乎没有单元测试范围。 到那时我们还没有API / UI测试。 该系统的代码质量令人失望。 我们知道了这一点,并梦想着将广阔的市场分成十二个服务,并重写系统中最丑陋的部分,从而拥有美好的未来。 我们甚至绘制了“成为建筑”图,但是说实话,对未来状态几乎没有做任何事情。
我的主要目标是教给开发人员工程实践并建立开发流程,使6个团队共同开发一个产品 。
随着团队的成长,我们更加缺乏清晰的流程和工程实践。 我们的发行版变得越来越大,因为越来越多的开发人员对该系统做出了贡献,但是我们没有自动回归测试,因此每个发行版都浪费了更多时间进行手动回归。 6个团队在不同的分支机构中进行了许多更改,这使我们遭受了痛苦。 当团队在发布之前将更改合并到单个分支中时,有时我们通常会花费多达4个小时来解决合并冲突。
狗屎发生
在2018年,Marketing 在电视上进行了我们的首次联邦广告宣传 。 对我们来说,这是一个巨大的事件。 我们花费了1亿卢布(150万美元)来资助这项运动-对我们来说是一笔不小的数目。 IT团队也为竞选做准备。 我们实现了自动化并简化了部署-现在只要在TeamCity中单击一个按钮,就可以将整体部署到12个国家/地区。 我们调查了性能测试并进行了漏洞分析。 我们尽力了,但是遇到了意想不到的问题。
广告活动很棒。 我们从每分钟100订单增加到300订单。 这是个好消息。 一个坏消息是,渡渡鸟IS无法承受如此大的负担而死。 我们达到了纵向扩展限制,无法再处理订单。 系统每3小时重启一次。 每分钟的停机时间使我们损失了数百万美元的资金,并且失去了愤怒的客户的尊重。
2年前,我以首席敏捷官的身份来到Dodo时,我非常渴望组建我们当时很小的团队-一支约12人的梦幻团队。 我立即开始介绍工程实践。 大多数团队很快就采用了结对编程,单元测试和DDD。 但是,并非一切都容易。 我必须克服开发人员,产品负责人和支持团队的阻力。
与工程实践相反,并不是每个人都喜欢功能团队的想法。 开发人员习惯于认为专注于一个组件的团队正在编写更好的代码。 目前尚不清楚如何将业务功能的快速发展与早就应该进行的复杂系统的大规模重构相结合。 连续不断的错误也需要不断关注。 支持团队赞成让自己的团队只专注于错误修复。 许多团队习惯于在单独的分支机构中工作,并且害怕经常整合。 我们每周发布不超过一次,并且每次发布都花了很长时间,需要大量的手动回归和UI测试支持。 我试图修复它,但是我的过程更改太慢且分散。
兴衰的故事
初始状态:整体架构
在追求快速发展业务功能的过程中,我们并不总是仔细考虑技术解决方案。 缺乏经验也影响了我们。 最初,公司负担不起聘请最佳开发人员的费用。 我们与愿意帮助创建Dodo IS的发烧友一起工作,他们相信公司的创始人Fedor,并且几乎为食物工作(当然是披萨)。 后来加入团队的开发人员遵循了已建立的架构,但该架构已过时。 因此,我们有了一个具有单个数据库的整体应用程序,该数据库将所有组件中的所有数据都存储在一个地方。 跟踪器,结帐,站点,登陆页面的API-系统的所有组件都与单个数据库一起工作,这成为了瓶颈。 我们的整体架构产生了整体问题。 一篇博客文章导致一家餐厅结帐中断。
真实的故事
为了说明整体架构的脆弱性,我仅举一个例子。 一旦我们在俄罗斯的所有餐馆都因为博客帖子而停止接受订单。 怎么会这样
有一天,我们的首席执行官-Fedor在他的博客上发表了一篇文章。 这篇文章很快获得欢迎。 Fedor博客网站上有一个柜台,显示我们连锁店中的许多比萨店和所有比萨店的总收入。 每当有人阅读Fedor的博客时,网络服务器都会向master数据库发送请求以计算收入。 这些请求使数据库超载,并且停止了服务餐厅结帐的请求。 因此,一个受欢迎的博客文章打乱了整个餐饮连锁店的工作。 我们迅速解决了这个问题,但是(在许多其他问题中)一个明显的信号是我们的体系结构无法满足业务需求,因此必须重新设计。 但是我们忽略了这些迹象。 我们实现了快速简便的解决方案(例如添加主数据库的只读副本),但是我们没有任何技术上的重新设计路线图。
整体架构很容易上手,因为它很简单。 但是它不能承受高负载是单点故障。
2017年初失败
2月14日是情人节 。 每个人都喜欢这个假期。 为了让恋人互相祝贺,我们在2月14日制作了一个特别的比萨饼-心形香肠。 我会永远记得2017年2月14日,因为在这一天,所有比萨店都在满负荷工作时,Dodo IS开始倒塌。 在每个比萨店中,我们有4-5片用于跟踪比萨饼制造商订购面团的订单,放入配料,烘烤或送达。 比萨店的数量达到300多个,每片每分钟更新一次状态。 所有这些请求在数据库上造成了巨大的负担,以致SQL Server不再承受并且数据库开始出现故障。 在销售最高峰的时候,Dodo IS放下的时间最不合适。 即将到来的假期是一个繁忙的假期:2月23日(陆军和海军日),3月8日(国际妇女节),5月1日(国际工人团结日)和5月9日(第二次世界大战胜利日)。 在这些假期中,我们预计订单量会更大。
你会死的那天 。 了解了我们的增长计划以及我们可以承受的负载极限后,我们计算出可以存活多长时间,也就是今天的高峰负载变得正常时。 世界末日的估计日期预计在六个月左右-8月或9月。 知道您的死亡日期,生活如何?
停止功能开发一年 。 我们必须与首席执行官Fedor一起做出艰难的决定。 也许是公司历史上最困难的决定之一。 我们停止了业务功能的开发。 我们以为我们会停3个月,但是很快我们意识到技术债务的数量如此之大,以至于3个月还不够,我们必须继续处理技术问题并推迟业务积压。 实际上,在明年,只有6个团队,我们只做过一项业务。 其余时间,这些团队都在偿还技术债务。 这笔债务使我们付出了巨大的代价-超过150万美元。
一年后有所改善
一年来,我们取得了以下显著成就:
- 我们已经自动化并加快了部署流程。 以前,部署是半手动的。 我们在大约2个小时内部署到了10个国家。
- 开始分裂巨石。 系统的最大负载部分-跟踪器-被拆分为具有自己的数据库的单独服务。 现在,跟踪器通过事件队列与系统的其余部分进行通信。
- 我们开始分离出纳员-造成高负荷的第二个组成部分。
- 重写了用户和设备身份验证系统。
我们的架构师负责管理技术积压。 我们使用体系结构更改来推动积压。 每个团队都可以自由地做正确的事情来创建有用的架构。 在第6支团队的一年中,我们为业务所做的只有一项重要功能。 其余时间,这些团队致力于解决技术债务问题。 看来我们可以为自己感到骄傲。 但是在我们面前有巨大的失望。
在联邦营销活动中失败。 第二次信任危机。
技术债务很容易积累,但很难偿还。 您不太可能事先知道要花多少钱。
尽管我们花了整整一年的时间来解决技术债务问题,但我们还没有准备好进行大规模的市场营销活动,因此陷入了业务困境。 您对丢包赢得了信任,而在存储桶中却失去了信任。 而且我们不得不再次获得它。
我们错过了必须放慢开发业务功能和解决技术债务的速度的那一刻。 当我们注意到时为时已晚。 我们再次承受重担。 系统崩溃,每3小时重启一次。 我们的生意损失了数千万卢布。

但是,由于这场危机,我们看到在极端条件下,我们可以更有效率地工作很多次。 我们每天发布20次。 每个人都是一个团队,专注于一个目标。 在危机的两个星期中,我们做了之前担心的事情,因为我们认为这将需要数月的工作。 异步顺序,性能测试,清晰的日志只是我们所做工作的一小部分。 我们渴望继续有效地工作,但不要加班和加压力。
经验教训
回顾之后,我们完全重组了我们的流程。 我们采用了LeSS框架,并通过工程实践对其进行了补充。 在接下来的几个月中,我们在采用工程实践方面取得了突破。 基于LeSS框架,我们已经实现并继续使用:
- 单一积压;
- 完全跨职能和跨组件的功能团队;
- 配对编程;
- 尝试暴民编程。
- 真正的持续集成,意味着在一个分支中来自9个团队的多个代码集成;
- 基于中继的开发简化了配置管理;
- 频繁发布:微服务的持续部署,每天整体发布多个版本;
- 没有独立的质量检查团队,质量检查专家是开发团队的一部分。
危机后我们选择的6种做法
1.聚焦的力量 。 在危机发生之前,每个团队都进行自己的积压工作并专门研究其领域。 在积压的任务中,有精细分解的任务,团队选择了几个任务进行冲刺。 但是在危机期间,我们的工作方式大相径庭。 这些团队没有特定的任务,而是有一个很大的挑战性目标。 例如,无论如何,移动应用程序和API必须每分钟处理300个订单。 如何实现目标取决于团队。 团队自己制定假设,在生产中快速检查它们并扔掉。 这正是我们想要继续做的事情。 团队不想成为愚蠢的编码员,他们想解决问题。
在解决复杂问题上,集中力量就体现出来了。 例如,在危机期间,尽管我们没有专门知识,但我们创建了一组性能测试。 我们还使接收订单的逻辑变得异步。 我们已经考虑了很长时间,并进行了交谈,在我们看来,这是一项非常艰巨而漫长的任务。 但是事实证明,如果他们不分散注意力并完全专注于问题,那么团队将有能力在2周内做到这一点。
2.定期的黑客马拉松 。 当所有团队都以一个目标为目标时,我们喜欢以这种模式工作,因此我们决定有时安排这样的“黑客马拉松”。 不能说我们定期执行它们,但是有几次。 例如,当所有团队清除日志并消除站点和API中500个错误的原因时,发生了500个错误的黑客马拉松。 目的是保持原木清洁。 清除日志后,新错误将清晰可见,您可以轻松配置警报的阈值。 它类似于单元测试-它们不能有点红色。
黑客马拉松的另一个例子是错误。 我们曾经有大量的错误待办事项清单,其中一些错误已经存在很多年了。 看来他们永远不会结束。 每天都有新的。 您必须以某种方式将错误和常规积压项目的工作结合起来。
我们通过4个步骤介绍了#zerobugspolicy策略。- 基于日期的初始错误清除。 如果该错误在待办事项集中的时间超过3个月,则只需将其删除。 很可能它存在了很长时间。
- 现在,根据剩余缺陷对客户的影响方式对其进行分类。 我们仔细地整理了剩余的错误。 我们只保留了使大量用户难以生活的缺陷。 如果这只是造成不便的原因,但是您可以解决-狠狠地删除。 因此,我们将错误的数量减少到25,这是可以接受的。
- Hackathon。 所有团队蜂拥而至,并修复了所有错误。 我们在几个冲刺中做到了这一点。 每个团队的每个冲刺都遇到了几个错误,并进行了修复。 经过2-3次冲刺后,我们积压了许多错误。 现在您可以输入#zerobugspolicy。
- #zerobugspolicy。 现在,每个新错误只有两种方式。 以太它是否会积压。 如果积压,我们将首先修复它。 积压中的任何错误的优先级均高于其他任何积压项。 但是为了进入积压,该错误必须很严重。 它会造成无法挽回的伤害,或者会影响大量用户。
3.临时项目团队到稳定的功能团队 。 项目团队有一个有趣的故事。 在危机期间,我们组建了一支由最熟练的老虎团队组成的团队。 危机结束后,团队决定继续这种做法并解散团队。 尽管我一点都不喜欢这个主意,但我还是让他们尝试了。 在短短2周内(一次冲刺),在下一次回顾中,团队放弃了这种做法(这一决定让我感到非常高兴)。 他们尝试并理解了为什么在稳定的功能团队中工作要舒适得多。 即使团队缺乏一些技能,他们也可以逐渐学习。 但是团队精神,支持和互助已经形成了很长时间,这需要几个月的时间。 短期项目团队一直处于形成和发展阶段。 您可以忍受几个星期,但不能一直这样工作。 团队尝试并了解稳定功能团队的好处是很好的。
4.摆脱手动回归 。 在危机之前,我们每周发布一次,在危机期间-每天发布数十次。 我们喜欢我们经常释放的能力。 我们赞赏进行小更改,快速部署它并立即从生产中获得反馈是多么方便。 因此,我们将发布方式更改为,从而影响了编程和设计的方式。 现在,我们每1-2天不断释放。 开发分支中的所有内容都已投入生产。 即使某些功能尚未准备就绪,这也没有理由不发布代码。 如果我们不想向用户显示一些尚未就绪的功能,则可以使用功能切换将其隐藏。 这种方法有助于我们逐步发展。
我们设定了摆脱手动回归的目标。 我们花了1.5年才达到目标。 但是,拥有一个长期的宏伟目标会使您思考实现该目标的步骤。
我们分三步完成了。- 自动化关键路径。 2017年6月,我们成立了质量检查小组。 该团队的任务是自动化Dodo IS最关键功能的回归-接单和生产。 在接下来的6个月中,新的质量检查团队由4人组成,涵盖了整个关键路径。 功能团队开发人员积极帮助他们。 我们在一起共同编写了一种美丽且易于理解的特定领域语言(DSL),即使客户也可以阅读。 与端到端测试并行,开发人员使用单元测试覆盖了代码。 使用TDD重新设计了一些新组件。 之后,我们解散了质量检查小组。 之前的质量检查小组成员加入了功能小组,以分享有关如何支持和维护自动测试的专业知识。
- 阴影模式。 进行自动测试后,在5个发行版中,我们以影子模式进行了手动回归。 团队仅依赖于自动化测试套件,但是当团队决定“我们准备发布”时,我们将进行手动回归以检查我们的自动测试是否遗漏了任何错误。 我们跟踪了手动检测到但自动测试未捕获的错误。 在发布5个版本之后,我们检查了数据并决定我们可以信任我们的自动测试。 没有重大的错误被错过。
- 删除手动回归。 当我们有足够的测试使我们开始信任它们时,我们便完全放弃了手动测试。 我们运行测试的次数越多,我们对它们的信任就越高。 但这是在我们开始自动化回归测试仅1.5年之后发生的。
5.性能测试是回归测试的一部分 。 在危机期间,我们创建了一组性能测试。 对我们来说,这是一个全新的领域。 但是,在短短2周的时间内,我们设法使用Visual Studio工具创建了一些性能测试。 这些测试帮助我们不仅发现了性能下降。 我们使用它们将合成负载添加到生产服务器中,以确定性能限制。 例如,如果自然生产负荷为100个订单/分钟,并且在性能测试的帮助下,我们又增加了50个订单/分钟,则可以查看生产服务器是否可以处理增加的负荷。 一旦发现异常或延迟增加,我们便停止测试。 通过进行这些实验,我们弄清了生产服务器可以处理的最大负载以及热点。
明年,我们将性能测试的工作交给了经验丰富的PerformanceLab团队。 他们与我们的开发人员和基础架构人员一起坐在一起,并帮助我们创建了一套强大的性能测试。 现在,我们每周运行这些测试,并在性能受到影响时向开发团队提供快速反馈。
某些工程实践经过反复完善。 例如,频繁发布。 我们从每周发布周期开始,并通过缓慢而脆弱的手动测试进行支持。 我们开发了一个星期的功能,并测试了另一个星期。 但是很难维持一个星期内多个用户所做的更改。 然后,当只发布单个团队所做的更改时,我们尝试了隔离的团队发布。 但是此过程失败了,因为每个团队都不得不在队列中等待数周。 然后,团队学习了频繁集成的好处,我们开始练习多个团队变更的联合发布。 开发人员开始尝试使用功能切换,并将未完成的功能推送到生产中。 最终,我们来到了持续集成和每天针对单片的多个发行版以及对微服务的持续部署。

另一个有趣的案例是我们的质量检查部门。 我们以前没有质量检查团队,而是有手动测试人员。 意识到测试自动化的需求后,我们成立了质量检查小组,但是从第一天起,这个小组就知道将有一天解散。 6个月后,该团队使我们的关键业务场景自动化,并在开发人员的帮助下编写了一种方便的领域特定语言(DSL)来编写测试。 团队分手了,质量工程师加入了功能团队。 现在,团队自己开发和维护自动测试。
今天,我们只有一个待办事项清单,有9个功能团队正在努力。 功能团队是稳定的,跨职能的,跨组件的团队。 我们的大多数团队都是功能团队。
6.关注工程实践 。 我们所有的团队都使用结对编程。 我认为结对编程是最简单但功能强大的实践之一,可帮助实现其他工程实践。 如果您不知道要开始哪种工程实践,则建议使用配对编程。
结果
危机给我们带来的主要结果是一场大变革。 我们醒了,开始行动。 这场危机使我们看到了最大的机会。 我们看到,我们可以更高效地工作许多次,并迅速实现我们的目标。 但这需要更改常规的工作方式。 我们不再害怕进行大胆的实验。 这些实验的结果是,在过去的一年中,我们大大提高了Dodo IS的质量和稳定性。 如果在2018年的春假期间我们的披萨店由于Dodo IS而无法正常工作,那么在2019年,披萨店的数量从300家增加到450家,Dodo IS完美地工作了。 在第二次市场营销活动和春季假期期间,我们悄悄地体验了新年的销售高峰。 这是很长一段时间以来的第一次,我们对系统的质量充满信心,并在晚上睡个好觉。 这是不断采用工程实践并专注于技术卓越的结果。
业务成果
如果工程实践对您的业务没有好处,则它们本身就不需要。 由于专注于技术卓越,因此我们提高了代码质量并以可预测的速度开发了业务功能。 发布已成为我们的常规活动。 我们每2天释放一次整体,每隔几分钟释放一次较小的服务。 这意味着我们可以快速为用户提供业务价值并更快地收集反馈。 由于功能团队的灵活性,我们获得了高速的开发。
今天,我们有480家比萨店在线,其中有400家在俄罗斯。 在今年的五月假期期间,我们的比萨店再次遇到了订单处理方面的问题。 但这次瓶颈是比萨店的客户服务。 尽管比萨店和订单越来越多,但Dodo IS的运转却像发条一样。
团队结果
今天,我们采用了多种工程实践:
- 完全跨职能和跨组件的功能团队。
- 配对编程/生物编程。
- 真正的持续集成,意味着在一个分支中来自9个团队的多个代码集成。
- 基于主干的开发简化了配置管理。
- 多个团队的共同目标。
- 主题专家在团队中。
- 没有独立的质量检查团队,质量检查专家是开发团队的一部分。
- 最终使用自动测试替换手动回归。
- 零错误政策。
- 技术债务积压。
- 停止将Line作为部署管道加速的驱动程序。
他们帮助9个团队处理一个通用代码和一个包含数十个组件的单一产品-一个移动和桌面站点,一个iOS和Android移动应用程序以及一个具有收银机,跟踪,餐厅展示,个人帐户的庞大后台,分析和预测。
有什么可以更好的
看来我们在工程实践上已经取得了良好的进展,但是我们只是起步阶段,仍有发展空间。 例如,我们尝试,但到目前为止还没有系统地进行mob编程。 我们研究了BDD测试编写方法。 我们在CI方面仍有增长的空间,我们知道即使每天进行一次集成也是不够的。 当我们成长为30个团队时,有必要更频繁地进行整合。 从TDD到ATDD的过渡我们仍在进行中。 我们必须创建一个可持续且可扩展的体系结构决策过程。
最重要的是,我们要加强技术卓越。
由于所有9个团队都在处理一个共同的待办事项和一个产品,因此团队强烈希望彼此合作。 他们学会了自己做出强有力的决定。
例如,团队自己提出并实施了以下做法。- 停止将Line作为部署管道加速的驱动程序(请参阅我的经验报告“停止生产线以简化部署管道”)。
- 用API测试替换UI测试。
- 单击自动部署。
- 托管Kubernetes。
- 开发团队部署到生产。
一些团队表示希望使用所有12种XP实践,并以XP Coach和Scrum Master的身份向我寻求帮助。
我们学到了什么
我希望我不要让危机发生。 作为开发人员,我感到自己有责任积累过多的技术债务,并且不提早发出警告:
- 工程实践可保护企业免受危机影响。
- 不要积累技术债务。 可能为时已晚,成本也太高。
- 进化的变化要比革命的变化长几倍。
- 危机并不总是一件坏事。 利用危机来革新流程。
- 但是,需要预先进行长期的进化准备。
- 不要盲目地实施您喜欢的所有实践。 一些练习正在等待中,当他来时,车队将毫无阻力地使用它们。 等待正确的时刻。
- 优化并适应您的情况。
- 随着时间的流逝,团队本身开始做出强有力的决定并加以实施。 给他们一个安全的环境,让他们尝试,失败并学习错误。
技术债务使我们陷入危机。 但是,从危机中,开发人员和商人都了解了专注于卓越技术和工程实践的重要性。 我们以危机为触发点,进行了大规模的组织和流程变更。
致谢
我要对所有帮助我从危机到LeSS转型的人表示极大的感谢。 我不断感受到您的支持。
非常感谢我们的首席执行官Fedor Ovchinnikov的信任。 您是拥有真正敏捷文化的公司的真正领导者。
非常感谢产品负责人Dmitry Pavlov,我的老朋友和培训师。
感谢Alex Andronov和Andrey Morevsky支持我担任我的角色。
非常感谢我们的第一个全职Scrum大师Dasha Bayanova,他相信了我,并始终以我的全力帮助和支持我。 您的帮助很难被高估。
特别感谢约翰娜·罗斯曼(Johanna Rothman),他在任何情况下都帮助我撰写了这份报告:休假,生病后康复。 约翰娜,很高兴与您合作。 非常感谢您的帮助,建议,对细节的关注和勤奋。