Comment bien documenter un système informatique ?

Je crois que je n’ai jamais écrit un titre aussi pompeux.

Enfin bref, cet article s’appuie sur différentes expériences que j’ai vécues … avec plus ou moins de succès.

Avant tout, posons la situation.

Quelques hypothèses

  • Vous faites partie d’une équipe de développement logiciel (je ne sais pas si ce que je vais décrire ici marche dans d’autres situations)
  • Vous disposez d’un outil de build moderne (Maven, Gradle, ou tous leurs équivalents)
  • Je vais parler ici de système, plus que de logiciel, parce que la plupart du temps, vous développez plusieurs logiciels livrables : un site web, un back-end, des plugins ou des applications clientes, …
  • Le système que vous développez est unique : personne d’autre dans le monde ne fait le même système que vous en utilisant les specs à votre disposition (ou pas)
  • Vous pouvez créer un nouvel artefact dans votre base de code pour stocker la partie principale de votre documentation

Quelques prises de conscience utiles

Vous avez donc, que vous utilisez une méthode agile ou en V, commencé par définir l’architecture de votre système avant de développer le code, qui a donné lieu à une structure de données.

Ca, c’est pour le système que vous allez livrer. Mais avez-vous conscience de l’autre système ?

6bwCQwX

Parce que vous faite quoi de votre gestionnaire de source ? De votre chaîne d’intégration et de déploiement continu ? A ce que je sache, ces éléments sont au minimum configurés pour votre équipe (dans le cas de git, ça va vite), et dans la plupart des cas hautement personnalisés selon vos besoins. Typiquement, vous avez deux systèmes à documenter :

  1. Celui que vous allez livrer
  2. Celui qui vous permet de le livrer

Quelqu’un à l’esprit joueur pourrait y voir une forme d’inception : développer un logiciel avec un ensemble de logiciels qu’on est en train de développer …

Il vous faut documenter à la fois le système que vous livrez, et celui que vous utilisez pour livrer.

Par ailleurs, est-il judicieux de parler de votre système, ou des différentes incarnations de celui-ci ?

3Bm98kT

Parce qu’après tout, votre système va connaître de multiples incarnations :

  • Architecture logique ne décrivant que les différents composants de l’application hors de toute réalité
  • Architecture physique incluant les différents environnements
    • Environnements de dév,
      • de recette
      • de prod
    • Environnements locaux
      • en PAAS
      • dans des conteneurs Docker

Dans l’idéal, votre documentation doit pouvoir se projeter sur ces différents environnements.

Quelques outils et méthodes utiles

D’abord, mettons-nous d’accord pour dire que, comme tout le monde, vous n’avez pas envie de vous répéter. Heureusement, ce problème de la non répétition est connu depuis longtemps. Regardez par exemple cet article : Single Source Information: An Agile Best Practice for Effective Documentation. Il est un peu vieux et prône donc l’utilisation de Dita XML. Ne vous enfuyez pas tout de suite, je suis sûr que vous allez voir où je veux en venir.

Quel format de document source utiliser ?

Effectivement, il faut limiter les sources de documentation et multiplier au contraire les formats de sortie. Ca revient à dire qu’il ne faut pas utiliser Office pour créer de la documentation, et pour plusieurs raisons :

  • Le livrable et la source documentaire sont identiques
  • Le système de gestion de révision intégré est loin derrière les outils de développement à notre disposition
  • Le travail collaboratif est pratiquement impossible
  • La création de documents lourds ne marche pas
  • L’inclusion de données externes mobiles est difficile à maintenir

En face, évidement, il y a eu XML (avec Docbook et Dita). Eux aussi avaient leurs défauts

  • Une syntaxe lourde
  • Des include incroyablement difficiles à maintenir
  • La nécessité d’utiliser des éditeurs spécialisés

Du coup, on pourrait penser que LaTeX pourrait être une réponse. Malheureusement, je crois que sa syntaxe n’est pas vraiment légère.

Du coup, je recommande chaudement d’utiliser AsciiDoc.

  • La syntaxe est simple
  • Il est possible de créer des structures de documents complexes (avec les include de fragments de documents)
  • Il existe des éditeurs WYSIWYG (comme par exemple AsciidocFX) qui facilitent réellement la montée en compétence
  • Et enfin, les documents finaux peuvent être générés pendant le build de votre projet, ce qui permet de raprocher la documentation et le code

