- Trouver l’équilibre entre la perfection et la vitesse n’est pas simple, mais il est important de viser un niveau de qualité adapté à la situation et de respecter les délais
- Il est efficace de commencer par développer un brouillon (rough draft), puis d’améliorer ensuite la qualité du code
- Assouplir les exigences ou réduire les demandes excessives peut améliorer la vitesse et l’efficacité
- Il faut prendre l’habitude d’éviter les distractions, de faire des commits fréquents en petites unités et de se concentrer sur l’essentiel
- Certaines compétences concrètes aident à développer plus vite, comme la lecture de code, la modélisation de données, le scripting, le débogage, l’orientation vers les fonctions pures, etc.
« Quel niveau de qualité un code doit-il avoir ? » – Critères de qualité et choix réalistes
- Au début, on veut que tout le code soit parfait
- On rêve d’un code où chaque fonction est rigoureusement testée, où les noms de variables sont élégants, les abstractions claires, et où il n’y a absolument aucun bug
- Mais avec le temps, on apprend la réalité : « il n’y a pas de bonne réponse universelle »
- Le niveau de qualité attendu varie selon le contexte
- Game jam de 24 heures : il n’est pas nécessaire que le code livré soit parfaitement propre ou sans bug
- Le plus important est de produire quelque chose qui fonctionne dans le temps imparti
- Logiciel de pacemaker : une seule erreur peut mettre une vie en danger
- Le plus haut niveau de fiabilité et de sécurité est indispensable
- La plupart des projets se situent entre ces deux extrêmes
- Certaines entreprises exigent une livraison rapide et tolèrent donc quelques bugs
- Certains projets exigent une grande qualité, mais avec un calendrier plus souple
- Dans le travail réel, la capacité à identifier cet équilibre est essentielle
- Il faut d’abord comprendre ce que l’équipe considère comme “suffisamment bien” (good enough)
- Examiner ensemble des critères concrets, comme le niveau de bugs acceptable ou les parties qui n’ont pas besoin d’être parfaites
- Son critère personnel est le suivant
- « Atteindre une qualité de 8/10 dans les délais »
- Le code remplit correctement sa mission, ne présente pas de problème critique, mais il peut rester quelques détails mineurs
- Le plus important est de livrer dans les temps
- Cela dit, ce critère aussi doit être ajusté avec souplesse selon le contexte du projet
- Parfois, on peut viser la perfection même si le planning glisse un peu
- D’autres fois, il est plus utile de finir vite, même avec un niveau de finition moindre
- « Atteindre une qualité de 8/10 dans les délais »
Rough drafts – Utilisation concrète et avantages du brouillon et du prototypage
- En développement logiciel aussi, comme en écriture, produire un premier brouillon (
rough draft,spike,walking skeleton) est extrêmement utile - Il s’agit de réaliser ce brouillon aussi vite que possible, puis de l’affiner ensuite pour en faire une solution aboutie
- Dans son cas, le code de brouillon est plein de bugs, avec des tests qui échouent, des commentaires TODO partout, des exceptions non gérées, un usage excessif de
print/logs,
aucune considération de performance, des messages de commit WIP, des paquets inutiles ajoutés, du code dupliqué, du hardcoding, des avertissements du linter, bref un vrai désordre - Ce processus peut sembler inefficace, mais le but est d’atteindre « un état où l’on comprend au moins la nature du problème »
- Évidemment, ce code à l’état de brouillon n’est pas envoyé tel quel en production ; il est impérativement nettoyé avant la livraison réelle
(même s’il arrive que l’équipe pousse à livrer le brouillon tel quel, il essaie d’y résister autant que possible) -
Principaux avantages de l’approche rough draft
- Elle fait émerger rapidement les “unknown unknowns”
- Il est bien plus avantageux de découvrir les obstacles inconnus au stade du prototype qu’une fois le produit terminé, dans du code qui finira jeté
- Beaucoup de problèmes disparaissent naturellement pendant le prototypage
- Une fonction lente ou une mauvaise structure peut finalement devenir inutile plus tard, ce qui évite de perdre du temps
- Il n’est pas nécessaire d’investir trop tôt dans l’optimisation ou les tests
- Elle renforce la concentration
- Elle évite les distractions comme le refactoring inutile, les hésitations sur les noms, ou les corrections d’autres parties de la base de code,
et permet de se concentrer uniquement sur le problème à résoudre maintenant
- Elle évite les distractions comme le refactoring inutile, les hésitations sur les noms, ou les corrections d’autres parties de la base de code,
- Elle évite l’abstraction prématurée inutile
- Quand on cherche d’abord une solution qui fonctionne vite, on tente moins de construire des abstractions inutiles pour un futur hypothétique
- On se concentre sur le problème immédiat et on évite une conception inutilement complexe
- Elle permet de communiquer clairement l’avancement
- Grâce au rough draft, il devient possible d’estimer plus précisément ce qu’il reste à faire
- Montrer d’abord quelque chose qui fonctionne permet d’obtenir rapidement des retours des parties prenantes et d’ajuster la direction
- Elle fait émerger rapidement les “unknown unknowns”
-
Comment l’appliquer concrètement
- Les décisions difficiles à annuler (
binding decision) doivent impérativement être testées au stade du brouillon- Exemple : le langage, le framework, le schéma de base de données et les grandes orientations doivent être validés tôt
- Tous les bricolages temporaires / hacks doivent être consignés dans des commentaires TODO ou équivalents
- Lors de la phase de
polish(finition), on peut tout passer en revue avecgit grep TODOpour corriger ce qu’il faut
- Lors de la phase de
- Développer en Top-Down
- Commencer par écrire le scaffold (squelette) de l’UI, de l’API, etc. à partir de la manière dont le système sera utilisé ; la logique interne peut être hardcodée ou implémentée provisoirement
- En pratique, l’UI et l’expérience d’usage évoluent souvent, ce qui modifie aussi la logique interne ; il est donc avantageux de partir des couches hautes
- Faire l’inverse, en implémentant parfaitement le bas niveau avant d’ajuster le haut niveau, est inefficace
- Séparer les petits changements dans des patchs distincts
- Si, pendant le rough draft, on découvre qu’une amélioration de la base de code ou une mise à jour de dépendance est nécessaire,
on isole cette partie dans une PR/un commit séparé pour l’intégrer rapidement - Cela réduit la complexité de l’ensemble des changements et accélère la revue et l’intégration
- Si, pendant le rough draft, on découvre qu’une amélioration de la base de code ou une mise à jour de dépendance est nécessaire,
- Les décisions difficiles à annuler (
Référence : “Throw away your first draft”, “The best simple system for now”, “YAGNI (You Aren’t Gonna Need It)”
Essayer de faire évoluer les exigences
- Le principe mis en avant est que faire moins est plus rapide et plus simple
- Dans le travail réel, il réfléchit toujours à la possibilité d’assouplir les exigences d’une tâche donnée
- Exemples de questions :
- Peut-on fusionner plusieurs écrans en un seul ?
- Faut-il vraiment gérer tous les edge cases compliqués ?
- Si l’on doit supporter 1000 entrées, est-ce que 10 ne suffiraient pas ?
- Peut-on remplacer un produit finalisé par un prototype ?
- Peut-on tout simplement supprimer cette fonctionnalité ?
- Exemples de questions :
- Cette approche améliore la vitesse et l’efficacité de développement
- Il essaie aussi d’orienter progressivement la culture de l’organisation vers un rythme un peu plus lent et plus raisonnable
- Les demandes de changements brusques et massifs fonctionnent mal
- En modifiant peu à peu la manière de proposer et de discuter, on fait évoluer l’ambiance progressivement
Éviter les distractions dans le code
- Au-delà de l’environnement externe (notifications, réunions), le fait de partir sur des sujets annexes pendant le travail de code est aussi un gros frein
- Il lui arrive souvent de corriger un bug, puis de démonter complètement une zone sans rapport, et au final de repousser la tâche initiale
- Deux pratiques concrètes :
- Régler un minuteur : définir une limite de temps pour chaque tâche, puis vérifier l’état d’avancement quand l’alarme sonne
- Cela aide à reprendre conscience quand une tâche prend plus de temps que prévu
- Faire un
git commitau moment de l’alarme procure aussi un petit sentiment d’accomplissement - (Cette méthode aide également à s’entraîner à estimer le temps)
- Le pair programming : travailler à deux réduit les dérives inutiles et aide à rester concentré
- Régler un minuteur : définir une limite de temps pour chaque tâche, puis vérifier l’état d’avancement quand l’alarme sonne
- Pour certains développeurs, éviter ces distractions vient naturellement, mais pour lui cela demande une attention consciente et une vraie habitude
Des changements petits et bien découpés
- Il a déjà eu un manager qui encourageait les gros patchs et les changements très larges,
mais il a constaté en pratique que c’était très inefficace - Il estime que des diffs petits et ciblés sont presque toujours préférables
- Ils sont moins pénibles à produire
- La revue de code devient plus simple et plus rapide, ce qui réduit aussi la fatigue des collègues et permet de repérer plus facilement ses propres erreurs
- En cas de problème, le rollback est plus facile et plus sûr
- Comme le périmètre modifié d’un coup est réduit, le risque d’introduire de nouveaux bugs diminue aussi
- Même les grosses fonctionnalités ou ajouts de fonctions se construisent par accumulation de petits changements
- Exemple : s’il faut ajouter un écran, on peut séparer les corrections de bugs, les upgrades de dépendances et l’ajout fonctionnel en patchs distincts
- Il souligne que les changements de petite taille aident à développer des logiciels plus vite et avec une meilleure qualité
Les compétences concrètes qui l’ont vraiment aidé à développer plus vite
Les idées évoquées plus haut sont assez abstraites, mais il existe aussi des compétences pratiques réellement efficaces pour accélérer le développement
-
Lecture de code (
Reading code) : c’est la compétence de développeur la plus importante qu’il ait acquise jusqu’ici- Savoir bien interpréter du code existant rend le débogage beaucoup plus facile
- Les bugs ou la documentation insuffisante dans l’open source / les bibliothèques tierces deviennent bien moins intimidants
- Lire le code des autres permet aussi énormément d’apprendre et améliore directement la capacité générale à résoudre des problèmes
-
Modélisation des données (
Data modeling) : même si cela prend du temps, il est important de concevoir correctement le modèle de données- Un schéma de base de données mal conçu entraîne plus tard toutes sortes de problèmes et des coûts de correction complexes
- Concevoir le système de façon à ce que les états invalides soient tout simplement impossibles à représenter réduit les bugs à la source
- C’est d’autant plus important lorsque les données sont stockées ou échangées avec l’extérieur
-
Scripting (
Scripting) : la capacité à écrire rapidement de petits scripts en Bash, Python, etc. maximise fortement l’efficacité- Il s’en sert plusieurs fois par semaine pour automatiser des tâches comme l’alignement de Markdown, le nettoyage de données ou la détection de doublons de fichiers
- En Bash, des outils comme Shellcheck permettent de prévenir à l’avance les erreurs de syntaxe
- Pour les tâches qui n’ont pas besoin d’être robustes, on peut aussi s’aider d’un LLM pour aller vite
-
Utiliser les débogueurs (
Debuggers) : l’usage d’un débogueur est indispensable pour diagnostiquer rapidement des problèmes et comprendre le flux du code, là oùprint/logs ne suffisent pas- Cela accélère énormément l’identification de la cause racine des bugs complexes
-
Savoir faire une pause au bon moment : prendre une vraie pause quand on est bloqué
- Il est fréquent qu’un problème insoluble après de longues heures se résolve immédiatement après 5 minutes de pause
- C’est aussi important pour l’efficacité de la concentration
-
Orientation vers les fonctions pures et les données immuables : programmation fonctionnelle
- Préférer les fonctions pures et les données immuables réduit les bugs, diminue la charge liée au suivi de l’état et augmente la clarté / la prévisibilité du code
- C’est souvent plus simple et plus efficace qu’une hiérarchie de classes complexe
- Ce n’est pas toujours possible, mais c’est l’approche qu’il envisage par défaut en premier
-
Utilisation des LLM (grands modèles de langage) : les LLM (par ex. ChatGPT) ont aussi leurs limites, mais ils apportent un vrai gain de vitesse dans les tâches de développement répétitives ou automatisables
- Il les utilise activement après avoir bien compris comment les intégrer à son code et quelles sont leurs limites
- Il s’appuie aussi sur les nombreux retours d’expérience, conseils et cas d’usage de la communauté
Toutes ces compétences ont été pratiquées de manière répétée pendant longtemps, et elles sont devenues un atout majeur pour développer rapidement
Résumé
- Voici les principales leçons qu’il a tirées de sa manière de développer rapidement des logiciels
- Définir clairement le niveau de qualité requis pour chaque tâche
- Rédiger rapidement un rough draft (brouillon) pour poser la structure d’ensemble
- Explorer en permanence les possibilités d’assouplir les exigences
- Rester concentré sans se laisser happer par les distractions
- Faire des changements petits et fréquents, avec des commits réguliers, et éviter les gros patchs
- Pratiquer continuellement des compétences concrètes (lecture de code, modélisation de données, débogage, scripting, etc.)
- Tout cela peut sembler évident, mais il lui a fallu longtemps pour en tirer réellement ces enseignements
2 commentaires
Il y a beaucoup de remarques auxquelles on s'identifie.
Les commentaires sont bons aussi, mais quand quelqu'un les organise et les formule ainsi, autrement dit quand il pose le cadre, j'ai l'impression que cela devient plus abouti à travers les objections, le soutien et les compléments qu'on y apporte.
P.-S. Je vois souvent ces derniers temps l'expression « technologie ennuyeuse » ; en anglais, c'est donc boring technology.
Avis Hacker News
Ces dernières années, j’ai appris à construire des systèmes rapidement et avec une robustesse suffisante
J’ai compris qu’il est important de maîtriser un outil en profondeur. Un outil que je connais bien est bien plus efficace qu’un autre qui semble plus adapté en surface. En pratique, Django est le bon choix pour la plupart des projets
Il m’est arrivé de démarrer un projet en me demandant si Django n’était pas trop lourd, mais au final le projet a largement dépassé son intention initiale. Par exemple, j’ai créé une application de page de statut et j’ai vite compris que chercher à contourner les limites supposées de Django était inefficace
Dans la plupart des applications qui correspondent bien au modèle Django, le modèle de données est central. Même pour un prototype, remettre à plus tard la refonte du modèle de données fait exploser le coût et la difficulté par la suite
La plupart des applications n’ont pas besoin d’être des SPA ni d’utiliser un framework frontend lourd. Certaines oui, peut-être, mais pour 80 % des pages, les vues Django traditionnelles suffisent. Pour le reste, on peut envisager AlpineJS ou HTMX
Dans la plupart des cas, développer soi-même est plus simple. Avec Django, on peut créer très vite un CRM, une page de statut, un système de support, un processus commercial, etc. C’est bien plus rapide que d’intégrer un CRM commercial
Choisissez des technologies d’une banalité presque ennuyeuse. Le trio Python/Django/Postgres règle la plupart des cas. On peut oublier Kubernetes, Redis, RabbitMQ, Celery, etc. Alpine/HTMX fait exception, parce que cela permet d’éviter l’essentiel de la stack JS
Redis et Kubernetes sont, pour moi, les « technologies ennuyeuses » de 2025. Les deux sont extrêmement stables, leur usage est clair, et leurs inconvénients sont déjà bien connus, donc elles inspirent confiance. Personnellement, j’en suis fan. Elles font exactement ce que j’attends d’elles, donc je leur fais confiance
Moi aussi, j’aime vraiment Django. On peut démarrer un projet et le déployer à une vitesse folle
Si l’on choisit vraiment des « technologies ennuyeuses », même Postgres mérite d’être reconsidéré
J’utilise assez souvent Celery dans les projets Django. Je n’aime pas sa complexité, mais dans un environnement PaaS, c’est malgré tout souvent le choix le moins douloureux
L’affirmation « la plupart des applications n’ont pas besoin de SPA ni de framework frontend lourd » semble entrer en conflit avec le conseil « maîtrisez un outil en profondeur »
Quand on laisse du code sous forme d’ébauche grossière, un manager le met souvent en production tel quel comme « version finale »
Du coup, j’écris du code robuste dès le début. Même mon harnais de test est presque au niveau de la production
L’essentiel, c’est de créer des modules d’excellente qualité. Les parties qui ont très peu de chances d’évoluer, ou dont les changements seraient extrêmement problématiques, je les isole complètement dans des modules indépendants importés comme dépendances
Grâce à ces modules, on peut développer de nouvelles applications très rapidement tout en maintenant un niveau de qualité élevé
Comme exemples utilisés directement, il y a RVS_Checkbox, ambiamara, RVS_Generic_Swift_Toolbox etc.
J’ai une question : en Swift, est-ce standard d’utiliser des motifs de commentaire comme "* ##################################################################" ?
L’approche change énormément selon la taille du projet
Pour un projet personnel ou une petite équipe, développer « vite et salement » est souvent optimal. C’est là la force du développement à petite échelle
À petite échelle, même s’il y a des bugs, on peut les corriger rapidement, et toute l’équipe comprend presque parfaitement l’ensemble du code
Quand l’échelle augmente, le coût des erreurs d’architecture ou des corrections de bugs explose. L’architecture devient inévitablement complexe, et les gros refactorings deviennent pratiquement impossibles. Dans cet environnement, la précision à chaque étape doit devenir la priorité absolue
Le contexte est vraiment essentiel. La définition de « grande échelle » peut varier, mais d’après mon expérience, se mettre d’accord tôt sur les API entre applications pour permettre au frontend comme au backend de travailler rapidement a toujours été la bonne décision
Dans ce genre de situation, il faut réduire la voilure du système. Tout le monde veut un énorme système, mais en réalité on n’en a pas besoin
On entend parfois que « dans une game jam de 24 heures, il n’y a pas besoin de se soucier de la qualité du code », mais d’après la plupart de mes expériences en hackathon et en revue de code, les équipes qui obtenaient les meilleurs résultats prenaient aussi au sérieux la qualité du code et un environnement de test rudimentaire
En réalité, les deux affirmations ci-dessus (pour aller vite il faut sacrifier la qualité du code vs les meilleures équipes ont un niveau de qualité plus élevé) ne se contredisent pas forcément. Les équipes performantes n’étaient pas nécessairement obsédées par la propreté du code
Dans le cas des game jams, être trop obsédé par la propreté du code peut au contraire nuire au résultat final. Des systèmes comme les UE blueprint montrent bien pourquoi il faut parfois privilégier le résultat plutôt que la « propreté »
Certaines personnes évaluent globalement la « propreté » du code, d’autres évaluent le coût/bénéfice précis d’améliorations de code non essentielles
Contrairement à l’idée que « le prototypage révèle des unknown unknowns inattendus », quand je découvre quelque chose pour la première fois, je vois presque toujours d’abord les avantages et beaucoup moins les défauts
En réalité, les vrais problèmes (unknown unknowns) apparaissent surtout au moment de finaliser la fonctionnalité : gestion des cas limites, messages d’erreur compréhensibles pour l’utilisateur, suppression des effets de bord, etc.
Il est probable que les unknown unknowns que je rencontre viennent de l’outil, du framework ou de la bibliothèque elle-même, tandis que l’auteur parle plutôt des unknown unknowns du domaine du problème
Je suis aussi d’accord sur le fait qu’une rough draft ne doit pas être trop grossière. Si on bâcle les parties qu’il ne faut pas bâcler, les vrais problèmes finissent par exploser.
Quand on construit un outil pour son propre usage, on peut s’en sortir avec quelque chose de bâclé, et même un outil fait très vite, plein de défauts, peut ne pas poser de problème en pratique
Dans le secteur tech actuel, où les restructurations se multiplient, c’est selon moi la plus grande menace pour la qualité logicielle et la productivité des ingénieurs
La peur du licenciement et la pression des résultats rapides tuent la créativité et l’esprit d’expérimentation, et provoquent le burn-out
Tout le monde se laisse emporter par l’effet de foule autour de sujets à la mode comme l’IA, dans un climat où il devient même difficile de formuler des critiques
C’est un problème plus urgent encore que le codage automatique par LLM
La plus grande menace pour la qualité logicielle a toujours été le fait que les consommateurs ne paient pas pour la qualité
Le vendor lock-in au niveau du langage ou de l’environnement de programmation est en réalité bien plus destructeur que le lock-in SaaS
Dans des cycles rapides comme une game jam de 24 heures, j’ai au contraire l’impression qu’un mauvais code est fatal
Plus le code est propre, moins on fait d’erreurs, moins il pèse sur la mémoire de travail, et plus il devient facile d’ajouter au dernier moment les changements ou fonctionnalités voulus, ou de corriger les problèmes
Dans un projet de 24 heures, ce qui fait le plus souvent dérailler le travail, ce n’est pas d’avoir écrit le code lentement, mais de se retrouver soi-même coincé dans un coin ou de tomber sur des problèmes imprévisibles
Bien sûr, cela ne veut pas dire qu’il faut corriger tous les bugs. Mais quand la qualité de base est faible, l’expérience du projet devient globalement pénible
Ce principe s’applique aussi aux projets qui disposent de plus de temps. Ce n’est pas parce qu’on a davantage de temps qu’il vaut mieux coder à l’arrache
Si l’on prend l’habitude d’écrire du bon code, on peut garantir la qualité sans coût supplémentaire. Et même si cela prend plus de temps, cela reste quelque chose qui en vaut la peine
Je pense pareil. J’ai fait plusieurs game jams, et le « code bancal » n’est acceptable que dans la dernière heure ou les deux dernières heures avant la deadline, et seulement dans des fichiers que personne d’autre ne touchera
Pour écrire du code vite et bien, il faut au final beaucoup écrire
Quand on est pressé, on ne s’occupe pas d’un asset loader sophistiqué : on balance simplement des fichiers statiques
Je pense que l’idée selon laquelle « écrire du bon code prend plus de temps » est un malentendu. Dès qu’il faut atteindre un certain niveau d’exigence, le bon code ne devient pas un obstacle à la vitesse
Le critère de ce qui est « good enough » varie tellement d’une équipe à l’autre que c’est la plus grande source de conflit dans ma carrière
Les gens issus de la big tech se plaignent de l’insuffisance des tests, et ceux venus de startups trouvent qu’on avance trop lentement
Il serait utile de documenter clairement le niveau de « good enough » et de le partager dans l’équipe
C’est exactement le rôle d’une charte d’équipe, c’est-à-dire un document sur « notre façon de travailler »
L’un des éléments importants que l’article ne mentionne pas est le ralentissement de la vitesse de développement au fil du temps
À mesure que le projet et l’équipe grandissent, la vitesse de développement ralentit naturellement
Autrement dit, même s’il faut sacrifier un peu de vitesse immédiate, il faut préparer tôt des tests, de la documentation, des logs de décision, des réunions Agile, etc., afin que la vitesse de développement se dégrade moins à long terme
Si l’on ne prépare pas à l’avance des éléments comme l’observability ou une structure de code facile à tester, l’impact négatif devient énorme plus tard
Je suis développeur solo, mais je ressens fortement l’importance de trois choses : le journal de décisions, les tests et la documentation
C’est aussi un schéma qui m’est familier. Je commence par une rough draft, ou par un petit bout de code qui sert à valider une idée en combinant un autre langage de script ou des exécutions manuelles