<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: William Scussel</title>
    <description>The latest articles on DEV Community by William Scussel (@scussel).</description>
    <link>https://dev.to/scussel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3450126%2F87ee1b46-4c98-4af1-97ec-de4b698ec439.jpeg</url>
      <title>DEV Community: William Scussel</title>
      <link>https://dev.to/scussel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scussel"/>
    <language>en</language>
    <item>
      <title>A aplicação estava saudável. O negócio estava parado.</title>
      <dc:creator>William Scussel</dc:creator>
      <pubDate>Wed, 02 Sep 2026 19:24:14 +0000</pubDate>
      <link>https://dev.to/scussel/a-aplicacao-estava-saudavel-o-negocio-estava-parado-1b6f</link>
      <guid>https://dev.to/scussel/a-aplicacao-estava-saudavel-o-negocio-estava-parado-1b6f</guid>
      <description>&lt;p&gt;Uma aplicação de produção começou a devolver erros de autenticação para usuários reais.&lt;/p&gt;

&lt;p&gt;O primeiro impulso foi procurar uma queda de servidor, um processo travado ou algum problema no banco.&lt;/p&gt;

&lt;p&gt;Nada disso tinha acontecido.&lt;/p&gt;

&lt;p&gt;Os servidores estavam ativos. Os processos estavam rodando. O container continuava respondendo ao health check.&lt;/p&gt;

&lt;p&gt;Mesmo assim, a operação estava parcialmente parada.&lt;/p&gt;

&lt;h2&gt;
  
  
  O teste que parecia inofensivo
&lt;/h2&gt;

&lt;p&gt;Durante uma atividade normal de desenvolvimento, foi gerado um novo login contra um ERP externo.&lt;/p&gt;

&lt;p&gt;A integração usava tokens de sessão fornecidos por esse ERP. A aplicação mantinha esses tokens em cache para não precisar autenticar a cada chamada.&lt;/p&gt;

&lt;p&gt;O detalhe perigoso era que desenvolvimento e produção usavam a mesma credencial externa.&lt;/p&gt;

&lt;p&gt;O fornecedor não tratava cada ambiente como uma identidade isolada. Um novo login podia invalidar ou deslocar uma sessão que a produção ainda estava usando.&lt;/p&gt;

&lt;p&gt;Poucos minutos depois, a aplicação começou a receber respostas equivalentes a “login ou senha inválido” em várias operações do ERP.&lt;/p&gt;

&lt;p&gt;O erro aparecia em massa, para usuários diferentes. Isso era um sinal importante: não era um registro específico nem um problema isolado de usuário. Era a autenticação da aplicação contra a dependência externa.&lt;/p&gt;

&lt;h2&gt;
  
  
  O servidor estava saudável
&lt;/h2&gt;

&lt;p&gt;Esse tipo de incidente é perigoso porque os indicadores tradicionais continuam verdes.&lt;/p&gt;

&lt;p&gt;O processo não caiu. O container não reiniciou. O endpoint de health check respondeu. O banco continuou disponível.&lt;/p&gt;

&lt;p&gt;Mas a função principal da integração estava quebrada.&lt;/p&gt;

&lt;p&gt;Em uma ocorrência, usuários perceberam o problema rapidamente e abriram chamados. Em outra, o erro ficou silencioso por dias. Os jobs continuavam executando, mas uma etapa importante retornava zero registros. Como não havia um alerta para esse resultado de negócio, a infraestrutura continuava reportando saúde.&lt;/p&gt;

&lt;p&gt;Essa diferença merece uma definição explícita:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saúde técnica significa que o sistema está executando.&lt;/p&gt;

&lt;p&gt;Saúde operacional significa que ele está entregando o resultado esperado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uma não garante a outra.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cadeia causal
&lt;/h2&gt;

