我已经学到了大量的编码,但是,它始终处于科学环境(不是计算机科学)中,完全是自学成才,没有任何人可以指导我正确的方向。因此,我的编码之旅一直很……混乱。我现在注意到,每当我构建某种类型的程序时,最终我都知道如何才能更优雅,更高效,更灵活,更容易地进行管理。在某些情况下,我实际上回过头来重新构建了东西,但是通常这在实践中不可行。尽管到目前为止我的大多数程序都相对较小,但是每次创建某些东西时都完全重写大型程序似乎很笨拙。
我只是想知道,这是否是正常的体验?如果没有,您如何防止这种情况发生?我已经尝试过预先计划一些事情,但是直到我开始敲定一些代码之前,我似乎无法真正预见到一切。
#1 楼
这种感觉是完全正常的,而且是预期的。这意味着您正在学习。几乎每次我开始一个新项目时,我都会朝一个方向开始,最后最终进行不同的设计。通常的做法是先开发原型,然后再开始开发真实的东西。重新访问旧代码并对其进行重构也很常见。如果您的设计是模块化的,这会更容易-您可以一次轻松地重新设计零碎的部件,而不必完全浪费您的旧设计。
对于某些人来说,它有助于首先编写伪代码。其他人发现从写注释描述代码将要执行的操作开始,然后在通用结构存在后再编写代码是有帮助的。这两件事将帮助您更好地规划设计,并避免进行重写的必要。
如果您是团队的一员,那么对设计过程进行审查至关重要。请别人检查您的代码,以便您可以学习如何改进设计。
#2 楼
这是一种非常普遍的经历我与大多数人互动,而我本人也有这种感觉。据我所知,其原因之一是您在编写代码时了解了更多有关领域和使用的工具的知识,这使您在编写程序后就认识到许多改进的机会。
另一个原因是,您可能会对理想的干净代码解决方案有所了解,然后现实世界及其凌乱的局限性就变成了现实,这迫使您编写不完善的解决方法,骇客可能会让您不满意。
“每个人都有一个计划,直到他们被打在脸上。”
该怎么办
在某种程度上,您将不得不学习接受您的代码将永远不会是完美的。一种帮助我解决问题的方法是“如果我讨厌一个月前编写的代码,则意味着我已经学习并成为一名更好的程序员”。
缓解此问题的一种方法是在您不断工作和重构时不断寻找潜在的改进。确保在重构和添加新功能/修复错误之间取得平衡。这不会解决较大的设计问题,但通常会使您拥有更引以为傲的更优美的代码库。
评论
我唯一要添加的答案是,在编写代码时学习并遵循TDD原则。然后,当您继续以开发人员身份发展并希望对现有代码进行更改时,这些测试将为重构和重写提供一个安全的环境。
– David Arno
18-10-26在7:55
@DavidArno使用当前测试重构的重点是什么?就像升级系统并在执行备份之前... 0娱乐。
–Džuris
18-10-26在10:10
@Džuris,:)是的,如果您喜欢挑战,请不要编写测试
– David Arno
18-10-26在10:23
@Džuris就像戴着降落伞跳下飞机一样!
– JollyJoker
18-10-26在11:25
@jamesqf-这是TDD和单元测试所做的错误描述-例如他们可以告诉您模型或算法已正确实现,并且重构时您没有破坏任何东西。他们无法告诉您模型实际上是有用的。在科学环境中和在其他环境中一样。
– Ant P
18-10-26在17:18
#3 楼
学习重构-逐步改进代码的技巧。我们一直都在学习,因此认识到自己编写的代码可以以更好的方式编写是非常普遍的。但是您应该能够转换现有代码以应用这些改进,而不必从头开始。
评论
这种做法还将帮助您学习编写模块化的代码,并且可以更轻松地对其进行重构。世界上所有的讲座都没有教给我这种习惯,就像不得不将旧的大功能分解为可管理的块一样,这样我就不必从头开始重写。每个新项目,我都会做得更好。
– JKreft
18-10-26在16:17
我认为学习重构的最好起点是:重构是关于新功能的
–通配符
18-10-26在19:38
#4 楼
如果您有出色的静态要求并很好地理解它们并且有时间进行详细分析,则有机会提出一个不错的设计,完成后仍然会感到满意。即使在这种幸福的情况下,您也可能会学习一些新的语言功能,这些功能本来可以帮助做出更好的设计。
但是,通常情况下,您不会很幸运:要求将不及一流且不完整,尽管您认为您理解它们,但事实证明您对某些领域做出了错误的假设。
然后,当用户查看您的初始可交付成果时,需求将发生变化。然后,用户无法控制的某些事情将发生变化,例如税法。
假设情况会发生变化,您所能做的就是设计。尽可能进行重构,但要意识到时间和预算通常意味着您的最终交付成果不如一开始就知道现在所知道的要优雅。
随着时间的推移,您会发现“会更好地挖掘您所收到的需求,并为您的设计中的某些部分可能需要灵活性来吸收变更提供帮助。
最后,接受变更是不变的而忽略一句话说“我可以做得更好”。为您提供了一个完整的解决方案感到自豪和高兴。
评论
或者,换一种方式:从一开始就学会设计灵活性,而不会引入不必要的开销。理想情况下,灵活性源自简单性。这样您的设计就有机会通过不断变化的需求的测试。
–cmaster-恢复莫妮卡
18-10-26在11:40
#5 楼
如果我从头开始完全重建程序,该如何避免总是感觉会更好呢?
您能做的就是创建一个废弃的原型,在开始做“真实”项目之前。快速又脏。然后,当您获得证明概念的原型时,您将了解系统以及如何正确执行操作。
但是不要感到惊讶,如果经过N年,您又回到了这段代码,并认为“什么是一团糟”。
评论
或者,您将原型展示给老板,他说:“很棒,那样就可以了。”将您转到另一个项目,您的原型就可以投入生产。
–Ryanfae苏格兰
18-10-26在13:20
这个建议是如此普遍,以至于有一句话。 “建立一个扔掉”。该建议被滥用,以至于有一句话:“如果建造一个要扔掉的东西,则可能会扔掉两个。”
–埃里克·利珀特
18-10-26在13:59
@EricLippert在相反的方向上,我的经历非常糟糕-如果我们制造一个要扔掉的东西,那么它不可避免地会被运送给客户。
–乔恩
18-10-26在14:56
@Jon:是的,这可能发生,尤其是当管理人员错误地“在用户界面看起来像它的工作原理”并且实际上在那里有任何代码时。就是说,我的经验一直都是,因为我的一位同事曾经说过:“我们有足够的时间将其错误地构建两次,而没有足够的时间将其正确地构建一次”。 :-)
–埃里克·利珀特
18-10-26在15:56
@EricLippert这就是最后构建GUI的原因,而不是为原型构建GUI的原因;)
–BЈовић
18-10-29在7:46
#6 楼
记住这一口头禅:完美是善的敌人。
完美的解决方案并不总是理想的解决方案。理想的解决方案是用最少的工作量就能达到“足够好”状态的解决方案。
它满足有关功能和性能的所有要求吗?
摆脱了无法在当前体系结构中解决的严重错误?
估计将来在维护此应用程序方面将投入多少工作。
重写的工作量是否会超过节省的长期工作量?
您的新设计是否有可能使情况实际上变得更糟?
如果您回答对所有这些问题都是肯定的,那么您的软件“足够好”,没有充分的理由从头开始重写它。相反,将您学到的设计课程应用于下一个项目。
每个程序员过去都有一些凌乱的程序是完全正常的。在我作为软件开发人员的职业生涯中,发生了几次,我查看了一些代码,想知道“是什么笨蛋写了这个烂摊子?”,检查了版本历史,并注意到几年前是我。
#7 楼
我已经尝试过预先计划事情,但是直到开始编写一些代码之前,我似乎无法真正预见所有事情。
认为完美的计划可以为您提供完美的软件设计/体系结构,但是事实证明这是绝对错误的。这有两个大问题。首先,“在纸上”和“代码”很少匹配,原因是因为很容易说出应该怎么做而不是实际去做。其次,不可预见的需求变更会在开发过程的后期就变得显而易见,而这些变化从一开始就无法被解释。
您是否听说过敏捷运动?这是一种思考方式,我们重视“响应变化”而不是“遵循计划”(除其他事项外)。这是宣言(快速阅读)。您还可以阅读有关Big Design Up Front(BDUF)以及缺陷的信息。
不幸的是,企业版“ Agile”是一堆虚假的东西(经过认证的Scrum管理员,繁琐的流程“敏捷”的名称,强制Scrum,强制100%的代码覆盖率等),通常会导致精简的流程更改,因为管理人员认为敏捷是一个流程,而且是灵丹妙药(两者都不是)。阅读敏捷宣言,听鲍勃叔叔和马丁·福勒这样的人发动这项运动,不要被“公司敏捷”的废话所吸引。
特别是,通常您只需要对科学代码进行TDD(测试驱动开发)就可以摆脱困境,并且您的软件项目很有可能会表现得很好。这是因为成功的科学代码主要具有超可用的界面,其性能是次要的(有时是竞争的)问题,因此您可以摆脱“贪婪”的设计。 TDD会迫使您的软件具有超强可用性,因为在实际实现之前,您要编写(理想情况下)如何调用这些东西。它还可以通过具有小的接口的小型函数来强制执行小功能,这些接口可以通过简单的“输入” /“输出”方式快速调用,并且可以在需求发生变化时进行重构。
I我想我们都可以同意
numpy
是成功的科学计算软件。它们的界面小巧,超级可用,并且所有功能都可以很好地配合使用。请注意,numpy
的参考指南明确建议使用TDD:https://docs.scipy.org/doc/numpy-1.15.1/reference/testing.html。过去,我曾将TDD用于SAR(合成孔径雷达)成像软件:而且我还可以断言它在该特定领域非常有效。注意:TDD的设计部分工作较少在像分布式系统这样的基本重构(例如,确定您需要软件高度并发)的系统上很难做到。例如,如果您必须设计一个拥有数百万并发用户的Facebook之类的东西,那么执行TDD(以推动您的设计)将是一个错误(在进行初步设计后仍然可以使用,只需进行“测试首次开发” ”)。在跳入代码之前,请考虑一下应用程序的资源和结构,这一点很重要。 TDD永远不会引导您使用高度可用的分布式系统。
如何避免总是觉得自己从头开始完全重建程序
,我会做很多事情更好吗?
鉴于以上所述,应该可以很明显地看出,完美的设计实际上是不可能实现的,因此追求完美的设计是愚蠢的。你真的只能接近。即使您认为可以从头开始重新设计,也可能仍然存在一些隐藏的需求,这些需求并没有显示出来。此外,重写至少需要花费与开发原始代码相同的时间。几乎可以肯定它不会更短,因为新设计很可能会遇到无法预见的问题,而且您还必须重新实现旧系统的所有功能。
考虑到您的设计仅在需求变化时才真正重要。如果没有任何改变,设计有多糟糕都没关系(假设它在当前用例中是完全可用的)。我使用的基线有22,000行切换语句(该函数甚至更长)。这是糟糕的设计吗?哎呀,那太可怕了。我们解决了吗?不。它可以正常工作,并且系统的那部分从未真正引起崩溃或错误。在我参与该项目的两年中,它只被触摸过一次,您猜到有人在开关中插入了另一个盒子。但是,花时间去修复很少碰到的东西是不值得的,事实并非如此。让不完美的设计保持原样,如果它没有破裂(或不断破裂),则不要修复它。所以也许您可以做得更好...但是值得重写吗?您将获得什么?
HTH。
#8 楼
我完全同意Andreas Kammerloher提供的答案,但我感到惊讶的是,没有人建议学习和应用一些编码最佳实践。当然,这不是灵丹妙药,但是使用面向开放的方法,设计模式,理解代码何时闻起来等等,将使您成为一个更好的程序员。研究什么是库,框架等的最佳用法。还有很多可以肯定的,我只是在摸索。这并不意味着您不会将旧代码视为完全是垃圾(实际上您会看到最老的程序比没有这些知识的情况更垃圾),但是使用每一个新编写的软件,您都会发现自己有所改进。还要注意,编码最佳实践的数量会随着时间的推移而增加,其中一些仅会发生变化,因此您实际上永远无法达到完美。接受这个或相当的路径。
另一个好处是进行代码修订。当您独自工作时,很容易偷工减料。如果您有第二个人查看您的代码,他们将指出您没有遵循这些最佳做法。这样,您将产生更好的代码并学到一些东西。
#9 楼
除了这里的其他出色答案外,我发现有帮助的一件事就是知道您要去哪里。很少有人会独自进行大量重构。但是,当您在代码库的每个区域上工作时,通常都可以在“隐藏的范围内”进行较小的重构。而且,如果您有目标,则可以利用这些机会逐步朝着正确的方向发展。
这可能需要很长时间,但是大多数步骤都会改善代码,最终结果将是值得的。
此外,感觉自己可以做得更好是一个好兆头!它表明您在乎工作质量,并且正在对其进行严格评估。因此您可能正在学习和改进。不要让这些事情让您担心-但不要停止这样做!
#10 楼
您无意间偶然发现了人类最大的挑战之一(冒险),人与机器之间的桥梁。人与物理结构之间的桥梁,例如土木工程,已经进行了大约200年或更长时间。由于软件开发实际上只是在90年代才成为主流,所以它已有30多年的历史了。我们已经了解到,它不只是一门社会科学的工程学科,我们才刚刚开始。
是的,您将尝试使用TDD,重构,函数式编程,存储库模式,事件采购,MV东西,Java脚本(<-执行此操作,这很疯狂),模型绑定,No Sql,容器,敏捷,SQL(<-执行此功能很强大)。
没有一个解决方法。甚至专家们仍然在抓紧稻草。
欢迎来到这里,这里很寂寞;但绝对令人着迷。
#11 楼
我要反驳一点。这是非常普遍的现象,但这是不可接受的。这表明您在编写代码时并没有意识到组织代码的好方法。这种感觉来自您的代码不简单。您的经验也是很长一段时间的,但是最近(过去几年),我一直在编写更多的代码,这些代码并没有使我失望觉得我需要丢掉一切。这大致就是我所做的:
请明确任何特定代码块的假设。如果不符合要求,则抛出错误。孤立地考虑此问题,而不依赖于软件其余部分正在执行的操作的详细信息。 (软件的其余部分正在执行的操作会影响您执行的假设以及所支持的用例,但是在违反假设时,它不会影响是否抛出错误。)
不要相信遵循以下规则和模式以及实践会产生好的代码。抛弃不明显和直接的思维方式和做法。这个很大。 OO和TDD通常以一种不以实际考虑为基础的方式进行教授,这是编写代码时应遵循的一组抽象原则。但这对于实际开发良好的代码完全无济于事。如果您完全使用OO或TDD,则应将其用作解决您所了解的问题的解决方案。换句话说,仅当您考虑问题并认为“好吧,这完全有意义,并且作为一个好的解决方案是非常明显的”时,才应使用它们。
当我在编写代码时,请注意两个问题:
我是否按照设计使用功能和库的方式使用它们?这包括将其用于要解决的问题或至少非常相似的问题。
这很简单吗?我的同事和我自己以后能否轻松遵循逻辑?有什么事情不是从代码中立即显而易见的吗?
我的代码现在更“程序化”,我的意思是它是根据执行的操作而不是使用的数据结构来组织的。我确实使用无法即时替换独立功能的语言的对象(C#和Java无法即时替换功能,Python可以)。我现在倾向于创建更多的实用程序函数,这只是避免了一些烦人的样板,因此我实际上可以阅读代码的逻辑。 (例如,当我需要处理列表中所有项的组合时,我将索引循环到扩展方法中,该方法返回
Tuple
,以便原始功能不会被那些实现细节所困扰。)现在有更多东西作为函数的参数,而不是让函数伸出其他对象来获取它。 (调用者取回或创建它,然后传递给它。)现在,我留下更多注释,这些注释仅通过查看代码即可解释不明显的内容,这使方法逻辑更容易理解。我只在有限的情况下编写测试,在这种情况下,我只关心自己所做的事情的逻辑,并且避免使用模拟。 (我对隔离的逻辑进行了更多的输入/输出测试。)结果是代码并不完美,但实际上似乎还可以,甚至在2或3年后。代码可以很好地响应更改。可以在不使整个系统崩溃的情况下添加或删除或更改较小的事物。在某种程度上,您必须经历一个混乱的时期,以便有一定的经验的。但是,如果事情仍然如此混乱,以至于您想将其全部扔掉并重新开始,那是有问题的。你不是在学习。
#12 楼
通过记住您的时间是有限的。而且您的未来时间也很有限。无论是用于工作,学校还是个人项目,当涉及到工作代码时,您都必须问自己“重写我的有限和宝贵时间的最佳利用方式是吗?”。或者,也许“这是我有限的时间中最负责任的使用方式”?有时答案肯定是肯定的。通常不会。有时它会在栅栏上,并且您必须使用自己的判断力。有时候,仅仅因为您会从中学到的东西就可以很好地利用您的时间。
我有很多项目,无论是工作的还是个人的,都会从移植/重写中受益。我还有其他事情要做。
#13 楼
这是学习的完全正常的部分。您会在旅途中发现错误。这就是您如何变得更好,而不是您应该避免的事情。
#14 楼
您可以给自己一个经验,以了解诱人的重写冲动通常是徒劳的。查找一些复杂度适中的旧的,冗长的,笨拙的开源项目。尝试从头开始重写它,看看如何做。从头开始的设计最终是否像您希望的那样优雅?
您的解决方案真的对吗?同样的问题吗?您遗漏了哪些功能?您错过了哪些最佳案例?
您为追求优雅而在原始项目中遗忘了哪些来之不易的教训?
将这些缺失的片段重新添加到设计中之后,它是否干净整洁?
最终,您的本能将从思考“我可以更好地重写此系统”转变为思考“该系统的不便之处可能表明某些复杂性尚未立即显现。 “
评论
正常,程序更多相关。
也有点相关
欢迎编程。如果您不看旧代码而认为“苦恼”,那么您应该开始担心,因为这意味着您已停止学习。
老实说,当我看到这个问题时,我笑了起来-因为从字面上看,我曾经说过的每个程序员,包括我自己,始终都具有这种感觉。如果您想进入一个自己觉得最终产品没有缺陷的领域,那么编程是错误的选择。