云顶娱乐手机版下载-云顶娱乐下载
服务热线:

新闻资讯

@程序员,欠下的技术债怎么还?

来源:日期:2019-09-04 17:54 浏览:

原标题:@程序员,欠下的技术债怎么还?

所谓“技术债务”,是指过去犯下的错误,最终需要通过重构来弥补。那么开发者该如何分辨哪些才是良好的技术债务呢?

作者 | Jon Thornton

译者 | 弯月,责编 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下为译文:

在软件工程领域中,“技术债务”是一个贬义词。人们在使用这个词的时候常常表达出某种遗憾,过去犯下的错误,最终需要通过重构来弥补。

然而,金融债务却没有遭遇到众口一词的谴责。比如你的朋友抵押贷款够买了新房子,你会说什么?肯定会说:“恭喜!”债券是基础设施和公共事业标准的融资形式。企业使用了各种债务,而华尔街却对股价上涨充满了信心。

技术债务与金融债务之间的不同之处在于意图——如果技术债务也并非总是意外,或者由错误的假设和意外情况导致,那会怎么样?你会如何处理技术债务的贷款呢?

如果我们将技术债务定义为将来必须完成的工作,那么就可以根据将来所需要花费的工作时间来计算开销。我们也可以选择现在就“投入”时间来完成这些工作。

这种心理上的模型可以帮助我避免浪费开销,或承担起力所不及的系统维护工作,同时也让我有机会看到有目的性地使用技术债务的机会。在以下几个案例中,我借助技术债务取得了项目的成功。

脚手架

在划分某个大项目主要功能的优先级时,通常我会首先验证项目中最具风险性的部分。这里的风险我指的是最容易出错的部分,因为这些部分很复杂、难以定义、未得到充分的理解,或者让我嗅到了“危险的气息”。由于这些是最艰难的部分,所以我会赶在代码库尚小且容易扩展的时候,首先构建这些部分。

展开全文

然而,仅仅构建有风险的组件还远远不够。我需要知道构建这些有风险组件的方式是否正确。然而,实践才能出真知,所以我会全力推动我的代码进入现实世界。这也就意味着我需要在构建应用程序的其余部分之前,找到一种方法有效利用这部分有风险的代码。

在我们团队开发电子邮件营销活动时,我们首先需要构建电子邮件编辑器。我们拥有顶级的设计和强大的内容编辑工具,我们希望构建一流的应用程序。几个月后,我们兴冲冲地向同事们展示了我们的编辑器,但当时我们还没有构建实际发送电子邮件的系统。如果这个编辑器只是一个无法发送电子邮件的玩具,那么我们就无法获得真实的反馈。

最终的产品需要快速可靠地发送数亿封电子邮件,但我们不需要立即实现这个功能。

所以,我们问自己:“怎样才能通过最简单的方式鼓励我们的同事使用这个编辑器?”当时我们公司有几百名员工,而且内部的测试人员可以容忍这个编辑器的不稳定性。与我们一直在考虑的健壮系统相比,构建一个发送几百封邮件的不稳定系统肯定会容易很多。于是,我们意识到我们可以构建一个廉价的应用程序,即便日后丢弃了也不可惜,目的只是为了快速获取用户的反馈,这就成了我们的脚手架系统。

如下自我实施的指导方针帮助我们顺利地实现了这个脚手架系统:

  • 严守预估的工时。如果我们无法在预定的时间范围内完成脚手架系统,那么就表明我们承担了过多的工作,需要重新考虑实现方法。
  • 从一开始就明确传达,这个系统会被抛弃。代码需要很好地封装,而且我们也没有浪费时间来讨论实现细节。
  • 我们明白脚手架的局限性,因此有意避免在故障会造成伤害的情况下使用脚手架。如果我们花时间清理脚手架造成的错误,那么必将带来更深入的债务,所以我们小心翼翼地测试脚手架的使用情况。
  • 利益相关者知道我们在有意承担债务。我们在利益相关者和用户前面夸大了这个系统的限制,告诉他们这些只是为了节约时间,而且在这个过程中确保他们知道之后我们需要投入时间来构建真正的电子邮件发送系统。

我们利用这个脚手架填补了依赖项空缺,目的是为了调整应用程序的构建顺序,确保尽早验证应用程序,同时又不会遇到构建组件的问题。

当代码在我们脑海中记忆犹新的时候,我们丢弃的电子邮件发送系统收获了很多有关电子邮件编辑器的反馈意见,这帮助我们加快了迭代的速度,并完善了我们的难题。当构建真正的电子邮件发送系统时,我们从脚手架系统的构建中吸取了大量经验。

硬编码

最近,我正在开发一个产品的仪表板,其中有一个功能是在某个区域显示最新的消息,比如:“祝所有订阅者节日快乐!”我们团队需要每隔一个月或两个月刷新仪表板在该区域显示的内容。