&lt;p&gt;O incidente não tinha uma única causa isolada. Ele era uma cadeia de decisões razoáveis que, combinadas, criaram um risco grande:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ambientes diferentes compartilhavam uma credencial externa.&lt;/li&gt;
&lt;li&gt;O fornecedor tinha regras de sessão concorrente que não estavam sob nosso controle.&lt;/li&gt;
&lt;li&gt;A aplicação reutilizava um token em cache e não tinha recuperação automática confiável.&lt;/li&gt;
&lt;li&gt;O token existia em mais de um lugar, como arquivo de ambiente, CI e Secret Manager.&lt;/li&gt;
&lt;li&gt;O deploy podia recriar configurações antigas.&lt;/li&gt;
&lt;li&gt;O health check verificava o processo, não a integração crítica.&lt;/li&gt;
&lt;li&gt;Os jobs não tinham alerta específico para volume de resultado inesperadamente zerado.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O primeiro incidente sugeriu que o fornecedor permitia apenas uma sessão por credencial. Testes posteriores indicaram uma hipótese mais cuidadosa: poderia existir um limite ou pool pequeno de sessões, com invalidação dos tokens mais antigos quando novas sessões eram criadas.&lt;/p&gt;

&lt;p&gt;O tamanho exato desse limite não foi provado. Para a operação, porém, o efeito era o mesmo: gerar sessões novas contra uma credencial compartilhada podia derrubar uma sessão ainda usada pela produção.&lt;/p&gt;

&lt;p&gt;Essa distinção é importante. Uma boa análise não transforma uma hipótese conveniente em fato só porque ela explica o primeiro sintoma.&lt;/p&gt;

&lt;h2&gt;
  
  
  O conserto emergencial
&lt;/h2&gt;

&lt;p&gt;A recuperação imediata foi operacional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gerar tokens novos usando credenciais de autenticação mais estáveis.&lt;/li&gt;
&lt;li&gt;Validar esses tokens contra endpoints reais antes de aplicá-los.&lt;/li&gt;
&lt;li&gt;Atualizar as configurações do ambiente legado, do CI e do ambiente em nuvem.&lt;/li&gt;
&lt;li&gt;Reiniciar os processos para que carregassem os valores novos.&lt;/li&gt;
&lt;li&gt;Observar chamadas bem-sucedidas durante um período de estabilização.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso restaurou o serviço, mas ainda era um band-aid. Trocar o valor de um segredo não remove o risco de ele ser revogado novamente.&lt;/p&gt;

&lt;p&gt;Também havia um problema de distribuição. Atualizar apenas o &lt;code&gt;.env&lt;/code&gt; do servidor não bastava se o próximo deploy recriasse o arquivo a partir de um parâmetro antigo do CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  A correção estrutural
&lt;/h2&gt;

&lt;p&gt;O desenho da correção precisava eliminar a dependência de intervenção manual:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Separar ambientes de verdade
&lt;/h3&gt;

&lt;p&gt;Desenvolvimento, homologação e produção precisam de credenciais externas diferentes sempre que o fornecedor permitir.&lt;/p&gt;

&lt;p&gt;Quando isso não for possível, a restrição deve ser tratada como um risco formal, com procedimento de diagnóstico e controles explícitos.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reautenticar automaticamente
&lt;/h3&gt;

&lt;p&gt;A aplicação não deve depender indefinidamente de um token de sessão salvo no ambiente. Quando o token expira ou é rejeitado, o sistema precisa obter outro de maneira controlada.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Compartilhar o cache entre processos
&lt;/h3&gt;

&lt;p&gt;Um cache em memória cria uma sessão diferente para cada processo. Com vários workers, cron e containers, a própria aplicação pode gerar sessões demais.&lt;/p&gt;

&lt;p&gt;Um cache compartilhado reduz esse comportamento e torna o estado da autenticação visível para todos os processos.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Coordenar a reautenticação
&lt;/h3&gt;

&lt;p&gt;Se vários processos perceberem o token inválido ao mesmo tempo, todos podem tentar fazer login. Isso cria uma nova rajada de sessões justamente no momento em que o sistema está se recuperando.&lt;/p&gt;

&lt;p&gt;Um lock distribuído permite que apenas um processo faça a reautenticação. Os demais aguardam o novo token aparecer no cache compartilhado.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Monitorar o resultado do negócio
&lt;/h3&gt;

