您必须维护的最不健全的程序是啥? [关闭]

Posted

技术标签:

【中文标题】您必须维护的最不健全的程序是啥? [关闭]【英文标题】:What's the most unsound program you've had to maintain? [closed]您必须维护的最不健全的程序是什么? [关闭] 【发布时间】:2010-09-18 12:08:14 【问题描述】:

我定期被要求对一个由真正的火箭外科医生建造的系统进行维护工作。它有很多错误,很难知道从哪里开始。

不,等等,我从头开始:在项目的早期,设计师被告知系统需要扩展,他读到可扩展性问题的根源是应用程序和数据库服务器,所以他确保尽量减少这种流量。如何?通过将所有应用程序逻辑放在 SQL Server 存储过程中。

说真的。大部分应用程序功能由 html 前端制定 XML 消息。当中间层接收到 XML 消息时,它使用文档元素的标记名称作为它应该调用的存储过程的名称,并调用 SP,将整个 XML 消息作为参数传递给它。它获取 SP 返回的 XML 消息并将其直接返回给前端。 应用层没有其他逻辑。

中间层有一些代码可以根据模式库验证传入的 XML 消息。但在确定 1)后我删除了它 1)只有一小部分消息具有相应的模式, 2) 消息实际上并不符合这些模式,以及 3) 在验证消息后,如果遇到任何错误,该方法会丢弃它们。 “这个保险丝盒真的很省时 - 它来自工厂,预装了便士!”)

我以前见过做错事的软件。很多。我已经写了不少了。但我从未见过任何这样的冷酷决心,在每一个可能的转折点都做错事,这体现在这个系统的设计和编程中。

好吧,至少他是按照他所知道的去做的,对吧?嗯。显然,他所知道的是Access。而且他并不真正了解 Access。或数据库。

这是这段代码中的一个常见模式:

从 TestCode 中选择 @TestCodeID,其中 TestCode = @TestCode 从 CountryWHERE CountryAbbr = @CountryAbbr 中选择 @CountryID 选择发票。*,测试代码。*,国家/地区。* 从发票 加入 TestCode ON Invoice.TestCodeID = TestCode.ID 在 Invoice.CountryID = Country.ID 上加入国家 WHERE Invoice.TestCodeID = @TestCodeID AND Invoice.CountryID = @CountryID