Comment dessiner les diagrammes ?

Evidement, ce qui vaut pour les documents vaut également pour les diagrammes : pourquoi utiliser Visio, yEd pour des diagrammes qui vont être partagés ?

Coup de bol, asciidoctor fournit une bibliothèque (asciidoctor-diagram) qui permet de générer un paquet de type de schémas différents en intégrant quelques bibliothèques bien pratiques, et en particulier PlantUML. C’est d’autant plus intéressant que ça limite, par la nature textuelle du diagramme, la taille du dessin qui va être créé (parce que je sais pas vous mais personnellement, quand je vois un diagramme avec plus de dix noeuds, je commence à pleurer un peu).

Quel plan utiliser ?

On arrive à l’une des questions cruciales.

Avant tout, une bonne documentation, c’est une bonne histoire. Par conséquent, tout plan de documentation qui ne permettra pas l’exposé de l’histoire du système ne pourra pas être un bon plan. Et par histoire, je ne veux pas dire son historique des décisions. Non, je parle d’une histoire au sens le plus journalistique du terme : qui ? quoi ? quand où ? comment ? et surtout pourquoi ? Si votre plan ne contient pas ces éléments sous une forme ou une autre, vous n’y arriverez pas.

Coup de bol, j’ai le bon plan. ou plutôt Simon Brown a le bon plan.

Vous connaissez Structurizr ? Non ? Eh bien allez y faire un tour, c’est très intéressant.

En revanche, à titre personnel, comme j’aime mettre plus de texte que de diagramme, je trouve sa page Documentation encore plus intéressante. Pour une raison en particulier : il y décrit le contenu d’une bonne documentation sous la forme d’un sommaire assez bien fichu. Le voici :

  1. Context
  2. Functional Overview
  3. Quality Attributes
  4. Constraints
  5. Principles
  6. Software Architecture
  7. Code
  8. Data
  9. Infrastructure Architecture
  10. Deployment
  11. Development Environment
  12. Operation and Support
  13. Decision Log

Eh bien le plan que je vous propose de suivre, c’est celui-là précisément.

Mais comment tout ça s’organise ?

Créer un artefact de documentation pour le système livrable

Dans votre gestionnaire de source, créez votre artefact de documentation. Généralement, je crée un module maven sous le projet principal. Si j’ai plusieurs sous-systèmes, je vais créer un module maven par sous-système et les agréger ensuite par chapitre (si le chapitre existe) au niveau principal

Pour ma part, ce sera un projet maven dans lequel je vais tout de suite ajouter le plugin asciidoc avec la configuration nécessaire pour générer du PlantUML.

Dans le dossier des sources asciidoc (donc dans mon cas dans src/docs/asciidoc), créez un dossier include dans lequel vous créez un fichier asciidoc pour chacun des chapitres de la doc.

Ecrivez votre doc

Pour chacun de ces chapitres, commencez par décrire le système d’une façon générale. Ne plongez dans les détails que si ces détails sont pertinents.

Ou générez votre doc

Il y a tout un tas d’éléments pour lesquels il peut être pertinent de générer certains éléments :

  • des diagrammes de classes extraits du code
  • des listes de classes implémentant certaines interfaces (avec des propriétés utiles)

Par pitié, ne vous répétez pas : vous pouvez, et devez, générer ces informations en asciidoc à partir de votre code.

Pour ça, j’utilise de façon systématique groovy : avant la génération de la doc, j’ai des scripts Groovy qui vont générer (dans target/generated/docs) les différents documents utiles.

Générez la doc livrable

Comme les fichiers asciidoc sont dans votre doc et utilisent le même système de build, vous pouvez générer la doc à chaque livraison, et même l’inclure comme artefact de votre build.