&lt;p&gt;O health check precisa responder perguntas além de “o processo está vivo?”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A autenticação com o ERP está funcionando?&lt;/li&gt;
&lt;li&gt;A última importação trouxe registros dentro do esperado?&lt;/li&gt;
&lt;li&gt;A fila recebeu itens quando deveria?&lt;/li&gt;
&lt;li&gt;A taxa de erro da dependência externa mudou abruptamente?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nem toda checagem precisa ser uma chamada pesada em produção. Métricas de resultado, idade do último sucesso e alertas de volume anormal já capturam uma classe importante de falhas silenciosas.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que esse incidente ensinou
&lt;/h2&gt;

&lt;p&gt;O erro mais fácil de apontar seria o login manual. Ele foi o gatilho de uma ocorrência, mas não explica sozinho por que a produção era vulnerável.&lt;/p&gt;

&lt;p&gt;O risco real estava na combinação de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;credencial compartilhada;&lt;/li&gt;
&lt;li&gt;sessão externa com regras pouco conhecidas;&lt;/li&gt;
&lt;li&gt;token sem renovação automática;&lt;/li&gt;
&lt;li&gt;configuração replicada em vários lugares;&lt;/li&gt;
&lt;li&gt;monitoramento sem visão do resultado operacional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse padrão aparece em muitos contextos. Pode ser um ERP, uma API bancária, um gateway de pagamentos ou qualquer fornecedor que use sessões, tokens e limites próprios.&lt;/p&gt;

&lt;p&gt;Quando uma integração é crítica, disponibilidade não pode ser medida apenas pelo estado do servidor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist para evitar o mesmo problema
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use credenciais distintas por ambiente.&lt;/li&gt;
&lt;li&gt;Nunca gere login de desenvolvimento contra uma credencial de produção.&lt;/li&gt;
&lt;li&gt;Documente se o fornecedor limita sessões ou invalida tokens antigos.&lt;/li&gt;
&lt;li&gt;Tenha refresh ou reautenticação automática.&lt;/li&gt;
&lt;li&gt;Use cache compartilhado quando houver múltiplos processos.&lt;/li&gt;
&lt;li&gt;Coordene a recuperação com lock distribuído.&lt;/li&gt;
&lt;li&gt;Mantenha uma fonte única para secrets e parâmetros de deploy.&lt;/li&gt;
&lt;li&gt;Alimente alertas com métricas de negócio.&lt;/li&gt;
&lt;li&gt;Teste também o caminho de falha da dependência externa.&lt;/li&gt;
&lt;li&gt;Diferencie health check técnico de health check operacional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A aplicação estava saudável. O negócio estava parado.&lt;/p&gt;

&lt;p&gt;O objetivo da arquitetura e da operação é garantir que essas duas frases nunca mais possam ser verdadeiras ao mesmo tempo.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>reliability</category>
      <category>ptbr</category>
    </item>
    <item>
      <title>Parei de usar IA como chatbot. Virei ela em infraestrutura.</title>
      <dc:creator>William Scussel</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:14:03 +0000</pubDate>
      <link>https://dev.to/scussel/parei-de-usar-ia-como-chatbot-virei-ela-em-infraestrutura-44ip</link>
      <guid>https://dev.to/scussel/parei-de-usar-ia-como-chatbot-virei-ela-em-infraestrutura-44ip</guid>
      <description>&lt;p&gt;A maioria usa IA assim: abre o chat, cola a pergunta, copia a resposta, volta pro editor, repete. A IA não sabe nada do seu mundo. Toda conversa começa do zero.&lt;/p&gt;

&lt;p&gt;Há um tempo parei de fazer isso. Hoje a IA vive no meu terminal, lê meus repositórios, conhece meus clientes, roda meus scripts e abre pull request. O contexto já está lá quando eu chego.&lt;/p&gt;

&lt;p&gt;Não é sobre achar o prompt mágico perfeito. É sobre montar um &lt;strong&gt;sistema&lt;/strong&gt; em volta da IA: contexto, ferramentas e um loop que se repete. Vou mostrar como isso se parece na prática.&lt;/p&gt;

&lt;h2&gt;
  
  
  A base: meu mundo num repositório versionado
&lt;/h2&gt;

