summaryrefslogtreecommitdiff
path: root/Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt
blob: 7936251c9457ffbc82d5e6635d55aacf99612aca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
Titre: Réduire les temps de développement sans sacrifier la qualité
Auteur: LeBouquetin
Date: Tue 21 Feb 2017 15:26:31 +0100
Lien: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite

Sommaire

  * Avant-propos[1]
  * Introduction[2]
  * Comment réduire les temps de développement ?[3]
  * Réduction de temps de développement avec perte de qualité[4]
  * Réduction de temps de développement sans perte de qualité[5]

  * Quel intérêt ?[6]
  * Quelques techniques[7]

  * Technique n°1 — ne pas prendre de décision à la place du client[8]
  * Technique n°2 — se limiter strictement au cahier des charges[9]
  * Technique n°3 — réduire le périmètre fonctionnel[10]
  * Technique n°4 — faire du « test-driven » sur les interfaces[11]
  * Technique n°5 — Faire de la conception progressive sur les composants 
    internes[12]
  * Technique n°6 — Diviser pour mieux régner[13]
  * Technique n°7 — Écrire du code simple[14]
  * Technique n°8 — Écrire un script de setup d'un environnement fonctionnel[15]


  * Conclusion[16]

Avant-propos

Ce journal parle de développement logiciel, de coûts, de qualité. Il est 
question de méthode et méthodologie plutôt que de technologies.

Ce journal est un peu long… vous êtes averti(e)s ;)

Introduction

Je dirige une entreprise dont l'activité principale (en terme de chiffre 
d'affaire) est de faire de la prestation de services : développements 
techniques, développements d'applications distribuées et applications web sur 
mesure.

Régulièrement les prospects et clients trouvent que les coûts sont élevés et 
cherchent à les réduire. C'est naturel.

Le métier de la prestation de service est de vendre des jours/homme. Lorsqu'on 
veut réduire les coûts, le premier réflexe est donc de réduire le nombre de 
jours/homme.

La problématique que j'essaie d'adresser ici est donc d'être capable de 
répondre à ces attentes, et si possible sans que ce soit au détriment de la 
qualité.

client : dans cet article, le client est soit un client de l'entreprise soit un
client interne — responsable produit, équipe marketing, utilisateur de l'outil 
interne, etc.

anglicismes : par avance, merci de ne pas me tenir rigueur des anglicismes 
présents dans ce journal. Le quotidien du développeur est fait d'anglicismes — 
pour le meilleur et pour le pire.

Comment réduire les temps de développement ?

La manière la plus simple de réduire les temps de développement est d'adopter 
une stratégie de développement "agressive". Cette stratégie "agressive" 
signifie avec perte de qualité.

Analogie : on pourrait faire un parallèle avec la compression d'images. Selon 
les algorithmes, la compression sera avec ou sans perte d'information. De la 
même manière, on pourra réduire les temps de développement avec ou sans perte 
de qualité — mais avec des niveaux de "compression" différents.

Réduction de temps de développement avec perte de qualité

A algoo, nous proposons 3 stratégies de développement qui essaient d'adresser 
différentes attentes en terme de temps de développement (mais également de 
qualité) :

 1.le développement de "prototype". On développe un démonstrateur, qui sera 
    jetable. Ca permet de valider un concept, faire des démos rapidement, mais 
    sans pérennité du code. Le coût en temps est réduit au maximum 
    puisqu'aucune réflexion sur la pérennité n'est menée.
 2.le développement de mvp[17]" — produit minimum viable. C'est un concept très
    prisé par les startups ; pour simplifier il s'agit d'implémenter le minimum
    de fonctionnalités dans un produit pour le rendre commercialisable. Dans ce
    type de développement, les principales problématiques de pérennité seront 
    prises en compte, mais pas les autres. On aura alors un logiciel 
    "moyennement évolutif".
 3.le développement en mode "ingénierie logicielle". C'est la stratégie de 
    développement long-terme : on fait de l'architecture, de la conception, des
    tests et de l'intégration continue — tests unitaires, test d'intégration, 
    tests fonctionnels, etc. C'est la stratégie que l'on favorise lorsqu'on 
    intervient sur un logiciel dont la durée de vie ciblée est de plusieurs 
    années.

