Language agnostic 您是否曾经进行过代码更改,只是测试,而不是试图完全理解您所做的更改';你做了什么?

Language agnostic 您是否曾经进行过代码更改,只是测试,而不是试图完全理解您所做的更改';你做了什么?,language-agnostic,testing,maintenance,Language Agnostic,Testing,Maintenance,我在一个有12年历史的代码库中工作,我是唯一的开发人员。 有时,我会根据直觉(或逻辑上的量子跃迁;-)做出一个非常小的改变 通常我会尝试解构这种变化,并确保我彻底阅读了代码 然而,有时,(现在越来越多)我只是测试,确保它达到我想要的效果。(我是一个非常彻底的测试人员,即使我读了代码也会进行测试) 这对我来说是可行的,令人惊讶的是(与我看到的大多数软件相比),很少有bug逃逸到野外 但我想知道的是,这是否只是编码的“艺术”方面。是的,在理想的世界中,您会彻底阅读更改后的每一段代码,但实际上,如果您

我在一个有12年历史的代码库中工作,我是唯一的开发人员。 有时,我会根据直觉(或逻辑上的量子跃迁;-)做出一个非常小的改变

通常我会尝试解构这种变化,并确保我彻底阅读了代码

然而,有时,(现在越来越多)我只是测试,确保它达到我想要的效果。(我是一个非常彻底的测试人员,即使我读了代码也会进行测试)

这对我来说是可行的,令人惊讶的是(与我看到的大多数软件相比),很少有bug逃逸到野外

但我想知道的是,这是否只是编码的“艺术”方面。是的,在理想的世界中,您会彻底阅读更改后的每一段代码,但实际上,如果您确信它只影响一小部分代码,这是一种常见的做法吗


我可以清楚地看到,在一个糟糕的程序员手中,这将是一个灾难性的方法。但是,我也见过一些程序员,他们表面上在阅读代码,并在左右(在他们自己的代码中,只有他们一直在做的代码中)分解东西。

我很可能不是这个问题的预期目标,因为我现在只是一名专业开发人员几年。无论如何,我还是要把我的2美分投进去

我总是这样做。当我在一个紧迫的最后期限内,我让代码可靠地工作,无论我是否理解整个事情


我通常会尽量晚些时候回去看看我做了什么——通常是在周末——并用复制/粘贴的代码更新代码以反映任何错误的决策。但最后期限是最重要的,特别是如果合同中有违约金的话。您可以随时发送更新,以便在以后修复任何问题。

有时更改代码和测试有助于您理解代码。 当然,当你做出你不理解的改变时,你总是踏上危险的道路。我认为您的成功不仅基于您的“经验”,还基于当前代码的稳定性。
您可以轻松地对编写良好的代码进行更改。但随着时间的推移,这种做法会逐渐流行起来。

如果你看代码时不理解它,现在是时候把它弄清楚并记录下来了。否则,每一个其他程序员都将不得不在他们必须进行更改时弄明白这一点。最好只做一次,把它做好,为将来的每个人节省时间和麻烦。

一旦您将遗留代码包装到一个双重安全的测试包中(良好的单元测试和良好的系统/集成/回归测试,两个层中的每一层都有很好的覆盖率),依靠测试捕捉误解是一种有效的短期战术方法。这就是为什么向遗留代码库添加测试成为一种高ROI策略的一部分:它使您能够更快地进行紧急bug修复或添加非常紧急的功能,因为它使您对所做更改的可靠性有了合理的信心,而不必透彻地理解现有代码的复杂性


然而,正如其他答案所暗示的那样,这些变化正在积累技术债务;至于其他债务,你越早还清,从长远来看,你的境况就越好。在这种情况下,“回报”不仅仅是关于内部的文档,而且通常可以是重构代码库以获得清晰性和延展性——真正好的测试也会帮助您自信地进行重构,就像它们对遗留代码的错误修复和功能增强一样。

不断

您希望您的代码由可以单独测试和理解的小块组成

每一小块都应该做一件事,并且做得很好,不管那块是函数、方法还是类

您希望能够将这些小的功能组合成更大的功能块,这样,无论内部多么复杂,每个组合在该功能的抽象级别上都保持简单

换句话说,即使是在高层次上,我们仍然应该能够用简单外部的TEM来描述复杂的内部

这就是安德鲁·柯尼格(Andrew Koenig)所说的“抽象是选择性无知”的意思。通过有意放弃WHO内部工作的知识,我们可以不考虑它是如何工作的,而是考虑它是如何工作的。 让我们举一个简短的例子。在高端,我们可能会说,“这个类在某些数据结构中找到最小的int”。这告诉我们它是做什么的,而不是它是如何做的,在这个高度抽象的层次上,这就是我们所关心的

我们有一个做某事的东西,它的模块化,我们可以用做同样事情的任何其他东西来代替它,不管它是如何做的。这是一个公共接口

现在在较低的级别上,它的工作原理可能是,在内部它是一个堆,或者优先级队列,或者其他什么

这些东西可以用树、自平衡树、甚至(次优)链表来实现。链表可能是次优的实现,但只要它做了同样的事情,我们就不会真正做到这一点,因为如果次优性恢复到足以减慢我们的程序的速度,我们就可以用相同的接口将其替换为更好的实现

这些都是通过树遍历实现的(预顺序:总是按照左、父、右的顺序进行)。这些都是通过节点上的简单操作来实现的

这里是重要的部分:因为out应用程序的其余部分不依赖于该模块的内部结构或副作用,所以从一个较差的实现替换为一个较好的实现不会改变我们的任何其他代码,它只是总体上加快了速度

每一层只与它上面和下面的层通信,这样每一层都可以被复制