&lt;p&gt;Tudo começa com um repositório que guarda o contexto que a IA lê. Versionado, porque evolui comigo, não vive na minha cabeça nem some quando troco de máquina.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tools/
├─ AI_DEFAULT.md    # instruções centrais, toda IA lê
├─ CLAUDE.md        # aponta p/ AI_DEFAULT
├─ projects.yml     # registro: alias de cliente → repo certo
├─ TOOLS.md         # catálogo de todos os scripts
├─ RUNBOOK.md       # deploy, settings, troubleshooting
├─ brand/           # cores, fontes, logos, templates
├─ clickup/         # tasks via terminal
└─ media/           # scripts de slides e vídeo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A peça central é um arquivo de instruções que &lt;strong&gt;toda&lt;/strong&gt; IA lê antes de trabalhar: como fazer commit, convenções de branch, tom de voz, onde mora cada coisa. Escrevo a regra uma vez, ela vale pra sempre.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frente 1: tarefa chata vira script
&lt;/h2&gt;

&lt;p&gt;Toda repetição manual virou comando. E foi a própria IA que escreveu e mantém esses scripts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um resumo do meu dia (tarefas + agenda) cai no meu WhatsApp toda manhã, sozinho.&lt;/li&gt;
&lt;li&gt;Crio e consulto tasks direto do terminal, sem abrir o navegador.&lt;/li&gt;
&lt;li&gt;Scaffolds geram um projeto de infra ou uma apresentação inteira em um comando.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A regra que sigo: se fiz à mão duas vezes, a terceira é um script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frente 2: eu estendo a própria IA
&lt;/h2&gt;

&lt;p&gt;Comandos e agents customizados ensinam a IA a trabalhar do meu jeito. Os que mais uso:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;/implement&lt;/strong&gt; pega uma task, cria a branch, escreve o código e abre o PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;code-reviewer&lt;/strong&gt; é um agent que revisa o diff antes de cada commit.&lt;/li&gt;
&lt;li&gt;Comandos pequenos pro trivial: criar lembrete, resolver um alias de cliente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A IA não é uma caixa fechada. É uma plataforma que dá pra programar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frente 3: IA no código de verdade, não em brinquedo
&lt;/h2&gt;

&lt;p&gt;Isso roda em código de cliente, não em projeto de fim de semana. Por isso o fluxo tem trava:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementação de front e back &lt;strong&gt;em paralelo&lt;/strong&gt; a partir de uma task.&lt;/li&gt;
&lt;li&gt;Um gate de qualidade &lt;strong&gt;bloqueante&lt;/strong&gt;: nada entra no repo sem o agent revisar.&lt;/li&gt;
&lt;li&gt;Commits pequenos e focados, uma mudança lógica por commit.&lt;/li&gt;
&lt;li&gt;PR pronto pra review, sem rastro de "isso foi IA".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O ponto não é a IA escrever rápido. É ela escrever dentro das minhas regras.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frente 4: a IA conhece meu mundo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Planos e documentação ficam onde a IA lê e escreve.&lt;/li&gt;
&lt;li&gt;Um registro de workspace resolve "fala do cliente X" pro repositório certo na hora.&lt;/li&gt;
&lt;li&gt;Memória persistente: ela lembra decisões entre sessões.&lt;/li&gt;
&lt;li&gt;Runbooks deixam a operação documentada e reutilizável.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eu digo um apelido. Ela resolve o cliente, acha os repos certos e já sabe o que é front e o que é back. Zero explicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  O padrão por trás de tudo
&lt;/h2&gt;

&lt;p&gt;As quatro frentes parecem diferentes, mas são a mesma receita:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contexto&lt;/strong&gt;: a IA enxerga seu mundo (repos, docs, clientes, memória).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ferramentas&lt;/strong&gt;: ela executa (scripts, git, APIs). Não só fala, faz.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop&lt;/strong&gt;: faz, verifica, corrige, com gate de qualidade fechando o ciclo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Acerte os três e qualquer tarefa repetitiva vira automação. Erre um e você volta pro chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  O efeito composto
&lt;/h2&gt;

&lt;p&gt;São cerca de 5 horas por semana que voltam pra mim, dezenas de PRs abertos por um único comando, e o número cresce toda semana porque cada automação libera tempo pra construir a próxima.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como você começa hoje
&lt;/h2&gt;