好的,好的。您也不信任查询优化器。但是这个怎么样? (最初,我打算在What's the best comment in source code you have ever encountered? 中发布此内容,但我意识到除了这一条评论之外,还有更多要写的内容,而且事情变得一发不可收拾。)在许多实用程序存储过程结束时,您将看到如下所示的代码:

-- 修复 NULL SET @TargetValue = ISNULL(@TargetValue, -9999)

是的,该代码正在做的正是你不能让自己相信它正在做的事情,以免你被逼疯。如果变量包含 NULL,他通过将其值更改为 -9999 来警告调用者。以下是这个数字的常用用法:

-- 获取目标值 执行 ap_GetTargetValue @Param1,@Param2,输出 @TargetValue -- 检查 NULL 值的目标值 如果@TargetValue = -9999 ...

真的。

有关此系统的另一个维度,请参阅 thedailywtf.com 上标题为 I Think I'll Call Them "Transactions" 的文章。我不是在编造这些。我发誓。

当我在这个系统上工作时,我经常会想起 Wolfgang Pauli 对一个学生的著名回答:“那是不对的。它甚至没有错。”

这真的不是有史以来最糟糕的程序。这绝对是我整个 30 年(哎呀)职业生涯中最糟糕的一次。但我还没有看到一切。你看到了什么?

【问题讨论】:

所以.... 这真的不是一个问题,而是一个发泄!我猜你是在反问:你能顶一下吗! ...嗯... 这似乎更适合您的博客或网站dedicated to discussion。 我问这个问题是因为我认为(并且仍然认为)它的答案可能有用。软件中的故障分析通常仅在软件完全故障后(如果当时)进行;只有通过辛勤工作才能保持生命的可怕的山寨往往只有一两个人才能真正理解。一个软件有多糟糕并且仍然有用?这些东西是如何产生的,需要哪些努力来支持它们?很难系统地探讨这些问题,但值得探讨。 这几乎就是我想说的:这是这种探索的开始,并不是一个真正可以回答的问题。在其他地方似乎更合适(但这并不一定会降低它的用处)。 FWIW,我发现这个问题是wtf tag cleanup 的一部分。 @rob 它绝对是isc-dhcp-server(意思是它的源代码)干得好伙计们! 【参考方案1】:

为特定公司维护 ASP 应用程序,该公司雇用开发人员来维护他们以前的雇员......所有这些应用程序都没有记录,也没有任何 cmets。

每个函数都被复制并粘贴到每个 ASP 页面中。所以没有定义任何功能或任何功能......每天我都被他们的环境所困扰,因为我必须首先远程到服务器,绕过DMZ。之后,我必须远程连接到必须进行更改的生产服务器。

【讨论】:

【参考方案2】:

我曾经不得不维护一个遗留的 C 应用程序,该应用程序以前是由一些失去编程意愿(可能还活着)的程序员编写和维护的。它有太多的 WTF 可提,但我记得有一个布尔函数,它在各种特殊情况下会返回 TRUE+1、TRUE+2 等。

然后我读了Roedy Green's essay 并笑了很多,直到我意识到我觉得这很有趣的原因是我从我维护的代码中认出了大部分示例。 (那篇文章经过多年的补充变得有些臃肿,但仍然值得一看。)

【讨论】:

【参考方案3】:

我曾经被要求帮助追踪 EDIF 阅读器中的周期性崩溃。几乎立刻我就开始头疼。原作者似乎觉得Yacc 会因为空格而惩罚他,并且他的 Yacc 语法是一个密集的、难以阅读的混乱。我花了几个小时对其进行格式化,为出现的缺失终端添加规则,构建声明以避免堆栈增长,瞧,崩溃消失了。

所以请记住,每次等待 Yacc 处理语法时,生成的解析器都会运行数千次。不要吝啬空格!

【讨论】:

【参考方案4】:

一个 php/mysql 驱动的在线联系人管理系统,其中联系人表没有自然键。有许多数据库字段实例包含分隔字符串形式的复合数据,随后需要由应用程序代码解析。

HTML和逻辑交织在一起,几乎没有使用任何函数,而是将代码剪切粘贴到几十个源代码文件中。数据没有被清理,因此带有(例如)嵌入垂直选项卡的字段导致 Ajax 调用返回的 XML 出现故障,最重要的是,文件包含数十个(如果不是数百个)由右大括号紧跟分号组成的空语句: ";"

【讨论】:

【参考方案5】:

我维护了ExtUtils::MakeMaker。 MakeMaker 肯定不是我必须维护的最糟糕的代码。这实际上是一个工程奇迹。然而,正是在这种独特的编码恐怖中,最关键的代码也是最可怕的。

MakeMaker 是大多数 Perl 模块的安装程序。当您运行“Makefile.PL”时,您正在调用 MakeMaker。如果 MakeMaker 中断,Perl 就会中断。 Perl 可以在所有东西上运行,因此 MakeMaker 必须在所有东西上运行。当我说一切时,我的意思是一切。每个奇异的 Unix 变体。 Windows 95 上。和VMS。是的,VMS。

MakeMaker 做什么? Makefile.PL 是一个 Perl 程序,它编写一个包含 shell 命令的 Makefile,这些命令通常运行 Perl,以构建和安装 Perl 模块。让我重复一遍:它编写 shell 命令来运行 Perl。 Perl,替代 shell 脚本的语言。

哦,它还可以编译和链接C代码。它还可以将 Perl 模块静态链接到 perl。哦,它可以管理 RCS 结帐。哦,还有你的发行版的 tarball 和 zip 文件。并做所有其他与安装模块相关的事情。

它必须以可移植、向后兼容的方式完成所有这些工作。它必须处理...中的变体和错误。

make(GNU make、BSD make、nmake、dmake、mms、mmk 等等) 外壳 Perl 文件系统(如果您认为这没什么大不了的,请尝试 VMS) C 编译器和链接器

它绝对不会失败,并且必须保持 100% 向后兼容。

哦,它几乎没有真正的扩展 API,所以它必须与人们为了扩展它而必须做的临时 Makefile 黑客保持兼容。

为什么它会做这一切? 15 年前,当 Perl 只在 Unix 上运行时,这似乎是个好主意。当你可以只使用 make 时,为什么还要编写一个完整的构建系统? Perl 是一种文本处理语言;我们只是用它来写一个 Makefile!

幸运的是,有一个替代品 Module::Build,我寄希望于它会迅速杀死 MakeMaker。但它的采用速度很慢,而且社区对这种变化非常抵触,所以我坚持维护 MakeMaker。

【讨论】:

【参考方案6】:

我曾经尝试写一个MP3 解码器。没用。

【讨论】:

假设双关语是有意的,你会得到我的支持,先生,不错,而且这些主观问题无论如何都不值得任何严肃的回答。 “不健全”,我猜。不是我听过的最好的双关语:P 不过,难以检测的奖励积分。 提耶。干得好那个人,很好地发现了那个punfinder :) +1 我曾经写了一个shell脚本来静音扬声器。【参考方案7】:

