Réflexions à propos de NodeJS et de Javascript plus globalement

Cela fait quelques jours que je me pose la question suivante : « Est-ce que Node.js ne va pas devenir une technologie incontournable / majeur dans les 2 ans qui viennent ? »

Le contexte

Je suis un développeur Python depuis de nombreuses années. J'aime ses librairies, j'aime ses outils, j'aime sa communauté.
J'aime tellement sa syntaxe que quand je vois la syntaxe d'autres langages, j'ai une réaction quelque peu épidermique à la lecture du code.
Avec le temps, l'habitude de la syntaxe Python minimaliste proche d'un pseudo code rend difficile la possibilité d'apprécier un autre langage. Je ne sais pas si c'est positif ou négatif, je me pose simplement la question. Enfin ceci est un autre sujet.

Cependant, mon regard se tourne de plus en plus vers Node.js... je n'ai pas franchi le pas, je n'ai rien développé en Node.js… mais je me demande si je ne passe pas à coté de quelque chose d'important.

Les forces de Node.js

  • Bien que Node.js date seulement de 2009 :

  • tous les développeurs web (front-end) connaissent le langage, il est très populaire. Par conséquent, pour passer à Node.js il n'y a pas le frein d'apprendre un nouveau langage comme ça serait le cas avec l'utilisation d'un framework web basé sur Ruby ou Python. Je pense que dans les mois qui viennent, de nombreux développeurs PHP vont passer à Node.js… surtout ceux qui regardaient ailleurs mais qui ne souhaitent pas apprendre un nouveau langage.

  • la possibilité de partager du code (librairies communes…) entre la partie client et serveur peut être intéressant. La question se pose de plus en plus étant donnée qu'on est de plus en plus amené à effectuer beaucoup de traitement coté client (exemple avec Backbone.js).
    Dernièrement, dans tous mes projets de développement web, j'ai utilisé un moteur de template coté client en plus du moteur de template coté serveur.
    Je dois gérer de plus en plus souvent une couche modèle coté client. Des vues coté client… Cela fait donc plein d'éléments en doubles, utilisés une fois sur le serveur, une fois sur le client. Donc deux technologies à maîtriser.
    Ces derniers jours, la visualisation du screencast du framework javascript Meteor m'a mis la puce à l'oreille.
    Autre exemple, il est possible d'utiliser la même API Canvas coté client et coté serveur (avec node-canvas).
  • Node.js semble être très rapide, ça tient très bien la monté en charge.

  • La vitesse de l'interpréteur Javascript est en constante progression V8 (JavaScript engine)

Faiblesses de Node.js