&lt;p&gt;Não precisa replicar meu setup. Comece com um passo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Escolha &lt;strong&gt;uma&lt;/strong&gt; tarefa chata que você repete toda semana.&lt;/li&gt;
&lt;li&gt;Peça pra IA virar script e &lt;strong&gt;versione&lt;/strong&gt; no repositório.&lt;/li&gt;
&lt;li&gt;Dê contexto: um arquivo de instruções que ela lê sempre.&lt;/li&gt;
&lt;li&gt;Feche o loop: deixe ela rodar, verificar e corrigir.&lt;/li&gt;
&lt;li&gt;Repita. Cada automação paga a próxima.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A pergunta não é "a IA consegue?". É "o que eu paro de fazer à mão hoje?".&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
      <category>ptbr</category>
    </item>
    <item>
      <title>O bug de 16 minutos: quando 'dado faltando' é uma corrida contra o relógio</title>
      <dc:creator>William Scussel</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:51:59 +0000</pubDate>
      <link>https://dev.to/scussel/o-bug-de-16-minutos-quando-dado-faltando-e-uma-corrida-contra-o-relogio-2e6i</link>
      <guid>https://dev.to/scussel/o-bug-de-16-minutos-quando-dado-faltando-e-uma-corrida-contra-o-relogio-2e6i</guid>
      <description>&lt;h2&gt;
  
  
  "Seu sistema está perdendo dados"
&lt;/h2&gt;

&lt;p&gt;Um cliente abriu o chamado, convicto. O relatório de retorno bancário enviado por e-mail toda manhã tinha &lt;strong&gt;423 linhas&lt;/strong&gt;. O mesmo relatório, gerado sob demanda no portal, tinha &lt;strong&gt;1.351&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;928 linhas, sumidas. É o tipo de número que faz você presumir o pior.&lt;/p&gt;

&lt;p&gt;No fim, nada foi perdido. O bug tinha 16 minutos de largura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 1: comparar antes de debugar
&lt;/h2&gt;

&lt;p&gt;Nada de código ainda. Primeiro dei um diff nos dois arquivos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mesmo dia.&lt;/li&gt;
&lt;li&gt;Mesmo banco.&lt;/li&gt;
&lt;li&gt;Mesmo filtro de status.&lt;/li&gt;
&lt;li&gt;Toda linha do arquivo do e-mail estava contida no arquivo do portal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ou seja, o dado não estava &lt;em&gt;errado&lt;/em&gt;. Estava &lt;em&gt;ausente&lt;/em&gt;. Problema diferente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 2: matar hipóteses com prova, não com opinião
&lt;/h2&gt;

&lt;p&gt;Três causas plausíveis, cada uma derrubada em produção com queries read-only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Está dividido entre bancos / anexos."&lt;/strong&gt;&lt;br&gt;
Não. Todas as 1.351 linhas pertenciam ao mesmo código de banco.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"O dedup contra o dia anterior é agressivo demais."&lt;/strong&gt;&lt;br&gt;
O relatório subtrai faturas já vistas no dia anterior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;array_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoje&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'invoice'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;array_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ontem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'invoice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repliquei contra os dados de produção. Removeu &lt;strong&gt;zero&lt;/strong&gt; linhas. Não era o culpado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Os dois relatórios filtram colunas de data diferentes."&lt;/strong&gt;&lt;br&gt;
Não. Os dois filtravam a mesma coluna &lt;code&gt;transaction_created_at&lt;/code&gt;, para a mesma data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Passo 3: a mesma query, rodada agora, retorna tudo
&lt;/h2&gt;