Ca vous garantit une documentation

  • à jour (puisqu’il est facile pour les développeurs de la modifier
  • propre (parce que Asciidoc génère facilement quele chose de joli)
  • conforme aux différents besoin de doc (puisqu’on peut facilement inclure les différentes parties de la doc dans différents documents)

Et ça marche bien ?

Chaque fois que j’ai utilisé cette méthode, j’ai réussi à produire de la documentation efficace, propre, et complète. Je ne sais pas si c’est une preuve

Mais où documente-t-on le système de développement ?

Dans la partie Development Environement, tout simplement.

Ca n’est pas un peu fastidieux de tenir à jour les diagrammes PlantUML ?

Ca peut rapidement l’être, c’est pour ça que je vous présenterai bientôt les différentes solutions auxquelles je pense pour … les générer, évidement ! (je dois d’ailleurs les tester sur mes programmes Codingame).

Tout ça n’est pas un nouveau nom sur une vieille idée ?

Bien sûr que si. L’idée, c’est le litterate programming, qui a connu des dizaines d’incarnation. Celle-ci est juste un peu plus moderne.

Publicités

Asciidoc for the win !

Il y a quelques temps, j’avais écrit un article expliquant pourquoi les documents Office n’étaient pas de bons livrables. J’y indiquais par ailleurs qu’Asciidoc me semblait une bonne alternative.

Mais un format, ça n’est qu’un format. Une documentation, ça n’est hélas pas qu’un format technique de document. C’est aussi, avant tout, le moyen de raconter l’histoire du logiciel. Et parfois, ça n’est pas facile

A la recherche du bon format

Pas son histoire au sens biographique, mais plus son usage, son architecture, bref, l’ensemble des éléments qui contraignent son code à avoir sa structure. Autrement dit, les éléments qui donnent les réponses aux questions invraissemblables que se posent un développeur.

you_down_wit_opc-yeah_you_know_me
C’est pas que je déteste, c’est que je comprend pas l’histoire

Et ça, que j’écrive ma doc dans Word, en Asciidoc, en LaTeX, la question se pose toujours : pourquoi ces décisions ont été prises.

Heureusement, j’ai découvert il y a quelques temps le format de documentation agile de Simon Brown, qu’il documente dans la doc de Structurizr.

Regardons ensemble le sommaire :

  1. Context
  2. Functional Overview
  3. Quality Attributes
  4. Constraints
  5. Principles
  6. Software Architecture
  7. Code
  8. Data
  9. Infrastructure Architecture
  10. Deployment
  11. Development Environment
  12. Operation and Support
  13. Decision Log

Et maintenant, imaginez ce que ça donne pour votre projet. Tous les aspects sont documentés dans un seul gros document Word. C’est pénible … heureusement, là, asciidoc peut vous aider.

Je vous montre : si votre doc est en asciidoc, vous pouvez faire des include.

Et donc, vous pouvez avoir un dossier de doc qui ressemble un peu à ça

  • monprojet.adoc
  • images/ Vous mettez toutes vos images là-dedans
  • includes Et chacun des chapitres de votre doc est un document séparé dans ce dossier.
    • /00_intro.adoc
    • /01_context.adoc
    • /02_functional_overview.adoc
    • /03_quality_attributes.adoc
    • /04_constraints.adoc
    • /05_principles.adoc
    • /06_software_architecture.adoc
    • /07_code Si vous avez des éléments à détailler, n’hésitez pas à créer des sous-dossiers
      • /un_detail.adoc
    • /07_code.adoc
    • /08_data.adoc
    • /09_infrastructure_architecture.adoc
    • /10_deployment.adoc
    • /11_development_environment.adoc
    • /12_operation_and_support.adoc
    • /13_usage.adoc
    • /14_decision_log.adoc
    • /99_faq.adoc

Du coup, avec tout ça, vous avez une documentation correctement découpée, dans laquelle vous mettez vos diagrammes UML (ou autres) générés avec asciidoctor-diagram, et vous êtes content …

Bon, et puis si vous n’êtes pas contents de ce format, Simon Brown mentionne également le support d’arc42, qui a l’air tout aussi intéressant :

A un détail près.

Parfois, il y a du code que vous voulez voir généré pour le mettre dans la doc.

Par exemple, si vous utilisez le ServiceLoader de Java, savoir quelles classes implémentent une interface sans avoir à chercher dans Eclipse, c’est cool.

Tant qu’à générer la doc, pourquoi ne pas générer la doc ?

Parce qu’en fait, votre doc asciidoc, vous la générez, pas vrai ? Si vous faites du maven, vous utilisez sans doute le maven-asciidoc-plugin. Je vais faire l’hypothèse que vous êtes dans ce cas.

Et dans ce cas, si vous avez besoin d’avoir un morceau de la doc qui dépende plus directement du code, vous pouvez tout à fait … utiliser Groovy pour générer des fichiers asciidoc qui vont bien !

Typiquement, pour mon exemple, j’ai un script Groovy qui a dans son CLASSPATH tous les modules de mon application, et qui à l’aide de reflections recherche les implémentations de l’interface pour générer un tableau Asciidoc les affichant.

Et comme ce script Groovy est lancé grâce à gmaven avant la génération de la doc, cette partie est toujours à jour.

Et pour un projet multimodule ?

Pour l’instant, j’ai juste testé ça une fois, et d’une façon non satisfaisante. Comme j’arrivais sur un projet existant, j’ai simplement ajouté un module doc isolé du reste de l’application.

Je pense que j’aurais eu une doc d’une bien meilleure qualité si j’avais mis la doc dans chaque module avec des includes bien costauds dans la doc du module global (voire même des morceaux complètement générés). Evidement, dans ce genre de cas, la génération de la documentation doit également être documentée …

Donc ça marche bien ?

Oh que oui !

Ecrire de la doc est presque agréable, et les aspects générationnels sont une distraction encore plus agréable. Sans même parler de la fusion de cette doc et des différents diagrammes qui y sont intégrés.

Mais je crois qu’on peut aller encore beaucoup plus loin. Pas directement avec cette technique, mais sans doute en amont, via des graphes.

On pourrait faire encore mieux ?

Je vous donne quelques exemples.

Pour décrire votre applications, vous utilisez en fait plusieurs graphes

  • L’architecture conceptuelle (ou fonctionnelle) décrit comment les composants remplissent les fonctions
  • L’architecture applicative organise ces composants en modules
  • L’architecture technique montre comment ces composants se déploient.

Tout ça est censé être à peu près couvert par Structurizr.

Mais d’autres éléments ne le sont pas : quand je décris une organisation de classe, c’est aussi un graphe. Et un diagramme de séquence pourrait tout à fait être construit à partir du code un peu annoté.

Imaginez que je dispose d’un modèle comme celui de Structurizr, mais enrichi à la fois avec les informations du code (pour produire les différents diagrammes de bas niveau), et pour lequel je puisse générer des diagrammes via une quelconque syntaxe de recherche des noeuds dans un graphe. Je pourrais alors demander facilement des trucs comme

Dessine-moi le diagramme de séquence partant de cette interaction Javascript et incluant les échanges avec le serveur, la base de données, et le composant Java bidule

Et d’un coup, je n’ai plus un simple outil de documentation, mais un outil d’analyse et de compréhension de mon code, que je peux ensuite faire produire un truc que j’ai bien galéré à faire il y a quelques temps :

Fais-moi un diagramme de composant où les classes sont coloriés en fonction de leur dette technique

Bon, c’est une très chouette idée, et je sais à peu près comment l’implémenter … Mais c’est malheureusement encore un projet de plus. Et mon temps est déjà bien rempli.

Ne livrez pas de document Office !

Vous travaillez sur un grand projet … ou un moyen.

Et par exemple, on vous demande de livrer de la doc sur le code, l’architecture. ou des éléments de volumétrie. Et là vous vous dites « je livre un Excel ! » ou « Je livre un Word ! ». Par pitié, ne le faites pas.

Pas parce que c’est moche, lourd, et peu conforme à vos valeurs éthiques de partage.

Mais plutôt parce que les fichiers Office ne sont pas compatibles avec un processus de développement, ou plutôt d’écriture ?

Parce qu’après tout, votre document a bien un cycle d’écriture, non ? Vous avez bien, sous une forme ou une autre, les étapes suivantes

  • Ecriture
  • Relecture
  • Livraison
  • Passage à la version suivante

Si ça vous rappelle le cycle de vie d’artefacts de développement, ça n’est pas un hasard, c’est la même chose.

Et d’un point de vue format, comment faire ?

D’abord, si votre livrable ne bouge plus, c’est que ça n’est pas le même format que celui dans lequel vous écrivez. Typiquement, quand vous livrez un PDF, par exemple, vous ne pouvez pas vraiment le modifier (mis à part hacks visibles).

Ensuite, dans l’idéal, vous cherchez un format qui vous permette de répondre aux critiques habituelles d’Office :

  • Pas de travail collaboratif possible
  • Pas de merge
  • Un outillage de composition de document assez faible

Autrement dit, vous cherchez un format source qui soit vraiment ça : un fichier source que vous pouvez gérer dans votre SCM, sur lequel vous pouvez faire des merge, et enfin que vous pouvez composer (ou compiler) dans votre chaîne de build. Eh bien, ce format existe. En fait, il y en a plusieurs, mais un seul qui marche : Asciidoc.

De nos jours, avec Asciidoctor, vous pouvez compiler votre doc via maven, et la gérer dans votre SCM (je le fais déja). Vous pouvez par ailleurs créer de gros documents en assemblant plusieurs fragments (voir leur fameux tag include). Et, comme vous créez votre doc avec maven, elle est packagée et livrée comme le reste de votre application.

Alors, pourquoi vous continuez à utiliser Office ? Parce que vos utilisateurs le demandent ? En vrai, je ne crois pas que ce soit une bonne raison.

#devoxxfr – L’Expérience Développeur et votre API

Et comment on fait de belles api alors ?
Evidement, ce talk parle d’apis rest/http. Mais à mon avis, ça peut se projeter sur d’autres domaines comme les interfaces entre modules (quels qu’ils soient).

Première chose à envisager : quels sont les utilisateurs ?

Donc d’abord, rendre l’api compréhensible.

Et rendre la documentation « inutile » : proposer des exemples de code, faire des guides d’utilisation (des howto, typiquement), et avoir un exemple complet d’utilisation. Toute cette documentation, écrivez-la avec des outils « connus ».
Permettre aux utilisateurs de contribuer de la doc est également une bonne façon de créer de la documentation facilement.

maven-notifier

J’avais téléchargé il y a quelques temps le jar de maven-notifier histoire de l’installer, et puis je l’avais oublié …

Il faut dire que j’avais même eu à un moment le projet de faire un concurrent de cet outil, et puis, me rendant compte qu’il exisstait déja, j’avais choisi de tenter de l’utiliser pour éventuellement le forker et l’améliorer.

Et j’avais oublié tout ça …

Heureusement, cette semaine, j’ai pris un peu de temps pour comprendre pourquoi, alors que j’avais ce fichier de configuration, aucun message n’était envoyé :

notifier.implementation = growl

J’ai donc décidé d’écrire un test unitaire exposant mon bug (parce que j’étais convaincu qu’il y avait un bug).

Donc j’écris mon test, et je me rends compte que ce test écrit des paquets de logs qui ressemblent à ça (tiré de mon issue github)

11:30:56.251 [New I/O client worker #1-1] 67 DEBUG jgntp.message - Message received
GNTP/1.0 -ERROR NONE
Error-Code: 400
Error-Description: Invalid key hash
Origin-Machine-Name: NDX-PC-W7
Origin-Software-Name: Growl/Win
Origin-Software-Version: 2.0.9.1
Origin-Platform-Name: Microsoft Windows NT 6.1.7601 Service Pack 1
Origin-Platform-Version: 6.1.7601.65536
X-Message-Daemon: Growl/Win
X-Timestamp: 20/05/2014 11:30:56
11:30:56.261 [New I/O client worker #1-1] 40 ERROR c.g.j.m.n.growl.Slf4jGntpListener - Registration error: NOT_AUTHORIZED - Invalid key hash
11:30:56.261 [New I/O client worker #1-1] 158 INFO  c.g.c.j.internal.io.NioTcpGntpClient - Scheduling registration retry in [3-SECONDS]
11:30:59.271 [pool-2-thread-1] 68 DEBUG c.g.c.j.internal.io.NioGntpClient - Registering GNTP application [Maven]
11:30:59.271 [New I/O client worker #1-2] 51 DEBUG jgntp.message - Sending message
GNTP/1.0 REGISTER NONE SHA512:111581B770A50B07372A85ED928A8B6F3C34180782D43E1335BC3737E4FC57E70D23EBEFEFB3BF2363619E80D36707EF121AB8D9DA8C6E638F1927633B0428E6.9F99086EC1E3AD095E60EA607E321303
Application-Name: Maven
Notifications-Count: 1

Notification-Name: build-status-notification
Notification-Display-Name: Build result status
Notification-Enabled: True

Bonc e qui est chouette avec un bon log, c’est qu’on comprend tout de suite le problème, en l’occurence fort simple. Sisi, fort simple, regardez ce message :

Registration error: NOT_AUTHORIZED - Invalid key hash

C’est simple, non ? Je n’ai pas passé de mot de passe.

Parce que si je regarde Growl for Windows, dans son onglet Security, c’est assez clair (limpide, en fait) : si je veux envoyer des notifications en TCP (et c’est précisément ce que permet la librairie JGNTP qu’utilise maven-notifier), il faut donner un mot de passe à Growl et à l’application.

J’ai donc modifié mon fichier maven-notifier.properties pour qu’il soit comme ça

notifier.implementation = growl
notifier.growl.password=NON NON NON VOUS NE SAUREZ PAS CE MOT DE PASSE

J’ai ajouté le même mot de passe dans Growl, et j’ai maintenant de jolies notifications Growl à chaque fin de build !

Merci Jean-Christophe !

Eh ben alors ça c’est malin !

Suite à ma découverte récente de ifttt, je me demandais ce que je pourrais bien pouvoir en faire.
Heureusement, Lifehacker m'a fourni la réponse ce matin. Tout à la fin, en fait, de ce long article, l'auteur explique qu'il s'est créé une recette pour recevoir par chat les dernières recettes publiques :

I'm really into ifttt right now, so I've created a recipe that IMs me whenever someone creates a new ifttt recipe so I can keep track of new tasks I might want to try out

Du coup, n'hésitant devant rien, j'ai repris sa recette à mon compte, histoire d'avoir un peu plus d'infos. Et ça donne des résultats assez sympathiques :

13:59:08 – ifttt@bot.im: crasny created "Send new #ifttt Recipe to #Twitter" on ifttt : (http://bit.ly/nAa6lz)
13:59:08 – ifttt@bot.im: scratchysingh created "I'm listening to…." on ifttt : your scrobbled track on your status. (http://bit.ly/qM6lII)
13:59:08 – ifttt@bot.imé: lencurrie created "Rain warnings!" on ifttt : (http://bit.ly/nPoAsd)
13:59:09 – ifttt@bot.imé: schouery created "GReader Shared Items to Twitter" on ifttt : (http://bit.ly/oYEe9A)

Bon, évidement, tout n'est pas bon à prendre, hein (il y a par exemple des dizaines d'alertes de pluie/neige/chaleur/whatever), mais dans le tas, il y a de bonnes idées, comme par exemple l'envoi automatique des liens partagés de Google Reader vers Twitter. Je ne m'en servirais pas, parce que je préfère passer par Google+ (pour lequel j'aimerais vraiment que ifttt fasse une super intégration de l'API), mais l'idée est sympathique.
Et puis, quand j'y réfléchis, je trouve cette idée enrichissante pour leur service : plutôt que de s'embêter à faire un best-of, comme le font les gens de StackOverflow avec leurs blogs, ils font simplement en sorte que les utilisateurs reçoivent en temps réel (ou presque) les nouveautés. Franchement, si je devais faire demain un service "public" sur le web, je réfléchirai à ce genre d'interaction avec mes utilisateurs.

Générer de l’UML plutôt que de le dessiner ?

même si je ne m'en sers pas, ça fait des années que je suis fan des outils de génération d'UML en tant que documentation, et donc depuis le code source.

Donc, pendant longtemps, je me contentais de savoir que UMLGraph et UMLSpeed existaient, tout en regrettant amèrement la mauvaise qualité de leur syntaxe et leur manque d'intégration.

Cette fois, jsigner et modsl me paraissent bien plus pratiques à utiliser. Pratiques, parce que codés en Java et intégrables facilement à Maven. (Dis donc, mon fan, ça serait pas une bonne idée de mettre ça dans notre doc ?)

Hélas, trois fois hélas, ils ne génèrent pas encore de diagramme de séquence, dont je suis personnellement assez fan.

Bah, ça ne m'empêche pas d'avoir bien envie de jouer avec, peut-être dans majick-properties.