我如何才能避免总是觉得自己完全从头开始重建程序,会做得更好呢?[关闭]


91

我已经学到了大量的编码,但是,它始终处于科学环境(不是计算机科学)中,完全是自学成才,没有任何人可以指导我正确的方向。因此,我的编码之旅一直很……混乱。我现在已经注意到,无论何时构建某种类型的程序,最终我都知道如何能够更加优雅,高效,灵活,易于管理地完成它。在某些情况下,我实际上回过头来重新构建了东西,但是通常这实际上是不可行的。尽管到目前为止我的大多数程序都相对较小,但是每次创建东西时都完全重写大型程序似乎很笨拙。

我只是想知道,这是正常的经历吗?如果没有,您如何防止这种情况发生?我已经尝试过预先计划一些事情,但是直到开始编写一些代码之前,我似乎无法真正预见到一切。


87
它的正常节目,更多
Ewan

7
尼尔


43
欢迎进行编程。如果您不看旧代码并认为“敦促”,那么您应该开始担心,因为这意味着您已停止学习。
卡托

9
老实说,当我看到这个问题时,我笑了起来-因为从字面上看,我曾经说过的每个程序员,包括我自己,始终都具有这种感觉。如果您想进入一个自己觉得最终产品没有缺陷的领域,那么编程是错误的选择。
Zibbobz

Answers:


4

这种感觉是完全正常的,而且是预期的。这意味着您正在学习。几乎每次我开始一个新项目时,我都会朝一个方向开始,最后最终进行不同的设计。

通常的做法是先开发原型,然后再开始开发真实的东西。重新访问旧代码并对其进行重构也很常见。如果您的设计是模块化的,这会更容易-您可以一次轻松地重新设计零碎的部件,而不必完全浪费您的旧设计。

对于某些人来说,先编写伪代码会有所帮助。其他人发现从写注释描述代码将要执行的操作开始,然后在通用结构存在后再编写代码,将很有帮助。这两件事将帮助您更好地计划设计,并避免重写的必要性。

如果您是团队的一员,那么进行审核的过程对于设计过程至关重要。请别人检查您的代码,以便您可以学习如何改进设计。


100

这是非常普遍的经历

与我互动的大多数人,以及我本人也有这种感觉。据我所知,其中一个原因是您在编写代码时了解了更多有关领域和使用的工具的知识,这使您在编写程序后就认识到许多改进的机会。

另一个原因是,您可能会对理想的干净代码解决方案有所了解,然后现实世界及其凌乱的局限性便挡在了您的脑海,迫使您编写不完善的解决方法和hack,这可能会让您不满意。

“每个人都有一个计划,直到他们受到打击为止。”

该怎么办

在某种程度上,您将必须学会接受代码永远不会是完美的。一种帮助我解决问题的方法是“如果我讨厌一个月前编写的代码,则意味着我已经学习并成为一名更好的程序员”。

缓解问题的一种方法是在您不断工作和重构时不断寻找潜在的改进。确保在重构​​和添加新功能/修复错误之间取得平衡。这不会解决大的设计问题,但通常会使您拥有更引以为傲的更完善的代码库。


18
我唯一要添加到此答案中的就是在编写代码时学习并遵循TDD原则。然后,当您继续作为开发人员进行改进并希望对现有代码进行更改时,这些测试将为重构和重写提供一个安全的环境。
David Arno

10
@DavidArno使用当前测试重构的重点是什么?这就像升级系统并在执行备份之前... 0娱乐。
Džuris

3
@Džuris,:)是的,如果您喜欢挑战,请不要编写测试
David Arno

6
@Džuris就像戴着降落伞跳下飞机一样!
JollyJoker

3
@jamesqf-这是TDD和单元测试所做的错误描述-例如,它们可以告诉您模型或算法已正确实现,并且重构时您没有破坏任何东西。他们无法告诉您模型实际上是有用的。在科学环境中和在其他环境中一样。
Ant P

46

学习重构- 逐步改进代码的艺术。我们一直都在学习,因此认识到自己编写的代码可以以更好的方式编写是非常普遍的。

但是您应该能够转换现有代码以应用这些改进,而不必从头开始。


4
这种做法还将帮助您学习编写模块化的代码,并且可以更轻松地对其进行重构。世界上所有的讲座都没有教给我这种习惯,就像不得不将旧的大功能分解为可管理的块一样,这样我就不必从头开始重写。每个新项目,我都会做得更好。
JKreft

我认为学习重构的最佳起点是:重构是关于新功能的
Wildcard'Oct

9

如果您有出色的静态要求,并且很好地理解它们并且有时间进行详细分析,那么您就有机会提出一个好的设计,完成后仍然会感到满意。

