是否可以编写不需要不断修改的软件?


23

我已经用许多不同的语言编写了许多软件,并且还“编写”了与使用Verilog和VHDL的FPGA一起使用的硬件。

我倾向于更喜欢编写硬件而不是编写软件,我认为主要原因之一是可以编写“完成”并且不需要修改的硬件:定义接口和功能,编写测试平台,实施硬件模块,然后使用模拟器测试其功能。然后,您可以依靠该硬件模块作为构建块来创建更大更好的产品:如果需要向该模块添加功能,则可以创建第二个模块并在其中添加功能。您永远不会丢掉原始模块,因为它可以正常工作并且仍然有用。

我对软件的主要不满之一是它永远不会“完成”。总有另一个功能要添加。通常,添加功能时会在以前正常工作的其他地方引入错误。只要不违反接口,在硬件中就不会发生这种情况。

明确地说,我并不是在建议使用某些功能列表来构建某个版本,而仅此而已:永远是这样:我赞成随着时间的推移进行迭代和发行多个版本以添加新功能。我只是不想在左侧戳代码而在右侧找到错误,而这似乎是在添加新功能之后发生的。

是否可以通过类似于“编写”硬件的方式来编写软件?有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?


8
您似乎在描述OCP
Oded

这个开放式原则的东西看起来很棒!有人成功使用过吗?
内森·法灵顿

2
@NathanFarrington:大多数设计模式(如GOF所述)都遵循OCP。一个示例是“ 模板方法模式”
Spoike 2012年

2
@NathanFarrington开闭原理是软件开发人员在设计软件时使用的通用原理。
杰斯珀2012年

1
我可以想象,我们今天使用的许多程序都是使用零碎的东西编写的,这些零碎的东西是20年前编写的代码的复本。
锦葵

Answers:


16

也许与诸如硬件之类的定义明确的接口和测试有关?

正是我的想法!

界面清晰,设计良好的模块在本质上趋于完美。想想像StringJava之类的东西。这是一个计算机程序,但是具有清晰的界面。没有已知的错误。它完美地完成了它应该做的事情。当然,在过去的15年中已经对其进行了广泛的测试,并且由于几乎所有程序都将Strings作为基本构建块,因此很快就会注意到其中的任何错误。任何“怪癖”-并不是严格的错误,而是值得关注的设计细节-例如此处http://www.jwz.org/doc/java.html中描述的那些,现在已经众所周知,因此可以纳入帐户。

Buggy软件在某种程度上是一个文化问题:人们习惯于使用有缺陷的软件,与硬件不同,软件通常可以在之后轻松地进行修复,因此它不必在一开始就进行完善(或者因为以前,因为嘿,我们必须立即发货,让我们修复在下一版本中)。但是在很大程度上,这是一个真正的复杂性问题:过去50年来,软件复杂性一直在稳步增加,但是人的大脑却是一样的。当实现完美的难度越来越大,以后解决问题(快速,自动构建,互联网分发)的难度越来越大,加上进度压力和缺乏纪律性时,结果就是如此。

有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?

可能没有灵丹妙药,而是最佳实践的良好结合,包括但不限于:

  • 简单的自治模块。换句话说,低耦合和高内聚。
  • 不变性。随着并发性的增长尤其重要。

值得注意的是,这两点都旨在降低复杂性。这就是关键。熵总是趋于增加,除非我们与之抗争,否则我们很快就会被复杂性淹没。有趣的是,在过去的几年中,编程语言一直在朝着鼓励甚至加强上述实践的方向发展。特别地,函数式语言的兴起仅在于:纯函数对于相同的输入总是返回相同的值,其中没有任何状态。然后,您仅需编写采用并返回不可变值的纯函数,并将不可避免的可变性限制在定义明确的小地方,而不是将其分散在各处。检查一下:http : //clojure.org/state


1
JWZ并不完全同意String类是bugfree - jwz.org/doc/java.html

1
它可能按设计工作,所以问题是基础设计是否损坏。但是,我同意String是一个非常可靠的类。

