

公司的执行团队最近问我为什么我们的团队能够如此迅速地开发出对我们产品的改进。 此博客从技术角度概述了一些关键准则,我们的团队遵循这些准则来安全快速地迭代产品。 我尽我最大的努力使这篇文章保持高水平,同时仍然提供技术细节。 我们谈论的技术不是我们发明的东西。
我的技术同志加入了团队,我将其视为减少为用户提供价值的摩擦的角色。 我们还旨在保持负责的应用程序的高质量。 我们通过删除,简化和自动化将更新交付到生产代码库的过程来完成了大部分工作。
请记住,有些项目可能具有固有的复杂性。 我们的团队使用的某些技术可能不适合您的项目。 根据我的经验,使用旧版软件可能很难或根本无法简化某些流程。
我们的团队优先考虑对生产代码库进行频繁的改进,而不是其他所有方面。 好吧,几乎-我们采取了心理健康之类的措施,让人们在生病的时候可以放假一天,并在分娩前保持良好状态。
生产中的代码是目标。 我们确保所有想法,设计和功能要求都有一条清晰的路径,说明我们如何将其投入生产。 我们用来加强此功能的一种技术只是在展示柜中显示工作代码。 我们禁止使用Powerpoint和设计模型。 我们还发现这使我们的展示柜更具吸引力和趣味性。
会议是可选的。 我们发现,这鼓励召集会议的人清楚地阐明会议的目的和价值。 由团队成员决定是否应参加会议。 我们希望防止开发人员受到不必要的干扰。 某人被打扰后可能需要长达30分钟的时间才能恢复生产状态。
花时间解决用户问题,而不是技术问题。 我们希望避免花费大量时间讨论要使用的技术或构建自己的技术。 我们使用了AWS Lambda,因此我们不必考虑服务器和扩展。 我们使用了Create React App,因此我们不必担心前端应用程序的构建配置。 我们做出了早期的技术决策并坚持了下来。
we 我们试图避免的事情。
- 当我们可以从其他地方重用它们时,我们避免花费数周时间来构建自己的工具或框架。
- 我们避免向团队添加不必要的复杂流程或仪式。 其中包括混乱的看板或敏捷流程,这些都没有使我们受益。
- 我们避免花费数周的时间来设计,构建和支持功能,而这些功能并没有证明它们对我们的用户有用。 我们的设计团队在基于用户反馈和分析的功能设计方面做得非常出色。
快速的发布周期意味着我们可以在用户准备就绪后立即向他们推出新功能。 我们还可以更快地从错误和中断中恢复。 高频部署的主要挑战是在不引入错误的情况下按步伐移动。 自动化此过程至关重要,因为它可以大大减少部署更新所需的时间和精力。
自动化部署。 快速交付要求部署过程尽可能无摩擦。 幸运的是,如今,我们有许多工具可以自动化整个部署过程。 我建议使用CircleCI和TravisCI之类的工具。 我们的设置是在将新代码添加到release分支时,该代码由我们的部署工具自动部署。
编写(和自动化)测试 。 在自动部署代码更改时,至关重要的是了解更改将产生的影响并停止部署会引入错误或回归的部署。 这意味着我们需要编写测试以确认代码是否按预期运行。
每当我们将新代码集成到发布分支时,CI工具都会自动运行我们的测试套件。 测试套件中的任何失败都将取消部署过程。 开发人员还将在本地运行该测试套件,以在推动更改之前确认一切正常。 与手动测试相比,自动测试还更快,更麻烦并且更不容易发生人为错误。
如果所有新代码都需要人工人工测试一个小时,则无法快速交付。 作为一个团队,我们同意为所有编写的代码编写测试。 对于编写测试不切实际的情况,我们需要给出一个理由。 每当我们修复错误时,我们还将编写覆盖该错误的测试。 我们确保已针对常见用户交互和旅程进行了测试(集成测试)。 以及组成我们应用程序的各个功能的测试(单元测试)。