即使在这种幸福的情况下,您也可能会学习一些新的语言功能,这些功能本来可以帮助改善设计。

但是,通常情况下,您不会那么幸运:这些要求将不那么出色和不完整,尽管您认为自己理解了这些要求,但事实证明,您在某些方面做出了无效的假设。

然后,随着用户对您最初的交付物的了解,需求将发生变化。然后,用户无法控制的某些事情将发生变化,例如税法。

您所能做的就是设计,假设一切都会改变。尽可能进行重构,但要意识到时间和预算通常意味着您的最终交付成果并不像一开始就知道现在所知道的那样优雅。

随着时间的流逝,您将更好地挖掘所收到的需求,并为您的设计中的某些部分可能需要灵活性以吸收变化提供帮助。

最后,接受变化是不变的,不要理会“我可以做得更好”的说法。对于您提供的解决方案感到自豪和高兴。


或者,换一种方式:从一开始就学习设计的灵活性,而不会引入不必要的开销。理想情况下,灵活性源自简单性。这样您的设计就有机会通过不断变化的需求的测试。
cmaster

3

我如何才能避免总是觉得自己完全从头开始重建程序,会做得更好呢?

您可以做的是先创建一个一次性原型,然后再开始执行“实际”项目。快速又脏。然后,当您获得原型以证明概念时,您将了解系统以及如何正确执行操作。

但是不要感到惊讶,如果经过N年,您又回到了这段代码,并认为“是一团糟”。


3
或者,您将原型展示给老板,他说:“很棒,那样就可以了。” 将您转到另一个项目,您的原型就可以投入生产。
RyanfaeScotland '18

2
这个建议是如此普遍,以至于有一句话。“建立一个扔掉”。该建议被滥用,以至于有一句话:“如果建造一个要扔掉的东西,则可能会扔掉两个。”
埃里克·利珀特

2
@EricLippert在相反的方向上,我的经历很糟糕-如果我们制造一个扔掉,那不可避免地会被运送给客户。
乔恩(Jon)

2
@Jon:是的,这可能发生,特别是当管理人员错误地“在用户界面看起来像它的工作原理”并且实际上在那里有任何代码时。就是说,我的经验一直都是,因为我的一位同事曾经说过:“我们有足够的时间将其错误地构建两次,而没有足够的时间将其正确地构建一次”。:-)
埃里克·利珀特

@EricLippert这正是最后构建GUI的原因,而不是为原型构建GUI的原因;)
BЈовић18年

3

记住这个咒语:

完美是善良的敌人

完美的解决方案并不总是理想的解决方案。在理想的解决方案是实现状态“足够好”与工作最少的一个。

  • 它是否满足有关功能和性能的所有要求?
  • 它是否包含您在当前体系结构中无法修复的严重错误?
  • 估计将来您将投入多少工作来维护该应用程序。重写的工作量是否会超过节省的长期工作量?
  • 您的新设计是否有可能实际上使情况变得更糟?

如果您对所有这些问题的回答都是“是”,则说明您的软件“足够好”,没有充分的理由从头开始重写它。而是将您学到的设计课程应用于下一个项目。

每个程序员过去都有几个凌乱的程序,这是完全正常的。在我作为软件开发人员的职业生涯中,发生了几次,我查看了一些代码,想知道“是什么白痴写了这个烂摊子?”,检查了版本历史,并注意到是几年前的我。


3

我已经尝试过预先计划一些事情,但是直到开始编写一些代码之前,我似乎无法真正预见到一切。

诱人的是,认为完美的计划可以为您提供完美的软件设计/体系结构,但是事实证明,这绝对是错误的。这有两个大问题。首先,“在纸上”和“代码”很少匹配,其原因是因为很容易说出应该如何做而不是实际去做。其次,无法预见的需求变更在开发过程的后期就变得显而易见,而这从一开始就不可能被考虑。

您听说过敏捷运动吗?这是一种思考方式,我们重视“响应变化”而不是“遵循计划”(除其他事项外)。这是宣言(快速阅读)。您还可以阅读有关Big Design Up Front(BDUF)的内容以及存在的陷阱。

不幸的是,企业版“敏捷”是一堆虚假的东西(认证的Scrum管理员,以“敏捷”为名的繁重流程,强制Scrum,强制100%代码覆盖率等),并且通常会导致asinine流程更改,因为管理者认为敏捷是一个过程和一个灵丹妙药(两者都不是)。阅读敏捷宣言,听鲍勃叔叔和马丁·福勒这样的人发动这项运动,不要被“公司敏捷”的废话所吸引。