1
极好的答案。我现在几乎只使用Python编写代码,并尝试利用功能性编程构造。是的,我认为不变性是关键,这是正确的。第一次创建软件模块时,即使我对其进行测试,也可能弄乱了界面,或者它的职责错了或太多了。因此,我制作了第二个模块,而第一个模块则一个人留下!如果希望的话,将来我仍然可以使用第一个模块,但是永远不要更改它,因为它虽然不完美,但是可以工作。因此具有不变性的功能语言可以像您建议的那样提供帮助。
内森·法灵顿

1
@JoonasPulakka:是的,如果有一个单行的软件摘要,则可能是“总是存在另一个错误”。:-)我认为那是Nathan的观点之一。
罗斯·帕特森

1
乔纳斯,你赢了。我已经开始学习Clojure。看起来这是使编程再次变得有趣的最佳方法。
内森·法灵顿

9

区别在于,与硬件相比,修改软件有多容易和便宜。如果硬件既容易修改又便宜,并且可以修改并交付给客户,那么他们会。

我想,如果我可以总结一下我的表达不佳的问题,那就是:“如何通过不向工作代码中引入错误并始终保持进步的方式来编写软件,从而获得更多乐趣?” 也许与诸如硬件之类的定义明确的接口和测试有关?

您绝对应该检查测试驱动的开发


我的问题的许多答案似乎都包含民间传说。与从一开始就设计漏洞相比,查找和修复软件错误是否一定要容易得多?修改软件的实际费用是多少?可能没有人真正知道。对于测试驱动的开发,测试可以重用的软件是有意义的。然后,该软件将成为一个实际的构建块,而不是一团泥巴。但是,如果明天要更改软件,测试软件就没有意义。我想我想知道我们是否真的可以通过构件而不是泥浆来制造软件。
内森·法灵顿

1
@NathanFarrington:我相信硬件/软件规范和设计的制作方式会有很大的不同。大多数软件制造商比他们的客户只能说“我想要一个能做到这一点的程序”的软件开发者,对自己的产品有更好的规范。几乎可以肯定会获得新功能,而不会获得新功能。
RCE 2012年

当然,如果有很多更改,某些测试可能也需要更改,但这与软件从文字处理器到Web服务器的更改不同。您的“新文档”功能仍将创建一个新功能,并且其测试应仍然有效。
RCE 2012年

您指出了另一个关键原则:规格越好,则在将来需要的更改就越少。但这并不是我所要解决的问题,因为您可以像在软件之前一样在硬件上添加功能。我认为OCP是真正的答案,很遗憾,这是一条评论,所以我不能将其标记为答案。还有一点是要始终取得进步,我认为TDD通过提供回归测试可以帮助实现这一目标。
内森·法灵顿

更换软件似乎比硬件更容易且成本更低,但这是真的吗?是的,我可以进行更改,并且几乎可以用我的手指立即进行新的构建。但是,构建仍需要通过验证/质量检查。机会成本是多少?我将要做什么而不是解决此错误。如果他们不需要重新验证软件,则质量检查人员会一直在做什么。是否推出了其他项目来将此修复程序推向市场?人们没有想到很多“隐性”成本。它可能更容易,但不一定便宜。
Pemdas 2012年

6

我将对您的一些评论发表评论,希望您能从这些评论中得到答案。

我对软件的主要不满之一是它永远不会“完成”。

这是因为解决方案规范不完整,或者因为提供增强功能的计划不准确。任何项目软件,硬件或任何其他情况都可能发生这种情况。

有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?

当然,创建独立的模块应大大减少依赖性。在设计软件时必须考虑这一点。您需要考虑,关注点,层,层,控制器对象,接口等的分离。

“如何通过不将错误引入工作代码并始终取得进步来使软件编写更加有趣?”

1-仔细理解要求。可能是您需要在设计之前考虑关闭需求。如果您进行迭代开发,则没有机会这样做。有些方法鼓励这样做,但就我个人而言,我认为这并不适合所有项目类型。根据可靠的要求构建软件可以实现更好的设计。

2-让您的用户了解这种长期的哲学。

3-仔细计划实施

4-设计之前的代码。

5,适当时使用通用设计。

6-使用原型作为设计确认工具。