我曾经做过一个用 BASIC 编写的 CAD 应用程序,公司的政策是每个程序都必须以语句开头:

错误恢复

jMM

【讨论】:

【参考方案8】:

刚从朗讯的研究生院毕业,我就得到了一个用 PL/I 编写的编译器和解释器来维护。正在编译的语言描述了一组复杂的完整性约束,解释器针对一个大型数据集运行这些约束,该数据集稍后将形成控制 4ESS 开关的初始数据库。 4ESS 过去和现在都是用于长途语音流量的电路交换机。

代码很混乱。有一个标签可以分支到称为“NORTH40”。我问原始开发者这是什么意思。

“这是范围检查的地方,你知道,检查以确保每个字段都有正确的值。”

“但为什么是‘NORTH40’?”

“你知道,‘家,靶场上的家。’”

“嗯?”

原来“NORTH40”是指农场的北部 40 英亩,在他这个城市长大的人看来,这与养牛场有着模糊的联系。

另一个模块有两个名为 TORY 和 DIREC 的并行数组,它们是并行更新的,因此对包含数据对的单个数组进行建模的尝试显然是错误的。我无法弄清楚名称并询问了开发人员。原来它们是要一起读的:“目录”。伟大的。

不得不编写完整性约束的可怜人没有用户手册来指导他们,只有口头传统和手写笔记。更糟糕的是,编译器没有进行语法检查,而且他们经常会指定一个被悄悄映射到错误逻辑中的约束,通常会产生非常糟糕的后果。

更糟糕的事情发生了。当我深入研究解释器的内部时,我发现它是围绕一个巨大的排序过程构建的。在排序之前是一个输​​入过程,它从原始数据和完整性约束生成排序输入数据。因此,如果您有一个包含 5,000 个主干定义的表,并且每个主干记录具有三个字段值,这些字段值在整个输入数据集中必须是唯一的,那么输入过程将创建 3 * 5,000 = 15,000 个排序输入记录,每个记录都是原始的以完整性约束编号为前缀的数据记录和要排序的字段值的副本。它没有进行三个 5,000 条记录排序,而是进行了一次 15,000 条记录排序。当您考虑到数百个表内和表间完整性约束以及一些非常大的表时,您遇到了组合噩梦。

我能够进行一些重构,记录语言,并添加带有可理解错误消息的语法检查,但几个月后我跳槽到了一个新的小组。

【讨论】:

【参考方案9】:

CAD/CAM 几何处理语言的解释器(P1 = 10,10; P2 = 20,20; L1 = P1,P2; - 那种东西),用微软 BASIC 专业开发系统 (PDS) 编写,具有最小长度的变量名称(它很快就用完了单个字母,所以转向双字母。PP,PQ​​,PR,有人吗?)。而且,公平地说,一些 cmets。意大利语。

有趣的是,它确实有效,并且我能够为其添加一些功能,但这就像业余牙科 - 很痛苦,当然不推荐......

【讨论】:

【参考方案10】:

我曾经写过的任何东西,最初都应该是一个快速的原型,但最终会停留一段时间。我的问题领域本质上需要大量一次性原型设计。对于这些原型,有时违反每个最佳实践和良好风格的规则是合理的,如果原型最终值得保留,只需完成它并稍后清理。然而,有时这些原型最终很难正常工作,但最终会成为守护者。在这些情况下,我通常会无限期地推迟重构/重写这个东西,因为我担心我永远不会让它再次工作。进一步削弱我的积极性的是,我的老板是一位完全不会编程的领域专家。

