Ok

En poursuivant votre navigation sur ce site, vous acceptez l'utilisation de cookies. Ces derniers assurent le bon fonctionnement de nos services. En savoir plus.

15/01/2008

Code size nightmare

Une des leçons que j'ai apprise sur le tas, c'est de se méfier de la taille du code en général (code bloat). Les symptômes du "code bloat" sont aussi variés que leurs sources, et les conséquences peuvent être dévastatrices. En fait, trop de code tue le code.
 
 
Duplication de code (le syndrôme CTRL-C CTRL-V), problèmes de maintenances et documentation, lenteur de compilation (et d'exécution), difficultées de debugage, complication du design... Etc etc. Une base de code trop grosse peut vite devenir le pire ennemi du programmeur.
 
 
Et le pire, c'est que ce problème n'est vraiment pas très bien géré par nos outils. Les IDE s'emmèlent encore souvent les pinceau, même avec "peu" de code (1 millions de ligne), les langages que nous utilisons poussent parfois à être verbeux (C++ n'est pas ce qui ce fait de mieux en terme de concision), même nos façon de penser nous ammènent à écrire parfois de plus en plus de code (certains design patterns par exemple).
 
 
Steve revient sur ce problème mieux que je ne saurai l'expliquer, en prenant l'exemple de Java. Mais ce problème se retrouve tout aussi bien en C++:
 
 
I happen to hold a hard-won minority opinion about code bases. In particular I believe, quite staunchly I might add, that the worst thing that can happen to a code base is size.


I say "size" as a placeholder for a reasonably well-formed thought for which I seem to have no better word in my vocabulary. I'll have to talk around it until you can see what I mean, and perhaps provide me with a better word for it.
 
 
The word "bloat" might be more accurate, since everyone knows that "bloat" is bad, but unfortunately most so-called experienced programmers do not know how to detect bloat, and they'll point at severely bloated code bases and claim they're skinny as a rail.
 
 
...
 
 
My minority opinion is that a mountain of code is the worst thing that can befall a person, a team, a company. I believe that code weight wrecks projects and companies, that it forces rewrites after a certain size, and that smart teams will do everything in their power to keep their code base from becoming a mountain. Tools or no tools. That's what I believe.
 
... 
 
 
I'm going to try to define bloat here. I know in advance that I'll fail, but hopefully just sketching out the problem will etch out some patterns for you.


There are some things that can go wrong with code bases that have a nice intuitive appeal to them, inasmuch as it's not difficult for most people to agree that they're "bad".
 
One such thing is complexity. Nobody likes a complex code base. One measure of complexity that people sometimes use is "cyclomatic complexity", which estimates the possible runtime paths through a given function using a simple static analysis of the code structure.
 

I'm pretty sure that I don't like complex code bases, but I'm not convinced that cyclomatic complexity measurements have helped. To get a good cyclomatic complexity score, you just need to break your code up into smaller functions.
 
 
Breaking your code into smaller functions has been a staple of "good design" for at least ten years now, in no small part due to the book Refactoring by Martin Fowler.
 

The problem with Refactoring as applied to languages like Java, and this is really quite central to my thesis today, is that Refactoring makes the code base larger. I'd estimate that fewer than 5% of the standard refactorings supported by IDEs today make the code smaller.
 
 
Refactoring is like cleaning your closet without being allowed to throw anything away. If you get a bigger closet, and put everything into nice labeled boxes, then your closet will unquestionably be more organized. But programmers tend to overlook the fact that spring cleaning works best when you're willing to throw away stuff you don't need.


This brings us to the second obviously-bad thing that can go wrong with code bases: copy and paste. It doesn't take very long for programmers to learn this lesson the hard way. It's not so much a rule you have to memorize as a scar you're going to get whether you like it or not.
 
 
Computers make copy-and-paste really easy, so every programmer falls into the trap once in a while. The lesson you eventually learn is that code always changes, always always always, and as soon as you have to change the same thing in N places, where N is more than 1, you'll have earned your scar.
 

However, copy-and-paste is far more insidious than most scarred industry programmers ever suspect. The core problem is duplication, and unfortunately there are patterns of duplication that cannot be eradicated from Java code.
 
 
These duplication patterns are everywhere in Java; they're ubiquitous, but Java programmers quickly lose the ability to see them at all.
 