这些都是非常好的建议。总结到现在为止我的想法:(1)使发布一个BIG DEAL,并在发布之前进行大量测试和质量检查,(2)使模块成为一个BIG DEAL,并确保它们具有定义明确的文档接口并带有针对这些测试的测试接口和断言,以查看是否违反了接口,并且一旦“释放”模块,就永远不会对其进行修改(OCP)。
内森·法灵顿

4

正如人们通常很快指出的那样,软件的好处之一是,与硬件相比,它应该很容易而且相对便宜。当您后来才意识到自己犯了根本错误时,这一点尤其重要。对硬件进行同样的操作,您将损失一百万美元,因此,正如您所说的,您使用了模拟器等,并从中测试了bazinga。我认为这是您转向软件时范式失败的地方。

进入一般的软件开发人员的头脑,您所拥有的是一个非常忙碌的人,而且期限非常紧迫。他的经理说,可以保留一些错误,因为您以后可以随时对其进行修复。测试通常是事后才想到的,但是即使在测试驱动的情况下,测试也保持最少,而代码却被最小化,并且经常采用捷径,从而可能会遗漏许多边界情况。该系统可能已进行了彻底的单元测试,但很少作为一个整体进行严格的测试,并且很少对压力进行任何程度的测试。此外,您是从头开始编写软件,并且在承诺编写软件之前几乎没有机会模拟该软件,这主要是因为我们很少使用与硬件中相同的细粒度构建块来编写软件。

回到OP的问题。您能否定义一个构建基块系统,从中可以衍生出所有软件?可能吧 会非常划算吗?可能不是,因为到那时您就开始开发足够强大的组件,测试和其他设备系统,以支持这一理想编程系统,您会发现自己的竞争已经将您击败了市场,甚至更糟糕的是,从普通程序员的角度来看,您可能会发现“千篇一律”的编程系统风格非常有限,而且很有可能无聊。我个人使用API​​,其中大部分模块代码已经完全完善和标准化,因此我现在要做的就是生成代码模板并填充空白。我的大部分时间都花在编写简单的连接器代码上,并尽可能快地将模块推出市场。这真是令人麻木。除了重复编写相同类型的事情外,几乎没有什么机会做更多的事情,因此,当另一个项目机会出现时,我抓住了能够做其他任何事情的机会。

那么,您如何才能交付高质量和完善的软件,却又真正享受其中的乐趣呢?我认为这取决于您选择的工具和方法。对我来说,答案是使用良好的BDD API,因为它使我能够创建非常易于阅读但高度粒度的代码。我可以使用最少的可重用方法构建一套测试,并以规范的语言描述我的测试。这样,除了我负责设计和检查构件之外,我接近于一种更加组件化的开发方法。另外,测试输出可精确指出测试中发生故障的确切部分,因此我不必猜测故障是在设置中还是在断言中。

调整方法也有帮助。我是应用精益开发原则的坚定拥护者,并将其与敏捷运动多年来一直在努力的其他许多技术和原则相结合。消除了我过去常常感到沮丧的大多数浪费做法,这极大地帮助了开发工作变得更加愉快。我仍然遇到有时(但希望不是很频繁)错误出现在我的代码中的问题,但是我现在发现自己有更多的时间,甚至有更多的动力去花更多的时间编写更强大的测试,并针对100测试覆盖率百分比。更好的是,看到所有这些绿灯都在我一天结束时感觉真好,


我很好奇,您认为测试很重要,但同时也令人麻木。
内森·法灵顿

@NathanFarrington感谢您指出这一点。我的意思是要积极谈论测试,但是我在键入其他内容时正在考虑这一点,因此该段完全错误!我已更正以适合我要阐明的实际要点!
S.Robins 2012年

3

我对软件的主要不满之一是它永远不会“完成”。总有另一个功能要添加。

如果这让您感到沮丧,请考虑其他职业。说真的

的软件是能够增加新的功能。首先发明“软件”的全部原因是为了使我们可以添加功能。

通常,添加功能时会在以前正常工作的其他地方引入错误。

这是质量检查问题。

只要不违反接口,在硬件中就不会发生这种情况。

在软件中也是如此。

有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?

是。您必须实际练习质量保证。