Les stratégies 1 et 2 sont moins couteuses que la 3, mais la qualité est 
partiellement sacrifiée et le niveau de finition également en faveur d'un temps
de développement réduit (à court terme)

Réduction de temps de développement sans perte de qualité

Je vais vous présenter ici les techniques que nous mettons en oeuvre pour 
réduire les coûts de développement sans pour autant réduire la qualité (voire 
au contraire).

Quel intérêt ?

Réduire les coûts sans sacrifier la qualité est évidemment intéressant pour 
l'entreprise ou le client car :
- soit vous avez la même chose pour un coût inférieur,
- soit vous avez un périmètre fonctionnel plus étendu pour le même prix.

Du côté des développeurs, en revanche, on a parfois l'impression qu'on essaie 
de brader les compétences.

En réalité il ne s'agit pas de brader des compétences mais :

  * d'industrialiser ce qui peut l'être sans surcoût,
  * d'optimiser l'exploitation des compétences de chacun, des développeurs, 
    certes, mais aussi des autres intervenants, et en particulier des clients.

Quelques techniques

Les différentes techniques présentées ci-dessous ne sont pas exhaustives et ne 
demandent qu'à être complétées via vos commentaires. Elles ne sont pas non plus
systématiques car chacune s'appliquera plus ou moins bien selon le projet, 
l'état d'avancement, les interlocuteurs — client interne, externe, client 
compétent techniquement, béotien, etc.

Technique n°1 — ne pas prendre de décision à la place du client

Objectif : éviter à tout prix de redévelopper une fonctionnalité non conforme 
aux attentes

Quand on développe un logiciel, on le fait toujours pour un client. Ce client 
peut être externe — c'est le cas classique d'un prestataire de service, interne
- on développe un logiciel à partir des demandes du marketing ou du responsable
produit, ou encore "perso" (on développe un outil dont on a soi-même besoin).

Les cahiers des charges et spécifications ne sont jamais complets. Il y a alors
systématiquement des sujets à trancher.

Les développeurs ont besoin de réponses pour avancer, mais ils n'ont pas la 
connaissance pour décider. Je parle bien de connaissance, pas de compétence : 
seul le client sait ce qu'il veut, et si le développeur sait ce dont le client 
a réellement besoin (cas typique : un client qui ne connait rien au métier du 
logiciel), il n'est toutefois pas en mesure de trancher seul.

