4

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

通常我会尝试解构该更改并确保我通读了代码。

但是有时,(这些天越来越多)我只是测试并确保它具有我想要的效果。(我是一个非常彻底的测试人员,即使我阅读了代码也会进行测试)。

这对我有用,并且令人惊讶的是(与我看到的大多数软件相比)很少有错误逃逸到野外。

但我想知道这是否只是编码的“艺术”方面。是的,在理想情况下,您会详尽阅读更改修改的每一段代码,但我在实践中,如果您确信它只会影响一小部分代码,这是一种常见的做法吗?

我显然可以看出这在一个可怜的程序员手中将是一种灾难性的方法。但是后来,我见过表面上正在阅读代码并左右破坏东西的程序员(在他们自己的代码基础上,只有他们一直在工作)。

4

8 回答 8

3

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

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

于 2009-05-02T04:49:28.537 回答
3

它偶尔发生在我身上。但我想强调另一个人在他们的评论中提到的一点。

  • 无论你明白什么部分,试着记录下来。下次进行其他更改时,您可能会为自己和他人节省大量时间。

而且,为了我的两分钱...

  • 您提到您“测试”您的工作并将其与之前的结果进行比较。几年前我读到一个人试图争论公司可以拥有的最有价值的东西不是代码本身,而是测试(即:JUnit)。他说,在软件的整个生命周期中,代码会经常更改——从小的修复和增强到完全重写。凭借成熟和经验,我理解他现在的意思,并尝试使用他的建议。通过测试用例,您可以证明您的新工作是功能性的并且不会破坏任何东西。我的意思是...尝试保存和重用您可能拥有的任何测试。一些程序员有时倾向于丢弃这些测试。当其他程序员也进行更改时,它们将变得非常有价值。

快乐的代码更改,

杰奇!

于 2009-05-02T05:07:30.270 回答
2

有时更改代码和测试可以帮助您理解代码。当然,当你做出你不理解的改变时,你总是在危险的地方。我认为您的成功不仅取决于您的“经验”,还取决于当前代码的稳定性。
您可以轻松地更改编写良好的代码。但随着时间的推移,这种做法会赶上你。
如果您查看代码并且不理解它,那么现在是弄清楚并记录它的时候了。否则,每个其他程序员都必须弄清楚何时必须进行更改。最好只做一次,做对,为以后的每个人节省时间和麻烦。

于 2009-05-02T04:38:25.800 回答
1

我很可能不是这个问题的预期目标,因为我现在只是几年的专业开发人员。但是,无论如何,我都会扔掉我的 2 美分。

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

我通常会尽力稍后再回去看看我做了什么——通常是在周末——并更新代码以反映复制/粘贴代码的任何错误决定。但是最后期限才是王道,尤其是如果合同中有因错过期限而受到处罚的情况。您可以随时发送更新以解决以后的任何问题。

于 2009-05-02T04:32:43.117 回答
0

不,我从不这样做。我希望我的代码 100% 合乎逻辑并且没有错误,并且在未来几年内其他人会阅读它。不完全理解我的代码上下文的整个概念似乎很危险。而阅读代码应该是确定系统准确运行的最快方式。如果不是这种情况,那么我建议代码的质量有问题。

在阅读的同时,我也在学习。我认为对我正在开发的系统的进展感到好奇是我专业标准的重要组成部分。有了这种好奇心,就有能力发展系统并改进它。如果我做不到这一点,我认为开发会很快变得乏味。

于 2009-05-02T06:13:08.397 回答
0

不断地!

于 2009-05-02T04:57:30.577 回答
0

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

每个小块都应该做一件事并做好,无论那块是函数、方法还是类。

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

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

这就是 Andrew Koenig 所说的“抽象是选择性无知”的意思。通过有意放弃某事物内部如何工作的知识,我们可以考虑的不是它是如何工作的,而是它了什么。

让我们举一个简短的例子。在高端,我们可能会说,“这个类在某些数据结构中找到最小的 int”。这告诉我们它做了什么而不是它是如何做的,在这个抽象层次上,这就是我们所关心的。

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

现在在较低级别上,它的工作方式可能在内部它是一个堆,或一个优先级队列,或其他任何东西。

这些东西可以用树、自平衡树,甚至(次优)链表来实现。链表将是次优实现,但只要它做同样的事情,我们就不会真正咖啡馆,因为如果次优恢复到足以减慢我们的程序,我们可以将其换成更好的实现具有相同的界面。

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

这是重要的部分:因为应用程序的其余部分不依赖于该模块的内部或副作用,所以将较差的实现换成更好的实现不会改变我们的其他代码,它只会加快整体速度。

每一层只与它上面和下面的层通信,因此每一层都可以被替换。从视觉上看,它看起来像圆圈中的圆圈,而不是维恩图的重叠。

如果你不得不依赖直觉,这表明你的代码有副作用,或者它与其他模块的接口过于宽泛,或者根本没有接口,而不是由自包含构建的代码,不相交,模块,你有重叠和交叉点和脆弱的代码。你没有把它分解成足够简单的片段来理解。

(废话,已经晚了,我担心我可以更好地分解这个解释。我会回来编辑这个。)

于 2009-05-02T04:58:13.710 回答
0

如果你改变一个方法,你应该彻底测试它,包括调用这个方法的方法。

于 2010-05-31T02:26:14.187 回答