1
我不是在尝试拖钓,是的,也许我并没有为软件着迷。但是您说的是“软件要点就是能够添加功能”。是不是真的?冯·诺依曼(von Neumann)发明了一种软件,该软件能够构建一台可以计算多个数学函数而又无需重新连接其逻辑和算术单元的计算机。我很好奇这种软件功能原理的来历。硬件具有功能,但是硬件的目的不是添加功能。
内森·法灵顿

我认为质量保证是指测试。是的,根据我的直觉,生产高质量的软件需要进行广泛的测试。但我认为这还不止于此。在硬件中,模块中可能存在错误。但是,当您添加新硬件时,它不会在现有硬件模块中引入新的错误。最终,可以找到并修复所有模块中的所有错误。但是在软件中,经常更改代码(更改模块)而不是添加代码,这可能会引入错误。我想我正在寻找一种纯粹是可加的软件开发方法。
内森·法灵顿

现在,我对您的答案有更明智的评论。我之所以对软件永不“完成”感到执着,可能是因为我一直对发行版使用一种非常松散的方法。一个新功能等同于下一个版本,无需进行回归测试且仅需很少的质量检查。如果发布是一件大事,那么我敢打赌,永无止境的软件将消失。
内森·法灵顿

@NathanFarrington:Turing发明了打破不断变化的Enigma代码的软件。“质量保证意味着您要进行测试”。假。我的意思是质量保证-开发的每个方面都应具有应满足的质量标准。测试是评估一种工件质量的一种(有限的)方法。“代码已更改……可能会引入错误”。正确。那是质量保证的失败-不是软件的固有功能。
S.Lott 2012年

我们绝对是一个话题。根据此链接,图灵的巨像不是通用的(在计算意义上),并且不使用存储的程序(软件)。
内森·法灵顿

2

是否可以通过类似于“编写”硬件的方式来编写软件?有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?

我建议您研究“ 形式方法 ”,以验证设计和软件的正确性。您用于硬件设计的模拟器工具正在尝试完成一些任务。我不认为目前用于正式方法的工具几乎可以在工业上使用,并且唯一有强烈动机去实现无缺陷的行业是航空电子医学(有趣的是,FDA明确表示“软件有所不同来自硬件”)。此外,如果您使用Verilog / VHDL进行开发,那么您将坚持使用二进制逻辑。这大大降低了复杂性。不会有等同于Y2K问题的硬件。

最大的问题是事情很复杂。而且您无法消除复杂性,只能四处移动。


1

可以编写“完成”且无需修改的硬件:定义接口和功能,编写测试平台,实现硬件模块,然后使用模拟器测试其功能。然后,您可以依靠该硬件模块作为构建块来创建更大更好的产品:如果需要向该模块添加功能,则可以创建第二个模块并在其中添加功能。您永远不会丢掉原始模块,因为它可以正常工作并且仍然有用。

在软件世界中,我们将“模块”称为库,并且使用方式相同。许多库的构建都使其功能良好,然后满意地坐着不动地工作,直到“重要的事情”导致下一个修订。可以将它们看作是用环氧树脂浇注的软件:-)

我对软件的主要不满之一是它永远不会“完成”。总有另一个功能要添加。通常,添加功能时会在以前正常工作的其他地方引入错误。只要不违反接口,在硬件中就不会发生这种情况。

g。也许您个人比其他许多非焊接铁“硬件”人员更好,但是我见过许多不良的电路设计,芯片故障(例如,著名的英特尔“ f00f”问题),但这并没有与整个领域交谈。随着人造硬件变得“更软”,这些问题变得更加难以预防。

是否可以通过类似于“编写”硬件的方式来编写软件?有没有一种好的软件开发方法,可以始终取得向前的进展,并允许添加新功能而无需重写现有代码和引入新的错误?

是。我们只是很少使用这些方法。它们的操作成本往往非常昂贵,并且大多数程序员都不喜欢在其限制范围内工作。但是,例如,当涉及人类生活时,是的,我们尽量不杀死用户。

最后一点:软件的财务模型不同于硬件,甚至是编程的硬件。大多数非消费者软件以及某些消费者软件的销售方式都鼓励变革。当您可以告诉企业“现在支付10,000美元,每年再付18%”时,您基本上可以每隔几年转售一次该产品。但是要证明这笔费用是合理的,您需要给客户他们想要的更改。嗯...考虑到苹果的硬件过时曲线,也许这根本没有什么不同-硬件只会让您真正重新购买它!


