- Les équipes d’exploitation conservent des procédures manuelles (toil) difficiles à éliminer complètement, comme les changements d’infrastructure ou le provisionnement de comptes ; à mesure que l’entreprise grandit, les étapes et les exceptions se multiplient
- Même si chaque étape semble automatisable, n’en scripter qu’une partie augmente le nombre d’outils à usage unique, tandis que les utilisateurs doivent toujours suivre de longues procédures documentées
- Un script do-nothing n’exécute pas réellement les tâches automatiquement : il encapsule chaque étape de la procédure dans une fonction, puis guide l’utilisateur étape par étape
- L’utilisateur risque moins de perdre le fil ou de sauter une étape, et le développeur peut plus facilement remplacer plus tard le texte d’instruction d’une étape donnée par du vrai code d’automatisation
- Cela ne réduit pas immédiatement la charge de travail manuel, mais abaisse le coût de démarrage de l’automatisation, ce qui permet d’éliminer progressivement le toil au fil du temps
Quand les procédures manuelles deviennent pénibles
- Toutes les équipes d’exploitation ont encore des procédures manuelles qui n’ont pas été automatisées, et il est difficile de faire disparaître totalement le toil
- Dans une entreprise en croissance, des procédures comme les changements d’infrastructure ou le provisionnement de comptes utilisateurs deviennent facilement un foyer important de toil
- Une procédure de provisionnement de compte utilisateur peut comporter plusieurs étapes comme celles-ci
- Générer la paire de clés SSH de l’utilisateur
- Committer la clé publique dans Git et la pousser sur master
- Attendre la fin du job de build
- Vérifier l’adresse e-mail de l’utilisateur dans l’annuaire des employés
- Transmettre la clé privée à l’utilisateur via 1Password
- Dans un environnement réel, la procédure peut atteindre 20 étapes, ou nécessiter de suivre en continu des embranchements et des cas particuliers pendant son exécution
- Ce type de tâche demande beaucoup de concentration, mais relève davantage d’une case supplémentaire à cocher que de la résolution d’un problème intéressant, ce qui en fait une corvée laborieuse
Les angles morts de l’automatisation partielle
- Une corvée laborieuse semble être une bonne candidate à l’automatisation
- On imagine facilement comment automatiser chaque étape
- Un ordinateur peut exécuter des instructions plus vite et plus précisément qu’un humain
- Le risque de dérive pratique (practical drift) est aussi plus faible
- Le problème est que l’automatisation de ce type de corvée donne souvent l’impression d’être tout ou rien
- On peut créer un script qui traite seulement l’étape 2 ou l’étape 5, mais cela ne réduit pas beaucoup la lourdeur de l’ensemble de la procédure
- Quand les scripts à usage unique se multiplient, chaque outil a ses propres conventions et comportements attendus, et l’utilisateur doit toujours suivre une documentation en plusieurs étapes
Comment fonctionne un script do-nothing
- Presque toute corvée laborieuse peut être transformée en script do-nothing
- L’idée centrale consiste à placer les instructions de la corvée dans le code et à encapsuler chaque étape dans une fonction
- Le flux d’un script d’exemple est le suivant
CreateSSHKeypairStepaffiche la commandessh-keygenet attend que l’utilisateur appuie sur EntréeGitCommitStepindique de copier la clé publique dans le dépôt Git, puis d’exécutergit commitetgit pushWaitForBuildStepindique d’attendre la fin sur l’URL du job de buildRetrieveUserEmailStepdemande de trouver l’adresse e-mail dans l’annuaire, de la saisir, puis l’enregistre danscontext["email"]SendPrivateKeyStepindique de créer dans 1Password un document contenant la clé privée et de le partager avec l’utilisateur correspondant à cette adresse e-mail
- Ce script n’exécute réellement aucune étape de la procédure : il donne des instructions à l’utilisateur une par une, puis attend qu’il les marque comme terminées manuellement
Le chemin vers l’automatisation
- À première vue, un script do-nothing peut sembler rendre la documentation plus difficile à lire, mais en pratique il sécurise mieux le flux de travail
- L’utilisateur a moins de chances de perdre sa position ou de sauter une étape
- Il devient plus facile de rester concentré et de mener la corvée jusqu’au bout
- Chaque étape étant séparée dans une fonction, le texte d’une étape donnée peut être remplacé par du code qui effectue réellement l’action
- Au fil du temps, une bibliothèque d’étapes réutilisables apparaît, ce qui rend les automatisations ultérieures plus efficaces
- Un script do-nothing ne réduit pas en lui-même la charge de travail manuel de l’équipe
- Son intérêt est de créer un état dans lequel il devient plus facile de commencer à automatiser ; l’équipe peut alors s’en servir pour éliminer le toil au fil du temps
1 commentaires
Avis sur Hacker News
J’aime beaucoup cette approche
Globalement, c’est une autre façon de définir une interface autour d’un processus. Ce processus peut être manuel ou automatisé, mais l’interface peut rester la même. C’est donc assez puissant quand on automatise des étapes
Il suffit de l’appliquer comme on le ferait à n’importe quel autre système
Par le passé, j’ai rempli manuellement des Google Sheets avant d’automatiser ça avec un script, et j’ai aussi fait en sorte que la création d’un ticket Jira soit automatiquement récupérée puis traitée. On peut démarrer plus vite, automatiser seulement les parties les plus pénibles, et il n’est pas nécessaire de tout automatiser
Un effet secondaire d’un script qui ne fait rien, c’est qu’il a plus de chances d’être réellement utilisé qu’une documentation, et donc d’être tenu à jour plus souvent
Je suis pour l’idée d’avoir une liste de scripts qui automatisent les tâches courantes. Mais dès que ces scripts commencent à être appelés par des services en production, cela crée une énorme dette technique pour la malheureuse personne qui devra démêler ce réseau de spaghetti
Plus encore, comme un processus centré sur l’humain a fini par codifier le système, il devient impossible d’apporter des changements favorables au système logiciel, par exemple le découper en composants. Au final, l’extension progressive consiste à pousser toujours plus de choses dans le processus, ce qui alimente un cycle auto-proliférant qui continue de s’ajouter au monolithe de scripts
Pendant les étapes 3 et 4, faire utiliser le processus de manière répétée par autant de personnes que possible, et l’ajuster en fonction des mauvais usages observés sur le terrain
4) automatiser le processus
Tout le monde est d’accord avec 1 et 2, la plupart finissent aussi par suivre sur 4, mais faire 3 amène davantage de gens à demander et à comprendre 4
La limite de cette approche, toutefois, c’est qu’elle convient si l’on peut automatiser le début ou la fin du processus, mais que s’il y a deux points d’automatisation isolés comme des îlots au milieu, ce n’est pas mieux qu’un outil en ligne de commande avec des invites
Il suffit d’expliquer ce qu’il faut faire, et de fournir du boilerplate, des exemples ou des exécutables paramétrés
Utiliser les notebooks comme un système de documentation de fait serait très chouette. Mais c’est trop lourd pour être ajouté comme couche au-dessus d’un SaaS cloud comme Confluence, et dans sa forme actuelle il y a aussi trop de possibilités de problèmes comme l’élévation de privilèges
Cette approche transforme une page d’instructions Confluence en walkthrough à moitié interactif. C’est le côté conscient
L’autre côté, c’est l’automatisation des tests. Au début, cela part d’un tas de XPath surajustés et d’étapes non documentées. Chaque fois que l’application change, on redécouvre ces étapes. C’est le côté inconscient
Espérons qu’on atteigne le sommet, avec quelque chose qui s’exécute rapidement, tout en sachant encore comment on en est arrivé là et pourquoi
Il suffit de demander confirmation à l’utilisateur avec
Execute command (y/N)?Je déteste vraiment copier-coller d’un terminal à un autre. Ça consomme aussi beaucoup d’attention
On ne devrait demander à l’utilisateur que les tâches manuelles qui ne sont pas encore des commandes :
Look up the e-mail address for foo. Paste it here:ouPut that shit in 1Password: Are you done (y/N)?Approche intéressante
Mais la tâche donnée en exemple ne fait que montrer à quel point la méthode de fourniture des clés SSH de cette entreprise était peu sûre. En réalité, il faudrait inculquer aux utilisateurs qu’ils doivent générer eux-mêmes leur clé privée, puis ne transmettre que la clé publique à l’administrateur système afin d’obtenir les droits d’accès. À aucun moment l’administrateur système ne devrait posséder une copie de la clé privée, même temporairement. L’étape 1Password ne devrait donc pas être nécessaire du tout
Pour référence, je suis l’auteur de github-keygen, un outil qui automatise la génération d’une clé SSH dédiée à l’accès GitHub ainsi que la configuration SSH pour ce contexte
https://github.com/dolmen/github-keygen
l’utilisateur doit générer lui-même sa clé privée et ne transmettre que la clé publique à l’administrateur systèmea toujours été pénible à implémenterNous sommes passés, pour SSH, à une authentification basée sur certificats, et nous ne déplaçons plus de clés publiques partout. Tout le processus est devenu vraiment plus simple
Ce n’est qu’environ un an après la publication initiale de cet article que j’ai fini par essayer cette approche.
À cause d’un bug dans notre chaîne d’outils, le runbook des hotfixes était à peu près deux fois plus complexe que le processus de release normal.
Ça n’a pas vraiment été reconnu à sa juste valeur, mais ce qu’on n’utilisait autrefois qu’une fois toutes les dix semaines environ, pour des incidents sev 1 ou des epics en phase finale, a fini par servir en moyenne une fois par semaine, et jusqu’à trois fois certaines semaines. Comme il n’était plus nécessaire de tout transformer en feature toggle, on a pu creuser beaucoup plus profondément dans la dette technique.
Dans une petite entreprise, si vous pouvez facilement répliquer les données de production vers un environnement de préproduction, vous ne verrez peut-être pas ce genre de résultat. Mais nous avions plus de 150 endpoints avec lesquels nous communiquions, en moyenne environ 3 par service, je dirais. Il y avait énormément de jeux de données, dont certains étaient collectés d’une manière qui ressemblait à Kafka avant l’existence de Kafka.
Une seule personne essayait de répliquer les données de production, et même elle manquait de temps et d’énergie, donc elle ne pouvait le faire qu’une ou deux fois par an. Ce rythme était bien plus lent que celui auquel les clients et les fonctionnalités évoluaient. Au final, on bricolait avec le processus de déploiement blue-green et jmeter pour comprendre si on s’en approchait, et comment mesurer le succès ou l’échec avant la mise en ligne.
En fin de compte, ce qui bloquait les gens, c’était un processus de build pénible et propice aux erreurs, et ça ne s’est débloqué qu’après que je l’ai automatisé à moitié.
Plus tard, avec l’augmentation de l’usage, j’ai recherché toutes les URL des étapes manuelles pour les mettre dans une table de correspondance dans l’outil, et les exposer aussi dans le processus de validation standard d’approbation des releases. Grâce à ça, le coordinateur allait un peu plus vite et était moins stressé. Le processus était tellement agaçant que trois équipes se relayaient pour se partager la charge.
Quand on dit « réduire l’énergie d’activation de l’automatisation des tâches », cela veut-il dire qu’un script qui ne fait rien finira plus tard par comporter des étapes qui automatisent réellement quelque chose ?
Vu comme un espace réservé pour une future automatisation, ça ressemble à un bon équilibre entre automatisation et efficacité. On peut faire un premier essai sans trop investir, et laisser des fruits faciles à cueillir pour plus tard, quand l’effort aura une valeur plus évidente.
Chaque étape de la procédure est désormais encapsulée dans une fonction, donc on peut remplacer le texte d’une étape donnée par du code qui effectue automatiquement l’action réelle.
class Foo(object): def run(self, context): ...Un objet avec une seule méthode d’exécution existe déjà en Python. Ça s’appelle une fonction.
def foo(context): ...Avec l’approche par fonctions, si une nouvelle fonction est nécessaire, on l’ajoute au niveau global. Pour une ou deux, c’est parfaitement acceptable, mais s’il y en a davantage, on se retrouve avec une pile de fonctions au même niveau, et les dépendances entre elles ne sont plus évidentes.
Dans l’une d’elles, il montre des exemples de simplification de code inutilement abstrait.
Excellent, mais impossible à interrompre.
Ce serait bien d’afficher toutes les étapes à l’avance, puis de cocher chaque élément au fur et à mesure. Parfois, il vaut mieux se préparer réellement avec une vue d’ensemble.
On pourrait aussi écrire un résumé dans un fichier de log.
Il y a tellement de choses qu’on peut améliorer que la solution la plus simple est peut-être justement la meilleure.
Cela dit, un script shell qui ne fait rien est tellement facile à commencer qu’il est difficile de ne pas aller au bout. Cet effort serait peut-être mieux investi dans l’automatisation d’une seule étape. On peut vite tomber dans le marécage amusant mais peu productif du choix de la bibliothèque TUI à utiliser et de la manière de structurer le tout.
Chaque étape est une règle nommée
*.done, et crée un fichier.doneune fois terminée. On peut interrompre à tout moment, modifier le script pour corriger quelque chose, puis reprendre avecmake.Mais écrire ce Makefile est vraiment pénible. Existe-t-il une meilleure solution ?
./do-the-thing.sh 2025, puis je crée un répertoire 2025 pour conserver l’état indiquant où on en est.Quand on valide la première étape, on peut faire un
touchdu fichier2025/first-step. Si le script plante ou est interrompu puis relancé, il vérifie ce fichier et saute la première étape.Quand quelque chose change et que l’automatisation ne fonctionne plus, c’est utile de pouvoir sortir sans perdre l’état, corriger le script puis le relancer.
En général, je fais en sorte que le script n’indique que l’étape manuelle suivante, puis se termine. Comme ça, le terminal reste disponible pour autre chose. L’historique des commandes permet de relancer facilement le script.
Il y a aussi eu des discussions précédentes. Beaucoup de commentaires.
https://news.ycombinator.com/item?id=29083367 - il y a 3 ans, 230 commentaires
https://news.ycombinator.com/item?id=20495739 - il y a 6 ans, 124 commentaires
Je ne peux pas exagérer à quel point j’aime cette approche.
Je l’ai appliquée avec succès à plusieurs projets. Mon exemple préféré est celui d’un robot chirurgical à 30 millions de dollars qui échouait aux tests en laboratoire à cause du « facteur humain ».
Je travaille dans un autre domaine, la pratique juridique, mais j’aimerais réfléchir à la manière dont cette approche pourrait s’appliquer dans notre entreprise.
J’aime cette approche. J’apprécie déjà de faire quelque chose de similaire dans des systèmes fondés sur des langages de programmation ayant un certain niveau de complexité. Du côté de la programmation fonctionnelle, il me semble qu’on appelle cela des trous (holes).
L’erreur
not implementedd’une interface suit une logique similaire, mais je pense qu’il y a une vraie valeur à écrire, pour chacune de plusieurs pièces interdépendantes, un petit élément trivial qui produit une sortie valide même si elle n’a pas de sens. Cela accélère la création de ces pièces, et rend beaucoup plus probable le fait de les tester et de les construire une par une. On réduit le besoin d’écrire plusieurs parties d’un coup avant de pouvoir commencer à tester.Dans le contexte des scripts, comme dans le cas d’usage traité dans l’article, cette validité au niveau des types peut parfois difficilement devenir importante. Car, du point de vue fonctionnel, une grande partie des effets de la ligne de commande relèveraient des effets de bord. Mais comme l’exécution se fait de manière séquentielle, l’impact est bien moindre, et grâce aux invites d’attente, on peut préserver l’ordre des opérations tout en conservant, à faible coût, les étapes manuelles qu’il aurait de toute façon fallu effectuer même sans script.
L’échafaudage est imparfait, mais fondamentalement utile.
En théorie, c’est bien, mais en pratique cela me semble difficile.
Si une équipe d’exploitation répète la même tâche, et voit qu’un script qui ne fait rien ne fait vraiment rien, dès qu’elle pense avoir mémorisé les étapes, ou qu’elle estime que le faire à la main est plus rapide ou plus intéressant, elle cessera vite de l’utiliser.
J’ai écrit beaucoup d’automatisation et de documentation pour des équipes d’exploitation, mais faire en sorte que les gens les utilisent, et continuent à les utiliser, a toujours été un problème. Même les changements de documentation devaient être annoncés. Dès que les gens savent faire quelque chose, ils arrêtent vite de lire la documentation.
Dans un monde parfait, cette approche est tout à fait pertinente, et je pourrais aussi l’utiliser pour mes tâches personnelles. Mais la réalité est rarement parfaite. Pour ma part, je n’emploierais probablement cette méthode que si 90 % était automatisé et qu’il ne restait qu’une étape non résolue. Même dans ce cas, une partie de l’équipe d’exploitation pourrait sauter cette étape manuelle et supposer que l’ensemble est une magie entièrement automatisée.