Oh mille mercis ! Je l'avais lu il y a quelques années sans pouvoir le retrouver.
Je me permets de copier-coller la traduction ci-dessous :
Synthèse des conseils de sabotage des organisations, dans un manuel de l'OSS de 1944. A priori c'est authentique
C'est malheureux, nous allons tous nous mettre à considérer plein de collaborateurs ou de supérieurs hiérarchiques persuadés de leur efficacité comme autant de saboteurs infiltrés. Personnellement, je trouve que ça explique tellement de chose dans notre organisation... ^^
En 1944, l’OSS (ancêtre de la CIA) proposait un petit manuel de sabotage des organisations à la portée de tous. J’ai essayé de synthétiser ça en 10 points avec mes propres mots :
1/ Insistez systématiquement pour que les procédures en place soient respectées et suivies à la lettre. Ne laissez jamais personne prendre une initiative qui pourrait accélérer le processus.
2/ Soyez verbeux, à l’oral comme à l’écrit. Multipliez les références et les anecdotes, attirez l’attention sur des problèmes secondaires et abstenez-vous de résumer votre avis ou vos recommandations.
3/ Remettez toujours la légitimité de votre instance de décision en question : faites valider chacune de vos décisions par la hiérarchie et multipliez les comités consultatifs aussi larges que possible.
4/ Montrez-vous extrêmement prudent sur tous les sujets, insistez sur le fait que toute erreur pourrait avoir de graves conséquences et invitez régulièrement le groupe à reconsidérer des questions déjà tranchées.
5/ Convoquez fréquemment des réunions, multipliez les participants (au motif que leur avis pourrait être utile) et insistez pour que la réunion se fasse en présentiel pour que tout le monde perde autant de temps que possible.
6/ Récompensez les meilleurs opérationnels en leur offrant (ou en appuyant leur candidature pour) des postes prestigieux mais pour lesquels ils ne sont pas compétents et qui les éloignent le plus possible du terrain.
7/ Montrez-vous aussi pointilleux que possible sur les questions de vocabulaire mais insistez sur le fait que les délais impartis ne permettent pas d’organiser des réunions spécifiquement dédiées à ce problème.
8/ Privilégiez systématiquement les échanges écrits, même pour les choses les plus anodynes, et demandez toujours (par écrit) le plus de précisions possibles avant de commencer à travailler effectivement.
9/ Assignez toujours les tâches critiques (i.e. potentiellement bloquantes) à des collaborateurs peu compétents ou lents et exigez que leur travail soit parfait avant de passer à l’étape suivante.
10/ À chaque dysfonctionnement, organisez une réunion (cf. point 5) afin d’établir une nouvelle procédure (cf. point 1) et discutez-en par écrit (cf. point 8) avec votre hiérarchie (cf. point 3).
À proposer en dojo !
Je cite cette section car elle résume à elle seule l'intégralité du propos :
Tutorials
A tutorial:
- is learning-oriented
- allows the newcomer to get started
- is a lesson
Analogy: teaching a small child how to cook
How-to guides
A how-to guide:
- is goal-oriented
- shows how to solve a specific problem
- is a series of steps
Analogy: a recipe in a cookery book
Explanation
An explanation:
- is understanding-oriented
- explains
- provides background and context
Analogy: an article on culinary social history
Reference
A reference guide:
- is information-oriented
- describes the machinery
- is accurate and complete
Analogy: a reference encyclopaedia article
Via Tiger 222.
Un résumé ultra-condensé :
- Abandonner les PowerPoints.
- Abandonner les séances de "brainstorming" à plusieurs.
- Tout écrire seul sur des mémos de 4 pages maxi.
- Structurer / Imposer un format pour les mémos.
- Accepter la critique écrite des mémos.
- Intégrer les remarques dans le mémos mis à jour.
- Arrêter l'oral et éviter de se répéter.
@Chlouchloutte je te prends ça... Merci :D
Pour Doudou, afin que tu organises mieux tes journées chargées 😃
Aparté
Après plusieurs jours de recherche et quelques semaines de remise en question, je peux enfin mettre à plat ce que j'ai compris du clean code et surtout de ce que devrait être des TU écrits en TDD pour un service RESTful. Pour vous faire saisir l'idée dernière cela, je vais considérer que nous écrivons un serveur RESTful en Sparkjava (je n'ai pas encore migré totalement vers Javalin).
Principe de base
Selon Martin Fowler, Robert Martin et Kent Bent, nous n'avons pas forcément le lien MaClasse => MaClasseTest. En réalité, nous avons une problématique à laquelle répond une interface derrière laquelle se trouve une implémentation pouvant avoir un groupe de classes. Notre classe de test va donc tester ce petit groupe de classes à travers des TU.
Pourquoi ne pas conserver le pattern UneClasse <=> UneClasseTest ?
Simplement parce qu'il corrèle nos tests à la structure et l'organisation de notre code (ie. noms et packages). De manière formelle, on dit que le code des classes des tests et le code des sources sont covariants ; c'est-à-dire que changer l'architecture de l'un, induit inévitablement un changement chez l'autre.
Alors qu'en réalité, les TU écrits en TDD s'attachent à ce qui doit être fait et non comment cela est fait. De facto, tester l'implémentation à travers une interface décroit le couplage et rend le code de nos tests contra-variant avec le code de nos sources.
Débutons par un exemple
Considérons un micro-service RESTful qui gère un carnet d'adresse. La première question à se poser est : quelle est la liste des fonctionalités qu'expose ce service ?
Nous allons en considérer que deux :
- Ajouter un contact
- Supprimer un contact
=> Pas de recherche ni modification pour cet exemple.
En BDD, nous testerions ce service REST en lui envoyant du JSON ou carrément depuis l'interface graphique via du Selenium ou du Protractor où nos uses cases auraient été rédigés en Gherkin. Dans notre exemple en TDD, nous allons tester directement les méthodes des routes Sparkjava en mockant les paramètres spark.Request et spark.Response pour qu'ils retournent les valeurs qui nous arrangent bien.
Architecture applicative
L'achitecture demeure en couche, même si une encapsulation forte (au sens Yegor Bugayenko du terme) est omniprésente.
__________________
| |
| ROUTES |
| ^ |
|--------|---------
| v |
| PERSISTENCE |
| ^ |
|--------|--------|
| v |
| BASE DE DONNÉES |
|_________________|
Tests unitaires & TDD
Avant d'écrire la route AddPerson, nous allons commencer par écrire un test qui échoue :
/*
* COPYRIGHT © ITAMETIS - TOUS DROITS RÉSERVÉS
* Pour plus d'information veuillez contacter : copyright@itametis.com
*
* -------------------------------------------------------------------
*
* Cet extrait de code est tiré des dojos de nos amis chez ITAMETIS.
* Merci de m'avoir permise de m'en service ici pour étayer mon
* propos.
*/
import org.testng.Test
import org.assertj.Assertions.assertThat
import org.mockito.Mock.mock
import org.mockito.Mock.when
import spark.Request
import spark.Response
class AddPersonTest {
private val route:Route = AddPerson()
@Test
fun `AddPerson route should be able to create a regular user in base`() {
// Given
val json = "{'name':'Wayne', 'firstName':'Bruce', 'mobile':'+33012345678' }"
val request = mock(Request::class.java)
val response = mock(Response::class.java)
when(request.body()).thenReturn(json)
// When
val result = route.handle(request, response)
// Then
assertThat(result).contains("id")
.contains("'name':'Wayne'")
.contains("'firstName':'Bruce'")
.contains("'mobile':'+33012345678'")
}
}
Implémentation de la route AddPerson
Les routes sont des objets dédiés à une préoccupation. Typiquement la route AddPerson s'écrirait de la façon suivante :
/*
* COPYRIGHT © ITAMETIS - TOUS DROITS RÉSERVÉS
* Pour plus d'information veuillez contacter : copyright@itametis.com
*
* -------------------------------------------------------------------
*
* Cet extrait de code est tiré des dojos de nos amis chez ITAMETIS.
* Merci de m'avoir permise de m'en service ici pour étayer mon
* propos.
*/
import com.jsoniter.JsonIterator
import com.jsoniter.JsonStream
import spark.Request
import spark.Response
import com.itametis.sample.dto.PersonDto
import com.itametis.sample.entity.People
import com.itametis.sample.entity.People.Person
class AddPerson:Route {
companion object {
private const val CONTENT_TYPE = "Content-Type"
private const val RESPONSE_TYPE = "application/json"
}
data class PersonDto @JvmOverload construtor(
val id:Long? = null,
val name:String = "",
val firstName:String = "",
val mobile:String = "",
)
override fun handle(request:Request, response:Response):Any {
// Récupération du DTO
val dto:PersonDto = JsonIterator.deserialize(request.body(), Person::class.java)
// Enregistrement de la personne en base
val person:Person = People.createIt(
"name", dto.getName(),
"firstName", dto.getFirstName(),
"phone", dto.getPhone()
)
// Conversion en JSON de la personne fraîchement créée en base (avec son ID)
response.header(CONTENT_TYPE, RESPONSE_TYPE)
return JsonStream.serialize(person)
}
}
Quant à la déclaration de cette route dans Sparkjava, imaginons qu'elle soit accessible depuis deux URL :
- POST:/api/person/contact
- POST:/api/contact
Nous la déclarerions de la sorte :
/*
* COPYRIGHT © ITAMETIS - TOUS DROITS RÉSERVÉS
* Pour plus d'information veuillez contacter : copyright@itametis.com
*
* -------------------------------------------------------------------
*
* Cet extrait de code est tiré des dojos de nos amis chez ITAMETIS.
* Merci de m'avoir permise de m'en service ici pour étayer mon
* propos.
*/
class Main {
companion object {
@JvmStatic
fun main(vararg params:String) {
val addRoute:Route = AddPerson()
Sparkjava.post("/api/person/contact", addRoute)
Sparkjava.post("/api/contact", addRoute)
}
}
}
Que remarque-t-on ?
Avec cette architecture nous remarquons plusieurs choses :
- Que nos routes sont adhérentes à Sparkjava et que nous pourrions encapuler les objets Request et Response dans un objet à nous afin de se découpler de Sparkjava.
- Que la couche de service a été supprimée. Le code est intégralement remonté au niveau de la route. Cela fait sens puisque le contrôleur
RESTful se substitue aux Services de JEE dès que l'on a une SPA. - Qu'il est possible de factoriser les routes en leur affectant plusieurs URL.
- Que la route est porteuse de son DTO => Autre route => Autre contrat d'interfaçage => Autre DTO. On peut tout de même s'autoriser à
faire hériter nos routes d'une route abstraite.
Améliorations possibles
On peut améliorer les routes en les décorants par d'autres routes implémentant la même interface :
/*
* COPYRIGHT © ITAMETIS - TOUS DROITS RÉSERVÉS
* Pour plus d'information veuillez contacter : copyright@itametis.com
*
* -------------------------------------------------------------------
*
* Cet extrait de code est tiré des dojos de nos amis chez ITAMETIS.
* Merci de m'avoir permise de m'en service ici pour étayer mon
* propos.
*/
class Main {
companion object {
@JvmStatic
fun main(vararg params:String) {
val addRoute:Route = LogRouteInvocation( // On log la requête quoi qu'il arrive
AuthorizationChecker( // Est-on autorisé à exécuter la route
DtoToJsonConverter( // Évite le 'return JsonStream.serialize(...)' et le 'response.header(...)'
AddPerson() // La route d'origine
)
)
)
Sparkjava.post("/api/person/contact", addRoute)
Sparkjava.post("/api/contact", addRoute)
}
}
}
Je veux que mes filles aient un Papa comme ça plus tard ! Il a l'air trooooop bien \o/
Cela fait longtemps que je me pose des questions sur Avaaz. Mais mes soupçons avaient été confirmés par cet article (car j'ai infiniment plus confiance en une association clomme Kokopelli qui produit quelque chose pour changer le monde et que l'on peut obtenir chez soi - et donc dont on mesure l'impact du travail de cette organisation -, plutôt qu'une structure dont je n'ai jamais vu le moindre impact réel comme Avaaz).
Je boucle avec cet article des Moutons Enragés : http://lesmoutonsenrages.fr/2014/07/19/ricken-patel-fondateur-davaaz-a-fait-ses-debuts-a-la-fondation-rockefeller/#more-66514