Pourquoi ? Parce que la solution "idéale" qu'il aura implémentée ne sera pas 
celle que le client veut donc probablement que le client ne sera pas satisfait 
(ce qui n'est pas bon), ou alors dans le pire des cas il va vouloir que le 
boulot soit refait, ce qui implique "re-développement", et dans tous les cas 
négociations, qui peuvent être coûteuses en temps (donc couteuses tout court, 
et pas la partie la plus agréable).

Exemple : un cahier des charges qui indiquerait "l'utilisateur doit pouvoir 
être notifié".

  * Est-ce qu'on veut des notifications par email ? des notifications sur sa 
    webapp ? par SMS ? Toutes ?
  * Est-ce que l'utilisateur choisi lui-même quand et comment il est notifié ?

Pour caricaturer, dans ce cas de figure, un développeur va avoir 2 réflexes :
- soit il va aller droit au but - avec un système de notifications tel qu'il 
l'imagine, lui,
- soit il va concevoir et implémenter un système complètement flexible.

Dans le premier cas, la solution ne sera pas évolutive, et s'il est parti sur 
de mauvaises bases, c'est potentiellement tout son travail qui sera à refaire, 
sans compter la frustration d'avoir "travaillé pour rien" ou "de ne pas être 
écouté".

Dans le second cas, on aura une solution "over-engineered" — de la surqualité, 
qui a un coût.

La solution : au moindre choix, il faut demander au client. Et idéalement au 
client final. On a vite fait de "savoir" lorsqu'on est chef de projet ou 
responsable produit, mais la réalité est la même : c'est le client qui sait.

Technique n°2 — se limiter strictement au cahier des charges

Objectif : développer uniquement ce qui est commandé.

Lorsque le client a rédigé son cahier des charges, il a pris le temps de 
réfléchir à ce dont il avait besoin. Idéalement, il fournira en plus du cahier 
des charge, un cahier des charges prévisionnel pour les versions suivantes, ou 
une stratégie d'évolution envisagée. Cette stratégie va permettre d'opérer des 
choix techniques et architecturaux mais ne doit en aucun cas générer de coûts 
de développement supplémentaires.

Parfois, en tant que développeur, on se dit "ah mais si je fais ça de telle 
manière, ça prend juste quelques jours de plus et ça permet d'avoir un système 
plus évolutif". Et en général plus complexe, donc plus fragile.

  * D'une part si cette flexibilité n'est exprimée nulle part il ne faut pas la
    prendre en compte — il sera temps de l'implémenter le jour où elle sera 
    vraiment demandée (si ce jour arrive),
  * d'autre part en règle général, plus un système est flexible, plus il est 
    complexe, plus il est fragile. Donc plus il nécessite de tests pour être 
    certifié "conforme".

Si une certaine flexibilité est obtenue avec quelques jours de plus, il sera 
probablement préférable soit de ne rien faire, soit de prendre ces quelques 
jours pour blinder la couverture de tests automatiques. Ainsi, le code reste 
simple et robuste, et la future évolution sera plus facile à implémenter car 
toute régression sera décelée via les tests automatiques.

Technique n°3 — réduire le périmètre fonctionnel

Objectif : développer uniquement ce qui est nécessaire.

Lorsqu'un client écrit un cahier des charges, il ne prend pas toujours le temps
de prendre le recul nécessaire pour évaluer la pertinence de ses attentes. 
Concrètement, il va indiquer dans son cahier des charges l'ensemble des 
fonctionnalités qu'il attend, mais lorsqu'on présente un chiffrage mettant en 
relief le temps de développement associé à chaque fonctionnalité, il n'est pas 
rare que l'urgence de certains points devienne toute relative.

Technique n°4 — faire du « test-driven » sur les interfaces

Objectif : (dé)terminer les spécifications de manière exhaustive.

Nous développons des applications web et des applications distribuées. Cela 
signifie en particulier la définition d'APIs (REST pour la majorité, mais pas 
nécessairement).

*Note : API signifie Application Programming Interface, il s'agit des 
interfaces qui vont permettre de piloter votre logiciel. 
*

Ces interfaces sont potentiellement très différentes du code qui est dessous, 
mais une chose est sûre : lorsqu'on entame le développement, on ne sait pas 
exactement ce qu'on doit faire.