&lt;p&gt;Foi aqui que a ficha caiu. Com os filtros &lt;em&gt;exatos&lt;/em&gt; que o job do e-mail usa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;bank_returns&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;occurrence_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-06-23'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;bank_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;credit_date&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- "somente pagos"&lt;/span&gt;
&lt;span class="c1"&gt;-- → 1351&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O e-mail tinha enviado 423. O banco de dados agora guarda 1.351. Com filtros idênticos. Nada na &lt;em&gt;query&lt;/em&gt; mudou. Algo no &lt;em&gt;tempo&lt;/em&gt; mudou.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 4: seguir os timestamps
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;07:15:45  job do relatório roda, monta e envia o e-mail
07:31:02  sistema importa o arquivo de retorno do banco (~12.700 lançamentos do dia)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O relatório roda às 07:15. O arquivo com a maior parte das baixas confirmadas do dia chega às 07:31. O e-mail é gerado &lt;strong&gt;16 minutos antes de o dado existir no banco&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Separe as faturas por esse corte e o gap bate exato:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- importadas até 07:15  → o que o e-mail viu   → 423&lt;/span&gt;
&lt;span class="c1"&gt;-- importadas após 07:15 → "faltando"           → 928&lt;/span&gt;
&lt;span class="c1"&gt;-- total                                        → 1351&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O portal "viu mais" por um motivo chato: foi consultado às 08:23, depois da importação.&lt;/p&gt;

&lt;h2&gt;
  
  
  O bug de verdade
&lt;/h2&gt;

&lt;p&gt;Não é lógica. É &lt;strong&gt;acoplamento temporal&lt;/strong&gt; entre uma tarefa agendada e a chegada de um dado externo. O relatório assume que os pagamentos de ontem já estão totalmente carregados às 07:15. O banco discorda.&lt;/p&gt;

&lt;p&gt;Correções, da mais barata para a melhor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rodar o relatório &lt;em&gt;depois&lt;/em&gt; da importação matinal.&lt;/li&gt;
&lt;li&gt;Melhor: disparar o relatório ao fim da importação, em vez de um horário fixo.&lt;/li&gt;
&lt;li&gt;Defensivo: o relatório validar que a importação esperada rodou antes de reportar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A lição
&lt;/h2&gt;

&lt;p&gt;"Menos linhas" quase nunca é "perdemos dados". Geralmente é "olhei no momento errado".&lt;/p&gt;

&lt;p&gt;Antes de anexar um debugger, faça duas perguntas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quando esse dado nasce?&lt;/li&gt;
&lt;li&gt;Quando eu o leio?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A maioria dos bugs de "dado faltando" morre aí mesmo.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>backend</category>
      <category>postgres</category>
      <category>ptbr</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>William Scussel</dc:creator>
      <pubDate>Wed, 17 Dec 2025 13:20:25 +0000</pubDate>
      <link>https://dev.to/scussel/-513o</link>
      <guid>https://dev.to/scussel/-513o</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/scussel" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3450126%2F87ee1b46-4c98-4af1-97ec-de4b698ec439.jpeg" alt="scussel"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/scussel/jenkins-na-aws-docker-2fgf" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Jenkins na AWS + Docker&lt;/h2&gt;
      &lt;h3&gt;William Scussel ・ Dec 3&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#jenkins&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>aws</category>
      <category>docker</category>
    </item>
    <item>
      <title>Jenkins na AWS + Docker</title>
      <dc:creator>William Scussel</dc:creator>
      <pubDate>Wed, 03 Dec 2025 20:33:52 +0000</pubDate>
      <link>https://dev.to/scussel/jenkins-na-aws-docker-2fgf</link>
      <guid>https://dev.to/scussel/jenkins-na-aws-docker-2fgf</guid>
      <description>&lt;p&gt;Olá, sou William Líder de squad na &lt;a href="https://innspire.dev" rel="noopener noreferrer"&gt;InnSpire.dev&lt;/a&gt;, quero começar a escrever sobre minhas experiencias no mundo da tecnologia, já que são mais de 20 anos trabalhando nessa area, quero compartilhar meus conhecimentos, sim, um pouco tarde para isso, mas como dizem: antes tarde do que mais tarde ainda.&lt;/p&gt;

&lt;p&gt;E escolhi começar com uma das ferramentas que mais me deu dor de cabeça — e também as maiores vitórias: &lt;strong&gt;Jenkins&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Já perdi dados, já quebrei pipelines, já fiquei até de madrugada resolvendo build travado… mas hoje tenho uma infraestrutura estável, barata e eficiente rodando na AWS.&lt;/p&gt;

&lt;p&gt;Neste artigo, quero mostrar &lt;strong&gt;como estruturamos Jenkins + Docker + AWS&lt;/strong&gt; e talvez te dar alguns insights para montar algo parecido.&lt;/p&gt;

