Dix pratiques pour être un mauvais programmeur

Il est malheureusement fréquent de rencontrer du code vraiment mauvais, principalement dans des petits projets, des projets "vites faits bien faits", mais également dans de plus grosses usines à gaz, ou souvent dans les entreprises qui éditent des logiciels propriétaires.

Mais est-il facile pour autant de devenir un mauvais programmeur ?

Voici 10 astuces qui permettront à tout un chacun de s'en approcher.

I. Obscurcir le code

La première chose que tout mauvais programmeur doit savoir, est qu'il est absolument hors de question de mettre des commentaires, à moins que ceux-ci soient tout sauf pertinents. Mieux, il peut être très intéressant de mettre un excès de commentaires totalement inutiles qui ne font qu'obscurcir davantage le code.

Par ailleurs, il est impératif de ne pas respecter les conventions d'indentation habituelle. Abandonnez même toute indentation, et favorisez l'écriture de plusieurs instructions sur une même ligne. N'hésitez pas à faire des fichiers de plus de 2 000 lignes, préférez les très grosses fonctions à plusieurs niveaux d'indentations.
Rajoutez à ça des goto et vous obtiendrez un code illisible.

II. Réinventer la roue

Vous avez un traitement basique à effectuer ? Vous doutez des fonctions standards ? Vous vous emmerdez pendant votre pause café ?
Pas de problème ! Réécrivez vous-même la fonction ou la classe en question !

Ceci a particulièrement son intérêt si vous utilisez un langage tel que Python ou Java, dans lesquels il est impossible de retrouver les performances des fonctions que vous copiez qui elles sont dans un langage bas niveau tel que le C.

Bien évidemment, encore une fois, prenez le soin de ne pas commenter, afin que vos collègues ne puissent pas saisir la subtilité de votre code et en soient dépendant.

Voici un bel exemple où un programmeur décide de réinventer le tri à bulle (pourtant déjà pas très efficace) en réduisant ses performances (source) :

 
void sortMe(ListType list)
{
  for(int i = 0; i < (list.entries() - 1); i++)
  {
    if(list[i] > list[i+1])
    {
      list.swap(i, i+1);
      sortMe(list);
    }
  }
}
 

III. Variables globales

Contrairement à ce que l'on peut supposer, le passage d'argument à une fonction est quelque chose qu'il faut éviter à tout prix.
Il est bien plus naturel de faire un maximum de variables globales. Ceci aura l'avantage de rendra le code bien moins lisible et moins naturel.

Par exemple :

 
#include <stdio.h>
#include <stdlib.h>
int a;
int b;
int result;
 
void calcul()
{
	result = a + b;
}
 
int main(int argc, char** argv)
{
	a = atoi(argv[1]);
	b = atoi(argv[2]);
	calcul();
	printf("%d\n", result);
 
	return 0;
}
 

Notez de non seulement grâce à ce code vous aurez utilisé des variables globales comme un gros gland, mais qu'en plus vous aurez pris soint de ne pas avoir fait de vérification de paramètres avant de les utiliser (si il y a bien trois cases dans argv, et si ce sont des entiers), ce qui augmente davantage encore votre niveau de mauvais programmeur.

IV. Convention de noms

Tout mauvais programmeur le sait : respecter une convention de nommage est quelque chose de fastidieux qui facilite la lisibilité de votre code.

Préférez donner des noms soit très courts (une lettre ou deux), soit très longs (plus de quinze lettres).

Voici un exemple de code très représentatif des mauvaises pratiques que vous devez de respecter :

 
 
class A
{
	int j;
 
public:
 
	A(int _j, int _k)
		: j(_j+_k)
	{}
 
	int GetTheJVariableWhichIsPrivate() const { return j; }
};
 

Ainsi que vous le voyez dans ce bref extrait que vous êtes encouragé à utiliser comme modèle, il faut absolument suivre les règles suivantes :

  • Toujours nommer vos variables privées avec une seule lettre non significative.
  • Vous pouvez pousser le vice jusqu'à nommer vos classes avec une seule lettre, comme dans cet exemple, mais là c'est d'un niveau bien plus élevé que ce vous pourrez espérer atteindre dans vos rêves les plus fous.
  • Dans vos fonctions (ici le constructeur), ne jamais donner de nom explicite aux arguments. Ainsi, en plus de ne pas savoir ce que fait la fonction, les utilisateurs de celle-ci ne sauront pas non plus comment s'en servir.
  • Détailler à mort le nom de vos fonctions et méthodes, mais sans donner d'information utile, juste ce que l'on sait déjà.
  • Notez aussi que le constructeur de A fait la somme de _j et _k pour la stocker dans l'attribut j de la classe, sans aucune raison apparente ni justification.

Vous pouvez aussi découvrir dans cet article un autre exemple de mauvaise convention de nommage.