Dans le meilleur des cas, une partie du travail consiste à "finir les 
spécifications"
(par exemple la gestion des cas d'erreur), dans le pire des cas, la tâche de 
développement consiste aussi à écrire les spécifications.

La meilleure manière de faire ce travail de spécification — en tout cas de le 
terminer, est d'écrire les cas de test. Cela permet de se concentrer sur 
l'utilisation (donc le besoin) et non sur l'implémentation (la manière de faire
les choses).

Concrètement l'idée est d'écrire tous les cas de test puis de les exécuter à 
chaque nouvelle itération du développement. D'un taux initial de 100% d'erreur,
on va finir par arrriver à 100% OK une fois que le développement est terminé.

Ca n'aura pas coûté plus cher et le gain sera double :

  * une conception adaptée au besoin,
  * un refactoring simplifié car spécification complète (via les cas de test) 
    et 100% testée

Dans l'hypothèse où l'on aurait commencé par le développement, puis écrit les 
tests, outre le fait qu'on prend le taureau par les cornes (on écrit la 
solution avant d'avoir écrit les spécifications), le risque est d'avoir conçu 
une solution qui ne va pas répondre à 100% des besoins, ce qui veut dire qu'on 
doit faire du refactoring ou qu'on accepte une couverture partielle des 
besoins.

Faire du "test driven" sur les interfaces est une bonne chose, mais sur le 
reste ? Pas forcément. 

Les meilleures spécifications qu'on pourra obtenir/définir seront toujours sur 
les interfaces. Dans ce contexte écrire les tests revient simplement à 
transcrire les spécifications en code.

Lorsqu'on travaille sur le développement (et la conception) interne(s), on a 
rarement des spécifications, on va plus (+) avancer à tâtons, ce qui correspond
à la technique suivante.

Technique n°5 — Faire de la conception progressive sur les composants internes

Objectif : avoir quelque chose de fonctionnel le plus rapidement possible. 

Lorsqu'on conçoit un nouveau module logiciel, on sait rarement à l'avance 
exactement comment il doit être conçu et comment il va se comporter.

En général, on découvre la réelle complexité progressivement parce qu'on pense 
au fur et à mesure à l'ensemble des cas de figure. Ce qui semblait initialement
simple devient (très) compliqué ; du coup la question de « refactoriser » le 
code devient récurrent, etc, etc.

L'idée ici est donc d'avoir au plus vite quelque chose qui fonctionne, et 
seulement ensuite de se poser la question de comment concevoir le module pour 
découper la complexité.

On assimile le métier via une première version "brouillon" puis on modélise ce 
métier proprement.

Cela signifie en gros :

 1.développer le composant fonctionnant dans le cas nominal en mode "proto"
 2.au cours du 1. on identifie naturellement les cas d'erreur possibles / à 
    traiter (et on les documente d'une manière ou d'une autre dans le code)
 3.on a un code fonctionnel, il est alors temps : 

 1.de rationaliser la conception,
 2.de gérer/traiter les cas d'erreur,
 3.d'écrire les tests unitaires une fois les composants clairement découpés.


On sépare les tâches d'assimilation métier et de modélisation logicielle.

Et pour la modélisation logicielle, rien de tel que la technique suivante.

Technique n°6 — Diviser pour mieux régner

Objectif : simplifier la maintenance et l'évolutivité du code.

Les développeurs n'aiment que rarement faire de la maintenance sur du code. 
Personnellement, je trouve que c'est la partie la plus passionnante du travail 
car les contraintes sont fortes.

Bien souvent les jeunes diplômés (en particulier) ne veulent pas faire de 
maintenance mais veulent concevoir un logiciel entier. Et lorsqu'il s'agit de 
reprendre du code, plutôt que le faire évoluer ils préfèrent souvent tout 
refaire de zéro.

Pourtant le code a une intelligence propre : il répond à des besoins métiers 
qui ont été accumulés en son sein, et lorsqu'on ré-écrit un logiciel 
complètement, c'est une grande partie de cette connaissance que l'on perd.

La raison pour laquelle la maintenance n'est pas sexy est que très souvent le 
code est un véritable nœud de fonctionnalités et responsabilités, et lorsqu'on 
aperçoit un bout et que l'on essaie de tirer sur la ficelle, c'est toute la 
pelote qui vient. Et plus on tire, plus les nœuds se resserrent ;)

Hors, ré-écrire un code complet est très coûteux, et carrément exorbitant si on
considère qu'on veut réécrire à périmètre fonctionnel constant. (attention : 
toutes les ré-écritures n'ont pas pour objectif d'être iso-fonctionelles)

La bonne solution pour faciliter la maintenance est de découper le code pour 
qu'il soit plus facile de le faire évoluer.

le principe est simple : "un module = une responsabilité". Et inversement.

Prenons un exemple concret : je veux faire un logiciel de gestion de tâches 
(todo). J'identifie spontanément 3 responsabilités :

 1.le stockage des informations
 2.l'intelligence du logiciel - le noyau
 3.l'interface "utilisateur" (ça peut être une API REST)

Du MVC ? Si on veut, mais c'est le niveau 1. du découpage.

Imaginons, qu'on ait développé le dit logiciel en 3 modules : une interface 
utilisateur en QT, un module "kernel" qui implémente l'intelligence et un 
module "database" qui stocke dans une base SQLite.