我们需要一个内容管理系统(CMS),我们的产品经理和设计人员可以通过这个系统自行更新仪表板上的消息。此外,产品经理还希望仪表板能在特定时间点更新,而工程师不希望为了响应这些需求而定时手动更新生产版本。

一种方法是构建数据库支持的CMS,帮助我们团队的各个成员在无需工程师干预的情况下,自行更新仪表板上的内容。

然而,这种方法的成本很高:额外的前端、验证以及数据管理。这些隐藏的成本是沉重的技术债务,将来必然给开发人员带来更多的维护工作,相比这个功能就得不偿失了。此外,仪表板上的消息内容可以持续数周而无需更新,因此即时更新内容的解决方案对我们并没有好处。

因此,我们没有采用会带来额外开销的基于数据库的CMS,而是将我们的内容存储在YAML文件中,并重用现有的工具来提供CMS功能。更新仪表板消息传递的用户界面不一定非要用浏览器界面,也可以是文本编辑器和Git。其他团队成员可以通过代码审查来验证这些更新,因此我们不需要花时间编写验证和错误处理程序。我们现有的CI工具可以跨环境自动同步内容更改。

为了实现在特定时间发表新内容,我们编写了更多的硬编码,如下所示:

ifnow>= NEW_CONTENT_DATE(新内容的日期)

show newcontent(显示新内容)

else

show old content(显示旧内容)

在采用硬编码时,我考虑了以下几个因素:

  • 这些更新需要几个小时才能生效吗?硬编码有一个明显的限制:这些更新的新代码需要部署到生产中。如果没有意识到这个限制,那么可能会引发棘手的维护问题,从而产生恶劣的技术债务。
  • 用户愿意接受Git的这个新界面吗?每个更新都需要与团队版本控制工具以及CI系统交互。为了避免更多的协调或培训的开销,我们测试了一下:参与这个过程的人中是否有人了解Git?
  • 是否有现成的UI、验证和数据模式?硬编码可以避免定义新模式,从而获得最大受益。如果应用程序现有的模式可以彻底解决问题,那么就不值得使用硬编码。

通常,下面这些设计模式都可以考虑使用硬编码:权限列表、表单字段选项和功能开关等。

无需修复所有的边缘案例

我曾经构建的一个功能允许用户创建最多10个项目,但不允许超过10个。这里面包含一个很常见的竞争条件:如果我们同时发出两个创建项目的请求,那么这两个请求都会计算现有的项目数,然后同时创建两个项目。如果此时我们已有9个项目,那么最终就会创建11个项目——超过了我们的限制。

大多数测试人员会认为这是一个bug,而bug是用户可见的技术债务。

在这种情况,我们本可以利用数据库事务解决这个问题,但我们使用的数据库是不支持事务的NoSQL。读/写锁都没问题,但我们需要在分布式系统中运行,因此需要分布式锁。如果我们编写一堆锁定代码来实现这种“完美”的限制,那么最终可能会引入其他意外的bug。而且我们必须花大量时间编写一堆锁定代码,而且之后的每位开发人员都必须理解这段锁定代码。

似乎我们付出了这么多努力只是为了不会多创建一个项目。那么非技术性的解决方案呢:如果我们故意不修复这种竞争条件,会怎么样?

我说的可不是对这个问题视而不见,留给下一位开发人员来处理!我说的“故意”是说搞清楚以下几个问题:

  • 如果创建了11个项目,那么会怎么样?嗯,实际上也没什么大不了。这是一个很武断的限制,应用程序的其余部分并不在意多出来的几个项目。
  • 我们可以了解一下这种竞争条件的发生频率是否高于预期?也就是说,通过一条数据库查询找出超过10个项目的账号,且每条项目记录都有一个时间戳。结果却发现生产环境中并没有这种竞争条件。

因此,我们大可以不必理会这笔技术债务。由于创建额外项目的影响很小且易于监控,我们可以将时间花在优先级更高的工作上,无需在这种不是问题的问题上浪费时间。

对于良好的技术债务要做到心中有数

许多沉重的技术债务都是因为构建的功能过多,而且花在维护和改bug上的时间超过了预期。这就好像你购买了太多地产,最终陷入溺水屋的困境(注:按揭买房者所欠银行的钱超过房子本身的价值)。

关键在于你清楚投入的时间,并了解你所承担的成本。由于构建的功能太少而引发的问题并不可怕,因为你随时可以构建更多功能。我们应该构建方便丢弃和替换的功能,这样代码会更加模块化。良好的技术债务的局限性很明显,且众所周知。你可以在代码的注释、README、常见问题解答以及与人的对话中澄清这些局限性。

你可以善加利用良好的技术债务,将时间集中在重要的工作上,加快构建软件的速度。

原文:https://engineering.squarespace.com/blog/2019/three-kinds-of-good-tech-debt

本文为CSDN翻译,转载请注明来源出处。

☞如何写出让同事无法维护的代码?