&lt;p&gt;Bom vamos lá, atualmente nosso servidor está dentro da AWS, na região da Virginia, é uma instancia do tipo &lt;strong&gt;t3a.medium&lt;/strong&gt; com 2vCPU e 4GB de memória, e dentro dela tenho uma instalação do Ubuntu 24.04 Server, gosto muito de trabalhar com essa distro em servidores, é muito versátil, mas isso pode ser um assunto para outro artigo quem sabe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4wqrh67jau05z100gpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4wqrh67jau05z100gpd.png" alt="CPU e memória do servidor" width="800" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como podem notar no print do servidor, já está rodando a algum tempo sem nenhuma interrupção, mas apesar da memória estar bem usada, todos os serviços que estão rodando dentro da maquina rodam bem, e está tudo dentro de containers, temos atualmente 21 containers rodando dentro deste servidor.&lt;/p&gt;

&lt;p&gt;Dentro dos 21 containers que temos, 2 são para instalações do Jenkins, só que com jobs diferentes, propósitos diferentes, 2 para agentes de build do Jenkins (1 para cada instalação), e outros dois containers muito importantes para auxiliar nesse fluxo todo, Nginx para gerenciar o roteamento, e o Portainer para verificar a saude de todos os containers que estão no servidor.&lt;/p&gt;

&lt;p&gt;A idéia de separar em dois containers diferentes, foi mais para dois times distintos conseguirem atuar de forma totalmente separada e poder destruir seu ambiente sem afetar o outro time, e com docker isso é muito mais simples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqnulr0mvdo9s1it0uio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqnulr0mvdo9s1it0uio.png" alt="Lista de containers" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora de fato sobre a configuração do Jenkins, no docker-compose eu tenho mapeado os diretórios que serão acessados pelo Jenkins e o agente, e uma peculiaridade é o mapeamento do arquivo docker.sock, isso foi necessário pois dentro de alguns jobs eu precisava fazer o restart de algum container, e sem isso não era possível acessar os containers 🫠&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jenkins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jenkins-innova&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:8080"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50000:50000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;jenkins_home:/var/jenkins_home&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;innova-network&lt;/span&gt;

  &lt;span class="na"&gt;jenkins-innova-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jenkins/inbound-agent&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jenkins-innova-agent&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;innova-network&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/srv/jenkins-innova:/home/jenkins/agent-workspace&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://jenkins-innova:8080"&lt;/span&gt;
      &lt;span class="na"&gt;JENKINS_AGENT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent"&lt;/span&gt;
      &lt;span class="na"&gt;JENKINS_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chave"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jenkins_home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;innova-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;




&lt;span class="c1"&gt;# Dockerfile&lt;/span&gt;

&lt;span class="s"&gt;FROM jenkins/jenkins:alpine3.21-jdk21&lt;/span&gt;

&lt;span class="s"&gt;USER root&lt;/span&gt;

&lt;span class="c1"&gt;# Instalar dependências sem modificar o Java existente&lt;/span&gt;
&lt;span class="s"&gt;RUN apk add --no-cache \&lt;/span&gt;
    &lt;span class="s"&gt;sudo \&lt;/span&gt;
    &lt;span class="s"&gt;docker-cli \&lt;/span&gt;
    &lt;span class="s"&gt;git \&lt;/span&gt;
    &lt;span class="s"&gt;bash \&lt;/span&gt;
    &lt;span class="s"&gt;curl \&lt;/span&gt;
    &lt;span class="s"&gt;ttf-dejavu&lt;/span&gt;

&lt;span class="c1"&gt;# Adicionar usuário Jenkins ao grupo Docker&lt;/span&gt;
&lt;span class="s"&gt;RUN addgroup -S docker &amp;amp;&amp;amp; adduser jenkins docker \&lt;/span&gt;
    &lt;span class="s"&gt;&amp;amp;&amp;amp; echo "jenkins ALL=(ALL) NOPASSWD&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALL" &amp;gt; /etc/sudoers.d/jenkins&lt;/span&gt;

&lt;span class="s"&gt;USER jenkins&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E daqui para frente é só sucesso, configurar e sair usando, se quiser que eu comente mais como são configurados os jobs que temos, comenta ai!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>aws</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