Un jour, mon client me demande s'il est possible de faire une interface web et 
qu'il aimerait pouvoir stocker dans une base de données MySQL, voire MongoDB.

Il y a du travail, mais on va pouvoir découper ça relativement facilement. Par 
rapport au cas précédent, les 3 modules se décomposent désormais 7 modules (7 
responsabilités) :

 1.l'instanciation du "driver de données" (une fabrique)
 2.le driver SQLite (qui va reprendre grosso modo le code de l'ancien composant
    "stockage des informations")
 3.le driver MongoDB (qui va implémenter une interface compatible avec l'ancien
    composant "stockage des informations")
 4.le noyau du logiciel (qui va désormais manipuler des objets génériques)
 5.l'interface utilisateur QT
 6.Un composant "web" qui implémente une API REST/Json
 7.Un composant "web" frontend qui se connecte sur l'API.

Les composants 1, 2 et 3 de l'ancien logiciel sont quasiment naturellement les 
2, 4 et 5 du nouveau.

Dans cette décomposition, chaque composant a une unique responsabilité. Et 
chaque responsabilité est gérée par un composant unique.

Note : les parties en gras qui signifient "et inversement" mettent en relief 
qu'une responsabilité qui serait découpée entre plusieurs composants rend la 
maintenance et l'évolutivité aussi difficile que des composants aux 
responsabilités multiples.

L'illustration caricaturale dans mon domaine est Django, qui n'incite en aucun 
cas à faire ce type de découpage :

  * Les vues Django implémentent de l'intelligence "métier" (et donc si on veut
    ré-implémenter les même fonctionnalités via une autre interface, c'est un 
    gros travail de refactoring),
  * La validation des données va être implémentée dans des formulaires ou dans 
    les modèles eux-même voire dans des "ModelView",
  * Lorsqu'on travaille avec Django Rest Framework (la seule vraie raison 
    d'utiliser Django en 2017), les serializers risquent d'implémenter toute la
    logique — et de fait devenir le "kernel" partiel d'une application.

Bien entendu on peut exploiter Django différemment, mais la pratique que l'on 
constate est celle-ci.

Technique n°7 — Écrire du code simple

Objectif : simplifier la maintenance et l'évolutivité du code.

« Ce qui se conçoit bien s'énonce clairement » — Nicolas Boileay-Despréaux.

Souvent les développeurs veulent faire des choses compliquées, et exploiter les
fonctionnalités d'un langage au maximum. Mais la réalité c'est qu'un logiciel 
qui est écrit simplement fonctionne aussi bien qu'un logiciel exploitant toutes
les particularités d'un langage.

Il fonctionne aussi bien, et ça ne coûte pas plus cher à écrire.
Et il coûte beaucoup moins cher à lire, donc à comprendre, donc à maintenir et 
faire évoluer.

Parfois les arguments sont d'écrire du code concis ou du code "esthétique", 
mais la concision trop extrême devient inaccessible.

Analogie : prenons un four qui propose 2 boutons rotatifs - un pour régler la 
position et un pour régler la température. Ce four est beaucoup plus simple à 
appréhender qu'un four avec un unique bouton rotatif sur lequel on peut 
"cliquer" pour entrer dans des menus. Esthétiquement certains diront que c'est 
plus joli - c'est subjectif. Par contre ce qui n'est pas subjectif, c'est que 
c'est moins naturel à utiliser et moins ergonomique. Pour le confirmer, il 
suffit de regarder ce qui se fait dans les cuisines des restaurants : on 
industrialise la cuisine, et les choix qui sont opérés sont représentatif : du 
gaz, des boutons ronds, un bouton par fonctionnalité.

Quand vous codez, imaginez que vous êtes un chef étoilé :)

Technique n°8 — Écrire un script de setup d'un environnement fonctionnel

Objectif : monter un environnement de test en 1 minute

Dans l'esprit du Joel Test[18], il est de bon ton d'écrire un script de setup 
qui permet d'obtenir un environnement fonctionnel en une commande voire deux. 
Cela permet de très rapidement intégrer un nouveau développeur (ou un nouveau 
poste de développement), cela permet aussi très rapidement de mettre au point 
un nouvel environnement de test, totalement vierge.

