如何以质量思维启动软件项目
构建高质量的软件并非易事,需要大量的实践和经验。
编写单元测试或文档等重要活动仍被许多人视为“额外的”,并且是开发人员在截止日期临近时首先削减的事情。
跳过这些活动可能会给你一种错觉,认为在短期内你会更快,但最终,你将在长期内付出代价,因为软件变得越来越纠结和复杂,越来越难以理解和维护,技术债务也越来越大。
Bug 会更频繁地出现。由于缺乏自动化测试,人们会开始害怕对代码库进行重大修改和重构。
开发人员在如此混乱的代码库中工作会感到沮丧。产品负责人也会感到沮丧,因为构建和发布新功能需要越来越多的时间。
产品经理必须明白,可维护性是产品交付的关键,因为它能让你持续交付功能。技术债务具有复利效应。所以,你为了交付功能而坚持削减的每一点成本,都是在借钱来偿还未来。—— Brian P Hogan
质量并不是你以后可以轻易添加的东西。
你可以尝试一下,但相信我,你等的时间越长,技术债务越累积,追赶上来就越困难。没有哪个开发人员愿意花几个月时间为那些可能根本就不是她写的旧代码编写单元测试。也没有哪个产品负责人愿意忍受几个月不发布新功能,只是清理代码。
为了避免这些问题并拥有更可持续的开发周期,您应该在编写一行代码之前就开始考虑质量。
软件质量远不止编写测试和确保软件无错误。它可以应用于软件开发生命周期的所有阶段,涵盖范围广泛,从拥有一个可运行且易于设置的本地开发环境,到自动化的部署流程。
在接下来的部分中,我将讨论我认为从项目第一天起就必须实施的一些主题,以便构建一个质量驱动且可持续发展的软件项目。
每个部分都可以单独成文,因此我将尽量更多地关注概念,而不会过多地涉及实施细节。
注意:在本文中,我将介绍一些我喜欢的工具,您可以在开发生命周期的不同用例中使用它们。这些工具仅供参考。本文的重点并非工具本身,而是流程和实践。您可以使用您喜欢或熟悉的任何工具。
定义开发流程、工具等
您应该做的第一件事就是定义您的开发流程以及您将使用的主要工具和技术。
应用程序代码和工件存储在哪里?如何跟踪问题?向代码库添加新变更的流程是怎样的?变更是否需要经过代码审查流程?代码审查流程包含哪些步骤?将使用哪种 Git 流程模型?谁可以贡献和合并代码?
所有这些问题都必须得到解答并记录下来。良好的文档至关重要,原因有很多,我将在下一节中详细讨论。
构建良好文档的基础
在软件开发中,文档编写非常重要,但通常被视为次要任务。如果你在团队、开源项目中工作,或者正在构建一些第三方使用的东西,例如面向公众的 API,那么文档编写就显得尤为重要。
如果您从一开始就考虑文档并定期进行,那么这将是一项更加简单和愉快的任务。
首先,一个简单的“README”文件介绍项目,解释如何在本地运行以及如何做出贡献,但您应该准备好记录从技术堆栈和工具、开发过程、编码标准、重要的架构和技术决策、API 端点等的所有内容。
您可能还想为最终用户编写某种“产品手册”。
如果您将所有内容都记录在案,这不仅有助于团队成员之间共享知识并确保每个人都保持一致,而且还可以让新人更轻松地加入项目并立即开始为项目做出贡献。
首先,你应该找一个地方来托管你所有的文档。它应该易于添加/更新内容,并且每个人都可以访问。
对于更技术性的文档,我认为最简单的入门方法是将 Markdown 文件写入项目仓库的“docs”文件夹中。您可以使用VuePress或Gatsby之类的工具将其转换为网站。这种方法的优点是文档更接近代码,并且受版本控制,方便开发人员保持更新。
对于更“通用”或产品文档,Confluence是企业中最流行的工具之一,但也有许多“wiki”系统可供使用。Notion也越来越流行。Outline也是一个不错的选择,而且它是开源的。
如果您正在构建 API,请查看OpenAPI 规范。
我更喜欢使用开放标准,因为它们有助于与其他工具集成以构建强大的工作流程并避免锁定,因此 Markdown 和 Open API 绝对是不错的选择。
定义并执行编码标准和指南
这在团队工作时尤其重要,并且有助于使您的代码保持一致且更易于阅读。
谁没有经历过这样的情况呢?一个很小的功能,最终却因为开发人员将编辑器配置为使用 2 个空格(而原始开发人员使用的是 4 个空格)而导致文件大量修改?这会给你的代码审查流程增加大量开销,并在代码库中留下无用的修改。
你无需重新发明轮子。大多数编程语言都有一些推荐的样式指南。PHP 有PSR-2 规范,Javascript 有AirBnb或Standard 规范,Python 有pep8 规范。
由于这些是常用的标准,您的开发人员很可能已经熟悉它们。当然,这些只是指导原则,您可以根据自己的喜好进行调整。只需确保所有内容都有完善的文档记录并得到团队的认可即可。
但是,如果没有人人遵守,那么编码标准就毫无意义。检查这些标准的使用情况绝不应该是一个手动过程,因为有很多工具可以自动完成。
您可以使用适用于 JavaScript 的ESLint或适用于 PHP 的PHP_CodeSniffer来检测代码是否违反标准。Prettier 、gofmt或PHP-CS-Fixer等工具甚至可以根据既定标准自动格式化代码并修复样式问题。
确保您的开发人员充分理解这些工具,并在他们的开发环境和 IDE 中运行它们。您还应该在交付管道上启用这些检查。稍后会详细介绍。
确保仓库中所有提交都遵循指定准则的一个好方法是利用git hooks在每次提交或推送时运行这些工具。我最近发现了pre-commit,它可以帮助你在项目中设置 git hooks。你可以尝试一下。
关于缩进规则以及制表符与空格的永恒争论,您可以使用与所有流行代码编辑器兼容的EditorConfig 。
您甚至可以更进一步,使用Commitizen或Conventional Changelog等工具来定义提交消息或更改日志等其他内容的标准。
除了与风格相关的标准之外,您还可以定义更多的架构标准,例如说项目必须遵循“六边形架构”。
这类标准很难自动执行,应该在结对编程或代码审查会议上进行检查。同样,如果你想让每个人都遵循这些标准,完善的文档和培训至关重要。
为了方便地创建一个已配置好所有这些工具并准备开始开发的新项目,您可以构建一些基础项目模板,供开发人员克隆。GitHub 刚刚推出了存储库模板,您也可以使用SAO或Yeoman等工具来实现此目的。
设置静态分析工具以尽早检测代码异味
影响代码库质量和可维护性的因素有很多。“代码异味”一词通常指源代码中可能预示更深层次问题的任何特征。
死代码、代码重复和高水平的循环复杂度都是代码异味的典型例子,应该尽快进行分析和修复。
编码标准保证了代码的一致性,而静态分析则能检测代码库中潜在的结构性问题。两者相辅相成,是迈向干净代码库的首要守门人。
每种编程语言都有一些静态分析工具。花点时间研究一下,并在你的项目中配置它们。
如果您想进一步了解项目代码质量的更详细视图和指标,请查看SonarQube、Code Climate或Codacy等工具。
虽然我并不认为这些工具一开始就必不可少,但它们可以提供一些额外的价值,并更好地了解与质量相关的指标,这在较大的团队中非常有用。
自动化测试套件
拥有强大的自动化测试套件至关重要,它不仅可以避免错误,还能让您更有信心地进行重构和重大变更。您对测试套件越有信心,就能越快地将新功能部署到生产环境中,而无需担心回归问题。
只有拥有良好的自动化测试套件,才能实现“持续交付”等实践。
自动化测试(更具体的单元测试)也是编写更好代码的重要驱动力,因为您将被“强制”编写更简单、更解耦的代码,并应用流行的设计模式(如依赖注入和其他 SOLID 原则),以使其可测试。
测试还可以用作现有功能以及所有可能场景和用例的文档形式。
从单元测试开始,然后是集成/服务测试,并添加几个 E2E 测试,只是为了保证您的应用程序可以处理请求。
在开始添加功能之前,为每种类型创建一个简单的“hello world”测试。这将确保您已设置好所有基础架构以执行这些测试,从而在开发新功能时更快地添加更多功能。
自动化测试是一个非常广泛且有趣的话题。如果你想更好地了解你的应用程序中应该包含哪些类型的测试以及如何构建它们,以下文章是一个不错的开始:
开发环境
我曾经参与过一些项目,新开发人员甚至需要花费数天时间才能在本地机器上运行项目。这通常是由于文档的缺乏或过时,以及工具的不足造成的。
这个过程应该尽可能简单和轻松,以便任何新开发人员都可以尽快开始开发。
我相信你只需执行一个命令就能配置好一个开发环境。得益于Docker和Docker Compose等工具,配合 Makefile 或一些 Shell 脚本,如今这变得容易多了。
拥有自动化和隔离的项目设置还将保证所有开发人员拥有相同版本的依赖项和运行时,从而减少由于版本不同而出现错误的可能性。
您应该力求使本地环境尽可能接近生产环境。如果您在生产环境中使用 Docker,则可以利用Docker 多阶段构建功能,并使用与生产环境中相同的基础镜像。
此设置并非一次性构建完成即可。添加新服务或配置时,它需要定期维护。我建议您在应用程序中进行任何需要更改本地开发脚本的更改时,都应立即执行,以免被遗忘。保持更新是项目所有成员的责任。
如果你能在新机器(可以是虚拟机)上定期测试你的脚本就更好了。你甚至可以添加一些自动化测试,至少检查一下所有应用服务是否正常启动。;) 一切皆有可能。
但开发环境不仅仅关乎应用程序本身。花些时间完善你的代码编辑器/IDE 以及任何辅助工具(例如:实时刷新)。许多 IDE 都提供配置文件,你可以通过版本控制与团队共享,这样全局配置就能在所有团队成员之间保持同步和一致。
交付管道
让我们来讨论一下这个难题中缺失的一块,我们将到目前为止讨论的所有内容粘合在一起,这就是交付管道。
交付管道定义了代码从 VCS 提交到生产过程中必须经过的所有阶段。
从一开始就正确配置基本的交付流程,将确保您拥有稳定且自动化的机制来将应用交付给最终客户,并且所有代码在部署到生产环境之前都必须通过您定义的质量检查。这样,您就可以专注于功能构建,更快、更高质量地交付。
不同项目的流水线复杂度可能有所不同。首先,我认为至关重要的阶段是“构建阶段”、“静态分析阶段”、“单元测试阶段”、“集成测试阶段”、“验收阶段”和“部署阶段”。
稍后,根据您的需要,您可能会考虑添加更多阶段,例如安全性和性能测试。
构建阶段
这个阶段其实没什么特别的。它从版本控制系统获取你的代码,并构建一个可部署的构件。它可以是 Docker 镜像、tarball、rpm 等等,并将生成的构件推送到某个构件仓库。
静态分析阶段
此阶段应验证编码规范,并运行静态分析检查以检测代码中的结构性问题。我们在“编码规范”和“静态分析”部分讨论的所有工具都应在此阶段执行。
单元测试阶段
此阶段应该运行应用程序的单元测试。即使你一开始只有一个“hello world”单元测试,如果一开始就将其添加到流水线中,那么之后的构建速度会更快,因为运行测试的基础设施已经到位。
集成测试阶段
此阶段将运行您的集成测试(例如:数据库测试)或服务测试。
在此阶段,您可能需要一些额外的服务,例如用于数据库集成测试的数据库或用于在服务测试期间存根应用程序外部依赖项的模拟服务器。
如今大多数流行的 CI / CD 工具都原生支持 Docker,因此可以很容易地生成 MySQL 容器,例如,使用它进行测试。
验收阶段
在这个阶段,您的应用程序应该部署到一些暂存环境,我们可以在其中触发一些高级自动化测试,如端到端测试(E2E)。
您还可以使用暂存环境进行一些手动、探索性或 UI 测试,或共享任何正在进行的工作功能。
在自动化测试方面,您应该根据测试金字塔将重点放在单元测试和集成测试上。
在验收阶段,只需进行一个简单的测试,检查您的应用程序在主页上是否返回 200 OK,并进行主用户流程测试即可。此测试将捕获任何环境配置错误,并确保最终用户可以访问应用程序。
部署阶段
在您的代码通过所有前面的阶段后,它就可以部署到生产环境中了。
部署本身可以是手动或自动步骤。您可能不需要从第一天就创建此阶段,但您至少应该拥有一个暂存环境,以便查看您的应用程序在本地环境以外的其他环境中的运行情况。
有很多很棒的工具可以用来定义你的交付管道。Jenkins在企业中仍然被广泛使用,但我更喜欢更现代的工具,比如CircleCI 、 GitLab或Drone CI。
GitLab 和 Drone 都是开源的,你可以将它们安装在自己的基础架构上,但如果你刚刚起步,则应该寻找它们的云解决方案。GitLab 是一个一体化解决方案,它提供了你所需的一切,包括版本控制、问题板、CI 管道、与 Kubernetes 集成以进行部署等等,是快速启动管道的绝佳工具。
要了解如何正确构建持续交付管道,请参阅以下文章:
补充:寻找自动化机会
思考您执行的每一个重复的手动流程,并寻找自动化的方法。从运行测试或下载数据库转储的 Bash 脚本,到 GitHub WebHook、聊天机器人,以及多种工具和 API 之间的集成。一切皆有可能。
结论
项目的可维护性和质量将在很大程度上受到您启动项目的方式的影响。
开始构建一个项目时就考虑质量比以后再添加质量要容易得多。
定义您的软件开发生命周期,包括 git-flow、贡献指南、编码标准指南、拥有强大的交付渠道和出色的文档,是成功、可维护和无错误的项目的重要第一步。
我是否需要为所有类型的项目都准备这些?就像软件工程中的所有事情一样,没有“一刀切”的方案。有很多因素需要考虑。非常小的应用程序或MVP?也许不需要。但对于你希望项目扎实且/或期望在未来几年内发展壮大的项目来说,这绝对是必要的。
对于 MVP 来说,一个好的做法是尽量少写代码,并利用现有的服务。然后,在首次发布后,如果你的想法得到了验证,就花时间按照这些做法,从头开始正确地重新构建它,而不是仓促地在上面添加新功能。
如果这样做需要几周以上的时间,也许你在 MVP 上花了太多时间,或者你的 MVP 本质上非常复杂,在这种情况下,一开始“浪费”一些时间来正确设置它不会有太大的区别,而且如果你的项目发展壮大,这将为你节省很多问题和麻烦。
凭借一些经验和良好的工具,按照本文中介绍的步骤操作并不需要花费太多额外的时间和精力。
谢谢阅读。