从来没有说过我比任何人都好。;-)当硬件出现错误时,它将成为新闻。当软件出现错误ummm时,等待软件始终会出现错误。我们不使用哪种方法,因为它们太昂贵且不好玩?
内森·法灵顿

0

通过不向工作代码中引入错误并始终保持进步,我如何才能获得更多的编写软件乐趣?

我很想找到您问题的最终答案。但是现实是没有简单的方法可以做到这一点,这就是为什么极限编程和TDD技术如此流行的原因。您需要接受改变,因为这将要发生。我不知道这种方式是否有趣,但是可以肯定地减轻压力;-)

http://en.wikipedia.org/wiki/Extreme_Programming

当您与硬件交互时,硬件需要x值,这就是所有(理论上),但是当您与人交互时,今天他们需要x,明天他们可能需要y,依此类推。正是这样,商务和人的需求发生了变化。因为Persons!=机器,所以不可能在大多数时间都不会改变的代码。

就像我在上一个/已删除的答案中所说的那样,请通过在开始编码之前让人们思考来尝试避免不重要的更改。让用户更多地参与决策等。弄清变更成本,制定更多计划等。这些不是“编码方式”,而是“非编码方式”,因为尽管需求更多,但变更更少,更有趣。


1
好答案。我已经完成了极限编程。似乎与我所寻找的完全相反,整个项目的方向可能会根据客户的需求而每周更改。我不反对迭代版本,我只是不希望第二版引入第一版中不存在的错误。从长远来看,前期设计可以节省工作量,这是正确的。
内森·法灵顿

就像我一直说的,最好的代码是没有代码。:-)
H27studio 2012年

0

是否可以用类似的方式编写软件?

是的。就像在开发硬件,测试所有内容一样小心,您的软件将具有相似的质量。

顺便说一句,您是否没有听说过硬件错误?这比任何软件错误都要难得多,而且更难修复(不仅仅是升级软件)


1
是的,硬件也有错误,甚至是成熟的经过测试的硬件(如处理器)。设计好的硬件,以便可以在软件中修复硬件错误!我最初提出这个问题的原因是,我编写了很多软件,但对于引入错误的难易程度以及整个系统的混乱程度,我始终感到沮丧。我是一个干净利落的人,因此硬件开发方法对我而言似乎总是更自然。它也可能与范围有关。
内森·法灵顿

1
@NathanFarrington该软件通常比硬件更复杂。对硬件进行了更彻底的测试。SW可以更轻松地进行更改,因此人们倾向于不那么注意。
2012年

0

我还要指出,硬件中的软件错误通常会杀死人。因此,需要采取更多措施来彻底确定需求并预先进行全面测试。而且这些要求不需要更改,直到硬件更改为止。而且由于新硬件可能需要重写,因此我怀疑这个问题也不会累积太多。

另一方面,业务需求会不断变化,有时您很难在要求更改之前将一项需求投入生产。有时,在将需求移交给生产之前,我已经多次对其进行了更改。这是几件事情的结果。首先,业务方面的项目利益相关者通常不愿意花时间来彻底定义他想要的东西,因为他“忙”,“重要”,人们不会死,家人会起诉他,或者如果他被判入狱,他结束了自己的过程。其次,项目利益相关者往往对他们希望硬件做什么有更好的了解,因为对他们来说硬件不是那么抽象。他们真的不知道自己想要什么,直到看到它。硬件问题较少。


您有一个有效的观点。我们传统上在硬件中所做的事情的类型得到了很好的理解:处理器,USB控制器,PCI Express端点,内存控制器等。然后,我们将所有这些应用程序业务逻辑推送到软件中。也许随着我们在软件堆栈中的发展,事情变得更加混乱,人们对它的了解也越来越少了?
内森·法灵顿

-1

有许多高级工具,可以将它们结合到应用程序中,这些工具具有许多成品“砖”。砖是成品,供您使用,您只需要将它们组合在一起即可。也许您认为这更容易...直到您的客户要求您进行一些怪异和意外的更改。

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.