Eu mantenho uma máquina de conteúdo que publica nas minhas redes sem mim. É simples de descrever: uma fila no Google Drive, um publicador em Python disparado a cada 15 minutos por um agente do sistema, travas de anti-duplicação e um pré-voo que confere o container antes de cada post.

Ela rodou sozinha de 9 a 13 de novembro. Hoje de manhã fui olhar e achei duas falhas que nenhum teste de unidade pegaria, porque nenhuma das duas estava no código.

Falha 1: o agendador apontava para a cópia errada

O launchd chamava o script por caminho absoluto:

<key>ProgramArguments</key> <array> <string>/usr/bin/python3</string> <string>/Users/eu/Projetos/<b>repo-antigo</b>/scripts/maquina_publicador.py</string> </array> 

O arquivo existia. O agendador rodava feliz. Só que a versão viva do publicador tinha migrado para outro repositório semanas antes, e lá já havia suporte a três contas em vez de uma. O agendador executava, com sucesso, um programa obsoleto.

Nenhum erro. Nenhum alarme. O sistema estava "funcionando".

Falha 2: uma trava de segurança derrubando o que deveria proteger

O publicador tem uma trava sensata: só publique se o site da marca estiver no ar. A implementação:

with urllib.request.urlopen(req, timeout=20) as r: if r.status == 200: return True, None return False, f"HTTP {r.status}" except urllib.error.HTTPError as exc: return False, f"HTTP {exc.code} ({exc.reason})" 

Todo dia, uma das contas era adiada com "site indisponível". O site estava no ar o tempo inteiro. Ele só respondia 308 Permanent Redirect para a versão com www, e o HTTPRedirectHandler padrão do urllib trata 301, 302, 303 e 307 — não trata 308. O redirecionamento virava HTTPError, e a trava lia aquilo como "fora do ar".

A correção é de três linhas:

except urllib.error.HTTPError as exc: # redirect é servidor no ar, só mudando de endereço if exc.code in (301, 302, 303, 307, 308): return True, None return False, f"HTTP {exc.code} ({exc.reason})" 

O padrão por trás das duas

As duas falhas são do mesmo tipo: o código estava correto e o ambiente não era o que eu imaginava. Esse é o erro mais caro em automação, porque ele falha em silêncio. Nada quebra, nada grita, só não acontece. E "não aconteceu" é indistinguível de "não havia nada para fazer".

Três coisas mudaram no meu processo depois disso:

  1. Toda trava precisa dizer o que viu, não só que falhou. "Indisponível" não é diagnóstico. "HTTP 308 para o www" é. A mensagem de erro que eu já tinha escrito foi o que me permitiu resolver em quinze minutos.

  2. Agendador aponta para caminho canônico, versionado junto do código. Nunca para uma cópia. Se o caminho pode apodrecer, ele vai apodrecer.

  3. O que roda sozinho precisa avisar quando não roda. Um job que só fala quando tem sucesso é um job que você descobre quebrado por acaso, dias depois.

Levei quinze minutos para consertar e cinco dias para perceber. Essa é a conta real da automação, e ninguém mostra essa parte.