Cover do episódio 108: O que aprendi tentando automatizar uma tarefa que eu odiava
#10821 de outubro, 20193 min leituraCódigo na PráticaS3 · 2018–2019

O que aprendi tentando automatizar uma tarefa que eu odiava

Tinha uma tarefa manual que tomava 40 minutos por dia. Passei uma semana automatizando. O script levava 2 minutos mas levou 3 meses para ficou estável o suficiente para confiar.

AutomaçãoScriptsProdutividadePython

Toda manhã eu gerava um relatório manual.

Exportava dados do CRM, colava numa planilha, calculava alguns totais, formatava, mandava por email para o cliente. 40 minutos. Todo dia.

Em outubro de 2019 automatizei isso. Levei uma semana. O script rodava em 2 minutos.

E nos três meses seguintes, o script quebrou umas oito vezes.


A automação estava certa. O problema era que eu havia automatizado o caminho feliz mas não os casos de exceção.

O script quebrava quando:

  • O CRM retornava mais de 1000 registros (a API tinha paginação que eu não tinha implementado)
  • Um campo vinha como null em vez de string vazia (meu código assumia string)
  • A API ficava fora do ar por 30 segundos (sem retry, o script falhava silenciosamente)
  • O email do destinatário tinha mudado (hardcoded no script)

Cada quebra eu corrigia. Depois de três meses o script era estável. Mas nesses três meses, toda vez que quebrava, eu precisava detectar (às vezes o cliente reportava antes de mim), entender o que tinha acontecido, corrigir, e explicar.


A lição que ficou: automatizar não é escrever código que funciona uma vez. É escrever código que funciona de forma confiável, mesmo quando as entradas são inesperadas, mesmo quando serviços externos falham, mesmo quando alguém muda o dado no CRM.

# a versão ingênua
data = api.get_leads()
report = generate_report(data)
send_email(report, "[email protected]")

# o que precisei implementar
def fetch_with_pagination(api, page_size=100):
    page = 1
    all_data = []
    while True:
        response = api.get_leads(page=page, size=page_size)
        if not response.data:
            break
        all_data.extend(response.data)
        page += 1
    return all_data

def fetch_with_retry(fn, max_attempts=3, delay=5):
    for attempt in range(max_attempts):
        try:
            return fn()
        except ApiUnavailable:
            if attempt < max_attempts - 1:
                time.sleep(delay * (attempt + 1))
    raise RuntimeError("API unavailable after retries")

Não é código complexo. É código que considera o que pode dar errado.


A regra prática que aprendi:

Quando você automatiza algo, você está construindo um contrato implícito com os usuários (nesse caso, o cliente e eu mesmo): "isso vai acontecer de forma confiável toda vez."

Contrato implícito que quebra com frequência é pior do que não ter automação nenhuma. Pelo menos manualmente você sabe se funcionou.

Automação boa tem três partes: o caminho feliz funcionando, tratamento de falha com mensagem clara, e alguma forma de você saber quando falhou antes do usuário. A terceira foi a que mais me custou aprender. Um script que falha silenciosamente e você só descobre quando o cliente pergunta "onde está o relatório de hoje?" — é automação que criou mais problema do que resolveu.