A chave da automação gradual: scripting do-nothing (2019)
(blog.danslimmon.com)- Nas equipes de operações, ainda restam procedimentos manuais (toil) difíceis de eliminar completamente, como mudanças de infraestrutura ou provisionamento de contas, e conforme a empresa cresce aumentam as etapas e exceções
- Cada etapa pode parecer automatizável, mas se apenas partes forem transformadas em scripts, cresce o número de ferramentas de propósito único e o usuário ainda precisa seguir documentos de procedimento longos
- Um script do-nothing não executa automaticamente o trabalho real; em vez disso, encapsula cada etapa do procedimento em uma função e orienta o usuário passo a passo
- O usuário tem menos chance de perder o ponto em que está ou pular etapas, e depois fica mais fácil para o desenvolvedor trocar o texto de orientação de uma etapa específica por código de automação real
- Embora não reduza o volume imediato de trabalho manual, ele diminui o custo inicial da automação, permitindo remover o toil de forma gradual ao longo do tempo
Quando procedimentos manuais viram um fardo
- Toda equipe de operações ainda tem procedimentos manuais que não conseguiu automatizar, e é difícil eliminar completamente o toil
- Em empresas em crescimento, procedimentos como mudanças de infraestrutura ou provisionamento de contas de usuário tendem a se tornar o centro de grandes cargas de toil
- Um procedimento de provisionamento de conta de usuário pode passar por várias etapas como estas
- Gerar o par de chaves SSH do usuário
- Fazer commit da chave pública no Git e dar push para a master
- Esperar a conclusão do job de build
- Confirmar o endereço de email do usuário no diretório de funcionários
- Entregar a chave privada ao usuário via 1Password
- Em ambientes reais, o procedimento pode chegar a 20 etapas, ou exigir acompanhar continuamente ramificações e casos especiais durante a execução
- Esse tipo de trabalho exige muita concentração, mas em vez de resolver problemas interessantes, se parece mais com marcar mais uma caixa na checklist, virando um trabalho arrastado
As lacunas deixadas pela automação parcial
- Esse trabalho arrastado parece um bom alvo para automação
- É fácil imaginar maneiras de automatizar cada etapa
- Computadores podem executar instruções com mais rapidez e precisão do que pessoas
- Também há menor chance de ocorrer deriva prática (practical drift)
- O problema é que automatizar esse tipo de tarefa muitas vezes parece algo de tudo ou nada
- Dá para criar scripts que cuidem só de 2 ou 5 etapas, mas isso não reduz muito o incômodo do procedimento como um todo
- À medida que aumentam os scripts de propósito único, cada ferramenta passa a ter convenções e comportamentos esperados diferentes, e o usuário ainda precisa seguir documentação com várias etapas
Como funciona o script do-nothing
- Quase todo trabalho arrastado pode ser convertido em um script do-nothing
- A ideia central é colocar as instruções da tarefa no código e encapsular cada etapa em uma função
- O fluxo do script de exemplo é o seguinte
CreateSSHKeypairStepexibe o comandossh-keygene espera até o usuário pressionar EnterGitCommitSteporienta a copiar a chave pública para o repositório Git e depois executargit commitegit pushWaitForBuildSteporienta a aguardar a conclusão no URL do job de buildRetrieveUserEmailSteporienta a localizar o endereço de email no diretório e recebê-lo como entrada, salvando-o emcontext["email"]SendPrivateKeySteporienta a criar um documento da chave privada no 1Password e compartilhá-lo com o usuário desse email
- Esse script não executa de fato nenhuma etapa do procedimento; ele apenas orienta o usuário uma etapa por vez e espera a conclusão manual
O caminho até a automação
- À primeira vista, o script do-nothing pode parecer só uma forma mais difícil de ler documentação, mas na prática ele ajuda a manter o fluxo de trabalho mais seguro
- Reduz a chance de o usuário perder em que ponto está ou pular etapas
- Fica mais fácil manter a concentração e levar a tarefa até o fim
- Como cada etapa fica separada em uma função, o texto de uma etapa específica pode ser substituído por código que execute a ação de verdade
- Com o tempo, surge uma biblioteca de etapas reutilizáveis, e os trabalhos posteriores de automação ficam mais eficientes
- O script do-nothing em si não reduz o volume de trabalho manual da equipe
- Seu efeito está em criar um estado em que começar a automatizar fica mais fácil, permitindo que a equipe elimine o toil gradualmente com o passar do tempo
1 comentários
Comentários no Hacker News
Gosto muito dessa abordagem
No geral, é outra forma de definir uma interface em torno de um processo. Esse processo pode ser manual ou automatizado, mas a interface pode permanecer a mesma. Então isso é bem poderoso na hora de automatizar etapas
Basta aplicar exatamente do mesmo jeito que você aplicaria a outros sistemas
Já fiz isso antes, preenchendo Google Sheets manualmente e depois automatizando com um script, e também fazendo com que, ao criar um ticket no Jira, ele fosse automaticamente coletado e processado. Dá para começar mais rápido, automatizar só as partes mais chatas, e nem é necessário automatizar tudo
Um efeito colateral de um script que não faz nada é que ele tem mais chance de ser realmente usado do que a documentação, então pode se manter atualizado com mais frequência
Sou a favor de ter uma lista de scripts que automatizam tarefas comuns. Mas, no momento em que esses scripts começam a ser chamados por serviços em produção, isso cria uma enorme dívida técnica para a pessoa azarada que vai ter de desembaraçar essa teia de espaguete
Indo além, como um processo centrado em pessoas acaba codificando o sistema, mudanças favoráveis ao sistema de software — por exemplo, dividir o sistema em componentes — se tornam impossíveis. No fim, o crescimento incremental vira uma forma de empurrar cada vez mais coisas para dentro do processo, e isso se torna um ciclo autorreprodutivo de continuar acrescentando coisas a um monólito de scripts
Enquanto se avança por 3 e 4, faz-se o processo iterar, com o maior número possível de pessoas usando-o, e o processo vai sendo ajustado de acordo com os usos indevidos que aparecem no mundo real
4) automatizar o processo
Todo mundo concorda com 1 e 2, e a maioria acaba concordando com 4 também, mas fazer 3 aumenta o número de pessoas que passam a pedir 4 e a entendê-lo
Só que a limitação dessa abordagem é que ela funciona bem se você puder automatizar o começo ou o fim do processo, mas, se houver dois pontos de automação isolados no meio, ela não é melhor do que uma ferramenta de linha de comando com prompts
Basta explicar o que precisa ser feito e fornecer boilerplate, exemplos ou executáveis parametrizados
Seria legal usar notebooks como um sistema de documentação de fato. Só que eles são pesados demais para serem colocados como camada sobre um SaaS em nuvem como o Confluence e, no estado atual, também há margem demais para problemas como escalonamento de privilégios
Essa abordagem transforma uma página de instruções do Confluence em um walkthrough meio interativo. Esse é o lado consciente
O outro lado é a automação de testes. Ela começa com um monte de XPath superajustado e etapas não documentadas. Cada vez que a aplicação muda, você acaba redescobrindo essas etapas. Esse é o lado inconsciente
Tomara que a gente consiga chegar ao topo e ainda assim executar tudo rapidamente, sem deixar de entender como chegamos lá e por que ficou daquele jeito
Basta confirmar com o usuário algo como
Execute command (y/N)?Odeio ter de copiar e colar de um terminal para outro. Isso também consome muita concentração
Só precisa perguntar ao usuário sobre o trabalho manual que ainda não virou comando:
Look up the e-mail address for foo. Paste it here:ouPut that shit in 1Password: Are you done (y/N)?Abordagem interessante
Mas a tarefa usada como exemplo só mostra o quão inseguro era o processo de provisionamento de chaves SSH dessa empresa. Na prática, o usuário deveria gerar a própria chave privada e entregar apenas a chave pública ao administrador do sistema para que ele conceda o acesso. Em nenhum momento o administrador deveria ter uma cópia da chave privada, nem temporariamente. Por isso, a etapa com 1Password nem deveria existir
Para constar, sou o autor do github-keygen, uma ferramenta que automatiza a geração de chaves SSH dedicadas ao acesso ao GitHub e a configuração de SSH nesse contexto
https://github.com/dolmen/github-keygen
o usuário deveria gerar a própria chave privada e entregar apenas a chave pública ao administrador do sistemasempre foi chata de implementarNós migramos do SSH para autenticação baseada em certificados, e não ficamos mais movendo chaves públicas para todo lado. O processo inteiro realmente ficou muito mais simples
Só fui tentar essa abordagem quase um ano depois de este texto ter sido publicado pela primeira vez
Por causa de bugs na nossa cadeia de ferramentas, o runbook de hotfix era cerca de duas vezes mais complexo que o processo normal de release
Não recebeu tanto reconhecimento assim, mas algo que antes era usado só em incidentes sev 1 ou em trabalho de épico na fase final, talvez uma vez a cada 10 semanas, passou a ser usado em média uma vez por semana, e em algumas semanas até 3 vezes. Como nem tudo precisava virar feature toggle, deu para atacar a dívida técnica muito mais a fundo
Se você estiver numa empresa pequena, onde é fácil copiar dados de produção para um ambiente de pré-produção, talvez não veja esse tipo de resultado. Mas nós nos comunicávamos com mais de 150 endpoints, algo como 3 por serviço em média. Havia muitos conjuntos de dados, e alguns deles eram coletados de um jeito tipo Kafka antes de existir Kafka
Só havia uma pessoa tentando replicar os dados de produção, e essa pessoa também não tinha tempo nem energia, então isso só acontecia uma ou duas vezes por ano. Esse ritmo era muito mais lento que o ritmo de mudança dos clientes e das funcionalidades. No fim, tivemos que mexer com o processo de deploy blue-green e com o jmeter para descobrir se estávamos chegando perto e como medir sucesso/falha antes de entrar em produção
No fim das contas, o que travava as pessoas era um processo de build cheio de detalhes e sujeito a erro, e isso só se resolveu depois que eu o automatizei pela metade
Mais tarde, conforme o uso aumentou, encontrei todas as URLs das etapas manuais, coloquei numa tabela de consulta dentro da ferramenta e também as expus no processo normal de validação para aprovação de release. Isso deixou o coordenador um pouco mais rápido e menos estressado. O processo era tão chato que três equipes se revezavam para dividir a carga
Quando se fala em “reduzir a energia de ativação da automação do trabalho”, isso quer dizer que um script que não faz nada acaba depois ganhando etapas que realmente automatizam alguma coisa?
Vendo como um placeholder para automações futuras, isso parece um bom equilíbrio entre automação e eficiência. Dá para fazer a primeira tentativa sem investir demais e deixar frutos fáceis ao alcance para quando, mais tarde, o esforço passar a ter um valor mais claramente justificável
Como cada etapa do procedimento agora está encapsulada em uma função, dá para substituir o texto de uma etapa específica por código que executa automaticamente a ação real
class Foo(object): def run(self, context): ...Um objeto com um único método de execução já existe embutido no Python. Isso se chama função
def foo(context): ...Na abordagem com funções, se surgir necessidade de novas funções, você vai adicioná-las no nível global. Com uma ou duas isso é totalmente ok, mas quando começam a ser mais, você acaba com um monte de funções no mesmo nível e as dependências entre elas deixam de ficar claras
Em um dos vídeos, ele mostra exemplos de simplificação de código abstraído sem necessidade
Excelente, mas não dá para interromper
Seria bom mostrar todas as etapas logo de cara e ir marcando cada item conforme avança. Às vezes é melhor se preparar de verdade com uma visão mais ampla
Também poderia deixar um log resumido em arquivo
Há tantas coisas que daria para melhorar que talvez a solução mais simples acabe sendo a melhor
Só que um script de shell que não faz nada é tão fácil de começar que fica difícil não ir até o fim. Talvez fosse melhor gastar esse esforço automatizando uma etapa. Você pode acabar caindo em pântanos divertidos, mas pouco produtivos, como decidir qual biblioteca de TUI usar ou como estruturar tudo
Cada etapa é uma regra chamada
*.donee, quando concluída, gera um arquivo.done. Dá para interromper a qualquer momento, mexer no script para corrigir algo e depois retomar commakeMas escrever esse Makefile é realmente doloroso. Será que existe uma solução melhor?
./do-the-thing.sh 2025, crio um diretório 2025 e guardo ali o estado de até onde chegueiAo aprovar a primeira etapa, você pode fazer
touchno arquivo2025/first-step. Se o script morrer ou for interrompido e depois executado de novo, ele verifica esse arquivo e pula a primeira etapaQuando algo muda e a automação deixa de funcionar, é bom poder sair, corrigir o script e executá-lo de novo sem perder o estado
Em geral, eu faço o script apenas informar a próxima etapa manual e encerrar. Assim o terminal fica livre para outras coisas. E pelo histórico de comandos fica fácil executar o script de novo
Há discussões anteriores também. Muitos comentários
https://news.ycombinator.com/item?id=29083367 - 3 anos atrás, 230 comentários
https://news.ycombinator.com/item?id=20495739 - 6 anos atrás, 124 comentários
Não dá para exagerar o quanto eu gosto dessa abordagem
Já a apliquei com sucesso em vários projetos. Meu exemplo favorito é um robô cirúrgico de 30 milhões de dólares que falhava em testes de laboratório por causa de “fatores humanos”
Eu estou em outra área, prática jurídica, mas queria pensar em como aplicar essa abordagem na minha empresa
Gosto dessa abordagem. Já gosto de fazer algo parecido em sistemas baseados em linguagens de programação com um certo nível de complexidade. No lado da programação funcional, acho que isso é chamado de buracos (holes)
O erro
not implementedem interfaces segue uma lógica parecida, mas vejo bastante valor em escrever algo trivial que produza uma saída válida, embora sem sentido, para cada uma das várias partes que dependem umas das outras. Isso acelera o processo de criar essas partes e aumenta bastante a chance de testá-las e construí-las uma de cada vez. Reduz a necessidade de escrever várias partes de uma vez antes de começar a testarComo no caso de uso tratado no texto, no contexto de scripts pode ser difícil essa validade em nível de tipos se tornar importante. Os efeitos da linha de comando, do ponto de vista funcional, provavelmente seriam em grande parte efeitos colaterais. Mas, por acontecerem de forma sequencial, o impacto é bem menor, e graças ao prompt de espera dá para preservar a ordem do trabalho mantendo, a um custo pequeno, etapas manuais que de qualquer forma teriam de ser feitas mesmo sem o script
É um apoio provisório, incompleto, mas fundamentalmente útil
Em teoria parece bom, mas na prática acho que seria difícil
Se a equipe de operações estiver repetindo a mesma tarefa e vir que um script que não faz nada realmente não faz nada, no momento em que achar que já decorou os passos, ou sentir que fazer manualmente é mais rápido ou mais interessante, vai parar de usar isso rapidamente
Já escrevi muita automação e documentação para equipes de operações, mas fazer as pessoas usarem isso e continuarem usando sempre foi um problema. Até mudanças na documentação precisavam ser anunciadas. As pessoas param de ler a documentação muito rápido depois que acham que já sabem fazer algo
Num mundo perfeito, essa abordagem faz bastante sentido, e eu mesmo talvez a usasse para coisas pessoais. Mas a realidade quase nunca é perfeita. Eu só usaria desse jeito se 90% já estivesse automatizado e ainda faltasse 1 etapa sem solução. Mesmo assim, parte da equipe de operações pode acabar pulando essa etapa manual e presumindo que tudo foi automatizado por mágica