如何加快大型遗留应用程序的开发速度?
软件公司的规模多种多样,包括小型初创公司、中型企业和大型企业。初创公司通常具有灵活性和迅速响应的特点,而大型公司则在开发庞大应用程序时进展较为缓慢。这些大型应用可能由数百名开发者耗费数年甚至几十年的时间开发而成,例如亚马逊市场、AutoCAD 或各种操作系统。考虑到它们雇佣的工程师数量,这些产品发布新功能或修复错误需要相当长的时间。以苹果为例,尽管 iOS 可能有数千名开发者,但每个版本之间的差异相对较小。
一些大型项目被称为 “遗留应用程序”,因为它们采用旧技术、积累了大量技术债务,难以进行修改。然而,进展缓慢、有传统感的项目并不仅限于企业、老系统,甚至不仅限于大型开发团队。许多现代应用程序也遭受相同的症状,即使它们是新的、采用最现代技术。可能存在复杂的业务逻辑,使得很难在不破坏其他部分的情况下进行修改。或者组织可能充斥着各种政策和官僚主义,导致工程师花费更多时间等待而非编写代码。又或者可能存在有缺陷的 CI/CD 流程,因此大量时间用于解决构建问题和冲突,而非开发新功能。许多问题都可能使项目变得像遗留项目一样。那么,是什么导致项目开发缓慢呢?这是否是任何老产品都难以避免的命运?我们是否能够预防或解决其中的一些问题?
下面,我们来讨论一下大型和遗留应用程序开发过程缓慢的八个原因。
我们先来具体定义一下这种复杂性。到底是什么让这些项目变得复杂并减缓了开发呢?
在一个庞大的产品中,有太多的功能和微妙之处,没有人能够全面了解。因此,当你添加新功能或进行小改动时,很容易出错。有时可能只是一个失败的测试,但有时会导致客户投诉或在生产中发现一些问题。发现缺陷的阶段越晚,修复的时间就越长。最糟糕的情况是客户在生产中发现问题。当你拥有成千上万的功能和数千万的用户时,这种情况经常发生,而且会花费大量时间。大型项目工作的一大部分时间都花在修复缺陷上。
随着时间的推移,产品经理提出了新功能。这些功能往往是草草加入的,而没有对系统进行重构,使得抽象层能够匹配新的功能集合。于是,这些添加、修改和修补是不断堆积的,使得代码库变得杂乱无章。当然,有一些技术手段可以最小化这种情况。你可以将项目拆分成微服务,分配更多时间进行重构,并实践 TDD 以设计一个松散耦合的系统。但现实与理论并不总是相符。当开发人员面临在 1 天内完成任务的选择,而不是额外请求两周的时间进行重构时,结果通常是选择前者。将这种情况乘以 1000,持续五年,你就得到了一个典型的企业软件项目。
尽管理论上自定义自动化应该成为项目构建流程的一部分,例如代码检查工具、代码生成机制、文件签名自动化等,但实际上这些机制经常出现问题。工程师需要花费大量时间了解如何修复它们或绕过它们,尤其是对于新工程师,当项目足够大时,他们可能在 “新手” 状态下停留多年。
除了自定义构建流程之外,大型项目还伴随着大量的内部工具,包括但不限于自定义测试工具、自定义部署工具、代码检查工具、转译器、静态分析工具、配置控制器、CI/CD 系统、专有源代码控制系统、文档引擎、代码审查工具、安全工具、监控工具和许可证生成器。了解所有这些工具是一项全职工作,而且随着每个自定义工具的增加,新工程师的入职时间也会变得更长。此外,这些工具并不总是完全可用,经常需要停止开发来修复它们中的错误,或者等待其他人来解决问题。
随着开发人员数量的增加,项目中的冲突也会增多。这些冲突可能是实际的合并冲突,也可能是由另一个团队引入的错误。对于每一个冲突,都需要停止当前工作并解决冲突,通常需要与另一个团队合作,这可能导致一系列延迟(详见下一节)。
有一些分支策略可以让你 “分支” 一段时间并在没有中断的情况下工作,但这并非解决所有问题的灵丹妙药。如果分支时间过长,合并将变得更加痛苦。如果分支时间太短,问题会更加频繁地出现。
正如预期的那样,项目越大,构建时间就越长。在庞大的项目中,构建时间同样庞大。虽然构建时间可以通过各种方式进行优化,甚至在大型项目中可能有专门的团队负责此事,但优化构建时间通常不是首要任务,因此相对较少的资源被投入其中。此外,致力于一个不断变化的目标,一个正在积极开发的项目是困难的,而且优化的速度通常慢于新问题出现的速度。
所以,我们如何处理这些问题呢?当一个应用程序变得足够庞大时,其中一些问题似乎是无法避免的。很多这些问题不管怎样都不会消失,但你可以在一定程度上减轻它们。以下是一些建议,来自我的个人经验:
将内部开发流程视为一等公民。在处理构建优化、内部工具、CI/CD 以及整体开发者体验的团队中,将一些资深工程师置于前沿。
将工程工具看待为公共工具。例如,如果你正在创建一个内部 API,就像对待为付费客户创建的 API 一样。这包括高可用性、出色的文档、向后兼容性等。
打造出色的新工程师入职流程,并保持清晰而全面的文档。
不要忽视技术债务(在合理范围内)。这可能涉及定期进行大规模重构努力或偶尔冻结功能以应对技术债务。
允许工程师参与黑客马拉松和创意周。尽管很多项目将关注产品,但每家公司都有充满激情的工程师,他们愿意致力于改善构建时间或修复内部工具。
我感觉我只是触及到了大型项目复杂性的冰山一角。我相信每家公司都有自己的痛点,如果你有一些有趣的案例,请随时在评论中分享。
然而,大型项目也有可能加速开发的优势。当某个项目经过几十年的发展时,通常会包含许多能够让你的生活变得更轻松的系统。这可能包括有用的内部包、出色的测试环境、内部配置工具,或是定制的 IDE 扩展。例如,你可能可以很快地启动一个新应用程序,因为你可以访问具有无限计算能力的云账户、快速设置 CI/CD 的方式,以及可以自动使用的监控框架。
“钱多了,合规担忧就多了”。对于科技公司来说,这一点绝对正确。在初创公司,隐私、安全和合规性可能被视为次要问题,但在大型企业中,它们却是头等大事。为了保持合规性,大公司付出了极大的努力,而这是有代价的。下面,我们来详细探讨每个合规性类别所带来的代价。
安全问题究竟如何拖慢开发进程?
在开发新功能时,大公司会实施安全政策,证明你的新功能不会引发漏洞。这可能需要填写包含一百个问题的表格,绘制多个 UML 图表,并经历一次安全审查,通常在每个步骤之间都有相当长的等待时间。
新功能是最大的障碍,但简单的代码更改也可能增加漏洞。这意味着每当你更改与攻击面积附近的任何内容时,你都需要从认证的安全官员那里获得安全批准,这可能是向走廊尽头的某人简单提问,也可能是一个充满官僚主义的漫长过程。
大公司热衷于他们的安全政策,其中一些最喜欢的是对社区项目的限制。如果你认为你可以使用你最喜欢的开源软件包,那就再想想吧。它安全吗?符合规定吗?许可证呢?是时候填写一些表格并安排委员会审查了。
如果在当今社会观念中有一样比安全更重要的东西,那就是隐私。我们曾经目睹了近年来多起引起公众关注的与隐私有关的丑闻(Meta,我在看着你)。如今,每位开发者都必须了解数据分类、GDPR 规定以及公司的政策,其中肯定有很多条款。这可能意味着拉取请求需要经过隐私审查。或者你对客户数据(如日志和遥测)的访问受到限制。或者遥测数据只能从世界上的某一个区域获取。这真是复杂。在一个没有客户的初创公司工作肯定更容易一些。
合规是指在各个方面遵守标准、法规和内部政策。安全和隐私是最关键的两个要求,此外还包括符合 SOC 和 HIPAA 等标准,遵守你所在地的法律规定,实现对客户的承诺,以及提高可访问性。不同的领域有不同的合规内容,但都不免要应对一些繁琐的程序。
忽视隐私和安全问题并不是一个好的解决方案,假设这两者必须得考虑,以下是一些建议:
在你自己的团队或小组中有工程师可以签署安全和隐私审查。
让合规的官僚主义和流程尽可能简单。如果需要填写表格,让它们简短易懂。如果需要审查,提供简便的安排方式。
当一家公司的程序员超过一定数量时,自然而然地会出现重大意见分歧。有些开发者喜欢使用制表符,而另一些喜欢使用空格。有些人喜欢在私有成员前加上 m_ 前缀,而另一些只加 _ 前缀,还有一些人不喜欢使用前缀。你可以允许每个人按照他们的喜好去做,但这将导致冲突,让没有人满意。更不用说,使用统一的风格编写的代码更容易阅读。
一些公司有编码规范,比如谷歌。为了避免任何混淆,谷歌对一切都提供了规则。你可以查看这个简短的 Java 编码指南,或者如果你睡不着觉的话,可以看看这个长达 82 页的 C++ 编码指南。每次代码审查都应该遵循这些指南;这就是为什么谷歌有 Certified Readability Reviewers,而且你需要确保每个代码审查都得到至少一个这样的审阅者的签署。
大公司的代码审查需要花费更多时间,你必须经历可读性、安全性和隐私性审查。此外,我发现大公司对代码审查的文化与小公司不同。在初创公司,你会匆忙创建一个 MVP 或者满足某个截止日期。而大公司则不会匆忙。质量和合规性被优先考虑,因为有数百万现有客户,每一个小错误都将代价高昂。
但情况并不那么糟糕,有很多方法可以加速代码审查。其中一种方式是投资于静态代码分析工具,这些工具可以自动修复你的代码,使其符合公司的编码风格。例如,可以参考 ESLint 用于 JavaScript 和 StyleCop 用于 C#。另一个想法是,如果你有经过认证的审阅者,最好在附近有一位经过认证的人,并且最好是在你自己的团队内。
庞大的团队需要通过会议来进行协作。会议是决定项目时间表、资源分配、设计审查、状态会议、回顾等方面必不可少的。一些协作可以通过电子邮件或聊天完成,但没有什么能比得上实时会议。在复杂的讨论中,即时响应对话至关重要。它允许你迅速澄清,提出更多问题,并更容易挑战想法。会议通过肢体语言和语调传达更丰富的沟通细微差别。它们有助于建立关系,对于健康和高效的工作环境来说是无价的。
如果你在一家大公司工作,你将参加很多会议。如果你是经理或高级个人贡献者,超过一半的时间都在参加会议。但有一些方法可以充分利用它,比如制定一个健康的会议文化,按时开始,制定议程,限定会议时间,并邀请仅必要的参与者。你可以发送会前准备文件,避免花费前 30 分钟解释问题。还可以设置主持人角色,主持人将允许每个人依次发言,保持主题的一致性。最后,每次会议后都要总结所做的决定,否则你可能需要另一次会议。
在初创公司,当你从零客户开始时,你可以通过这个方便的数字取得很大的进展。没有客户意味着没有投诉。要花一些时间才能拥有一些客户,更需要时间才能获得数十个客户,然后是数百个,接着是数千个,依此类推。在规模上的每一次跃升都伴随着新的保障措施。在现代软件公司,减少手动测试的趋势是实施自动化测试和金丝雀部署。金丝雀部署意味着每个版本更新首先部署给一小部分客户,然后逐渐扩大到更大的范围,再逐渐扩大,如果一切正常,最终会到达所有客户。
这听起来不错,而且有很多优点,但任何事物都有代价。在这种情况下,代价就是开发时间。通常情况下,直到某个变更影响到所有的生产环境,你才能继续开发。原因可能是由于 API 更改或其他原因,你需要来自生产环境的遥测数据。除此之外,还会有偶尔的回滚。想象一下,你 3 周前的变更因为在后期发现了一个错误而被还原了。到了这个时候,你可能已经编写了许多依赖于那段旧代码的新代码,甚至部署了它。现在,你需要进行一次明智的修复,解开这个混乱,部署它,并等待三个星期以确保一切正常。
在这个现实中,有一些提高生产力的小窍门。大多数公司使用功能标志,这意味着代码更改可以远程立即关闭。你可以将功能标志默认设置为 “关闭”,然后按照自己的步调激活你的功能,而不是与金丝雀部署同时进行。但这些只是缓解工具。每个变更仍然需要一些时间才能到达客户,你将花费时间等待。避免浪费时间的唯一方法是并行处理多个任务。
小公司更愿意冒险,推出创新性产品。拥有成功产品的成熟公司不想冒险失去这只金鹅。每一次变更都经过仔细考虑,决策依赖于客户反馈和遥测数据,而不是某位产品经理的直觉。
这种流程是明智的,但也很慢,整理客户反馈或构建一个良好的 A/B 测试需要时间,在运行这样的测试时,首先需要提前仔细计划,因为如果出了什么问题,将浪费很多时间。通常情况下,还需要在代码中为测试添加新的遥测事件,并等待它们被部署。在初始准备之后,需要运行足够长的时间来获得具有统计学意义的结果,然后分析这些结果,而不会陷入已知的偏见或受到外部因素的影响。在这个过程中可能需要几周,甚至可能几个月。有时,A/B 测试没有产生确切的结果,需要进行另一次测试。其他时候,利益相关者不会批准任何建议的变更,你所有的努力都将白费。
公司越大,你花在实际工作上的时间就越少。我说的不是与工作相关的会议,而是与你日常工作无关的活动。这可能包括团队活动、集体活动、公司活动、市政厅会议、全员会议、安全培训、隐私培训、多元包容研讨会、公司文化日、编程马拉松、特邀演讲、欢乐时光、绩效评估、与直接经理的一对一会议、与直接上级的一对一会议,以及与你一起工作的其他五位利益相关者的偶尔一对一会议。这些可能很重要,但它们总会累积成大量时间,而不是用于软件开发。
在软件开发中,我们追求尽可能高的生产力,但考虑到这份清单,我认为大公司在很多方面做得相当不错。或者至少对它们而言是正确的。例如,初创公司可能不应花费太多时间讨论编码风格,因为等到他们摸清楚的时候,资金可能已经耗尽。而在大公司,由于有许多拥有不同意见的开发人员,花费大量时间编写指南是有道理的。清单上的其他项目,如金丝雀部署和特性门,也是如此。一个只有五个客户的初创公司不需要五个生产环境或复杂的特性门工具。但是,对于有一千万客户的应用,其中的问题可能会造成数十亿的损失,这是有必要的。
有些事情对任何人都不太愉快。没有人喜欢漫长的构建时间。我们大多数人不喜欢涉及政治的事务。会议在一定程度上可能很好,但有时整整一天过去了,什么都没做成。
尽管在传统应用程序或任何大公司中,开发可能会慢一些,但大公司也有很多优势。例如,薪酬更好,有更多交友的机会,出色的设施,很多人可以向他们学习,而且你有机会参与一些世界上最重要的产品的开发。
Michael Shpilt,在以色列工作的软件开发人员和博主,目前在微软任职。他的博客主要涵盖 .NET、Web 技术、性能、调试和职业等方面的内容。
原文链接:
https://michaelscodingspot.com/slow-development-in-big-companies/
声明:本文由 InfoQ 翻译,未经许可禁止转载。
点击底部阅读原文访问 InfoQ 官网,获取更多精彩内容!
Taylor Swift 身陷不雅照风波:AI 越强、Deepfakes 越猖狂,微软和推特们无法推责
Linus 开喷谷歌内核贡献者:你的代码是垃圾!网友:我们熟悉的 Linus 回来了
微信扫码关注该文公众号作者