...
 
 
 
The other seminal industry book in software design was Design Patterns, which left a mark the width of a two-by-four on the faces of every programmer in the world, assuming the world contains only Java and C++ programmers, which they often do.
 

Design Patterns was a mid-1990s book that provided twenty-three fancy new boxes for organizing your closet, plus an extensibility mechanism for defining new types of boxes. It was really great for those of us who were trying to organize jam-packed closets with almost no boxes, bags, shelves or drawers.
 
 
All we had to do was remodel our houses to make the closets four times bigger, and suddenly we could make them as clean as a Nordstrom merchandise rack.
 

Interestingly, sales people didn't get excited about Design Patterns. Nor did PMs, nor marketing folks, nor even engineering managers. The only people who routinely get excited about Design Patterns are programmers, and only programmers who use certain languages.
 
 
Perl programmers were, by and large, not very impressed with Design Patterns. However, Java programmers misattributed this; they concluded that Perl programmers must be slovenly, no good bachelors who pile laundry in their closests up to the ceiling.
 

It's obvious now, though, isn't it? A design pattern isn't a feature. A Factory isn't a feature, nor is a Delegate nor a Proxy nor a Bridge. They "enable" features in a very loose sense, by providing nice boxes to hold the features in.
 
 
But boxes and bags and shelves take space. And design patterns – at least most of the patterns in the "Gang of Four" book – make code bases get bigger.
 
 
Tragically, the only GoF pattern that can help code get smaller (Interpreter) is utterly ignored by programmers who otherwise have the names of Design Patterns tatooed on their various body parts.
 
... 
 
 

13:00 Publié dans Code | Lien permanent | Commentaires (7)

Commentaires

Un truc que tu remarques rapidement quand tu utilises des langages genre python / ruby, c'est que tu gagnes pas mal justement sur le code de "tuyauterie", genre tu veux des associations de truc, tu utilises un tuple tu n'es pas obligé de créer un nouvel objet pour gérer le bordel.

Pareil pour les objets qui servent simplement de foncteur sur une collection qui sont gentillement remplacer par des expressions lambda.

Écrit par : Whirly | 15/01/2008

Je ne comprends pas sa digression sur les DP : « Design Patterns was a mid-1990s book that provided twenty-three fancy new boxes for organizing your closet ».

Pas du tout ! Le bouquin a permis de mettre des étiquettes sur ce qui était déjà rangé.

Le GoF le dit : les DP existaient avant le bouquin. Sauf que, comme M. Jourdain, les programmeurs n'avaient pas forcément de nom sur les solutions auxquelles ils arrivaient fréquemment. Tout en se rendant compte qu'ils reproduisaient ces patterns régulièrement.

Donc forcément « A design pattern isn't a feature », puisque c'est un langage commun.

« sales people didn't get excited about Design Patterns ». C'est pas leur boulot non plus. Ce n'est pas du domaine de leur expertise. Par contre, ils ont eux aussi des « patterns » dans leur métier.

Et prendre Perl en exemple... Perl le langage Write Only dans lequel si tu ne connais pas le moindre recoin des trois dernières versions de la norme du langage, tu es incapable de déchiffrer l'une des 47,5 façons d'écrire le même code.