【讨论】:

【参考方案11】:

我曾经是一名 COBOL 程序员(不寒而栗)。我们所有的代码都属于“不健全”的类别。在 COBOL 中,您没有命名空间,所有变量都是全局变量,并且文件名和其他资源有很多强制重复。要调用过程,您需要设置全局变量,调用过程,然后检查这些全局变量(或其他可能被设置的变量)的内容。

不过,最糟糕的是维护一个在我出生(我出生于 1967 年)之前编写的 COBOL 程序,它的唯一流控制方法是 GOTO。这绝对是一团糟,无法遵循。对变量类型的微小更改可能需要几天时间才能完成。没有自动化测试,手动测试计划也从未保存,因此每次更改都需要编写新的手动测试计划,详尽地执行,并与代码一起上交。

具有讽刺意味的是,这正是 COBOL 如此成功的原因。 COBOL 通常由作业控制语言 (JCL) 执行。由于 COBOL 很弱,程序不会做很多事情,所以 JCL 会分配一些磁盘空间(通常下到柱面级别),并执行一个小的 COBOL 程序来读取数据,然后只写出您需要的数据。然后 JCL 可能会调用一个排序程序来对结果文件进行排序。然后将调用另一个 COBOL 程序来读取已排序的文件并汇总数据,并可能重新提取所需的结果。并且可能会再次使用 JCL 将文件移动到其他位置,然后调用另一个 COBOL 程序来读取结果并将它们存储在数据库中,等等。每个 COBOL 程序往往只做一件事,并创建了 Unix 管道模型的原始版本——这一切都是因为 COBOL 太难维护或做任何复杂的事情。我们有松耦合和紧内聚(程序之间,而不是程序之间),因为几乎不可能以任何其他方式编写 COBOL。

【讨论】:

【参考方案12】:

您必须维护的最不健全的程序是什么?

我曾经写过的一切!

说真的。我阅读博客、收听播客和关注此类网站的次数越多,我每天学到的就越多。每天我基本上都意识到我昨天写的所有东西在某种程度上都是错误的。我很同情那些维护我在职业生涯早期写的东西的可怜虫。

【讨论】:

哦,我也是。但是糟糕的代码和根本上非常错误的代码之间存在类别差异。编写这种代码需要的不仅仅是缺乏经验。这需要大量的毫无根据的自信。【参考方案13】:

我正在维护我们在 Intranet 中使用的调度 Web 应用程序。当我被问到是否可以从调度程序中删除代理时,我想,当然可以。 当我查看源代码时,我发现这个代理一天中的每个小时都是单独编码的。他一周中的每一天都是如此。该地区每个代理商的每周都是如此。大约 5 个区域中的每个区域也是如此。 Html 文件中到处都是 asp 代码。

有一天,我花了一些时间来猜测这些不同的文件中有多少行代码,我估计大约有 300000 行。30 万行代码,一次是手写的,然后是复制粘贴的代码。

但是这个数字很快让我的经理相信我们很快就需要一个新的日程安排应用程序。

【讨论】:

【参考方案14】:

我刚刚开始的那个。

    没有源代码控制。 所有源代码都是实时编辑的。为了防止错误,有一些备份文件,如 db-access.php.070821 散布在源代码树中。 代码异常脆弱 - 几乎没有错误检查的方式,如果确实如此,绝对不会回退。

【讨论】:

一定要等几天,因为他们正在努力解决他们的错误。它使生成的面部表情更加有趣。 这是一个分布式互联网项目,但有真正的现金。我一直在讨论 svn,到目前为止还没有发生任何事情。 只是破坏构建。然后让它坏了一个星期。然后也许他们会看到使用版本控制的好处 =D 绝对不是。这个项目涉及真金白银。

以上是关于您必须维护的最不健全的程序是啥? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET 上启动维护过程的最佳实践是啥 [关闭]

在asp.net核心中实现“维护模式”

实现自定义“beforeunload”对话框的最不显眼的方式是啥?

.NET 中用于编写验收测试的最新维护最多的框架是啥? [关闭]

Yii2 中的本地组件是啥? [关闭]

您的 SQL Server 备份/维护计划是啥?