解决方案
如果您正在进行测试驱动开发,则可以通过重构解决几乎所有问题。我毫不费力地改变了主要的设计决策,并挽救了十年前的代码库。
唯一的例外是当您发现您的架构从头到尾都是完全错误的。例如,如果您使用线程编写应用程序,但您发现需要一堆异步状态机。到那时,就扔掉初稿吧。
其他提示
尽早丢弃,稍后重构
对于小型系统来说,丢弃是可以的,但如果系统规模很大,你就没有资源这样做。
但是,您可以创建一个小型试点项目,仅实现实际项目的非常重要的功能。经过一些尝试、错误、学习和扔掉东西之后,你最终会获得一个坚实的核心,并对实际项目有更好的理解。然后,您可以通过添加所需的所有功能来扩大项目的规模。但一旦到达那里,你就无法丢弃核心。只有重构。
如果你足够无情,重构的最终结果将非常接近你从头开始重建所得到的结果,但在此过程中你不会被困在一个不工作的系统中。
《人月神话》的中心点之一是,软件开发的困难部分是弄清楚要说什么,而不是如何说。
我最近对此的解释是,从初稿中获得的最大价值是以测试的形式收集和保存的需求。如果您小心翼翼地不测试并非系统实际要求的东西,那么您 能 重构你的方法来摆脱混乱。
只要您不将自己编写的代码陷入必须开始丢弃测试的陷阱,就可以丢弃所需数量的代码,而不会损失大量实际工作。
我的一般建议是将现有系统从糟糕的设计重构为具有更好设计的系统。这可以维护系统并允许随时部署它。如果您从头开始,可能需要一段时间才能部署,或者永远无法部署。
如果您正在谈论只是在没有现有系统的情况下编写一些全新的代码,那么通常最好编写一点代码,无论您想要什么,然后将其丢弃,因为它从未部署并重新开始(使用 TDD)。
到了一定程度,重构就是浪费时间。你只需要从头开始。如果你保持你的设计相当灵活,并且你认识到你还不知道一切,你就不必扔掉任何东西。当然,一个类可能会变得多余,但你不会丢弃整个系统。
灵活的设计对于能够正确重构是必要的。没有设计或严格的设计意味着您最终会扔掉一些东西 - 要么因为您无法重构,要么因为不断的重构降低了代码库的可维护性。很少有人足够细致和自律,能够完成一长串的小重构以保持完整性。除非你有一支全明星球队,否则这种退化就会发生!
长话短说:你可以通过重构来摆脱大多数麻烦。但有时,您将无法重构某些设计元素。当这种情况发生时,就该重新开始了——尽管希望您可以重复使用现有的一些组件。
不同的情况需要不同的方法。就我个人而言,我更喜欢尽可能重构以获得更好的设计。重构比重写产生的错误更少。
但是,即使您打算扔掉一个,编写一堆验收测试以确保您的第二个版本走在正确的轨道上仍然是一个好主意。然后,您可以逐步迁移到下一个版本,同时确保从用户的角度来看您的功能不会发生变化。听起来有点像重构,只是有点草率。
当谈论敏捷时,你可以两者都做,但一般来说,你会做 尖峰 (原型)只是为了尝试特定问题,了解它们并能够做出更好的估计。当您在真正编写应用程序代码时进行简单的尖峰和重构时,请丢弃。
亲切的问候
当我尝试解决新问题或新功能时,我会制作原型。之后,我将根据我学到的知识重建它。实际上,这听起来很像重构......什么?也许是同一件事?嗯...
我认为有时扔掉一个是最好的方法,但这可能会造成伤害。我发现有效的一件事就是扔掉一个,但要好好选择你的技术。
例如,我用 Ruby on Rails 编写了一个大型代码库,在过去的 2-3 年里,RoR 取得了很大进步。我还在架构方面做出了一些需要修复的决定。所以,我扔掉了一个,并从头开始构建一个新的。然而,我仍然可以使用 70-80% 左右的旧代码,因为我仍在使用 Ruby 和 Rails 编写代码。
对此有帮助的主要因素是 Rails 迫使您编写结构良好的代码,并将业务逻辑和表示层分开。我第一次并没有得到完美的结果,但由于一切都很好地分离和干燥,将代码移植到 Rails v2.1,重新架构问题区域,并重新编写一些“问题”功能已经成为一个问题。相当无痛的体验。
因此,通过从一开始就选择一项出色的技术,我就可以扔掉一项技术,但仍然保留 70-80% 仍然有效的旧技术。
在《人月神话》后来的一篇文章中,布鲁克斯警告说,他发现如果你确实打算扔掉 1 个,那么你最终会扔掉 2 个!
我在现实生活中亲眼目睹过这种情况;我们将项目1版本分配给了平庸的程序员,因为“我们计划以后将其扔掉 - 无论如何我们都会。”我们最终不得不将其重写为2版,但是那也被扔掉了。我从未见过版本 3——该公司倒闭了。
我认为,当布鲁克斯说“打算扔掉一个,您无论如何都会“更像是“尚待发现的错误数量是'n+1'”的说法。也就是说,这是关于墨菲定律的一项仅仅是一个严肃的陈述,而不是实际建议。从中吸取的教训是,原型是有价值的,好的写作就是重写,并且不要害怕放弃那些不起作用的东西。
然而,这必须归结为一个判断,因为正如 Joel Spolsky 在几篇文章中谈到的那样,放弃并重新开始的选择很诱人,因为代码编写比阅读更容易,编写比维护更有趣,所以你的自然倾向总是会是重新开始,即使这并不是最好的做法。
我认为您的版本控制系统在这里发挥着重要作用。如果您运行一个具有简单分支的分布式版本控制系统(现在有 git、mercurial),那么您将能够更轻松地构建原型,更轻松地重构,同时仍然拥有有效的工作副本。其他任何事情都需要更多的纪律。
作为该组织的开发经理,我“不允许”编写生产代码。
我(ab)使用这一规则来消除快速、肮脏的概念验证代码,以解决一个或其他的症结点,然后我将其签入源代码管理并指向一个“适当的”开发人员并说“这就是它的方式”完成了,现在就好好做吧。”
这已经是我们最接近“扔掉一个”的情况了,而且我可能最多花了几个小时才把事情搞定。对于此类工作来说,花时间进行错误处理、边界检查和所有其他构成良好代码的工作是浪费时间,但这意味着那些获得报酬编写生产代码的人可以花他们的时间编写生产代码的时间,并且在代码审查时间时不要有“这只是一个原型”之类的借口。
建造一个然后扔掉常常被用作不做好工作的借口。这意味着您实际上并没有在此过程中遇到足够多的问题,无法学到足够的知识来充分利用任何人的时间。而做得好,却直接扔掉,更是浪费。
正如一些人之前所说,任何软件中最重要的功能就是它的发布。考虑到这一点,我会在任何一天构建“一个让人们付钱给我的产品”,而我在重构方面的无情是只允许足够的重构来获得一个可以工作并且可以合理维护的产品。
构建一个可以扔掉的任何支持分支概念的配置管理系统都很容易。如果您正在对现有系统进行根本性的设计更改,而该系统已在现场并且是您的薪水来源;你该死的更好的分支;原型;如果不起作用就扔掉它。
重构大型遗留摇钱树系统通常会导致普通的老式黑客攻击。我想重构听起来比黑客攻击要好得多。