Alors certes, c'est concis. Mais c'est typiquement un langage où la concision n'apporte pas la facilité à refaire. C'est le paradigme TIMTOWTDI qui tue Perl (ou qui le fait adorer, d'ailleurs), car en fait de plus d'une façon de faire, il y en a 2 milions, dont 99% biscornues et 40% que personne ne comprendra (j'exagère et j'invente les chiffres, je précise).

Même dans des langages comme Python / Ruby (que j'apprécie), il y a des DPs.

(et là, faut que je m'arrête car je risque d'en écrire encore des pages)

Écrit par : Mokona | 16/01/2008

Humm, je sais pas...

J'avoue que je partage avec Whirly une forme de passion interdite pour les langages dynamiques, ne serait ce que pour la réduction en tuyauterie en tout genre ^_^

De plus, je suis convaincu que tu seras d'accord pour admettre que s'il y ait plus d'une façon de faire les choses en perl, celà s'applique tout autant au C où au C++. Il suffit d'aller voir des merveilles sur le site de l'IOCCC !

http://www.ioccc.org/

C'est de toutes façon cette versatilité qui en font des langages dit génériques et donc désirables.

Ce qui m'ammène a ma seconde passion secrète, les langages dit DSL (Domain Specific Langage), où, en reduisant le propos et l'objectif du langage, tu réduis de même la gamme des écritures possibles d'un même programme.

Bref, on se disperse ^_^

Je comprend parfaitement que l'on puisse être d'accord ou pas avec l'argumentation de Steve, mais le coeur de ma conviction ne porte que sur cette simple idée:

Trop de code tue le code.

Et malheureusement, nous ne disposons pas de réelle métrique pour évaluer correctement à quel moment notre code devient vraiment "bloated"...

Écrit par : Daz | 16/01/2008

-> Langage dynamiques

J'aime aussi. J'apprécie Python et je l'utilise. Je n'ai pas une grosse expérience de Ruby, mais ça à l'air sympa. Perl, j'en fais des cauchemars.

Oui bien sur, en C aussi, il y a plusieurs manières de faire la même chose. C'est pour cela que l'on réfléchi à la façon de faire "bien" les choses. Le "bien" étant délicat à définir, le GoF a au moins donné des noms à des patterns qui ont été considérées comme "bien de fait". C'est pour cela que je ne comprends pas sa critique à ce propos. Elle me semble plus être une pique que quelque chose qui s'insère dans sa démonstration.

L'IOCCC, c'est marrant, c'est une forme d'art, mais cela fait quand même bien longtemps que, professionnellement, je ne vois plus de code qui ressemble à ça en production.

Ma dent contre Perl vient du fait qu'à chaque fois que je tombe sur un script Perl, j'ai l'impression que son auteur a absolument voulu faire de l'ASCII ART. Et toujours maintenant, là, en 2007, après quelques années d'existence de Perl.

Et comment faire pour que Perl soit lisible : se mettre des barrières, des moules, suivre des patterns,... Ah ben tiens, comme partout alors.

Bref, son argument sur Perl ne tiens pas (et en plus tombe sur une langage de programmation que je ne supporte pas)

-> Trop de code tue le code

Oui ! Et bien sûr "trop" est difficilement quantifiable car la quantité de code nécessaire dépend de ce que l'on fait. 2000 lignes, c'est trop gros pour implémenter un "grep" en C++, mais ce n'est pas beaucoup pour un navigateur Web (je passe sur la solution : HTMLText.CreateInstance(), on reparle des DSL après :) ).

Mais pas assez de code peut aussi tuer le code. Encore l'exemple de Perl : Perl permet d'écrire des programmes en quelques caractères. Le grep dont je parle juste avant, en Perl, c'est tout petit. Ca tombe bien, Perl est à la base fait pour ça.

Mais la concision du code peut amener à l'incompréhension. À utiliser des astuces immondes qui ne sont pas safe, ou pas lisibles. Ou bien ce petit bout de code peut se retrouver à être utilisé pour ses effets de bords.

Et finalement, ce petit bout de code optimisé se retrouve à être aussi pénible à modifier que le gros code verbeux.

La taille du code n'est donc pas une variable qui peut quantifier le code bloat de manière indépendante. Ni de manière linéaire (ie : peu de code == bien, beaucoup de code == pas bien).

-> DSL

Tout à fait d'accord aussi. Avec ce bémol que certains pourraient penser à ne pas utilise des syntaxes "juste parce que c'est différent" (genre un langage qui utiliserait pour délimiteurs de textes %, parce que c'est fun, là où l'instinct (qu'entraine l'utilisation massive par les autres langages) fait intuiter ".

Digression terminée :)

Les DSL, donc, sont une bonne chose, je ne peux le nier. Tant qu'ils restent spécialisés.

Zut autre digression : en effet, beaucoup de langages spécialisés au départ se retrouvent à devenir de plus en plus versatiles jusqu'à perdre l'objectif initial de vu. C'est un symptôme classique. Et je n'ai cité aucun langage commençant par P.

Mais n'est-ce pas ce que tant à faire le programmeur de langages "versatiles" en s'installant dans un framework le plus complet possible ?

boost offre tout un tas de choses au C++ par exemple (dont les tuples).

Java et C# offrent une API avec un maximum de choses "standard". Rien n'empêche de refaire les choses à sa façon dans ces langages.

Un "moteur" de jeu (la définition est toujours un peu vague) va offrir de quoi programmer l'IA au travers d'un set de fonctions précises... et l'IA pourra d'ailleurs être écrite dans un langage dynamique bindé au moteur.

J'ai donc tendance à penser que, dans l'application à un champ précis d'un langage versatile, il y a une volonté de restreindre les possibilités, en factorisant le maximum de choses.

-> Refactorisation et prise de poids

Je reviens sur quelque chose qu'il dit à propos de la refactorisation qui, d'après lui, fait invariablement augmenter le poids du code.

Disons le tout net, je ne suis pas d'accord. Ou alors on ne fait pas les choses de la même manière. La semaine dernière encore, en réfléchissant sur une partie de code développée un peu rapidement, en exposant l'architecture, en la redéfinissant (pour ça, on a bossé avec UML, surement encore une invention inutile :p) on a fini par avoir quelque chose de bien plus simple, plus léger, plus maniable.

Mais il y a effectivement une maladie dans la refactorisation qui entraine une prise de poids : la reluctance à effacer du code que l'on a écrit. Du code a été écrit, on y a passé du temps, il ne sert plus, certes, mais il POURRAIT servir. Et finalement, il reste dans un coin... au cas où.

Cela me semble normal : il est difficile de détruire quelque chose que l'on a construit. D'ailleurs, il est souvent plus facile de refactoriser quelque chose que l'on n'a pas écrit soi-même.

Il en parle d'ailleurs : But programmers tend to overlook the fact that spring cleaning works best when you're willing to throw away stuff you don't need.

Mais ce ne sont ni la refactorisation ni le nommage de structures qui sont en cause.

-> Copy/Paste

Je ne vais pas encore faire un gros paragraphe, je vais donc y aller rapidos :

- copy/paste == bad
- avoid copy/paste
- factor code !
- or refactor it / re-think it
- and use well known and readable patterns in the process

À moins qu'il ait une recette miracle ? Malheureusement, il pose que, en Java, le copy/paste ne peut pas s'éviter.

Peut-être que son article est plutôt une critique de Java/C++/C#, plutôt que trouver les symptômes de code "bloated". Ou alors est-ce qu'il dit que, puisqu'il n'y a pas de solution, Java/C++/C# amènent forcément à du code non maintenable ?

Écrit par : Mokona | 16/01/2008

Petit ajout, ou plutôt précision pour comprendre là où je bloque sur l'article à propos des DP.

Pour lui, on a une pièce mal rangée, on applique les DPs et la pièce est rangée avec une étiquette sur chaque boite.

Pour moi, on a une pièce mal rangée, on applique les DPs et on a la même pièce, toujours aussi mal rangée, mais avec des noms sur les parties de la pièces qui avaient une structure reconnaissable.

Les DPs ne donnent pas de méthode de refactorisation, ne donnent pas de plan généraux d'architecture. Ils donnent juste des noms à des structures utilisées.

Écrit par : Mokona | 16/01/2008

Et après, on s'étonne qu'il n'ait pas voulu publié son article à l'intention des programmeurs expérimentés ^_^

Bon, en premier lieu, accepte mes excuses, car je ne vais pas pouvoir rebondir ici sur chacun des points que tu avances :-)

Trois point toutefois:

Je ne crois pas que Steve ait voulu trucider les Design Patterns (et moi non plus d'ailleurs). Je crois juste qu'ils ne sont qu'une réponse incomplète et parfois même éronée à certains problèmes - loin de la panacée à laquelle beaucoup de développeurs adhèrent.

Tu as raison, les DSL ont souvent tendance à devenir générique, en particulier si ils ont eu du succès. ^_^

Enfin, en matière de refactoring (en tout cas dans notre industrie), il s'agit encore d'une tache considéré au mieux comme un besoin ponctuel, au pire comme une perte de temps.

Ca n'est pas que le processus en soi soit mauvais (évidemment), mais plutôt la façon dont les campagnes de refactoring sont menés (ou pas). C'est donc le facteur humain qui pêche ici...

Écrit par : Daz | 16/01/2008

> Bon, en premier lieu, accepte mes excuses, car je ne vais pas pouvoir rebondir ici sur chacun des points que tu avances :-)

Tu es tout excusé :)

Écrit par : Mokona | 16/01/2008

Les commentaires sont fermés.