Cela réduit le frein au test, et augmente donc le nombre de tests et donc le 
nombre de bugs détectés (et probablement aussi le nombre de bugs résolus).

Plus on peut tester, plus on trouve de bugs, moins la qualité diminue. Écrire 
un tel script prendra probablement quelques heures en début de projet, mais ces
heures "perdues" seront largement compensées par les heures perdues à mettre en
place des environnements, à tester ou ne pas tester car le setup est trop 
pénible.

Inutile d'en dire plus. 

Conclusion

Selon les cas et les projets, nous n'appliquons pas nécessairement toutes ces 
techniques.

Un point à prendre également en compte et qui n'apparait pas ici : derrière 
l'idée de réduire les temps de développement se cachent différents aspects. On 
y trouvera naturellement des notions de coûts — moins de jours de développement
coûtent moins cher (c'est le cas initial à l'origine de ce journal), mais on y 
trouve aussi des notions de time-to-market. Moins de temps de développement, 
c'est une arrivée sur le marché plus rapide. Ce qui génère potentiellement des 
gains différents.

Le time-to-market… c'est notamment la raison pour laquelle pas mal de startup 
fabriquent une première version en mode "mvp" voire carrément en mode "proto" :
cela permet de prendre les parts de marché initiales, quitte à complètement 
redévelopper le produit par la suite — une fois que le marché a été pris, la 
concurrence devient difficile, la startup est devenue rentable et les dépenses 
de R&D nécessaires à la construction d'un système propre et robuste deviennent 
envisageables.
Télécharger ce contenu au format Epub[19]

Lire les commentaires[20]

Liens:
[1]: http://linuxfr.org/journaux.atom#avant-propos (lien)
[2]: http://linuxfr.org/journaux.atom#introduction (lien)
[3]: http://linuxfr.org/journaux.atom#comment-r%C3%A9duire-les-temps-de-d%C3%A9veloppement (lien)
[4]: http://linuxfr.org/journaux.atom#r%C3%A9duction-de-temps-de-d%C3%A9veloppement-avec-perte-de-qualit%C3%A9 (lien)
[5]: http://linuxfr.org/journaux.atom#r%C3%A9duction-de-temps-de-d%C3%A9veloppement-sans-perte-de-qualit%C3%A9 (lien)
[6]: http://linuxfr.org/journaux.atom#quel-int%C3%A9r%C3%AAt (lien)
[7]: http://linuxfr.org/journaux.atom#quelques-techniques (lien)
[8]: http://linuxfr.org/journaux.atom#technique-n1--ne-pas-prendre-de-d%C3%A9cision-%C3%A0-la-place-du-client (lien)
[9]: http://linuxfr.org/journaux.atom#technique-n2--se-limiter-strictement-au-cahier-des-charges (lien)
[10]: http://linuxfr.org/journaux.atom#technique-n3--r%C3%A9duire-le-p%C3%A9rim%C3%A8tre-fonctionnel (lien)
[11]: http://linuxfr.org/journaux.atom#technique-n4--faire-du-test-driven-sur-les-interfaces (lien)
[12]: http://linuxfr.org/journaux.atom#technique-n5--faire-de-la-conception-progressive-sur-les-composants-internes (lien)
[13]: http://linuxfr.org/journaux.atom#technique-n6--diviser-pour-mieux-r%C3%A9gner (lien)
[14]: http://linuxfr.org/journaux.atom#technique-n7--%C3%89crire-du-code-simple (lien)
[15]: http://linuxfr.org/journaux.atom#technique-n8--%C3%89crire-un-script-de-setup-dun-environnement-fonctionnel (lien)
[16]: http://linuxfr.org/journaux.atom#conclusion (lien)
[17]: https://fr.wikipedia.org/wiki/Produit_minimum_viable (lien)
[18]: https://www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/ (lien)
[19]: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite.epub (lien)
[20]: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite#comments (lien)