少量频繁更新。 以较小的增量更新代码库可以提高为用户提供改进的速度。 较小的更新更易于集成到代码库中。 我们的代码审查过程变得更加严格。 对于开发人员来说,审查小型拉取请求更容易,更快捷。 由于新代码的表面积很小,因此识别新代码的问题和影响要容易得多。
我们发现一种有用的技术是将设计质量保证(QA)审核过程移至拉动请求级别。 与每隔几天对大量更改进行一次质量检查相比,这使质量检查过程更加集中,更快。 作为一个团队,我们达成了一项协议,我们将使PR保持较小。
我们还同意,我们将在半天之内审查PR。 如果我们无法在该时间范围内进行审核,则需要告知作者。
we 我们试图避免的事情。
- 我们避免了手动执行本来可以自动化的任务。
- 我们避免了一次全部部署大量代码带来的风险。 部署具有未被发现的错误或仅在代码大规模运行时才浮出水面的错误的情况并不少见。 我们的部署的表面积越大,这些问题将产生的影响越大。
“对我来说,只有一个定义良好的代码定义。 设计良好的代码就是易于更改的代码” – Dave Thomas
如果更改代码既困难又耗时,则能够按需部署代码更改就毫无意义。 编写易于理解和更新的代码有助于我们快速迭代。 作为一个技术团队,我们在代码审查过程中互相追究责任,以确保我们尽可能清楚,简单地编写代码。
编写模块化和可重用的代码。 功能是我们应用程序的基础。 这些功能应该小巧,分离并且具有单一目的。 这使开发人员更容易遵循和理解应用程序的逻辑。 重新利用现有功能以减少编写的代码量更加容易。 更改功能更为安全,因为功能的表面积非常小。 更改的影响更容易理解。 作为一个团队,我们将仔细检查彼此的拉取请求并提供反馈,以帮助彼此编写尽可能好的代码。
为人类编写代码。 我们编写代码的两个主要用户:运行代码的计算机和阅读和更改代码的人员。 大多数开发人员都擅长为计算机编写代码。 如果您的代码可以运行或符合要求而没有错误-这意味着您在为计算机编写代码方面做得很好。 一些开发人员忘记了为人类编写代码。 如果代码难以理解,开发人员将需要更长的时间来理解和更新代码。 我们专注于明确每个功能的目的,输出和输入。
类型系统使功能的输入和输出变得清晰。 使用Go时使用内置类型系统,使用JavaScript时使用Flow。
我们为变量选择了描述性名称。 这样可以使变量保存哪些数据或执行什么功能变得更加清晰。
//这两个函数执行相同的操作函数a(arr){
返回arr.filter(it => it.age <30)
}
函数getUsersUnder30(userList){
返回userList.filter(user => user.age <30)
}
编写可测试的代码。 每当我们更改代码时,我们都需要确保所做的更改没有使代码的先前功能退步。 这是进行测试的巨大好处之一。 它为更改代码增加了一定的安全性。 通过以简化编写测试的方式编写代码,使生活变得更轻松。 编写易于测试的代码的技术是使用纯函数 。
纯函数是给定相同输入的函数,将始终返回相同的输出。 这些功能非常易于测试。 如果您有兴趣了解有关纯函数的更多信息,那么Eric Elliot有一篇很棒的文章描述了纯函数。
不幸的是,如果函数具有副作用 ,则不能将其编写为纯函数。 副作用是超出其功能范围的事情。 这可能是诸如写入文件或发送API请求之类的操作。 在单位级别测试副作用可能很棘手,因此将这些副作用与我们的纯函数分开了。
we 我们试图避免的事情。
- 我们避免了浪费时间手动测试可以自动化的方案。
- 我们避免为了速度而牺牲代码质量。 为了速度而牺牲代码质量是多余的。 您不仅更有可能引入错误,而且还创建了最终将很难更改和调试的代码库。 这将大大降低您提供新功能和错误修复的能力。
谢谢阅读!