代码库的简介

75 阅读8分钟

我们都希望拥有易于维护的代码库,所以我们一开始就抱着最好的愿望,使我们的代码库(或代码库的一角)可维护和易于理解。随着时间的推移,随着代码库的增长,管理依赖关系(JS、CSS、图片等)会变得越来越困难。 随着项目的增长,越来越多的代码库成为 "部落知识"(只有你或其他几个人知道的知识),这种知识会导致 "技术债务"(无论这个词是否准确)。

我喜欢让我的代码库不仅对我(写代码的人),而且对我的队友、未来的维护者和6个月后的自己都是可控的。我想我们都可以同意,这是一个伟大的理想,我们应该在我们的代码库中努力实现。有很多不同的工具和技术供我们使用,以实现这一目标。

我不想讨论是否要对你的代码进行注释(你应该这样做),以及你的注释应该是关于什么的(你在注释中解释你为什么要做一些出乎意料的事情,这样后面的人就可以理解那些导致出乎意料或奇怪的代码的决定。(好吧,也许我确实想谈一谈这个问题)。相反,我想把重点放在这些代码注释的位置上。我们通常把这些注释和它们所解释的代码 "放在一起",尽可能地把它放在相关代码的附近。

请考虑一下,如果我们用不同的方式来做这件事。如果我们把这些注释放在一个完全独立的文件中,会怎么样?一个巨大的DOCUMENTATION.md 文件,或者甚至一个docs/ 目录,映射到我们的src/ 目录。听起来很有趣吧?是的,对我来说也不是。如果不把我们的注释和它所解释的代码放在一起,我们会遇到一些严重的问题。

  • **可维护性。**它们会更快地失去同步性或过时(比它们已经做的更快)。我们会移动或删除一个src/ 文件而不更新相应的docs/ 文件。
  • **适用性。**人们在看src/ 中的代码时,可能会错过docs/ 中的重要注释,或者没有注释自己的代码,因为他们没有意识到他们正在编辑的src/ 文件有一个现有的docs/ 文件。
  • **使用的方便性。**从一个地方到另一个地方的上下文切换,在这种设置下也是一个挑战。要处理多个位置的文件会使你很难确保你有维护一个组件所需的一切。

我们当然可以为这种代码注释风格制定一个公约,但为什么我们要这样做呢?把注释和它们所解释的代码放在一起不是更简单吗?

那又怎样?

现在,你可能在想"是啊,这就是为什么没有人做这个docs/ ,每个人都只是把他们的注释和代码放在一起。 这很明显。你的观点是什么?"我的观点是,共同定位的好处无处不在。

HTML/视图

以HTML为例。将我们的评论放在一起的所有好处也都转化到了我们的模板上。在React这样的现代框架之前,你会把视图逻辑和视图模板放在完全独立的目录中。这也是上述问题的根源所在。如今,在React和Vue中,把这些东西放在同一个文件中是很常见的。 在Angular中,即使不在同一个文件中,模板文件至少也是紧挨着与之相关的JS文件。

CSS

另一个很适用的概念是CSS。我不会和你争论CSS-in-JS的优点(它非常棒),但它的好处是超乎寻常的。在这里了解更多

测试

这个文件共处的概念也很适用于单元测试。在一个项目中,有一个src/ 目录和一个test/ 目录,其中充满了单元测试,试图反映src/ 目录,这种情况有多常见?上面描述的所有陷阱在这里也适用。我可能不会走到把单元测试放在完全相同的文件中,但我也不完全排除这是一个有趣的想法(实现方法留给读者去练习)。

为了使代码库更容易维护,我们应该把测试文件和它们所测试的文件或文件组放在一起。这可以确保当新人(或6个月后的我)来到代码中时,他们可以立即看到该模块已被测试,并使用这些测试作为参考来了解该模块。当他们进行修改时,它会提醒他们更新(添加/删除/修改)测试,以考虑到他们的变化。

状态

应用程序/组件状态也有同样的好处。你的状态与使用它的用户界面越是脱节/不直接,它就越难维护。本地化状态的好处甚至超过了可维护性,它还能提高你的应用程序的性能。在你的应用程序组件树的一个角落里发生的状态变化将比在树顶发生的状态变化重新渲染的组件少得多。本地化你的状态。

"可重复使用的 "实用程序文件

这也适用于 "实用 "文件和函数。想象一下,你在写一个组件的时候,看到了一段不错的代码,它可以被提取到自己的函数中。你把它提取出来,然后想:"啊......我打赌很多人都可以用这个。"所以你把它拉出来,放到你的应用程序的utils/ 目录中,然后继续你的生活。

后来,你的组件被删除了,但你写的实用程序却不在人们的视线范围内,它仍然存在(连同它的测试),因为删除它的人认为它被更广泛地使用。多年来,工程师们努力工作,以确保该功能和它的测试继续正常运行,甚至没有意识到它已经完全不需要了。浪费了精力和认知负荷。

如果相反,你只是把那个函数直接留在使用它的文件中,故事就会完全不同。我并不是说不要对复杂的实用功能进行单元测试(请做),但是把它们放在离需要它们的地方较近的地方有助于避免问题。

看在上帝的份上,请删除这个ESLINT规则和所有类似的规则。

该原则

共同定位的概念可以归结为这个基本原则。

尽可能地将代码放在与之相关的地方。

你也可以说"一起变化的东西应该尽可能地靠近"。(Dan Abramov曾经对我说过类似的话)。

开放源码变得简单(-er)

除了避免前面讨论的问题之外,以这种方式构建项目还有其他好处。把一个组件变成一个开源项目,通常就像把文件夹复制/粘贴到另一个项目并发布到npm一样简单。然后你只需在你的项目中安装它,并更新你的require/import 语句,你就可以了。

例外情况

当然,对于横跨整个系统或部分系统的文档,以及事物如何集成在一起,有一个很好的论据。那么,你会把跨越组件的集成或端到端测试放在哪里呢?你可能认为这些都是例外,但它们实际上可以很好地遵守上面提到的原则

如果我的应用中有一部分与用户认证相关,并且我想记录这个流程,我可以在有所有与用户认证相关的模块的文件夹中放一个README.md文件。如果我需要为该流程编写集成测试,我可以将这些测试的文件放在同一个文件夹中。

对于端到端的测试,通常在项目的根部进行更有意义。它们超出了项目本身,进入了系统的其他部分,所以对我来说,把它们放在一个单独的目录中是有意义的。他们并没有真正映射到src/ 文件。事实上,E2E测试并不真正关心src/ 是如何组织的。重构和移动src/目录中的文件,不需要改变E2E的测试。

总结

我们的目标是建立尽可能简单的维护软件。 我们通过将注释放在同一地点获得的可维护性适用性易用性的好处,也可以通过将其他东西放在同一地点获得。如果你没有试过,我建议你试一试。

P.S. 如果你担心违反 "关注点分离",我建议你看看Pete Hunt这个演讲,重新评估一下这意味着什么😀。

P.P.S. 我还应该注意到,这对图片和其他任何资源都非常适用。当你使用像webpack这样的工具时,将这些资源放在一起也是非常容易的。老实说,这是webpack的核心价值主张之一,我认为。