Je ne sais pas si mes remarques sont exactes, je n'ai aucune expérience en Node.js.

  • Exemple dans la partie Bad Use Cases du guide Felix's Node.js Convincing the boss guide il est indiqué que les framework Node.js sont moins matures que ce que l'on peut trouver en Ruby, Python et Php (bon pour ce dernier j'ai des doutes, je pense que la communauté Node.js est déjà plus mature, plus structurée que la communauté Php… enfin bon…).
  • Je ne pense pas que Sequelize soit aussi mature que SQLAlchemy
  • Je n'ai pas trouvé d'outil comme Whoosh en Javascript (c'est un moteur de recherche léger). J'ai tout de même trouvé Node-xapian

Je ne sais pas si il est encore tôt pour passer à Node.js mais si la communauté continue à être aussi dynamique que ces 2 dernières années… alors les pièces manquantes seront bientôt créées.

Est-ce que l'on va retrouver Javascript partout ?

Voici une petite liste :

Quand je vois cette liste, je me pose sérieusement la question : est-ce que Javascript est le langage incontournable de ces 10 prochaines années ? Certes il est déjà incontournable dans le navigateur, mais pour le reste ?


Édition 5 mai 2012 :

  • j'ai publié ce billet sur LinuxFR, j'ai reçu 90 commentaires…
  • j'ai publié ce billet sur la mailing list de l'Afpy, j'ai reçu 25 commentaires… malheureusement les archives de cette liste n'est pas publique

merci beaucoup pour vos retours d'expériences !

Read and Post Comments

Note à propos des « Content Repository » dans le cadre d'un CMS

Dimanche dernier, j'ai étudié le thème « Content Repository », voici quelques notes concernant mes recherches.

Séparation des Web CMS en deux couches

J'ai trouvé un article qui fait la distinction entre deux types de CMS, enfin il parle de WCMS (Web CMS) :

  • « Content Production Systems (CPS) »
  • « Presentation Management Systems (PMS) »
Personnellement, cet article me fait penser à une réflexion qui m'est déjà venu de nombreuses fois : là où l'auteur de l'article voit deux systèmes, deux types d'applications distinctes, je vois en fait deux couches… Bon en fait, l'auteur semble le dire aussi à la fin de son article quand il parle de CPS + PMS.
Je reprends ci-dessous à peu près la description qu'il fait d'un CPS et d'un PMS :
  • CPS - c'est le système qui gère la couche données, la logique métier du CMS, comme :
    • l'enregistrement des ressources (Pages, Images, Dossiers…)
    • un moteur de recherche plain texte (avec filtre…)
    • un workflow
    • un système de versionning
    • import / export de données
  • PMS - c'est le système qui pour résumer "affiche" les pages :
    • la navigation
    • système de template
    • layouts
    • skins / thèmes
    • back office d'édition

« Content Repository »

À mes yeux… je ne fais pas bien la distinction entre un Content Repository et un CPS.

Si j'essaie de les différencier, je dirais qu'un Content Repository est une librairie ou un serveur alors qu'un CPS est une application, avec des Interfaces Utilisateur…

Dans la suite de l'article, je vais partir du principe que Content Repository est la même chose qu'un CPS même si ce n'est pas totalement exact.

Dans le monde Java, une spécification nommée JCR (Java Content Repository) décrit ce qu'est un Content Repository et comment l'utiliser (spécification d'une API standard).

Il existe plusieurs implémentations de JCR mais la plus connue semble être Jackrabbit de la fondation Apache. Je l'avais rencontré il y a quelque années lorsque j'ai étudié de loin Nuxeo.

J'ai été agréablement surpris de découvrir les projets PHP suivants (ce qui confirme que je ne suis pas le seul à me poser ces questions) :

J'ai appris l'existence d'un projet de Content Repository pour Node.js :

J'ai aussi trouvé Lily qui est un Content Repository basé sur Hadoop, HBase et SOLR dans un écosystème Java + API Rest.

Spécificités techniques d'un CPS

J'ai trouvé un autre article qui traite des spécificités techniques d'un CPS. Voici ci-dessous sa liste de "requirements" :

  • Richly structured content types
  • Unstructured binary objects
  • Relationships / references / associations
  • The ability to evolve content models over time (what I call “schema evolution”)
  • Branch / merge (in the Source Code Management (SCM) sense of the term)
  • Snapshot based versioning
  • ACID transactions
  • Scalability to large content sets
  • Geographic distribution

Chacun de ces points sont détaillés dans son article et c'est fortement intéressant. Par exemple, je trouve le point « Branch / Merge » très juste… bien que je ne sache pas tout à fait comment l'implémenter simplement.

À partir de cette liste, voici la liste des "requirements" de mon Content Repository idéal :

  • Base de données où je peux stocker des objects JSON ainsi que des blobs (exemples : base de données NoSQL comme MongoDB, CouchDB)
  • Base de données où je peux stocker des collections (pour des dossiers), des liens (pour des redirections), des associations (pour des catégories, des tags…)
  • API Rest qui permettent d'accéder plus ou moins à toutes les features du Content Repository
  • Moteur de recherche (basé par exemple sur : Xapian, elasticsearch)
  • Un système de workflow
  • Versionning (peut-être basé sur Git ou Mercurial)
  • Système d'import / export de données (je ne sais pas si il y a un format standard pour les CMS… enfin je n'ai jamais trouvé)
  • Support d'une API CMIS (bien que je la trouve peu élégante… c'est bien une API faite par le monde Java)
  • Accès WebDAV
  • Gestion des utilisateurs

Pour le moment, je n'ai pas trouvé le Content Repository de mes rêves.

Le standard CMIS « Content Management Interoperability Services »

Le standard CMIS est un autre élément intéressant à prendre en compte dans un CPS.

CMIS est une couche d'abstraction dont le but est de permettre de faire communiquer plusieurs instances de CMS identiques ou différentes ensemble. Elle doit permettre par exemple d'importer les données d'une instance à une autre.

Il existe de nombreux clients CMIS pour différent langage…

La suite…

J'ai prévu d'écrire d'autres billets à propos des Content Repository :

  • Un retour d'expérience et mes réflexions concernant un premier Content Repository que j'ai écrit en Python, basé sur la base de données NoSQL ZODB et le moteur de recherche Whoosh. Il comporte aussi un connecteur WebDAV.

  • Un autre billet sur une proposition d'API RESTful pour un Content Repository… quelque chose de naturel et vraiment dans l'esprit RESTful.

    Exemple :

    GET http://example.com/blog/my-post         => est dans l'esprit RESTful
    GET http://example.com/post?name="my-post"  => est moins dans l'esprit RESTful
    

    Dans l'API REST de CMIS j'ai trouvé beaucoup de requête suivant la seconde forme d'URL et très peu ou pas sous la première forme d'URL.

Read and Post Comments

Idée d'une librairie basée sur Selenium pour tester des formulaires web

Introduction

Je viens de commencer à utiliser Selenium pour tester des formulaires, divers pages de listes de données… dans une application métier.

Mes objectifs :

  • tester l'ajout de nouvelles entités (1)
    • les données sont injectées dans un formulaire d'une page html
    • une page de résultat est utilisées pour vérifier que les données ont bien été enregistré (ça peut être une page d'édition)
  • tester la modification d'une entités (2)
    • les données sont injectées dans un formulaire d'une page html
    • une page de résultat est utilisées pour vérifier que les données ont bien été enregistré (ça peut être une page d'édition)
  • tester la validité d'une page de liste (3)
  • tester la validité des résultats d'un moteur de recherche (4)

Pour le moment je me suis concentré uniquement sur les points 1 et 2.

Première méthode (à l'arrache)

J'ai commencé par réaliser des fonctions du type :

  • login()
  • add_customer(data)
  • edit_customer(data)
  • check_customer(data)
  • add_customer(data)
  • edit_customer(data)
  • check_customer(data)

Les fonctions de type "add" et "edit" prennent en paramètre des données à injecter dans des pages. Les fonctions de type "add" correspondent aux pages d'ajout d'entités, les fonctions de type "edit" correspondent aux pages de modification d'entités.

Ensuite j'ai des fonctions "check", là aussi je passe en paramètre des données qui seront utilisées comme valeur de vérification face à des pages de résultats ou pages d'éditions (une page d'édition contient déjà des données, le but ici est de vérifier leurs validitées).

Ma variable "data" est du type :

data = [
    ("reference", u"C1345"),
    ("firstname", u"Stéphane),
    ("lastname", u"Klein"),
    ...
]

Dans mes fonctions ("add", "check"…) j'ai une boucle qui parcourt la structure de données et utilise les fonctions suivantes soit pour injecter des données, soit pour tester la validité des données.

def inject_value(driver, name, value):
    element = driver.find_element_by_id(name)
    if element.tag_name == 'input':
        if element.get_attribute("type") == "checkbox":
            if element.is_enabled() != value:
                element.click()
        else:
            element.clear()
            element.send_keys(value)

    elif element.tag_name == 'select':
        option_element = element.find_element_by_xpath(".//option[@value='%s']" % value)
        option_element.click()

    elif element.tag_name == 'textarea':
        element.clear()
        element.send_keys(value)

def check_value(driver, name, value):
    element = driver.find_element_by_id(name)
    if element.tag_name == 'input':
        if element.get_attribute("type") == "checkbox":
            return element.is_enabled() == value
        else:
            return element.get_attribute("value") == value

    elif element.tag_name == 'select':
        return element.get_attribute("value") == value

    elif element.tag_name == 'textarea':
        return element.text == value

Pour le moment, cela fonctionne correctement mais je trouve mon code fastidieux pour plusieurs raisons :

  • j'aimerais pouvoir définir des valeurs par défaut pour les formulaires
  • j'aimerais pouvoir choisir d'autres types de "selecteur", pour le moment je fais des recherches uniquement par ID
  • j'aimerais pouvoir facilement indiquer le type de champ, car pour le moment je fais de l'auto détection… mais cela ne sera pas toujours faisable

Ce que j'aimerais avoir

À noter que ce code n'est pas complet… c'est un brouillon.

from sealchemy import Form, TextField, SelectField, BooleanField

...

class AddCustomer(Form):
    __submit__ = Submit(name="_same")

    reference = TextField(default=u"C1345")
    type_user = SelectField(default=u"external")
    firstname = TextField(required=True)
    lastname = TextField(required=True)
    activated = BooleanField(default=True)
    comment = TextAreaField(default=u"")

    def go_to_page(self):
        self.driver.get("/customers/add/")

class EditCustomer(Form):
    __submit__ = Submit(name="_same")

    reference = TextField(default=u"C1345")
    type_user = SelectField(default=u"external")
    firstname = TextField(required=True)
    lastname = TextField(required=True)
    activated = BooleanField(default=True)
    comment = TextAreaField(default=u"")

    def go_to_page(self, id):
        self.driver.get("/customers/%s/" % id)

    def go_to_last_inserted(self):
        """Va sur la page du dernier client qui a été ajouté"""
        ...

Cela ressemble beaucoup à l'API de wtforms que j'utilise dans mon projet. Cela ressemble aussi à FormAlchemy que j'aime aussi.

__submit__ permet d'indiquer le champ à utiliser par la commande submit.

Note : je n'utilise pas une seule classe pour faire mes traitements "add" et "edit" car les formulaires d'ajouts et d'édition sont en pratique souvent différents.

L'interface de la classe de type Form :

class IForm(zope.interface.Interface):
    def inject():
        """Cette méthode injecte les données vers le formulaire HTML"""

    def submit():
        """Cette méthode lance le submit du formulaire"""

    def inject_and_submit():
        """Exécute inject et ensuite submit"""

    def check():
        """Cette méthode retourne True si les données correspondent aux
           données présentes dans le formulaire HTML"""

    def clear():
        """Réinitialise la valeur de tous les champs de l'instance avec
           les valeurs par défauts"""

    def populate(values):
        """Affecte des valeurs aux champs de l'objet."""

La classe Session de mon projet :

class MyProject(Session):
    def __init__(self, login, password):
        ...

    def login(self):
        ...

    add_customer = AddCustomer()
    edit_customer = EditCustomer()

Dans MyProject, j'ai ajouté les propriétés add_customer et edit_customer afin que ces objets aient accès à l'objet driver de Selenium.

Exemple d'utilisation :

session = MyProject(login="username", password="password", url="http://localhost:5000/")
session.login()

values = {
    "reference": u"C1871",
    "firstname": u"Stéphane",
    "lastname": u"Klein",
}
session.add_customer.populate(values)
session.add_customer.go_to_page()
session.add_customer.inject_and_submit()
session.edit_customer.go_to_last_inserted()
session.edit_customer.populate(values)
assert session.edit_customer.check()

Plus d'informations à propos des classes Field

Les classes de type Field comme TextField, BooleanField … ont un constructeur avec plusieurs paramètres :

  • name (optionnel) : définit la méthode de recherche du champ par l'attribut "name"
  • id (optionnel) : définit la méthode de recherche du champ par l'attirbut "id"
  • xpath (optionnel) : définit la méthode de recherche via xpath
  • required (optionnel) : définit si le champ est requis ou non
  • default (optionnel) : définit la valeur par défaut du champ

Par défaut, si j'ai ceci :

class ...(Form):
    firstname = TextField()

C'est équivalent à cela :

class ...(Form):
    firstname = TextField(name="firstname")

Conclusion, questions

Je vous ai donc présenté l'API que j'imagine créer sous le nom de "sealchemy".

J'ai plusieurs questions :

  • est-ce que vous pensez que cette librairie serait utile ?
  • est-ce que l'API, le mode de fonctionnement est judicieux ?
  • est-ce que cela vous intéresse ?
  • est-ce que vous avez déjà créé quelque chose du même genre ?
  • quelle est votre méthode pour faire ce genre de test ?

Merci d'avance pour vos commentaires.

Read and Post Comments

Traduction du billet de Ian Bicking « À propos des librairies de génération de formulaires HTML »

En mars 2007, Ian Bicking a écrit un billet à propos des librairies de génération de formulaires web : « On form libraries »

J'ai trouvé ce billet très intéressant… bien que j'aie quelques commentaires à faire… mais cela fera l'objet d'un autre billet de ma part.

J'ai décidé de traduire ce document et voici le résultat ci-dessous.

Concernant la traduction, j'ai pris une assez grande liberté. J'ai retravaillé certaines phrases pour les rendre - je l'espère - plus simples à comprendre.
J'ai aussi pris certaines libertés au niveau de la mise en forme.
Mon document n'est donc pas une traduction fidèle, j'ai toutefois essayé de garder autant que possible l'esprit du billet original.
Traduction

À propos des librairies de génération de formulaires HTML

Il y a un certain temps (ntd - février 2005), j'ai écrit un billet à propos de la génération de formulaires web. J'y faisais part de mon expérience acquise lors du développement de FunFormKit, du développement des premières versions de FormEncode, et j'y expliquais aussi ce qu'est devenu FormEncode (uniquement une librairie de validation).

Depuis, sont arrivées quelques nouvelles librairies de génération de formulaires web dans l'écosystème Python; ToscaWidgets, Django newforms, et quelques autres dont j'ai entendu parler.
Je n'aime pas critiquer gratuitement le travail des autres, mais je tiens à dire que je pense que ces librairies ont adopté une mauvaise stratégie pour atteindre leurs objectifs.
Écrire une librairire de génération de formulaires est une tâche difficile. Il faut trouver le bon compromis entre une certaine simplicité d'utilisation et l'étendue des fonctionnalités.
Potentiellement, le nombre de fonctionnalités pour ce type d'outil est très élevé. Exemples :
  • comment modifier finement le code HTML généré ?
  • comment contrôler la mise en forme (le layout) ?
  • comment gérer la répétition d'éléments ?
  • comment gérer les éléments qui requièrent du Javascript ?
  • comment gérer les ressources coté serveur du type CSS ou Javascript ?
  • Comment gérer les communications client-serveur, par exemple supporter l'auto-complétion ?
Une librairie qui gère autant de fonctionnalités ne peut pas rester simple à utiliser. Une "petite" librairie qui reste simple ne pourra gérerer qu'une petite partie de ces fonctionnalités.
Cependant, pour générer des formulaires "simplistes", il n'est pas utile d'avoir recours à une librairie de génération de formulaires.

Cycle de vie de la création d'une librairie de génération de formulaires

Je vais maintenant expliquer pourquoi, à cause de leur conception, ils ne peuvent pas obtenir un bon compromis entre simplicité et fonctionnalité.
Il existe des problèmes qui n'ont pas de solution. Et contrairement aux apparences, c'est le cas des outils de générations de formulaires.
Le cycle de vie de création et de maintenance d'une librairie de génération de formulaires est pratiquement toujours le même.
Un développeur a certain besoins. Généralement il n'aime pas écrire du code HTML bien que cela ne le dérange pas de travailler avec un langage de template qui est plus difficile à utiliser. Il n'est sans doute pas très excité par les tâches manuelles et fabriquer une librairie de génération de formulaires est une agréable diversion.
Dans tous les cas, il fait le bon choix (enfin, celui qu'il pense être le bon) et il essaie d'utiliser une librairie existante.
Il se trouve que la librairie est complexe à utiliser, elle ne correspond pas à la façon dont il a appréhendé le problème, il est difficile d'identifier l'origine des erreurs, les petits ajustements qu'il souhaite apporter (lui, son chef ou son client) sont à l'origine de la plupart de ses difficultés.
Et là, il se dit « Je peux faire cela plus simplement ! ».
Le développeur a raison. Il sait exactement ce dont il a besoin (maintenant), et ce qui peut améliorer sa productivité.
Il crée donc une nouvelle librairie de génération de formulaires web.
Le problème est que la vie de cette nouvelle librairie ne s'arrête pas là. Elle ne peut pas s'arrêter là ! Pourquoi ? Parce que quelqu'un utilise cette librairie, et par conséquent le code doit être maintenu.
Une librairie de génération de formulaires est une chose difficile à maintenir, car elle a toujours besoin d'évoluer.
Des nouveaux types de champ doivent être ajoutés. Des nouveaux cas particuliers doivent être pris en compte. Pour ce qui concerne l'interface utilisateur, des cas particuliers, non prévus initialement, doivent pouvoir être traités.
Une bonne interface utilisateur est généralement spécifique au besoin, adapté au cas par cas. L'interface utilisateur doit être construite en fonction du besoin de l'utilisateur final et non pas en fonction des contraintes du développeur.
Avec le temps, le développeur a de nouveaux besoins.
Par exemple, une simple liste de cases à cocher contient un très grand nombres de choix. Il faut maintenant pouvoir l'afficher sur deux colonnes.
Conséquence : pour gérer ce nouveau cas, le développeur ajoute un peu de code à la fois dans la librairie et dans l'application.
Ensuite, le développeur souhaite que les champs textes contiennent le nom des champs (leurs labels) quand leurs contenus sont vides. Suite à cela, le système de validation devient un peu plus complexe à gérer.
Ensuite, le développeur aura peut être besoin de gérer des formulaires à plusieurs étapes… Ou alors des formulaires avec des champs optionnels…
De fil en aiguille, à chaque fois du code est ajouté à la librairie ainsi que dans l'application (à condition bien sûr, dans le meilleur des cas d'avoir le contrôle de ces deux parties).
Avec le temps, la librairie devient de plus en plus complexe, tandis que pendant ce temps, le développeur se familiarise avec son propre code et devient de plus en plus distant des autres développeurs qui devront peut être un jour intervenir dans son projet.
Et là, nous n'avons même pas abordé le sujet des non programmeurs qui souhaitent seulement modifier un tout petit peu le rendu HTML du formulaire. Ensuite vient le problème de turnover…

Et un jour, c'est la catastrophe, quelqu'un vient et dit « Je peux faire cela plus simplement ! » et c'est reparti pour un tour, on recommence l'histoire depuis le début.

Une autre approche

La partie validation
Je pense que le problème n'est pas pris par le bon coté. Je pense qu'il y a une meilleur approche, mais je suis trop fainéant pour la réaliser.
Bon…, je vais expliquer le principe, avec le petit espoir que quelqu'un réalisera le code :) .
Tout d'abord, la partie « validation » : j'aime la façon dont FormEncode fonctionne, je pense que la stratégie est bonne.
L'étape de validation est totalement indépendante du reste du système, vous pouvez programmer le type de validation que vous souhaitez, vous pouvez l'intégrer dans un autre système…
La partie « formulaire »
Ensuite, la partie « formulaire ».
La solution la plus simple est d'écrire directement le formulaire en code HTML avec son langage de template préféré.
Mais ceci est ennuyeux, les développeurs vont se plaindre… Pourquoi ?
  1. Remplir les valeurs par défauts dans les champs est ennuyeux. Il faut prendre en compte différents cas : les valeurs de la requête (par exemple en mode modification), les valeurs par défauts lorsque le formulaire est vide…
  2. La mise en place de l'affichage des messages d'erreurs est aussi une tâche ennuyeuse.
  3. Il faut écrire correctement les balises <label>… ce qui est une tâche rébarbative… c'est peut être ce qui explique que de très nombreux sites, même de grande notoriété, n'intègrent pas ces balises.
  4. Je suppose que l'HTML est généralement casse-pieds, bien que le langage soit seulement légérement plus verbeux que le code de programmation qui permet de le remplacer.
  5. La partie Javascript peut être ennuyeuse, beaucoup de développeurs web ne sont pas très à l'aise avec ce langage.
  6. Il est ennuyeux de placer correctement les fichiers ressources Javascript et CSS, et d'effectuer ensuite la liaison des classes CSS avec les bonnes balises…
  7. Les appels Ajax sont réellement ennuyeux à mettre en place.
  8. Les imbrications de champs, les répétitions de groupes de champs… sont une autre source d'ennuis.
  9. Et pour finir, il est difficile de réutiliser tout cela.
Ma réponse est que tous ces problèmes peuvent être résolus sans librairie de génération de formulaires.
formencode.htmlfill répond aux points 1 et 2. Ce n'est pas parfait, mais c'est utilisable.
Quelque chose comme WebHelpers peut être utile pour le point 4.
Aucun de ce ces outils ne doit être la solution ultime. Ils peuvent tous être améliorés mais attention, il ne faut pas pousser trop loin leurs perfectionnement ! Sinon ils vont devenir de trop grands consommateurs de temps.
Un peu de « Unobtrusive JavaScript » peut résoudre le problème numéro 5.
Je pense que la mise en oeuvre d'un outil spécifique pour le point 6, pourrait être pratique : j'imagine quelque chose comme <input type="date" js-require="DateInput"> qui chargerait automatiquement les bons fichiers Javascript et CSS, et qui appellerait du code là où est le champ DateInput.

Les nouvelles spécifications Web Forms 2.0 de WHAT-WG peuvent parfaitement gérer les points 7 et 8… un outil construit sur ce modèle ferait parfaitement l'affaire.

Une fois que vous avez tout cela, je pense que le point 9 peut être plus ou moins atteint.
En conclusion, vous n'avez pas besoin d'un framework de génération de formulaires, vous avez juste besoin d'un outil tout simple qui génère du code HTML et qui respecte certaine conventions.
Ce qui est remarquable , c'est que cette technique est uniquement basée sur des abstractions déjà présentes : HTML et Javascript.
De plus, chaque outil peut être utilisé de manière indépendante. Il est possible de remplacer chaque brique par autre chose, une nouvelle implémentation… qui correspond parfaitement au besoin du projet.
Ce qui est difficile à accepter, c'est que ces outils semblent terriblement simplistes, ils ont peu de fonctionnalités comparé aux systèmes de génération de formulaires automatique.
Doucement mais surement, ces petits outils peuvent gagner la course, quelque soit la manière… pendant ce temps, les autres systèmes de génération de formulaires vont souffrir du mal du cycle de la réimplémentation… Tandis que de simples outils vont mieux supporter les épreuves du temps.
Et même si ces simples outils n'arrivent pas à atteindre l'objectif souhaité, il est possible d'en remplacer un ou plusieurs sans mettre en péril tout l'édifice.

Maintenant, ça serait génial que quelqu'un se lance dans la fabrication des pièces manquantes, car j'essaie vraiment de ne pas commencer de nouveaux projets supplémentaires.

Ian Bicking

Depuis cette date, tous les outils décrits dans ce billet n'ont pas été réalisés. Toutefois formencode et formencode.htmlfill remplissent très bien leurs rôles respectifs.

Je tiens aussi à souligner que depuis, les librairies de génération de formulaires ont bien évolué, comme par exemple WTForms que j'ai étudié hier. Cette dernière reste très très simple d'utilisation.

Dans un prochain billet, je donnerai mon point vu personnel sur ce sujet.

Read and Post Comments

Quelques bonnes pratiques qui simplifient la maintenance de la documentation technique d'un projet

1   Introduction

Au quotidien, j'essaie autant que possible d'être un développeur agile. Agile dans ma façon de "coder", agile dans ma façon de concevoir la documentation technique de mes projets.

Dans cet article, je vais dresser une liste de quelques bonnes pratiques qui permettent de réaliser et de maintenir efficacement la documentation technique d'un projet. J'essaie personnellement de les suivre dans mes projets bien que cela ne soit pas toujours le cas... et c'est mal :).
L'origine de la plupart de ces bonnes pratiques proviennent de lectures diverses ou de l'observation de projets existants.
Quelques-unes d'entre elles viennent de réflexions personnelles.

2   Problématique

En génie logiciel, la mise à jour des documentations techniques est un problème récurrent.
Il est très difficile d'avoir assez de rigueur pour tenir à jour les documentations tout au long de la vie d'un projet.

Au cours de la vie d'un projet, de nombreux événements viennent perturber les plans initiaux des développeurs : cahier des charges incomplets ou erronés, évolution du besoin initial, erreurs de conceptions ou d'analyse... il faut ajouter à cela le refactoring nécessaire à l'amélioration constante de la qualité et la consistance du code source.

Ces événements ont des conséquences directes sur le code source : modification des API, création de nouvelles classes, suppression d'autres classes, création de nouvelles librairies, changement d'algorithme... Si l'on ajoute à ces changements, le manque de temps, la mise à jour de la documentation passe à la trappe.

Le problème de mise à jour de la documentation a un autre effet pervers : une personne qui consulte la documentation n'a pas connaissance du niveau de mise à jour de celle-ci, un doute persiste concernant l'exactitude des informations qui s'y trouvent. Il se peut que suite à de nombreuses expériences négatives (documentations obsolètes), une personne puisse prendre l'habitude de lire directement le code source car c'est la seule source d'information dont l'exactitude soit certaine.

3   Solutions existantes

3.1   Quelques bonnes pratiques

Quelques bonnes pratiques peuvent aider fortement à la bonne tenue de la documentation :

  • la documentation doit être le plus près possible du code source

    En pratique, il est plus facile de tenir à jour la documentation quand celle-ci est intégrée dans le code source.
    En Python, les docstrings permettent d'intégrer la documentation à l'intérieur du code source. La documentation est au plus près du code, lorsque le code est modifié le développeur est mieux à même à mettre tout de suite à jour la documentation.
    Lorsqu'une documentation n'est pas sujette à être intégrée dans le code source, une bonne pratique est de l'intégrer dans un fichier texte enregistré dans le répertoire qui correspond le mieux avec la partie du projet qu'elle documente.
    Par exemple, si le fichier documente l'utilisation d'une librairie, il est judicieux d'enregistrer la documentation à la racine du code source de la librairie.
    Si la documentation traite du projet dans son ensemble, il est judicieux d'enregistrer le fichier à la racine du projet...
    L'idée est toujours la même : garder la documentation le plus près possible du code qu'elle documente.
  • la documentation doit être facilement modifiable

    Généralement, les développeurs utilisent un éditeur texte (plain text) pour éditer du code source. La documentation doit être éditable aussi facilement que le code source du programme. C'est pour cela qu'il est conseillé d'écrire la documentation au format texte (plain text) plutôt que dans un format spécifique comme Writer d'OpenOffice.org...
    En Python, le format communément utilisé est le reSTructuredText, un format texte enrichi mais de manière non intrusif.
    Là encore, le but est de favoriser la mise à jour de la documentation en rendant sa modification le plus simple possible.
  • éviter au maximum la redondance

    Certaines informations clés qui sont au coeur de la documentation technique sont déjà présentes dans le code source du projet.
    Partant de ce constat, il peut être judicieux d'automatiser l'extraction de ces informations vers la documentation afin d'éviter la redondance et par conséquent éviter les besoins de mises à jour et les risques d'erreurs.
    La documentation peut être en partie générée à partir du code source du projet.

    Exemples pratiques :

    • génération automatique de l'API d'une librairie à partir du code source ( exemples d'outils python : Epydoc [1], Sphinx [2]);
    • génération d'un diagramme de classe à partir du code source (voir futur billet à propos de code2uml);
    • génération d'un diagramme de base de données à partir du code sql qui permet la création de la structure de la base de données (voir futur billet à propos de code2uml);
    • génération d'une liste de tâches à réaliser (todo list) à partir de marques à l'intérieur du code source (voir exemple dans Zope3 [3]). Ceci evite d'avoir deux fois la même information : une fois dans le code source à l'endroit précis où la tâche doit être réalisée et une fois dans la liste des tâches à réaliser. De plus, cette pratique a l'avantage de situer la marque d'indication de la tâche au plus près du code source.
    • Les scénarios de tests automatisés d'interfaces utilisateurs peuvent être utilisés pour générer automatiquement des screncasts des applications. Ils pourront alors être utilisés comme documentation à destination des utilisateurs finals. De plus, ils auront l'avantage d'être toujours à jour et valides (si les tests sont réalisés avec succès).
  • donner à la documentation un intérêt fonctionnel

    Une autre bonne pratique est de donner un intérêt fonctionnel aux documentations. C'est-à-dire que cette documentation doit être utile pour générer ou réaliser d'autres choses. Si la documentation automatise certaines tâches et simplifie la vie du développeur, ce dernier aura plus de motivation pour réaliser et mettre à jour sa documentation.

    Exemples :

    • Les doctests sont de bons exemples: cette documentation sert à la fois de tutoriels et de scripts de tests de bon fonctionnement du code source;
    • Un document de spécification (cahier des charges) bien formalisé peut être utilisé pour communiquer à propos de l'état d'avancement d'un projet.
[1]http://epydoc.sourceforge.net/
[2]http://sphinx.pocoo.org/
[3]http://amy.gocept.com/~ctheune/XXXreport.html

4   Est-ce que la documentation doit générer du code source ou alors est-ce que le code source doit générer de la documentation ?

Je pense personnellement que la meilleure solution est de générer de la documentation à partir du code source. Pourquoi ?
Parce que la première solution, celle qui consiste à générer du code source à partir de la documentation peut fonctionner correctement seulement lors de la phase initiale du projet. Lorsqu'il existe déjà du code source, il est difficile de modifier ce code à partir de la documentation sans prendre le risque de casser beaucoup de chose.
D'autre part, lorsqu'un développeur est pressé, correction de bug... il va toujours modifier le code source, il ne va pas modifier la documentation, exécuter une regénération... Le coeur d'un projet est toujours son code source, c'est la matière première du projet et l'on doit utiliser cette matière pour générer d'autres informations / documents... et non l'inverse.

5   Pourquoi ce billet ?

J'ai écrit ce billet car il me sert d'introduction pour un prochain billet qui traitera de mon application qui permet de générer un graphe UML à partir du code source d'un programme.

6   Outils et références

Livres :

Outils Python :

  • Epydoc : pour générer automatiquement la documentation d'une API, librairie...
  • Sphinx : outil qui permet entre autres de générer une documentation cohérente à partir de différents fichiers reSTructuredText ainsi qu'à partir du code source d'un projet
  • doctest : outil qui permet de tester un programme à partir d'un jeu de tests écrit sous la forme reSTructuredText
Read and Post Comments

Mes flux :