V. Copié/coller

La factorisation consiste à mettre à un seul endroit du code utilisé plusieurs fois. Ceci facilite la maintenance de ce code, et c'est pourquoi vous devez l'éviter à tout prix !

Au contraire, préférez la duplication de code, en n'oubliant pas si possible de prendre, au passage, les quelques commentaires qui subsistaient dans le code original. Bien sur, si vous êtes amené à modifier la copie de ce code, surtout ne pas mettre à jour ces commentaires, afin d'obscurcir davantage.

Voici un exemple de bout de code que je n'invente pas, tout droit sortit d'un projet très connu sur IRC, qui n'est autre que CS, le service IRC, utilisé en particulier par le réseau IRC Orange avec le pseudo IriX :

 
if(caddr == NULL)
{
   add_userinfo(csadminchan, nick, atoi(level), FALSE, FALSE, FALSE, FALSE, FALSE, cryptpass, time(NULL));
   add_user_addrinfo(csadminchan, nick, usrhost);
}
if(caddr != NULL)
{
   add_userinfo(csadminchan, nick, atoi(level), FALSE, FALSE, FALSE, FALSE, FALSE, cryptpass, time(NULL));
   add_user_addrinfo(csadminchan, nick, usrhost);
   add_user_addrinfo(csadminchan, nick, ip_addr);
}
if(caddr == NULL)
{
   send_to_server(":%s NOTICE %s : Profile [%s] [ %d ] Autoop [OFF] Protection [OFF] Suspendu [OFF]\n",
                  bot.nick, nwho, nick,atoi(level));
   send_to_server(":%s NOTICE %s : Mask %s\n", bot.nick, nwho, usrhost);
   write_single_channel(csadminchan);
   info1[0] = '\0';
   add_infoinfo(csadminchan, nick, info1);
   write_single_infolines(csadminchan);
   return;
}
if(caddr != NULL)
{
   send_to_server(":%s NOTICE %s : Profile [%s] [ %d ] Autoop [OFF] Protection [OFF] Suspendu [OFF]\n",
                  bot.nick, nwho, nick,atoi(level));
   send_to_server(":%s NOTICE %s : Masks %s %s\n", bot.nick, nwho, usrhost, ip_addr);
   write_single_channel(csadminchan);
   info1[0] = '\0';
   add_infoinfo(csadminchan, nick, info1);
   write_single_infolines(csadminchan);
   return;
}
 

VI. Organisation des classes n'importe comment

Lorsque vous faites de la programmation objet, il est indispensable de ne PAS réfléchir à l'organisation de vos classes.

Voici quelques pratiques à suivre :

  • Préférez les grosses classes usines à gaz à tout faire, avec un maximum de méthodes dedans.
  • Faites beaucoup de liens entre vos classes, afin que toutes dépendent les unes des autres.
  • N'hésitez pas à faire des appels qui partent dans tous les sens. Par exemple la méthode A::Blah() appelle B::Truc() qui appellera C::Foo() rappelant A::Bar(), et ainsi de suite. (C'est la programmation spaghetti).
  • Ne créez pas d'attributs privés. L'encapsulation est inutile et complexifie inutilement le code. Rendez toutes vos variables publiques. Après tout cela facilite les comportements indésirables.

VII. Ne pas respecter le cycle de développement

Une mauvaise pratique qu'il est indispensable de suivre, est la restructuration du code pendant une phase de stabilisation du projet, par exemple après la sortie d'une RC.

Rien de tel en effet que retarder la release en modifiant un composant de base utilisé un peu partout dans le code, afin de rendre celui-ci instable et de nécessiter une RC supplémentaire. Un bon moyen est par exemple de changer totalement l'interface d'une classe très utilisée.

À ce propos, en particulier pour une bibliothèque, il est recommandé à chaque version de changer complètement l'API sans garder de retro compatibilité. Après tout cela donnera bien plus de travail aux utilisateurs de votre bibliothèque et favorisera une diminution du chômage.

VIII. Préférer le hard coding aux fichiers de configuration