特别是,通常您只需要对科学代码进行TDD(测试驱动开发)就可以摆脱困境,并且您的软件项目很有可能会表现得很好。这是因为成功的科学代码大多具有超可用的界面,其性能是次要的(有时是竞争的)问题,因此您可以摆脱“贪婪”的设计。TDD迫使您的软件具有超强可用性,因为在实际实现之前,您编写(理想情况下)如何调用它们的代码。它还可以通过小的接口强制执行小的功能,这些接口可以通过简单的“输入” /“输出”方式快速调用,并使您处于重构的良好位置 以防需求改变。

我认为我们都可以同意这numpy是成功的科学计算软件。它们的界面小巧,超级可用,并且所有功能都能很好地配合使用。请注意,该numpy参考指南明确建议使用TDD: https //docs.scipy.org/doc/numpy-1.15.1/reference/testing.html。过去,我曾将TDD用于SAR(合成孔径雷达)成像软件:而且我还可以断言,它在该特定领域非常有效。

注意: TDD的设计部分在难以进行基本重构(例如确定您的软件需要高度并发)的系统中效果不佳,例如在分布式系统中。举例来说,如果你要设计的东西,如Facebook,你有几百万的并发用户,进行TDD(驱动设计),将是一个错误(仍是好的使用后,你有一个初步的设计,只是做“测试第一发展”)。跳入代码之前,请考虑一下应用程序的资源和结构,这一点很重要。TDD绝不会引导您使用高度可用的分布式系统。

我如何才能避免总是觉得自己完全从头开始重建程序,会做得更好呢?

鉴于上述情况,应该可以很明显地看出,完美的设计实际上是不可能实现的,因此追求完美的设计是愚蠢的。你真的只能接近。即使您认为可以从头开始重新设计,也可能仍然存在一些隐藏的需求,这些需求并没有显示出来。此外,重写至少需要花费开发原始代码的时间。几乎可以肯定它不会更短,因为设计可能会遇到无法预料的问题,而且您还必须重新实现旧系统的所有功能。

要考虑的另一件事是,只有需求改变时,设计才真正重要。如果没有任何改变,设计有多糟糕都没关系(假设它在当前用例中是完全可用的)。我在一个具有22,000行切换语句的基线上工作(该函数甚至更长)。这是糟糕的设计吗?哎呀,太可怕了。我们解决了吗?不。它可以正常工作,并且系统的那部分从未真正引起崩溃或错误。在我参与该项目的两年中,它只被触摸过一次,您猜到有人在开关中插入了另一个盒子。但是,花时间去修复如此少见的东西是不值得的,事实并非如此。让不完美的设计保持原样,如果它没有破裂(或不断破裂),则不要修复它。所以也许您可以做得更好... 但这值得重写吗?你会得到什么?

HTH。


2

我完全同意Andreas Kammerloher提供的答案,但令我惊讶的是,没有人建议学习和应用一些编码最佳实践。当然,这不是灵丹妙药,但是使用面向开放的方法,设计模式,了解代码何时闻起来等等,将使您成为一个更好的程序员。研究什么是库,框架等的最佳用法。还有很多可以肯定的地方,我只是在摸索。

这并不意味着您不会将旧代码视为垃圾(实际上您会看到最旧的程序比没有这些知识的垃圾还要多),但是每编写一份新软件,就会看到你进步。还要注意,编码最佳实践的数量会随着时间的推移而增加,其中一些仅会发生变化,因此您实际上永远无法达到完美。接受这个或相当的路径。

另一件事是进行代码修订。当您独自工作时,很容易偷工减料。如果您有第二个人检查您的代码,他们将指出您没有遵循这些最佳做法。这样,您将产生更好的代码,并且您将学到一些东西。


1

除了这里的其他出色答案之外,我发现有帮助的一件事就是知道您想去哪里

很少需要独自进行大量重构。但是,当您在代码库的每个区域上工作时,通常都可以在“雷达之下”进行少量的重构。如果您有目标,也可以利用这些机会逐步朝着正确的方向发展。

这可能需要很长时间,但是大多数步骤都会改进代码,并且最终结果值得。

另外,觉得自己可以做得更好是一个好兆头!它表明您在乎工作质量,并且正在对其进行严格评估。因此您可能正在学习和改进。不要让这些事情让您担心-但不要停止这样做!


1

您无意间偶然发现了人类最大的挑战之一(冒险),人与机器之间的桥梁。人与物理结构之间的桥梁,例如土木工程,已经进行了大约200年以上的历史。

由于软件开发实际上只是在90年代才成为主流,因此它已有30多年的历史了。我们已经知道,与其说它是一门社会科学,不如说是一门工程学科,而我们才刚刚开始。

是的,您将尝试TDD,重构,函数式编程,存储库模式,事件源,MV东西,Java脚本(<-执行此操作,这很疯狂),模型绑定,No Sql,容器,敏捷,SQL(<-执行此操作它功能强大)。

没有一个解决方法。甚至专家们仍然在抓紧稻草。

欢迎,并被警告,这是一个孤独的地方。但绝对令人着迷。


1

我要反对一点。这是非常普遍的现象,但是不可接受。这表明您在编写代码时并没有意识到组织代码的好方法。这种感觉来自您的代码不简单

您的经验也是很长一段时间的,但是最近(过去几年),我一直在编写更多的代码,这些代码并没有使我觉得需要扔掉所有东西。这大致就是我所做的:

  1. 要明确任何特定代码块的假设。如果不符合要求,则抛出错误。孤立地考虑此问题,而不依赖于软件其余部分正在做什么的细节。(软件的其余部分正在执行的操作会影响您执行的假设以及所支持的用例,但不会影响在违反假设时是否引发错误。)
  2. 停止相信遵循规则,模式和实践将产生好的代码。抛弃不明显和直接的思维方式和做法。这个很大。OO和TDD通常以不基于实际考虑的方式进行教授,这是编写代码时应遵循的一组抽象原则。但是,这对于实际开发良好的代码完全无济于事。如果您完全使用OO或TDD,则应将其用作解决您所了解的问题的解决方案。换句话说,仅当您考虑问题并认为“好吧,这完全有意义,并且作为一个好的解决方案是非常明显的”时,才应使用它们。没过
  3. 在编写代码时,请注意两个问题:
    • 是否按照设计使用的方式使用功能和库?这包括将其用于要解决的问题类型,或者至少用于非常相似的问题。
    • 是不是简单?我的同事和我自己以后能否轻松遵循逻辑?有什么事情不是从代码中立即显而易见的吗?

我的代码现在更具“过程性”,我的意思是它是根据执行的操作而不是使用的数据结构来组织的。我确实使用无法即时替换独立功能的语言的对象(C#和Java无法即时替换功能,Python可以)。我现在倾向于创建更多的实用程序功能,这只是避免了一些烦人的样板,因此我实际上可以阅读代码的逻辑。(例如,当我需要处理列表中所有项目组合时,我将索引循环到扩展方法中,该方法返回Tuple,这样原始函数就不会被那些实现细节所困扰。)我现在将更多的东西作为参数传递给函数,而不是让函数伸向其他对象来获取它。(调用者取回或创建它,然后传递给它。)现在,我留下更多注释,这些注释仅通过查看代码即可解释不明显的内容,这使得遵循方法的逻辑更加容易。我只在有限的情况下编写测试,在这种情况下,我只关心自己所做的事情的逻辑,并且避免使用模拟。(我对隔离的逻辑进行了更多的输入/输出测试。)结果是代码并不完美,但实际上似乎还可以,甚至2或3年后。代码可以很好地响应更改。在整个系统不崩溃的情况下,可以添加,删除或更改一些小事。

在某种程度上,您必须经历一个混乱的时期,这样您才能有一些经验。但是,如果事情仍然如此混乱,以至于您想将其全部扔掉并重新开始,那是有问题的。你没有学习。


0

通过记住您的时间是有限的。而且您的未来时间也很有限。无论是用于工作,学校还是个人项目,在涉及工作代码时,您都必须问自己“重写此代码是对我有限和宝贵时间的最佳利用吗?”。或者,“这是我有限的时间中最负责任的使用方式”吗?

有时答案肯定是肯定的。通常不会。有时它会在栅栏上,并且您必须使用自己的判断力。有时候,仅仅因为您会从中学到的东西就可以很好地利用您的时间。

我有很多项目,无论是工作项目还是个人项目,都将从端口/重写中受益。我还有其他事情要做。


0

这是学习的完全正常的部分。您会发现自己犯错了。

这样您才能变得更好,这不是您应该避免的事情。


0

您可以给自己一个经验,以了解诱人的重写冲动通常是徒劳的。查找一些复杂度适中的旧的,冗长的,笨拙的开源项目。尝试从头开始重写它,然后看看您如何做。

  • 从头开始的设计最终是否像您希望的那样优雅?
  • 您的解决方案确实解决了同样的问题吗?您遗漏了哪些功能?您错过了哪些优势案例?
  • 在追求优雅的过程中,您抹去了原始项目中哪些来之不易的教训?
  • 将这些缺失的部分重新添加到设计中之后,它是否像没有它们一样干净?

最终,您的直觉将从思考“我可以更好地重写此系统”转变为思考“该系统的不便之处可能表明某些复杂性尚未立即显现出来”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.