Le hard-coding est tout un art qu'il est absolument nécessaire de maîtriser lorsqu'on est un mauvais programmeur. Il consiste à laisser dans le code des éléments de configuration, tels que le nom d'hôte de la base de donnée, les chemins d'accès aux données ("C:\Mes Documents" par exemple sous Windows 98, qui est un chemin par défaut mais qui pourrait avoir été configuré autrement par l'utilisateur), etc.

Le summum est de laisser dans le code le mot de passe d'un serveur utilisé en prod dans votre entreprise, surtout lorsque ce serveur est accessible depuis l'extérieur.

Une autre forme de hard-coding un peu plus modérée, est l'usage des magic-numbers. Il consiste en laissant des tailles de tableaux, par exemple, dans le code, plutôt que des constantes. Ceci a le merveilleux effet de rendre très difficile la modification de la taille d'un tableau utilisé à plusieurs endroits dans le code.
À ce propos, quelque chose qu'il faut absolument pratiquer, est l'utilisation de tailles excessives pour les buffers. Quoi de mieux en effet que de gaspiller la mémoire.

IX. Hacking

La pratique du hack est quelque chose de primordial dans la génération de code de merde.
Il consiste à préférer la modification locale et non généralisée d'une partie du code afin de résoudre un problème, plutôt que de généraliser davantage le système actuel en rajoutant des mécanismes permettant la résolution de ce problème, ce qui serait plus long à développer sur le moment, mais bien plus profitable sur le long terme.

Voici un exemple légèrement comique, qui est issue du code de Windows 2000 (rendu public illégalement il y a plusieurs années).

La fonction en question est CScriptHolder::OnScriptError(), qui apparament est un handler appellé en cas d'erreur dans un script JS. En voici un extrait :

 
        hr = errRecord.Init(pScriptError, pDoc);
        if (hr)
            goto Cleanup;
 
        // HACK fix for IE5 bug# 58568
        if (OutLook98HackReqd(pDoc, errRecord._uLine, errRecord._uColumn))
            goto Cleanup;
 

Il semblerait donc qu'un bug dans IE5 oblige le parseur à être un peu plus tolérant avec lui. Une fonction est donc dédiée à la détection de cette erreur. Et quelle fonction ! Admirez :

 
BOOL OutLook98HackReqd(CDoc *pDoc, ULONG uLine, ULONG uCol)
{
    if (((36 == uLine) && (35 == uCol) ||
        (51 == uLine) && (28 == uCol)) &&
        !_tcsicmp(pDoc->_cstrUrl, _T("outday://")))
        return TRUE;
 
    return FALSE;
}
 

X. Préférer les fonctions insécuriées telles que scanf(), strcpy(), etc

Vous ne deviendrez un mauvais programmeur que si prenez un soin particulier à y parsemer des failles en tous genres et autres petits bugs très joyeux à corriger.

Une chose à savoir est d'avant tout préférer les fonctions telles que strcpy(), scanf(), strcat() plutôt que respectivements strncpy(), fgets() ou strncat() (qui, au demeurant, ont des comportements pouvant également poser problème si elles sont mal utilisées).

Le top est quand même d'appeler strcpy() à partir d'une chaîne récupérer par le réseau. Par exemple :

 
void func(char* buf)
{
	char args[15][100];
	int i = 0;
	char* ptr;
 
	strtok(buf, " ");
	do
	{
		strcpy(args[i], ptr);
	}
	while((ptr = strtok(NULL, " ")));
 
	call_cmd(i, args);
}
 
void recv_msg(int sock)
{
	char buf[1024];
 
	recv(sock, buf, sizeof buf - 1, 0);
 
	func(buf);
}
 

Grâce à ce morceau de code erroné, vous pouvez envoyer un message au socket du type "a a a a a a a a a a a a a a aaaaa[100 fois][votre code frauduleux]" pour introduire des instructions dans la pile du programme.

Les exemples sont très multiples avec les diverses fonctions, donc allez y de cœur joie avec les dépassements de tampon.

Conclusion

Nous avons vu dix méthodes pour pourrir un projet qui ont fait leur preuve, ainsi que la majeur partie des programmeurs peuvent en témoigner. On a également été tous amené à en utiliser, soit par ignorance, soit par commodité, ou encore par faute de temps.

Cependant, il en existe bien d'autres. Je recommande à ce propos la lecture de cet article sur les Anti-Pattern qui en donne un bon nombre.

4 Responses to “Dix pratiques pour être un mauvais programmeur”


  • Effectivement, tu retrouveras d’ailleurs dans mon billet deux liens vers des exemples présents sur thedailywtf.
  • Il faut ajouter à cela qu’il faut mettre tout son code dans le même fichier (ne surtout pas utiliser de librairies) et si possible, tout mettre dans le main(), l’usage des fonctions étant réservé à la programmation avancée ;)
  • Up !
    je reconnais pas mal de monde la dedans :
    1 : les codeurs de thales
    2 : bis !
    3 : Poupoudoum
    4 : moi ! les noms à rallonge, c’est mon dada !
    5 : Poupoudoum, nicolas (à siter aussi pour le 1 d’ailleurs pour ce dernier)
    6 :moi, again… même si ce n’est plus vraiment une vérité
    7 : thales, once more :S
    8 : encore et toujours :S :S
    9 : et oui, là aussi thales (si si, ils font des avions de chasse ! )… ,microsoft et nicolas
    les deux derniers vont de paire
    10 : heuuuu je ne sais pas, je ne m’en préoccupe pas vraiment, alors moi aussi surement

Leave a Reply




Bear