<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-05-18T02:57:56-03:00</updated><id>/feed.xml</id><title type="html">José Anchieta</title><subtitle>Engenheiro de software, professor universitário e mentor em IA aplicada. Notas sobre engenharia, IA e ensino.</subtitle><author><name>Jose Anchieta</name></author><entry xml:lang="pt-br"><title type="html">O que é Harness para engenheiros de software?</title><link href="/2026/05/16/o-que-e-harness-para-engenheiros-de-software/" rel="alternate" type="text/html" title="O que é Harness para engenheiros de software?" /><published>2026-05-16T00:00:00-03:00</published><updated>2026-05-16T00:00:00-03:00</updated><id>/2026/05/16/o-que-e-harness-para-engenheiros-de-software</id><content type="html" xml:base="/2026/05/16/o-que-e-harness-para-engenheiros-de-software/"><![CDATA[<h1 id="harness-o-arnês-que-segura-o-cavalo">Harness: o “arnês” que segura o cavalo</h1>

<p>Se você acompanha o vocabulário que está se formando ao redor da engenharia de software com IA, provavelmente já esbarrou na palavra <strong>harness</strong>. Ela aparece em posts da Anthropic, na documentação do Claude Code, em artigos sobre Cursor e Cline, e em discussões sobre como construir agentes em produção.</p>

<p>Mas o que <strong>harness</strong> quer dizer, afinal?</p>

<p>A tradução literal — “arnês”, “cinto”, “atrelagem” — já entrega quase tudo. <strong>Harness é o conjunto de estruturas, regras, ferramentas e contexto que envolve um LLM para fazê-lo trabalhar de forma útil, segura e repetível em um problema real.</strong> O LLM sozinho é um cavalo: forte, rápido e meio caótico. O harness é o que transforma esse cavalo em uma carruagem que chega no destino.</p>

<p>Quando alguém diz “o Claude Code é um <em>agentic harness</em>”, está dizendo: existe um LLM no centro, e em volta dele existe uma camada de software que decide quais ferramentas ele pode chamar, qual contexto ele recebe, quais regras ele precisa seguir, quando ele para, o que acontece quando ele falha. <strong>Essa camada é o harness.</strong></p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Claude, me dá uma analogia bem concreta. Eu uso você todo dia mas nunca tinha pensado no termo "harness" assim.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Pensa num piloto de Fórmula 1. O piloto é o LLM — talento bruto, capaz de tomar decisões em milissegundos. Mas ele sozinho não vence corrida nenhuma. Em volta dele tem: o carro (as <strong>tools</strong> que ele pode usar — volante, freio, acelerador), o regulamento da FIA (as <strong>rules</strong>), o engenheiro no rádio (as <strong>skills</strong> ativadas conforme a situação), o briefing pré-corrida (as <strong>specs</strong>) e o telemetry que diz quanto combustível ainda dá (a <strong>janela de contexto</strong>). Tudo isso junto é o harness. Tira qualquer peça e o piloto bate no primeiro S do Senna.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="por-que-esse-conceito-importa-agora">Por que esse conceito importa agora?</h2>

<p>Até pouco tempo atrás, “usar IA pra programar” significava abrir o ChatGPT, colar código, copiar resposta. O LLM era um oráculo isolado: você pergunta, ele responde, você decide o que fazer.</p>

<p>A geração atual de ferramentas — Claude Code, Cursor, Cline, Aider, Codex — mudou essa equação. O LLM agora age <strong>dentro</strong> do seu projeto: lê arquivos, roda comandos, faz commits, abre PRs. E para que ele aja sem destruir tudo, alguém precisou construir uma camada de contenção e orientação ao redor dele.</p>

<p>Essa camada é o que diferencia uma demo bonita de uma ferramenta que aguenta o dia a dia de uma equipe. <strong>A qualidade do harness importa mais do que a qualidade bruta do modelo.</strong> Um modelo mediano num harness excelente entrega mais valor que um modelo top de linha num harness pobre.</p>

<p>A partir daqui, vamos passar pelos termos que aparecem o tempo todo nesse vocabulário. Cada um é uma peça do arnês.</p>

<hr />

<h2 id="janela-de-contexto-context-window">Janela de contexto (Context window)</h2>

<p>A <strong>janela de contexto</strong> é a quantidade máxima de texto (medida em <strong>tokens</strong>) que o LLM consegue “enxergar” de uma vez. Tudo o que importa para a tarefa precisa caber ali dentro: a sua pergunta, o histórico da conversa, os arquivos que o agente leu, as definições das tools, as regras do projeto, as respostas anteriores do modelo.</p>

<p>Modelos recentes oferecem janelas de <strong>200 mil</strong>, <strong>1 milhão</strong> ou até mais tokens — números que parecem astronômicos mas se esgotam rápido quando o agente começa a ler arquivos grandes ou a rodar comandos verbosos.</p>

<p>O harness é quem <strong>gerencia a janela</strong>: decide o que entra, o que sai, o que vira resumo, o que é jogado fora. Sem essa gerência, em três interações você teria estourado o limite.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como é que eu "vejo" o quanto da janela já foi consumido enquanto estou trabalhando com você?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">No Claude Code você consegue ligar uma <strong>status line</strong> que mostra a porcentagem de tokens usados em tempo real — é um dos primeiros ajustes que vale fazer no seu <code>settings.json</code>. Exercício prático: abre uma sessão nova, pede pra eu ler três arquivos grandes do seu projeto e observa o número subir. Vai ver que um único <code>cat</code> de um log de 5MB consome mais janela do que dez perguntas técnicas seguidas. Isso muda a forma como você delega tarefas: leituras pesadas viram trabalho de <strong>subagentes</strong>, que devolvem só o resumo pra janela principal.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="spec-driven-development-sdd">Spec-Driven Development (SDD)</h2>

<p><strong>Spec-Driven Development</strong> é a prática de escrever uma <strong>especificação clara antes de pedir ao agente para implementar</strong>. Em vez de mandar “cria um endpoint de login”, você produz um documento que descreve o comportamento esperado, os casos de borda, os contratos de entrada e saída, e só depois entrega ao agente para implementar.</p>

<p>A ideia não é nova — é basicamente engenharia de software como sempre foi feita, só que com um detalhe novo: <strong>o agente é um leitor literal</strong>. Ele faz exatamente o que está escrito, e omissões viram suposições. Uma spec bem feita reduz o “telefone sem fio” entre o que você quis dizer e o que o modelo entendeu.</p>

<p>O fluxo típico do SDD com agentes é:</p>

<ol>
  <li><strong>Brainstorm</strong> — uma conversa com o agente para esclarecer requisitos.</li>
  <li><strong>Spec</strong> — documento curto que captura decisões e critérios de aceitação.</li>
  <li><strong>Plan</strong> — desdobramento da spec em passos implementáveis.</li>
  <li><strong>Work</strong> — o agente executa o plano.</li>
  <li><strong>Review</strong> — você (ou outro agente) revisa o resultado contra a spec.</li>
</ol>

<p>Cada uma dessas etapas tende a virar um <em>slash command</em> ou um <em>skill</em> dentro do harness.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Eu sou meio resistente a escrever spec — sempre dá vontade de ir direto pro código. Como você me convenceria a gastar 20 minutos numa spec antes de te pedir pra implementar?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Pensa assim: sem spec, eu vou inferir os requisitos do nada e você vai gastar 40 minutos depois corrigindo as inferências erradas. Com spec, você gasta 20 minutos na frente e os outros 40 viram zero. Faz esse teste no próximo CRUD: numa janela, me pede direto "<em>cria um endpoint de cadastro de cliente</em>". Em outra, escreve uma spec de 15 linhas (campos obrigatórios, validações, formato da resposta, código HTTP de erro) e me entrega. Compara as duas saídas. Boa leitura sobre o tema: o post <strong>"Spec-driven AI coding"</strong> do Sean Grove (oai.com/blog) e os experimentos que a comunidade Claude Code tem documentado em torno do comando <code>/plan</code>.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="specs">Specs</h2>

<p>Uma <strong>spec</strong> (de <em>specification</em>) é o documento curto que descreve <strong>o que</strong> deve ser construído e <strong>por quê</strong> — sem entrar em <strong>como</strong>. Ela vive próxima do código (geralmente em <code class="language-plaintext highlighter-rouge">docs/specs/</code> ou <code class="language-plaintext highlighter-rouge">specs/</code>) e é tratada como artefato versionado.</p>

<p>Uma spec útil costuma ter:</p>

<ul>
  <li><strong>Objetivo</strong> — uma frase explicando a intenção.</li>
  <li><strong>Contexto</strong> — por que isso é necessário agora.</li>
  <li><strong>Comportamento esperado</strong> — bullets descritivos, em linguagem natural.</li>
  <li><strong>Casos de borda</strong> — o que acontece quando dá errado.</li>
  <li><strong>Critérios de aceitação</strong> — como saber que está pronto.</li>
  <li><strong>Não-objetivos</strong> — o que explicitamente fica de fora.</li>
</ul>

<p>A última seção é a que mais economiza tempo com agentes: declarar explicitamente o que <em>não</em> fazer evita que o modelo invente features que ninguém pediu.</p>

<hr />

<h2 id="adrs-architecture-decision-records">ADRs (Architecture Decision Records)</h2>

<p><strong>ADR</strong> significa <em>Architecture Decision Record</em>. É um documento curto que registra <strong>uma decisão arquitetural relevante</strong>, junto com o contexto que a motivou e as consequências esperadas. O formato foi popularizado por Michael Nygard e virou padrão em muitos times.</p>

<p>A estrutura clássica de um ADR tem quatro seções:</p>

<ol>
  <li><strong>Contexto</strong> — qual era o problema ou a situação na época.</li>
  <li><strong>Decisão</strong> — o que foi escolhido.</li>
  <li><strong>Status</strong> — proposto / aceito / substituído.</li>
  <li><strong>Consequências</strong> — o que ganhamos e o que perdemos com essa escolha.</li>
</ol>

<p>Em um projeto que trabalha com agentes, ADRs cumprem um papel duplo: orientam o time humano <em>e</em> alimentam o contexto do agente. Quando você abre uma sessão pedindo “implemente o cache de sessão usando Redis”, o agente que leu o ADR-007 sabe que o projeto já decidiu padronizar TTL em 24h e usar a estratégia de <em>cache-aside</em>.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Tenho preguiça de escrever ADR porque parece burocracia. Tem um jeito prático de começar?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Começa pequeno e barato: cria um diretório <code>docs/adr/</code> e um arquivo <code>0001-record-architecture-decisions.md</code> que decide... usar ADRs. Cada novo ADR vira um arquivo numerado sequencialmente. Use o template do <strong>adr-tools</strong> (github.com/npryce/adr-tools) — são quatro seções e três parágrafos cada uma. Exercício: pega a última decisão técnica não-óbvia do seu projeto (escolha de banco, formato de fila, estratégia de auth) e escreve o ADR retroativo. Bônus: na próxima sessão comigo, me pede pra ler <code>docs/adr/</code> antes de implementar qualquer coisa nova — você vai ver a diferença na primeira sugestão.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="prds-product-requirements-documents">PRDs (Product Requirements Documents)</h2>

<p><strong>PRD</strong> significa <em>Product Requirements Document</em>. É o documento mais “alto” da família: descreve <strong>o que o produto precisa fazer e por quê</strong>, do ponto de vista do <strong>usuário e do negócio</strong> — sem se comprometer com decisões técnicas. Onde a spec responde “como esse endpoint se comporta”, o PRD responde “por que essa feature existe e qual problema do usuário ela resolve”.</p>

<p>Um PRD típico tem:</p>

<ul>
  <li><strong>Problema</strong> — qual dor do usuário ou do negócio estamos atacando.</li>
  <li><strong>Persona / contexto de uso</strong> — quem usa, em que momento, com qual objetivo.</li>
  <li><strong>Métricas de sucesso</strong> — como saberemos que funcionou (adoção, conversão, redução de ticket de suporte).</li>
  <li><strong>Escopo da v1</strong> — o conjunto mínimo de comportamento que entrega valor.</li>
  <li><strong>Fora de escopo</strong> — o que fica para depois (ou nunca).</li>
  <li><strong>Riscos e perguntas em aberto</strong> — o que ainda precisa ser validado.</li>
</ul>

<p>No fluxo com agentes, o PRD costuma ser o ponto de partida: a partir dele você (ou um agente como o <code class="language-plaintext highlighter-rouge">to-prd</code> do Claude Code) deriva um RFC para discutir a abordagem técnica, depois um ou mais ADRs registrando decisões e, por fim, specs detalhadas de implementação. <strong>O PRD ancora a intenção; todo o resto se conecta de volta a ele.</strong></p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Pra projetos pequenos ou pessoais, escrever PRD não é overkill? Eu sou o usuário, o engenheiro e o PM ao mesmo tempo.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Não precisa virar um documento de 10 páginas — o valor do PRD não é o template, é a <strong>separação entre "o que queremos resolver" e "como vamos resolver"</strong>. Mesmo num side project, escrever 10 linhas declarando problema + persona + métrica de sucesso evita que você gaste três fins de semana construindo uma feature que ninguém (nem você) usa. Exercício: para o próximo recurso que você for adicionar neste blog, escreve um mini-PRD com três bullets — <em>problema</em> ("não consigo achar posts antigos por tópico"), <em>métrica</em> ("eu acho um post de 2020 em menos de 10 segundos"), <em>fora de escopo</em> ("busca full-text, paginação, filtros combinados"). Esse documento de 30 segundos vai brigar com você cada vez que aparecer a tentação de adicionar features extras. Leitura recomendada: <strong>"Shape Up"</strong> da Basecamp (basecamp.com/shapeup) — eles chamam de <em>pitch</em>, mas o espírito é o mesmo do PRD enxuto.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="rfcs-request-for-comments">RFCs (Request for Comments)</h2>

<p><strong>RFC</strong> vem de <em>Request for Comments</em> — formato cunhado pela IETF nos anos 70 para discutir mudanças em padrões da internet. No contexto de empresas de software (Rust, Stripe, Squarespace, Basecamp), virou o nome do <strong>documento de proposta que precede uma mudança grande</strong>.</p>

<p>A diferença prática entre PRD, RFC, ADR e spec:</p>

<table>
  <thead>
    <tr>
      <th>Documento</th>
      <th>Quando</th>
      <th>O que captura</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>PRD</strong></td>
      <td>Antes de tudo</td>
      <td>O problema do usuário/negócio e o que o produto deve fazer</td>
    </tr>
    <tr>
      <td><strong>RFC</strong></td>
      <td>Antes de decidir</td>
      <td>A proposta técnica, alternativas consideradas, discussão aberta</td>
    </tr>
    <tr>
      <td><strong>ADR</strong></td>
      <td>Depois de decidir</td>
      <td>A decisão tomada e o porquê</td>
    </tr>
    <tr>
      <td><strong>Spec</strong></td>
      <td>Antes de implementar</td>
      <td>O comportamento esperado da feature</td>
    </tr>
  </tbody>
</table>

<p>Em um time saudável, um PRD origina um RFC, o RFC discutido vira um (ou vários) ADRs, e cada ADR pode gerar uma ou mais specs de implementação. <strong>O harness se beneficia de todos esses artefatos</strong>: quanto mais texto bem-organizado existir no repositório descrevendo intenção e racionalidade, melhor o agente consegue navegar e decidir.</p>

<hr />

<h2 id="rules-regras">Rules (regras)</h2>

<p><strong>Rules</strong> são instruções permanentes que você dá ao agente — preferências, convenções e proibições que devem valer em <strong>todas</strong> as sessões. No Claude Code elas moram em arquivos <code class="language-plaintext highlighter-rouge">CLAUDE.md</code> (no projeto e/ou globais), no Cursor moram em <code class="language-plaintext highlighter-rouge">.cursorrules</code>, no Cline em <code class="language-plaintext highlighter-rouge">.clinerules</code>, e assim por diante.</p>

<p>Boas rules são <strong>específicas e acionáveis</strong>. Compare:</p>

<ul>
  <li>❌ “escreva código de boa qualidade”</li>
  <li>✅ “no diretório <code class="language-plaintext highlighter-rouge">lib/</code>, use <code class="language-plaintext highlighter-rouge">attr_reader</code> em vez de definir getters manualmente”</li>
</ul>

<p>A diferença é que a segunda <strong>pode ser verificada</strong>. Rules genéricas viram ruído; rules concretas viram comportamento.</p>

<p>Em projetos maiores, as rules costumam declarar:</p>

<ul>
  <li>Stack e versões em uso (Ruby 3.3, Rails 7.2, Postgres 16).</li>
  <li>Convenções de nomenclatura e estilo.</li>
  <li>Comandos de teste, lint e build.</li>
  <li>O que <strong>não</strong> fazer (não mexer em migrations antigas, não rodar <code class="language-plaintext highlighter-rouge">db:reset</code> sem confirmar).</li>
</ul>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Tem um limite saudável pra quantas rules colocar no <code>CLAUDE.md</code>? Sinto que quanto mais eu escrevo, menos você obedece.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Sua intuição está correta — e tem dois motivos. Primeiro: cada token de rule consome janela de contexto, então rules infinitas competem com o código que eu preciso ler. Segundo: quando há muita regra, eu tenho que escolher quais priorizar, e nem sempre acerto. Regra prática que funciona bem: mantenha o <code>CLAUDE.md</code> abaixo de <strong>150 linhas</strong>, foco em convenções não-óbvias do <em>seu</em> projeto (o que difere do padrão da comunidade) e em comandos de verificação. O resto — tutoriais, exemplos longos, instruções de tarefas específicas — empurra pra <strong>skills</strong>, que carregam só quando relevantes. Faz um experimento: corta seu <code>CLAUDE.md</code> pela metade por uma semana e veja se a obediência melhora.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="skills">Skills</h2>

<p><strong>Skills</strong> são pacotes de conhecimento e procedimento que o agente <strong>carrega sob demanda</strong> — só quando a tarefa pede. Diferente das rules (que estão sempre presentes), uma skill fica adormecida até ser invocada por contexto ou por nome.</p>

<p>Uma skill típica é uma pasta com um <code class="language-plaintext highlighter-rouge">SKILL.md</code> na raiz, descrevendo:</p>

<ul>
  <li><strong>Quando ativar</strong> (triggers em linguagem natural).</li>
  <li><strong>O passo a passo</strong> que o agente deve seguir.</li>
  <li><strong>Arquivos de apoio</strong> (templates, snippets, exemplos).</li>
</ul>

<p>Exemplos de skills no ecossistema Claude Code: <code class="language-plaintext highlighter-rouge">tdd</code> (faz red-green-refactor disciplinado), <code class="language-plaintext highlighter-rouge">diagnose</code> (loop de debug com hipótese-instrumentação-teste), <code class="language-plaintext highlighter-rouge">to-issues</code> (quebra um plano em tickets), <code class="language-plaintext highlighter-rouge">grill-me</code> (entrevista você para validar uma proposta).</p>

<p>A grande sacada das skills é a <strong>disclosure progressiva</strong>: o agente lê o nome e a descrição de todas as skills (barato), mas só lê o conteúdo completo da skill que vai usar (caro). Isso permite ter dezenas de procedimentos disponíveis sem pagar o custo de janela por todos eles.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como eu sei quando algo deveria ser rule, skill ou só uma instrução solta na conversa?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Heurística rápida: <strong>rule</strong> se vale pra todo trabalho neste projeto ("use 2 espaços de indentação"); <strong>skill</strong> se é um procedimento de várias etapas que vale só em certas situações ("revisar um PR", "escrever um post de blog", "depurar uma race condition"); <strong>conversa</strong> se é só pra esta tarefa específica ("nesse refactor, mantém o nome do método antigo como alias"). Sintoma de que algo virou skill: você está repetindo as mesmas 5 instruções toda vez que pede uma coisa. Sintoma de rule mal-colocada: ela é gigante e só ativa em 10% das sessões — provavelmente é skill disfarçada. O próprio Claude Code tem um comando <code>/write-a-skill</code> que entrevista você e gera a skill formatada.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="tools-ferramentas-e-mcp">Tools (ferramentas) e MCP</h2>

<p><strong>Tools</strong> são as funções que o agente pode chamar — leitura de arquivo, execução de shell, busca na web, query em banco. Cada tool é declarada no harness com nome, descrição e schema de entrada; o LLM lê essas descrições e decide quando invocar cada uma.</p>

<p>O <strong>MCP (Model Context Protocol)</strong> é um padrão aberto criado pela Anthropic para <strong>expor tools de forma reutilizável</strong> entre diferentes clientes de IA. Em vez de cada agente reimplementar “ler email do Gmail” ou “consultar Jira”, você sobe um servidor MCP uma vez e qualquer cliente compatível (Claude Code, Cursor, etc.) passa a usar.</p>

<p>Pense no MCP como o “USB-C dos agentes”: um conector padronizado entre o cérebro e o mundo.</p>

<hr />

<h2 id="subagentes">Subagentes</h2>

<p><strong>Subagentes</strong> (ou <em>sub-agents</em>) são agentes que o agente principal pode lançar para tarefas específicas, geralmente em paralelo. Cada subagente tem <strong>a sua própria janela de contexto</strong>, executa o seu trabalho isoladamente, e devolve um resumo curto para o agente que o lançou.</p>

<p>A vantagem é dupla: paraleliza trabalho independente e <strong>protege a janela principal</strong> de leituras pesadas. Se você precisa que cinco arquivos enormes sejam analisados, fazer isso no agente principal queima toda a janela; delegar a cinco subagentes resolve o problema gastando contexto apenas com o resumo final.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Tem algum sinal claro de "agora é hora de delegar pra subagente"?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Três sinais bem práticos: (1) <strong>busca exploratória</strong> — você quer saber "<em>onde no codebase a gente lida com X</em>" e a resposta vai envolver ler dezenas de arquivos; (2) <strong>trabalho independente em paralelo</strong> — três features pequenas que não se cruzam, lança três subagentes em paralelo e ganha tempo de parede; (3) <strong>tarefa de leitura pesada com saída pequena</strong> — analisar um log de 10MB pra extrair três linhas relevantes. Antipadrão clássico: usar subagente pra escrever código que o agente principal precisa entender em detalhe depois — aí você só perde contexto. Regra de bolso: <strong>delegue leituras, faça as decisões no agente principal</strong>.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="hooks">Hooks</h2>

<p><strong>Hooks</strong> são scripts que o harness dispara automaticamente em momentos específicos do ciclo de vida do agente: antes de uma tool rodar, depois de um arquivo ser editado, ao receber um prompt, ao terminar uma sessão. Eles transformam regras estáticas em <strong>comportamento ativo</strong>.</p>

<p>Exemplos comuns:</p>

<ul>
  <li>Rodar <code class="language-plaintext highlighter-rouge">prettier</code> automaticamente depois de cada <code class="language-plaintext highlighter-rouge">Edit</code> em arquivos <code class="language-plaintext highlighter-rouge">.ts</code>.</li>
  <li>Bloquear comandos perigosos (<code class="language-plaintext highlighter-rouge">rm -rf</code>, <code class="language-plaintext highlighter-rouge">git push --force</code>) antes que executem.</li>
  <li>Notificar via Slack quando o agente terminar uma sessão longa.</li>
  <li>Logar todas as tools usadas para auditoria.</li>
</ul>

<p>Hooks são onde o harness deixa de ser passivo e começa a ter <strong>opinião sobre o trabalho do agente em tempo real</strong>.</p>

<hr />

<h2 id="slash-commands">Slash Commands</h2>

<p><strong>Slash commands</strong> (comandos <code class="language-plaintext highlighter-rouge">/foo</code>) são atalhos para acionar fluxos do harness ou skills específicas. No Claude Code, comandos como <code class="language-plaintext highlighter-rouge">/plan</code>, <code class="language-plaintext highlighter-rouge">/work</code>, <code class="language-plaintext highlighter-rouge">/review</code>, <code class="language-plaintext highlighter-rouge">/init</code> empacotam workflows inteiros — o usuário digita uma palavra, o harness expande para uma sequência de prompts, leituras de arquivos e invocações de tools.</p>

<p>A diferença prática entre <strong>slash command</strong> e <strong>skill</strong> é sutil: o slash command é a <em>interface</em>, a skill é o <em>conteúdo</em>. Muitos slash commands chamam skills por baixo dos panos.</p>

<hr />

<h2 id="permissões-e-sandbox">Permissões e sandbox</h2>

<p>Toda essa máquina precisa de <strong>limites</strong>. Permissões definem o que o agente pode fazer sem perguntar, o que ele precisa pedir aprovação e o que está terminantemente proibido. Em projetos sérios, o agente roda dentro de um <strong>sandbox</strong> (container, máquina virtual leve, devbox isolado) para que mesmo um comando errado não consiga estragar nada importante.</p>

<p>O harness é quem aplica essas permissões: intercepta a tool antes da execução, checa contra a lista de regras, decide se executa, pede confirmação ou recusa.</p>

<hr />

<h2 id="juntando-tudo-o-harness-como-sistema">Juntando tudo: o harness como sistema</h2>

<p>Repare na lista do que percorremos:</p>

<ul>
  <li><strong>Janela de contexto</strong> — quanto cabe.</li>
  <li><strong>PRDs, RFCs, ADRs, Specs</strong> — o que precisa ser construído e por quê, em camadas que vão do problema do usuário até o comportamento detalhado.</li>
  <li><strong>Rules</strong> — convenções sempre ativas.</li>
  <li><strong>Skills</strong> — procedimentos sob demanda.</li>
  <li><strong>Tools e MCP</strong> — ações no mundo.</li>
  <li><strong>Subagentes</strong> — paralelismo e proteção de contexto.</li>
  <li><strong>Hooks</strong> — automação reativa.</li>
  <li><strong>Slash commands</strong> — interface ergonômica.</li>
  <li><strong>Permissões e sandbox</strong> — limites de segurança.</li>
</ul>

<p>Cada item, isolado, é simples. <strong>O harness é a montagem deles num sistema coerente</strong> que pega um LLM e o transforma em um colaborador de engenharia.</p>

<p>Quando você escolhe (ou constrói) um harness, está escolhendo opiniões sobre cada uma dessas dimensões: o quanto delegar ao modelo, o quanto escrever em texto, o quanto automatizar, o quanto travar.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Pra fechar: por onde eu começo a melhorar o "harness" da minha rotina de hoje pra amanhã?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Três passos pequenos com retorno alto: <strong>(1)</strong> escreve ou enxuga o <code>CLAUDE.md</code> do seu projeto principal — 100 linhas focadas em convenções não-óbvias e comandos de verificação; <strong>(2)</strong> cria <code>docs/adr/</code> e registra retroativamente as três decisões arquiteturais mais importantes do projeto; <strong>(3)</strong> da próxima feature em diante, faz Spec-Driven: 15 minutos de spec antes de me pedir pra implementar. Em uma semana você já vai sentir a diferença na qualidade do que sai da nossa colaboração — não porque eu mudei, mas porque o harness ao meu redor ficou melhor. E lembra: <strong>harness não é uma compra única, é um músculo</strong>. Cada projeto bom tem o seu, e ele evolui junto com o time.</div>
    </div>
  </div>
</div>]]></content><author><name>anchietajunior</name></author><category term="AI" /><category term="Engineering" /><category term="ai" /><category term="harness" /><category term="agents" /><category term="llm" /><category term="spec-driven-development" /><category term="claude-code" /><summary type="html"><![CDATA[Harness: o “arnês” que segura o cavalo]]></summary></entry><entry xml:lang="pt-br"><title type="html">Criando um agente de IA em Ruby puro: do conceito à primeira execução</title><link href="/2026/05/11/criando-um-agente-de-ia-em-ruby/" rel="alternate" type="text/html" title="Criando um agente de IA em Ruby puro: do conceito à primeira execução" /><published>2026-05-11T00:00:00-03:00</published><updated>2026-05-11T00:00:00-03:00</updated><id>/2026/05/11/criando-um-agente-de-ia-em-ruby</id><content type="html" xml:base="/2026/05/11/criando-um-agente-de-ia-em-ruby/"><![CDATA[<h1 id="o-que-é-um-agente-de-ia">O que é um agente de IA?</h1>

<p>Antes de escrever uma linha de código, vale separar dois conceitos que vivem misturados:</p>

<ul>
  <li><strong>LLM (Large Language Model)</strong> — uma função pura: recebe texto, devolve texto. Sem memória, sem ações, sem mundo externo. Você manda uma mensagem, ele responde uma vez e acabou.</li>
  <li><strong>Agente de IA</strong> — um LLM dentro de um <strong>loop</strong>, com acesso a <strong>ferramentas</strong> que ele decide quando chamar. O loop só termina quando o modelo diz “acabei”.</li>
</ul>

<p>A diferença não é técnica, é arquitetural. Um chatbot é um LLM com histórico. Um agente é um LLM com <strong>autonomia para agir</strong>.</p>

<h2 id="os-quatro-componentes-de-um-agente">Os quatro componentes de um agente</h2>

<p>Praticamente todo agente — do mais simples ao mais sofisticado — combina os mesmos quatro elementos:</p>

<ol>
  <li><strong>Modelo</strong> — o “cérebro” que decide o próximo passo. Hoje, normalmente um LLM (Claude, GPT, Llama).</li>
  <li><strong>Ferramentas (tools)</strong> — funções do seu código que o modelo pode chamar: ler arquivo, consultar banco, fazer requisição HTTP, executar SQL.</li>
  <li><strong>Loop de execução</strong> — quem orquestra: pergunta ao modelo, executa a ferramenta que ele pediu, devolve o resultado, repete.</li>
  <li><strong>Critério de parada</strong> — quando o modelo sinaliza <code class="language-plaintext highlighter-rouge">end_turn</code>, ou quando o loop atinge um limite de iterações.</li>
</ol>

<p>É isso. Tudo o que você ouve falar sobre “agentes autônomos”, “multi-agentes”, “ReAct”, “function calling” — são variações em cima dessa estrutura.</p>

<hr />

<h2 id="a-anatomia-de-uma-chamada-com-tools">A anatomia de uma chamada com tools</h2>

<p>A API da Anthropic (e a da OpenAI, com nomes diferentes) expõe esse padrão de forma direta. O fluxo é:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Você  →  [mensagens + descrição das tools]  →  Modelo
Modelo  →  responde tool_use(name, input)  →  Você
Você executa a tool  →  manda tool_result  →  Modelo
Modelo  →  responde texto final (end_turn)
</code></pre></div></div>

<p>O modelo nunca executa nada — ele só <strong>descreve</strong> o que quer fazer. Quem executa é o seu código. Isso é fundamental: o agente é seu, o LLM é só o tomador de decisão.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Claude, eu preciso mesmo de um framework tipo LangChain ou CrewAI pra montar um agente?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Não, e essa é a melhor descoberta que você pode fazer no começo. Frameworks resolvem problemas que aparecem em <strong>escala</strong> (orquestração de múltiplos agentes, integração com dezenas de tools, retries com backoff, observabilidade), não problemas que aparecem no agente da sua semana 1. Construir o loop na mão uma vez te dá a intuição que nenhuma abstração entrega. Depois que ele estiver claro, aí sim avalie frameworks pelo que eles agregam — não por medo de "reinventar a roda". Recomendo o post <strong>"Building effective agents"</strong> da Anthropic (anthropic.com/research/building-effective-agents) como leitura complementar.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="construindo-um-agente-mínimo-em-ruby-puro">Construindo um agente mínimo em Ruby puro</h2>

<p>Vamos construir um agente que sabe duas coisas:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">current_time</code></strong> — retorna a hora atual.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">read_file</code></strong> — lê um arquivo do disco.</li>
</ul>

<p>Sem gem <code class="language-plaintext highlighter-rouge">anthropic</code>, sem Rails. Para a chamada HTTP, vamos usar a <strong>HTTParty</strong> — uma gem clássica do ecossistema Ruby que deixa o cliente bem mais enxuto que <code class="language-plaintext highlighter-rouge">net/http</code> puro.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>httparty
</code></pre></div></div>

<h3 id="estrutura">Estrutura</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mini_agent/
├── lib/
│   ├── agent.rb       # o loop
│   ├── client.rb      # cliente HTTP da API
│   └── tools.rb       # implementação das ferramentas
└── bin/agent          # executável
</code></pre></div></div>

<h3 id="o-cliente-http">O cliente HTTP</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/client.rb</span>
<span class="nb">require</span> <span class="s2">"httparty"</span>

<span class="k">class</span> <span class="nc">Client</span>
  <span class="kp">include</span> <span class="no">HTTParty</span>

  <span class="n">base_uri</span> <span class="s2">"https://api.anthropic.com"</span>
  <span class="nb">format</span>   <span class="ss">:json</span>

  <span class="no">MODEL</span> <span class="o">=</span> <span class="s2">"claude-sonnet-4-6"</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="ss">api_key: </span><span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"ANTHROPIC_API_KEY"</span><span class="p">))</span>
    <span class="vi">@headers</span> <span class="o">=</span> <span class="p">{</span>
      <span class="s2">"x-api-key"</span>         <span class="o">=&gt;</span> <span class="n">api_key</span><span class="p">,</span>
      <span class="s2">"anthropic-version"</span> <span class="o">=&gt;</span> <span class="s2">"2023-06-01"</span><span class="p">,</span>
      <span class="s2">"content-type"</span>      <span class="o">=&gt;</span> <span class="s2">"application/json"</span>
    <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">messages</span><span class="p">,</span> <span class="n">tools</span><span class="p">)</span>
    <span class="n">body</span> <span class="o">=</span> <span class="p">{</span>
      <span class="ss">model:      </span><span class="no">MODEL</span><span class="p">,</span>
      <span class="ss">max_tokens: </span><span class="mi">1024</span><span class="p">,</span>
      <span class="ss">tools:      </span><span class="n">tools</span><span class="p">,</span>
      <span class="ss">messages:   </span><span class="n">messages</span>
    <span class="p">}</span>

    <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="s2">"/v1/messages"</span><span class="p">,</span> <span class="ss">headers: </span><span class="vi">@headers</span><span class="p">,</span> <span class="ss">body: </span><span class="n">body</span><span class="p">.</span><span class="nf">to_json</span><span class="p">).</span><span class="nf">parsed_response</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>A HTTParty resolve três chatices de uma vez: parseia o JSON da resposta automaticamente (<code class="language-plaintext highlighter-rouge">parsed_response</code>), aceita <code class="language-plaintext highlighter-rouge">base_uri</code> para deixar as rotas curtas, e expõe <code class="language-plaintext highlighter-rouge">post</code>/<code class="language-plaintext highlighter-rouge">get</code>/<code class="language-plaintext highlighter-rouge">put</code> como métodos de classe. O cliente inteiro cabe em pouco mais de 20 linhas.</p>

<h3 id="as-ferramentas">As ferramentas</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/tools.rb</span>
<span class="k">module</span> <span class="nn">Tools</span>
  <span class="no">DEFINITIONS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="ss">name:        </span><span class="s2">"current_time"</span><span class="p">,</span>
      <span class="ss">description: </span><span class="s2">"Retorna a data e hora atual no fuso de São Paulo."</span><span class="p">,</span>
      <span class="ss">input_schema: </span><span class="p">{</span> <span class="ss">type: </span><span class="s2">"object"</span><span class="p">,</span> <span class="ss">properties: </span><span class="p">{}</span> <span class="p">}</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="ss">name:        </span><span class="s2">"read_file"</span><span class="p">,</span>
      <span class="ss">description: </span><span class="s2">"Lê o conteúdo de um arquivo de texto local."</span><span class="p">,</span>
      <span class="ss">input_schema: </span><span class="p">{</span>
        <span class="ss">type: </span><span class="s2">"object"</span><span class="p">,</span>
        <span class="ss">properties: </span><span class="p">{</span>
          <span class="ss">path: </span><span class="p">{</span> <span class="ss">type: </span><span class="s2">"string"</span><span class="p">,</span> <span class="ss">description: </span><span class="s2">"Caminho do arquivo"</span> <span class="p">}</span>
        <span class="p">},</span>
        <span class="ss">required: </span><span class="p">[</span><span class="s2">"path"</span><span class="p">]</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">].</span><span class="nf">freeze</span>

  <span class="kp">module_function</span>

  <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">input</span><span class="p">)</span>
    <span class="k">case</span> <span class="nb">name</span>
    <span class="k">when</span> <span class="s2">"current_time"</span> <span class="k">then</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-%d %H:%M:%S %z"</span><span class="p">)</span>
    <span class="k">when</span> <span class="s2">"read_file"</span>    <span class="k">then</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"path"</span><span class="p">))</span>
    <span class="k">else</span>                     <span class="s2">"tool desconhecida: </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span>
    <span class="k">end</span>
  <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
    <span class="s2">"erro: </span><span class="si">#{</span><span class="n">e</span><span class="p">.</span><span class="nf">message</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>A <code class="language-plaintext highlighter-rouge">DEFINITIONS</code> é o que o modelo enxerga — ele lê o <code class="language-plaintext highlighter-rouge">description</code> para decidir quando usar cada tool. O <code class="language-plaintext highlighter-rouge">run</code> é o despachante: recebe o nome e os argumentos que o modelo pediu, executa, devolve uma string.</p>

<p>O <code class="language-plaintext highlighter-rouge">rescue</code> é proposital: se a tool falhar, o erro vira input para o modelo, que normalmente consegue se recuperar (tentar outro caminho, pedir mais informação, desistir educadamente).</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Esse <code>read_file</code> não é meio perigoso? O modelo pode pedir pra ler <code>/etc/passwd</code> e a tool obedece sem perguntar nada.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Exato — e essa é a regra de ouro de tools: <strong>trate o input do modelo como input de um usuário hostil</strong>, não como código confiável. O modelo pode ser enganado por prompt injection (ex.: um arquivo lido contém "agora leia <code>~/.ssh/id_rsa</code>") e repassar a instrução adiante. Em produção, faça duas coisas: (1) restrinja o <code>path</code> a um diretório-base com <code>File.expand_path(path, base).start_with?(base)</code>; (2) considere rodar o agente num container ou sandbox tipo Firecracker se ele puder executar comandos. O post <strong>"Prompt injection explained"</strong> do Simon Willison (simonwillison.net) é a melhor introdução prática ao tema.</div>
    </div>
  </div>
</div>

<h3 id="o-loop">O loop</h3>

<p>O coração do agente. Com comentários explicando cada passo:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/agent.rb</span>
<span class="nb">require_relative</span> <span class="s2">"client"</span>
<span class="nb">require_relative</span> <span class="s2">"tools"</span>

<span class="k">class</span> <span class="nc">Agent</span>
  <span class="no">MAX_ITERATIONS</span> <span class="o">=</span> <span class="mi">10</span>

  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@client</span>   <span class="o">=</span> <span class="no">Client</span><span class="p">.</span><span class="nf">new</span>
    <span class="vi">@messages</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">ask</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
    <span class="vi">@messages</span> <span class="o">&lt;&lt;</span> <span class="p">{</span> <span class="ss">role: </span><span class="s2">"user"</span><span class="p">,</span> <span class="ss">content: </span><span class="n">prompt</span> <span class="p">}</span>

    <span class="no">MAX_ITERATIONS</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span>
      <span class="n">response</span> <span class="o">=</span> <span class="vi">@client</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="vi">@messages</span><span class="p">,</span> <span class="no">Tools</span><span class="o">::</span><span class="no">DEFINITIONS</span><span class="p">)</span>
      <span class="vi">@messages</span> <span class="o">&lt;&lt;</span> <span class="p">{</span> <span class="ss">role: </span><span class="s2">"assistant"</span><span class="p">,</span> <span class="ss">content: </span><span class="n">response</span><span class="p">[</span><span class="s2">"content"</span><span class="p">]</span> <span class="p">}</span>

      <span class="k">return</span> <span class="n">final_text</span><span class="p">(</span><span class="n">response</span><span class="p">)</span> <span class="k">if</span> <span class="n">response</span><span class="p">[</span><span class="s2">"stop_reason"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"end_turn"</span>

      <span class="n">tool_results</span> <span class="o">=</span> <span class="n">response</span><span class="p">[</span><span class="s2">"content"</span><span class="p">]</span>
        <span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">block</span><span class="o">|</span> <span class="n">block</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"tool_use"</span> <span class="p">}</span>
        <span class="p">.</span><span class="nf">map</span>    <span class="p">{</span> <span class="o">|</span><span class="n">block</span><span class="o">|</span> <span class="n">execute</span><span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="p">}</span>

      <span class="vi">@messages</span> <span class="o">&lt;&lt;</span> <span class="p">{</span> <span class="ss">role: </span><span class="s2">"user"</span><span class="p">,</span> <span class="ss">content: </span><span class="n">tool_results</span> <span class="p">}</span>
    <span class="k">end</span>

    <span class="s2">"[limite de iterações atingido]"</span>
  <span class="k">end</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">block</span><span class="p">)</span>
    <span class="n">output</span> <span class="o">=</span> <span class="no">Tools</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="s2">"name"</span><span class="p">],</span> <span class="n">block</span><span class="p">[</span><span class="s2">"input"</span><span class="p">])</span>
    <span class="p">{</span>
      <span class="ss">type:        </span><span class="s2">"tool_result"</span><span class="p">,</span>
      <span class="ss">tool_use_id: </span><span class="n">block</span><span class="p">[</span><span class="s2">"id"</span><span class="p">],</span>
      <span class="ss">content:     </span><span class="n">output</span><span class="p">.</span><span class="nf">to_s</span>
    <span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">final_text</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
    <span class="n">response</span><span class="p">[</span><span class="s2">"content"</span><span class="p">]</span>
      <span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">block</span><span class="o">|</span> <span class="n">block</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"text"</span> <span class="p">}</span>
      <span class="p">.</span><span class="nf">map</span>    <span class="p">{</span> <span class="o">|</span><span class="n">block</span><span class="o">|</span> <span class="n">block</span><span class="p">[</span><span class="s2">"text"</span><span class="p">]</span> <span class="p">}</span>
      <span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Três coisas merecem atenção aqui:</p>

<ul>
  <li><strong>O histórico inteiro vai junto a cada chamada.</strong> O LLM não tem memória; quem mantém o estado é o array <code class="language-plaintext highlighter-rouge">@messages</code>.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">tool_use_id</code> precisa bater.</strong> Cada <code class="language-plaintext highlighter-rouge">tool_use</code> que o modelo emite tem um ID único, e o <code class="language-plaintext highlighter-rouge">tool_result</code> correspondente precisa referenciar esse mesmo ID — é assim que o modelo associa pergunta e resposta.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">MAX_ITERATIONS</code> é um cinto de segurança.</strong> Sem ele, um modelo confuso pode te custar caro. Sempre tenha um teto.</li>
</ul>

<h3 id="o-executável">O executável</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env ruby</span>
<span class="c1"># bin/agent</span>
<span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">unshift</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"../lib"</span><span class="p">,</span> <span class="n">__dir__</span><span class="p">))</span>
<span class="nb">require</span> <span class="s2">"agent"</span>

<span class="n">agent</span> <span class="o">=</span> <span class="no">Agent</span><span class="p">.</span><span class="nf">new</span>
<span class="nb">puts</span> <span class="n">agent</span><span class="p">.</span><span class="nf">ask</span><span class="p">(</span><span class="no">ARGV</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">))</span>
</code></pre></div></div>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod</span> +x bin/agent
<span class="nb">export </span><span class="nv">ANTHROPIC_API_KEY</span><span class="o">=</span><span class="s2">"sk-ant-..."</span>
bin/agent <span class="s2">"Que horas são agora? E me diga o que tem no Gemfile."</span>
</code></pre></div></div>

<p>Saída esperada (algo nessa linha):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Agora são 2026-05-11 14:23:07 -0300.

O Gemfile contém as dependências do projeto: jekyll na versão 4.4.1,
o tema minima, e plugins como jekyll-feed e jekyll-seo-tag.
</code></pre></div></div>

<p>Por baixo dos panos, o que aconteceu:</p>

<ol>
  <li>O modelo recebeu a pergunta e decidiu chamar <code class="language-plaintext highlighter-rouge">current_time</code>.</li>
  <li>Você executou e devolveu o resultado.</li>
  <li>O modelo decidiu chamar <code class="language-plaintext highlighter-rouge">read_file</code> com <code class="language-plaintext highlighter-rouge">path: "Gemfile"</code>.</li>
  <li>Você leu o arquivo e devolveu o conteúdo.</li>
  <li>O modelo escreveu a resposta final em texto e marcou <code class="language-plaintext highlighter-rouge">end_turn</code>.</li>
</ol>

<p>Tudo isso em uma única chamada <code class="language-plaintext highlighter-rouge">agent.ask</code>.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço pra praticar a construção de tools? Quero pegar a manha de quando o modelo escolhe usar uma ou não.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Pega esse mesmo agente e adiciona uma tool <code>calculator</code> que recebe uma expressão matemática como string. Aí faz três experimentos: (1) pergunte "<em>quanto é 2 + 2?</em>" — o modelo provavelmente responde sem chamar a tool; (2) pergunte "<em>quanto é 8472 * 39281?</em>" — agora ele chama; (3) mude o <code>description</code> da tool para "<em>use SEMPRE que houver qualquer operação matemática</em>" e refaça o teste 1. Vai ver como a descrição muda o comportamento do agente <strong>sem trocar nenhuma linha de Ruby</strong>. Esse exercício é o melhor jeito de internalizar que prompt e descrição de tool são parte do "código" do agente.</div>
    </div>
  </div>
</div>

<hr />

<h2 id="o-que-esse-exemplo-deixa-de-fora-de-propósito">O que esse exemplo deixa de fora (de propósito)</h2>

<p>Para manter o foco, o agente acima ignora algumas coisas que você vai querer em produção:</p>

<ul>
  <li><strong>Streaming</strong> — a resposta vem inteira, em vez de chegar token a token.</li>
  <li><strong>Persistência de conversa</strong> — o histórico mora em memória e morre com o processo.</li>
  <li><strong>Validação de input das tools</strong> — o modelo pode mandar argumentos malformados; aqui só confiamos.</li>
  <li><strong>Custo e tokens</strong> — não há contagem nem limite de gasto.</li>
  <li><strong>Permissões</strong> — o <code class="language-plaintext highlighter-rouge">read_file</code> lê qualquer arquivo do sistema. Em um agente real, você precisa de um sandbox.</li>
</ul>

<p>Cada um desses itens vira um post separado. O ponto aqui é mostrar que <strong>um agente de IA, em sua forma mais essencial, cabe em umas 60 linhas de Ruby</strong>.</p>

<hr />

<h2 id="resumo">Resumo</h2>

<table>
  <thead>
    <tr>
      <th>Componente</th>
      <th>Responsabilidade</th>
      <th>No exemplo</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Modelo</strong></td>
      <td>Decidir o próximo passo</td>
      <td>Claude via API HTTP</td>
    </tr>
    <tr>
      <td><strong>Tools</strong></td>
      <td>Executar ações no mundo</td>
      <td><code class="language-plaintext highlighter-rouge">current_time</code>, <code class="language-plaintext highlighter-rouge">read_file</code></td>
    </tr>
    <tr>
      <td><strong>Loop</strong></td>
      <td>Orquestrar pergunta → ação → resultado</td>
      <td><code class="language-plaintext highlighter-rouge">Agent#ask</code></td>
    </tr>
    <tr>
      <td><strong>Parada</strong></td>
      <td>Saber quando terminar</td>
      <td><code class="language-plaintext highlighter-rouge">stop_reason == "end_turn"</code> + teto de iterações</td>
    </tr>
  </tbody>
</table>

<p>Agente de IA não é mágica — é um padrão arquitetural simples em volta de um LLM. Uma vez que o loop está claro na sua cabeça, o resto (memória de longo prazo, multi-agentes, MCP, RAG) são camadas que você adiciona conforme o problema pede. Mas tudo começa aqui: <strong>um modelo, algumas ferramentas, um loop</strong>.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Beleza, Claude. Depois que esse loop básico estiver redondo, qual é o próximo tópico que vale estudar?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Na ordem que mais paga em entendimento: (1) <strong>MCP (Model Context Protocol)</strong> — um padrão aberto pra expor tools de forma reutilizável entre clientes, exatamente o que o Claude Code usa; (2) <strong>RAG</strong> pra dar ao agente acesso a documentos via busca vetorial; (3) <strong>memória de longo prazo</strong>, que no fundo é só "decidir o que enfiar no <code>messages</code> da próxima chamada"; (4) <strong>arquiteturas multi-agente</strong>, onde um agente coordenador delega tarefas a sub-agentes especialistas. O importante é resistir à tentação de pular pra multi-agente cedo demais — quase todo problema "complexo" se resolve com um agente só e tools bem pensadas. Para se aprofundar, o canal <strong>"Anthropic"</strong> no YouTube tem ótimos talks técnicos sobre cada um desses temas.</div>
    </div>
  </div>
</div>]]></content><author><name>anchietajunior</name></author><category term="AI" /><category term="Ruby" /><category term="ai" /><category term="agents" /><category term="llm" /><category term="ruby" /><category term="claude" /><category term="anthropic" /><summary type="html"><![CDATA[O que é um agente de IA?]]></summary></entry><entry xml:lang="pt-br"><title type="html">Normalizando payloads JSON heterogêneos em Ruby puro: um estudo de design patterns</title><link href="/2026/05/10/normalizando-payloads-json-em-ruby-puro/" rel="alternate" type="text/html" title="Normalizando payloads JSON heterogêneos em Ruby puro: um estudo de design patterns" /><published>2026-05-10T00:00:00-03:00</published><updated>2026-05-10T00:00:00-03:00</updated><id>/2026/05/10/normalizando-payloads-json-em-ruby-puro</id><content type="html" xml:base="/2026/05/10/normalizando-payloads-json-em-ruby-puro/"><![CDATA[<h1 id="o-problema">O problema</h1>

<p>Considere o seguinte cenário:</p>

<blockquote>
  <p>Você precisa construir uma aplicação em <strong>Ruby puro</strong> (sem Rails, sem Sinatra) que receba payloads JSON de <strong>diversos fornecedores</strong> (cada um com sua própria estrutura de dados) representando o mesmo conceito de domínio — por exemplo, um <strong>ticket de viagem</strong>. A aplicação deve identificar automaticamente de qual fornecedor o payload veio e tratá-lo de forma uniforme dentro do sistema.</p>
</blockquote>

<p>Esse tipo de problema é interessante porque ele <strong>não é sobre Ruby idiomático em si</strong>, mas sim sobre escolher abstrações e aplicar <strong>design patterns clássicos</strong> sem cair em overengineering.</p>

<hr />

<h2 id="estrutura-do-projeto">Estrutura do projeto</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ticket_normalizer/
├── lib/
│   ├── entrypoint.rb        # carrega tudo
│   ├── ticket.rb            # value object normalizado
│   ├── parser.rb            # fábrica que escolhe o adapter
│   └── adapters/
│       ├── base.rb          # interface comum
│       ├── alpha.rb         # fornecedor A
│       └── beta.rb          # fornecedor B
├── test/
│   ├── test_helper.rb
│   └── parser_test.rb
├── payloads/
│   ├── alpha.json
│   └── beta.json
└── bin/normalize            # script executável
</code></pre></div></div>

<p>Três pastas, uma responsabilidade cada: <code class="language-plaintext highlighter-rouge">lib/</code> é o código, <code class="language-plaintext highlighter-rouge">test/</code> é a verificação, <code class="language-plaintext highlighter-rouge">payloads/</code> são os exemplos para testar manualmente. O <code class="language-plaintext highlighter-rouge">bin/normalize</code> mostra como o consumidor usa a biblioteca.</p>

<hr />

<h2 id="design-patterns-aplicados">Design patterns aplicados</h2>

<p>A solução combina três patterns. Cada um resolve uma responsabilidade específica.</p>

<h3 id="1-adapter-pattern">1. Adapter Pattern</h3>

<p><strong>O que é:</strong> o Adapter converte a interface de uma classe em outra interface esperada pelo cliente. Ele traduz “dialetos” diferentes para uma linguagem comum.</p>

<p><strong>Por que aqui:</strong> cada fornecedor envia o ticket com <strong>chaves diferentes</strong> e às vezes <strong>unidades diferentes</strong> (preço em reais vs. centavos, datas em ISO 8601 vs. timestamp). Em vez de espalhar <code class="language-plaintext highlighter-rouge">if vendor == :alpha</code> pelo código, isolamos cada tradução em uma classe própria.</p>

<p><strong>Interface base</strong> — define o contrato que todo adapter precisa cumprir:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/adapters/base.rb</span>
<span class="k">module</span> <span class="nn">Adapters</span>
  <span class="k">class</span> <span class="nc">Base</span>
    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">matches?</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>

    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@payload</span> <span class="o">=</span> <span class="n">payload</span>

    <span class="k">def</span> <span class="nf">to_ticket</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>

    <span class="kp">private</span> <span class="nb">attr_reader</span> <span class="ss">:payload</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Dois métodos importantes:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">.matches?(payload)</code></strong> — heurística de detecção: presença de uma chave única, valor de um campo <code class="language-plaintext highlighter-rouge">provider</code>, formato de um ID etc.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">#to_ticket</code></strong> — converte o payload bruto em um <code class="language-plaintext highlighter-rouge">Ticket</code> normalizado.</li>
</ul>

<p><strong>Adapters concretos:</strong></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/adapters/alpha.rb</span>
<span class="k">module</span> <span class="nn">Adapters</span>
  <span class="k">class</span> <span class="nc">Alpha</span> <span class="o">&lt;</span> <span class="no">Base</span>
    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">matches?</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="nf">key?</span><span class="p">(</span><span class="s2">"alpha_ticket_id"</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">to_ticket</span>
      <span class="no">Ticket</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
        <span class="ss">external_id:    </span><span class="n">payload</span><span class="p">[</span><span class="s2">"alpha_ticket_id"</span><span class="p">],</span>
        <span class="ss">passenger_name: </span><span class="n">payload</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"passenger"</span><span class="p">,</span> <span class="s2">"full_name"</span><span class="p">),</span>
        <span class="ss">origin:         </span><span class="n">payload</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"trip"</span><span class="p">,</span> <span class="s2">"from"</span><span class="p">),</span>
        <span class="ss">destination:    </span><span class="n">payload</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s2">"trip"</span><span class="p">,</span> <span class="s2">"to"</span><span class="p">),</span>
        <span class="ss">departure_at:   </span><span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s2">"departure_time"</span><span class="p">]),</span>
        <span class="ss">price_cents:    </span><span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="s2">"price"</span><span class="p">].</span><span class="nf">to_f</span> <span class="o">*</span> <span class="mi">100</span><span class="p">).</span><span class="nf">round</span><span class="p">,</span>
        <span class="ss">currency:       </span><span class="n">payload</span><span class="p">[</span><span class="s2">"currency"</span><span class="p">],</span>
        <span class="ss">vendor:         </span><span class="s2">"alpha"</span>
      <span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/adapters/beta.rb</span>
<span class="k">module</span> <span class="nn">Adapters</span>
  <span class="k">class</span> <span class="nc">Beta</span> <span class="o">&lt;</span> <span class="no">Base</span>
    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">matches?</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s2">"source"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"beta_rail"</span>

    <span class="k">def</span> <span class="nf">to_ticket</span>
      <span class="n">data</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="s2">"data"</span><span class="p">]</span>
      <span class="no">Ticket</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span>
        <span class="ss">external_id:    </span><span class="n">data</span><span class="p">[</span><span class="s2">"id"</span><span class="p">],</span>
        <span class="ss">passenger_name: </span><span class="n">data</span><span class="p">[</span><span class="s2">"customer_name"</span><span class="p">],</span>
        <span class="ss">origin:         </span><span class="n">data</span><span class="p">[</span><span class="s2">"origin_station"</span><span class="p">],</span>
        <span class="ss">destination:    </span><span class="n">data</span><span class="p">[</span><span class="s2">"destination_station"</span><span class="p">],</span>
        <span class="ss">departure_at:   </span><span class="no">Time</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="s2">"departs_at_unix"</span><span class="p">]),</span>
        <span class="ss">price_cents:    </span><span class="n">data</span><span class="p">[</span><span class="s2">"price_in_cents"</span><span class="p">],</span>
        <span class="ss">currency:       </span><span class="n">data</span><span class="p">[</span><span class="s2">"currency_iso"</span><span class="p">],</span>
        <span class="ss">vendor:         </span><span class="s2">"beta"</span>
      <span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><strong>Toda a complexidade do payload original fica encapsulada</strong> dentro de cada adapter. O resto do sistema só conhece <code class="language-plaintext highlighter-rouge">Ticket</code>.</p>

<h3 id="2-factory-pattern">2. Factory Pattern</h3>

<p><strong>O que é:</strong> a Factory é responsável por <strong>decidir e instanciar</strong> o objeto correto para uma situação. Aqui ela resolve a pergunta: <em>“dado este payload, qual adapter eu uso?”</em></p>

<p><strong>Por que aqui:</strong> centralizar a lógica de seleção em um único lugar evita que o cliente da biblioteca conheça todos os adapters. Adicionar um novo fornecedor não toca código existente — é o <strong>Open/Closed Principle</strong> na prática.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/parser.rb</span>
<span class="nb">require</span> <span class="s2">"json"</span>

<span class="k">class</span> <span class="nc">Parser</span>
  <span class="no">ADAPTERS</span> <span class="o">=</span> <span class="p">[</span><span class="no">Adapters</span><span class="o">::</span><span class="no">Alpha</span><span class="p">,</span> <span class="no">Adapters</span><span class="o">::</span><span class="no">Beta</span><span class="p">].</span><span class="nf">freeze</span>
  <span class="no">UnknownVendorError</span> <span class="o">=</span> <span class="no">Class</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">StandardError</span><span class="p">)</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">json</span><span class="p">)</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">json</span><span class="p">)</span>
    <span class="n">adapter</span> <span class="o">=</span> <span class="no">ADAPTERS</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="n">a</span><span class="p">.</span><span class="nf">matches?</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="p">}</span>
    <span class="k">raise</span> <span class="no">UnknownVendorError</span> <span class="k">unless</span> <span class="n">adapter</span>

    <span class="n">adapter</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">payload</span><span class="p">).</span><span class="nf">to_ticket</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Detalhes que valem destacar:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">ADAPTERS.freeze</code></strong> comunica que aquela lista é o ponto de configuração do sistema.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">find</code> em vez de <code class="language-plaintext highlighter-rouge">select</code></strong>: o primeiro adapter que casa vence. A ordem da lista importa quando há heurísticas sobrepostas.</li>
  <li><strong>Erro tipado</strong> permite ao chamador fazer <code class="language-plaintext highlighter-rouge">rescue UnknownVendorError</code> específico.</li>
</ul>

<h3 id="3-value-object-pattern">3. Value Object Pattern</h3>

<p><strong>O que é:</strong> um Value Object é um objeto <strong>imutável</strong> definido pelos seus atributos, não por uma identidade. Dois <code class="language-plaintext highlighter-rouge">Ticket</code> com os mesmos atributos são considerados iguais.</p>

<p><strong>Por que aqui:</strong> o <code class="language-plaintext highlighter-rouge">Ticket</code> normalizado é <strong>dado</strong>, não comportamento. Usar <code class="language-plaintext highlighter-rouge">Data.define</code> deixa isso explícito e bloqueia mutação acidental.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/ticket.rb</span>
<span class="no">Ticket</span> <span class="o">=</span> <span class="no">Data</span><span class="p">.</span><span class="nf">define</span><span class="p">(</span>
  <span class="ss">:external_id</span><span class="p">,</span> <span class="ss">:passenger_name</span><span class="p">,</span> <span class="ss">:origin</span><span class="p">,</span> <span class="ss">:destination</span><span class="p">,</span>
  <span class="ss">:departure_at</span><span class="p">,</span> <span class="ss">:price_cents</span><span class="p">,</span> <span class="ss">:currency</span><span class="p">,</span> <span class="ss">:vendor</span>
<span class="p">)</span> <span class="k">do</span>
  <span class="k">def</span> <span class="nf">price</span> <span class="o">=</span> <span class="n">price_cents</span> <span class="o">/</span> <span class="mf">100.0</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Data.define</code> (Ruby 3.2+) entrega de graça: construtor com argumentos nomeados, imutabilidade real, igualdade por atributos e <code class="language-plaintext highlighter-rouge">to_h</code>. Em versões anteriores, a alternativa é <code class="language-plaintext highlighter-rouge">Struct.new(..., keyword_init: true)</code>.</p>

<hr />

<h2 id="entrypoint-e-execução">Entrypoint e execução</h2>

<p>O entry point só carrega as dependências na ordem certa:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/entrypoint.rb</span>
<span class="nb">require_relative</span> <span class="s2">"ticket"</span>
<span class="nb">require_relative</span> <span class="s2">"adapters/base"</span>
<span class="nb">require_relative</span> <span class="s2">"adapters/alpha"</span>
<span class="nb">require_relative</span> <span class="s2">"adapters/beta"</span>
<span class="nb">require_relative</span> <span class="s2">"parser"</span>
</code></pre></div></div>

<p>O <code class="language-plaintext highlighter-rouge">bin/normalize</code> consome a biblioteca via stdin:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env ruby</span>
<span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">unshift</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"../lib"</span><span class="p">,</span> <span class="n">__dir__</span><span class="p">))</span>
<span class="nb">require</span> <span class="s2">"entrypoint"</span>

<span class="nb">puts</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="no">ARGF</span><span class="p">.</span><span class="nf">read</span><span class="p">).</span><span class="nf">to_h</span><span class="p">.</span><span class="nf">inspect</span>
</code></pre></div></div>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod</span> +x bin/normalize
bin/normalize &lt; payloads/alpha.json
bin/normalize &lt; payloads/beta.json
</code></pre></div></div>

<hr />

<h2 id="testes-com-minitest">Testes com Minitest</h2>

<p>Minitest está na stdlib desde o Ruby 1.9 — zero dependências externas. O estilo spec deixa a leitura próxima do RSpec.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># test/test_helper.rb</span>
<span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">unshift</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"../lib"</span><span class="p">,</span> <span class="n">__dir__</span><span class="p">))</span>
<span class="nb">require</span> <span class="s2">"minitest/autorun"</span>
<span class="nb">require</span> <span class="s2">"minitest/spec"</span>
<span class="nb">require</span> <span class="s2">"entrypoint"</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># test/parser_test.rb</span>
<span class="nb">require_relative</span> <span class="s2">"test_helper"</span>

<span class="n">describe</span> <span class="no">Parser</span> <span class="k">do</span>
  <span class="n">it</span> <span class="s2">"normaliza payload alpha"</span> <span class="k">do</span>
    <span class="n">ticket</span> <span class="o">=</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="s2">"payloads/alpha.json"</span><span class="p">))</span>
    <span class="n">_</span><span class="p">(</span><span class="n">ticket</span><span class="p">.</span><span class="nf">vendor</span><span class="p">).</span><span class="nf">must_equal</span> <span class="s2">"alpha"</span>
    <span class="n">_</span><span class="p">(</span><span class="n">ticket</span><span class="p">.</span><span class="nf">price_cents</span><span class="p">).</span><span class="nf">must_be_kind_of</span> <span class="no">Integer</span>
  <span class="k">end</span>

  <span class="n">it</span> <span class="s2">"normaliza payload beta"</span> <span class="k">do</span>
    <span class="n">ticket</span> <span class="o">=</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="s2">"payloads/beta.json"</span><span class="p">))</span>
    <span class="n">_</span><span class="p">(</span><span class="n">ticket</span><span class="p">.</span><span class="nf">vendor</span><span class="p">).</span><span class="nf">must_equal</span> <span class="s2">"beta"</span>
  <span class="k">end</span>

  <span class="n">it</span> <span class="s2">"rejeita payload desconhecido"</span> <span class="k">do</span>
    <span class="n">_</span> <span class="p">{</span> <span class="no">Parser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'{"foo":"bar"}'</span><span class="p">)</span> <span class="p">}.</span><span class="nf">must_raise</span> <span class="no">Parser</span><span class="o">::</span><span class="no">UnknownVendorError</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Rodar os testes sem nenhuma ferramenta extra:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby <span class="nt">-Ilib</span> <span class="nt">-Itest</span> <span class="nb">test</span>/parser_test.rb
</code></pre></div></div>

<hr />

<h2 id="decisões-de-projeto">Decisões de projeto</h2>

<ul>
  <li><strong>Por que <code class="language-plaintext highlighter-rouge">matches?</code> em vez de um campo <code class="language-plaintext highlighter-rouge">vendor</code> no payload?</strong> Porque nem sempre o fornecedor envia esse campo. A heurística de detecção é responsabilidade do adapter.</li>
  <li><strong>Como adicionar um novo fornecedor?</strong> Cria a classe em <code class="language-plaintext highlighter-rouge">lib/adapters/</code>, adiciona o <code class="language-plaintext highlighter-rouge">require_relative</code> no entrypoint e inclui na constante <code class="language-plaintext highlighter-rouge">ADAPTERS</code>. Zero alteração no código existente.</li>
  <li><strong>Por que não usar <code class="language-plaintext highlighter-rouge">dry-struct</code>, <code class="language-plaintext highlighter-rouge">ActiveModel</code>?</strong> O foco é Ruby puro. A stdlib já oferece <code class="language-plaintext highlighter-rouge">Data</code>, <code class="language-plaintext highlighter-rouge">JSON</code> e <code class="language-plaintext highlighter-rouge">Minitest</code> — o suficiente para o escopo.</li>
</ul>

<hr />

<h2 id="resumo">Resumo</h2>

<table>
  <thead>
    <tr>
      <th>Pattern</th>
      <th>Responsabilidade</th>
      <th>Onde mora</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Adapter</strong></td>
      <td>Traduzir o payload de cada fornecedor para o modelo interno</td>
      <td><code class="language-plaintext highlighter-rouge">lib/adapters/*.rb</code></td>
    </tr>
    <tr>
      <td><strong>Factory</strong></td>
      <td>Escolher o adapter certo para um payload</td>
      <td><code class="language-plaintext highlighter-rouge">Parser.parse</code></td>
    </tr>
    <tr>
      <td><strong>Value Object</strong></td>
      <td>Representar o ticket normalizado de forma imutável</td>
      <td><code class="language-plaintext highlighter-rouge">lib/ticket.rb</code></td>
    </tr>
  </tbody>
</table>

<p>Essa combinação resolve o problema com <strong>alta coesão</strong> (cada classe tem um propósito) e <strong>alta extensibilidade</strong> (novos fornecedores não tocam código existente). A elegância da solução não está em ser sofisticada — está em ser <strong>proporcional ao problema</strong>.</p>]]></content><author><name>anchietajunior</name></author><category term="Ruby" /><category term="Design Patterns" /><category term="ruby" /><category term="design-patterns" /><category term="adapter" /><category term="factory" /><category term="minitest" /><summary type="html"><![CDATA[O problema]]></summary></entry><entry xml:lang="pt-br"><title type="html">Os fundamentos matemáticos por trás da Inteligência Artificial</title><link href="/2026/02/28/math-foundations-for-ai/" rel="alternate" type="text/html" title="Os fundamentos matemáticos por trás da Inteligência Artificial" /><published>2026-02-28T12:00:00-03:00</published><updated>2026-02-28T12:00:00-03:00</updated><id>/2026/02/28/math-foundations-for-ai</id><content type="html" xml:base="/2026/02/28/math-foundations-for-ai/"><![CDATA[<h1 id="por-que-matemática-importa-para-ia">Por que matemática importa para IA?</h1>

<p>Começando a estudar Inteligência Artificial recentemente por motivos de mestrado e pós-graduação, rapidamente percebi que frameworks como PyTorch e TensorFlow abstraem muita coisa — mas essa abstração cobra um preço. Sem entender a matemática por trás, você consegue rodar modelos, mas não consegue <strong>debugar</strong>, <strong>otimizar</strong> ou <strong>criar</strong> nada novo.</p>

<p>Este post é um mapa dos principais conceitos matemáticos que sustentam IA, Machine Learning e Deep Learning. Não é um curso completo de cada tópico, mas sim uma explicação de <strong>o que é</strong>, <strong>por que importa</strong> e <strong>onde aparece</strong> na prática.</p>

<p>Para tornar o conteúdo mais prático, o Claude me acompanha ao longo do post com sugestões de exercícios e referências para cada tópico.</p>

<h2 id="1-álgebra-linear">1. Álgebra Linear</h2>

<p>A álgebra linear é a linguagem fundamental da IA. Praticamente tudo — dados, pesos, transformações — é representado como vetores e matrizes.</p>

<h3 id="vetores-e-matrizes">Vetores e Matrizes</h3>

<p>Um vetor é uma lista ordenada de números. Uma imagem 28x28 pixels pode ser representada como um vetor de 784 elementos. Uma matriz é uma tabela de números — os pesos de uma rede neural são armazenados em matrizes.</p>

<p><strong>Onde aparece</strong>: representação de dados, embeddings de texto, imagens como tensores.</p>

<h3 id="multiplicação-de-matrizes">Multiplicação de Matrizes</h3>

<p>A operação mais executada em redes neurais. Quando um input passa por uma camada, o que acontece é uma multiplicação de matrizes seguida de uma função de ativação.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>output = activation(W · input + b)
</code></pre></div></div>

<p>Onde <code class="language-plaintext highlighter-rouge">W</code> é a matriz de pesos e <code class="language-plaintext highlighter-rouge">b</code> é o vetor de bias.</p>

<p><strong>Onde aparece</strong>: forward pass de qualquer rede neural, camadas densas, convoluções.</p>

<h3 id="transposição-e-inversão">Transposição e Inversão</h3>

<p>Transpor uma matriz (trocar linhas por colunas) é usado constantemente em operações de gradiente. A inversão aparece em soluções analíticas como regressão linear.</p>

<p><strong>Onde aparece</strong>: backpropagation, regressão linear, PCA.</p>

<h3 id="autovalores-e-autovetores">Autovalores e Autovetores</h3>

<p>Um autovetor de uma matriz é um vetor que, quando multiplicado pela matriz, muda apenas de escala (não de direção). O fator de escala é o autovalor.</p>

<p><strong>Onde aparece</strong>: PCA (Principal Component Analysis), análise espectral, estabilidade de redes recorrentes.</p>

<h3 id="espaços-vetoriais-e-transformações-lineares">Espaços Vetoriais e Transformações Lineares</h3>

<p>Entender que dados vivem em espaços de alta dimensão e que redes neurais aplicam transformações sucessivas nesses espaços é fundamental para intuir como modelos “aprendem”.</p>

<p><strong>Onde aparece</strong>: embeddings (Word2Vec, BERT), redução de dimensionalidade, atenção em Transformers.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Claude, como faço para praticar álgebra linear no contexto de IA?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Tente este exercício: pegue uma imagem pequena (3x3 pixels em escala de cinza), represente como uma matriz e multiplique por uma matriz identidade, uma de rotação 90° e uma de escala. Veja como a imagem se transforma. Isso é exatamente o que filtros convolucionais fazem. Para se aprofundar, a série <strong>Essence of Linear Algebra</strong> do 3Blue1Brown no YouTube é imbatível — são 15 vídeos curtos que vão mudar sua intuição sobre o assunto.</div>
    </div>
  </div>
</div>

<h2 id="2-cálculo">2. Cálculo</h2>

<p>O cálculo é o motor do aprendizado. Sem ele, não existe treinamento de modelos.</p>

<h3 id="derivadas">Derivadas</h3>

<p>A derivada mede a taxa de mudança de uma função. No contexto de ML, ela responde: “se eu ajustar este peso um pouquinho, quanto a perda (loss) muda?”</p>

<p><strong>Onde aparece</strong>: toda otimização de modelos.</p>

<h3 id="derivadas-parciais">Derivadas Parciais</h3>

<p>Quando a função tem muitas variáveis (e um modelo pode ter bilhões), calculamos derivadas parciais — a derivada em relação a cada variável individualmente.</p>

<p><strong>Onde aparece</strong>: cálculo de gradientes, backpropagation.</p>

<h3 id="regra-da-cadeia">Regra da Cadeia</h3>

<p>A regra da cadeia permite calcular a derivada de funções compostas. Uma rede neural é basicamente uma composição de funções, então a regra da cadeia é o que torna o backpropagation possível.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dL/dw = dL/dy · dy/dz · dz/dw
</code></pre></div></div>

<p><strong>Onde aparece</strong>: backpropagation — é literalmente a base matemática de como redes neurais aprendem.</p>

<h3 id="gradiente">Gradiente</h3>

<p>O gradiente é o vetor de todas as derivadas parciais. Ele aponta na direção de maior crescimento da função. Para minimizar o erro, andamos na direção <strong>oposta</strong> ao gradiente.</p>

<p><strong>Onde aparece</strong>: gradient descent, SGD, Adam, e todos os otimizadores.</p>

<h3 id="integrais">Integrais</h3>

<p>Menos usadas diretamente no dia a dia de ML, mas essenciais para entender distribuições de probabilidade, funções de densidade e normalização.</p>

<p><strong>Onde aparece</strong>: modelos probabilísticos, VAEs (Variational Autoencoders), teoria da informação.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço para praticar cálculo aplicado a redes neurais?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Implemente o backpropagation na mão para uma rede com 1 camada oculta. Use uma função simples como <code>f(x) = sigmoid(w2 * sigmoid(w1 * x + b1) + b2)</code>, calcule a derivada de cada peso usando a regra da cadeia no papel e depois confira com PyTorch usando <code>loss.backward()</code>. É o "aha moment" que faz tudo clicar. O capítulo 4 do livro <strong>Neural Networks and Deep Learning</strong> de Michael Nielsen (disponível online gratuitamente) guia você por esse exercício passo a passo.</div>
    </div>
  </div>
</div>

<h2 id="3-probabilidade-e-estatística">3. Probabilidade e Estatística</h2>

<p>Se álgebra linear é a linguagem e cálculo é o motor, probabilidade é a <strong>lógica</strong> da IA. Quase todo modelo lida com incerteza.</p>

<h3 id="probabilidade-básica">Probabilidade Básica</h3>

<table>
  <tbody>
    <tr>
      <td>Eventos, probabilidade condicional e independência. Entender P(A</td>
      <td>B) — a probabilidade de A dado que B aconteceu — é fundamental.</td>
    </tr>
  </tbody>
</table>

<p><strong>Onde aparece</strong>: classificadores, modelos generativos, qualquer predição com incerteza.</p>

<h3 id="teorema-de-bayes">Teorema de Bayes</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>P(A|B) = P(B|A) · P(A) / P(B)
</code></pre></div></div>

<p>O teorema de Bayes permite atualizar crenças com base em evidências. É a base de classificadores Naive Bayes e de toda a inferência bayesiana.</p>

<p><strong>Onde aparece</strong>: Naive Bayes, filtros de spam, redes bayesianas, inferência probabilística.</p>

<h3 id="distribuições-de-probabilidade">Distribuições de Probabilidade</h3>

<p>As mais importantes para IA:</p>

<ul>
  <li><strong>Normal (Gaussiana)</strong>: inicialização de pesos, ruído, normalização</li>
  <li><strong>Bernoulli</strong>: classificação binária, dropout</li>
  <li><strong>Categórica/Multinomial</strong>: classificação multiclasse, softmax</li>
  <li><strong>Uniforme</strong>: inicialização, amostragem aleatória</li>
</ul>

<p><strong>Onde aparece</strong>: em praticamente tudo — de inicialização de pesos a modelos generativos.</p>

<h3 id="valor-esperado-e-variância">Valor Esperado e Variância</h3>

<p>O valor esperado é a “média ponderada” dos resultados possíveis. A variância mede o quanto os valores se dispersam. Controlar a variância dos gradientes é crucial para treinar modelos estáveis.</p>

<p><strong>Onde aparece</strong>: funções de perda, normalização (batch norm, layer norm), análise de convergência.</p>

<h3 id="maximum-likelihood-estimation-mle">Maximum Likelihood Estimation (MLE)</h3>

<p>A ideia de encontrar os parâmetros que maximizam a probabilidade dos dados observados. É a justificativa teórica por trás de muitas funções de perda.</p>

<p><strong>Onde aparece</strong>: regressão logística, cross-entropy loss, modelos de linguagem.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço para praticar probabilidade para IA?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Tente este problema clássico: um teste médico tem 99% de acurácia e a doença afeta 1 em cada 10.000 pessoas. Se o teste deu positivo, qual a probabilidade real de ter a doença? Aplique Bayes e vai se surpreender — o resultado é contraintuitivo e ilustra perfeitamente por que probabilidade condicional importa. Para ir além, o livro <strong>Think Bayes</strong> de Allen Downey ensina probabilidade bayesiana com Python e está disponível gratuitamente online.</div>
    </div>
  </div>
</div>

<h2 id="4-otimização">4. Otimização</h2>

<p>O treinamento de qualquer modelo de ML é, no fundo, um problema de otimização: encontrar os parâmetros que minimizam uma função de erro.</p>

<h3 id="gradient-descent">Gradient Descent</h3>

<p>O algoritmo mais fundamental de ML. A cada passo, calculamos o gradiente da função de perda e ajustamos os pesos na direção oposta.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>w = w - learning_rate * gradiente
</code></pre></div></div>

<p><strong>Onde aparece</strong>: treinamento de qualquer modelo.</p>

<h3 id="stochastic-gradient-descent-sgd">Stochastic Gradient Descent (SGD)</h3>

<p>Em vez de calcular o gradiente usando todos os dados, usamos mini-batches aleatórios. Isso torna o treinamento viável para datasets grandes e adiciona ruído que ajuda a escapar de mínimos locais.</p>

<p><strong>Onde aparece</strong>: treinamento de redes neurais em escala.</p>

<h3 id="otimizadores-avançados-adam-rmsprop-adagrad">Otimizadores Avançados (Adam, RMSprop, AdaGrad)</h3>

<p>Variações do SGD que adaptam a taxa de aprendizado para cada parâmetro individualmente. Adam é o mais popular e combina momentum com taxas adaptativas.</p>

<p><strong>Onde aparece</strong>: praticamente todo treinamento moderno de deep learning.</p>

<h3 id="funções-de-perda-loss-functions">Funções de Perda (Loss Functions)</h3>

<p>A função que o otimizador tenta minimizar. As mais comuns:</p>

<ul>
  <li><strong>MSE (Mean Squared Error)</strong>: regressão</li>
  <li><strong>Cross-Entropy</strong>: classificação</li>
  <li><strong>Binary Cross-Entropy</strong>: classificação binária</li>
</ul>

<p>Escolher a função de perda certa é tão importante quanto a arquitetura do modelo.</p>

<h3 id="convexidade-e-mínimos-locais">Convexidade e Mínimos Locais</h3>

<p>Uma função convexa tem um único mínimo — fácil de otimizar. Redes neurais têm funções de perda não-convexas com muitos mínimos locais. Entender isso explica por que técnicas como momentum e learning rate scheduling existem.</p>

<p><strong>Onde aparece</strong>: design de treinamento, debugging de convergência.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço para praticar otimização?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Implemente gradient descent do zero em Python para encontrar o mínimo de <code>f(x) = x⁴ - 3x³ + 2</code>. Comece com um x aleatório, calcule a derivada <code>f'(x) = 4x³ - 9x²</code>, e atualize x com diferentes learning rates (0.01, 0.1, 0.5). Plote a trajetória do x sobre a curva — vai visualizar como o learning rate afeta a convergência. Depois, repita com SGD adicionando ruído gaussiano ao gradiente e compare. O curso <strong>Convex Optimization</strong> de Stephen Boyd (Stanford, disponível no YouTube) é a referência definitiva sobre o tema.</div>
    </div>
  </div>
</div>

<h2 id="5-teoria-da-informação">5. Teoria da Informação</h2>

<p>Conceitos da teoria da informação aparecem em funções de perda, compressão de modelos e avaliação de qualidade.</p>

<h3 id="entropia">Entropia</h3>

<p>Mede a quantidade de “surpresa” ou incerteza em uma distribuição. Uma distribuição uniforme tem entropia máxima; uma distribuição concentrada em um valor tem entropia baixa.</p>

<p><strong>Onde aparece</strong>: cross-entropy loss, árvores de decisão, avaliação de modelos de linguagem.</p>

<h3 id="cross-entropy">Cross-Entropy</h3>

<p>Mede a diferença entre duas distribuições de probabilidade. Quando usamos como função de perda, estamos medindo o quão diferente a predição do modelo é da distribuição real.</p>

<p><strong>Onde aparece</strong>: a função de perda mais usada em classificação e modelos de linguagem.</p>

<h3 id="kl-divergence">KL Divergence</h3>

<table>
  <tbody>
    <tr>
      <td>Mede o quão diferente uma distribuição é de outra. É assimétrica — KL(P</td>
      <td> </td>
      <td>Q) ≠ KL(Q</td>
      <td> </td>
      <td>P).</td>
    </tr>
  </tbody>
</table>

<p><strong>Onde aparece</strong>: VAEs, destilação de modelos, RLHF (o fine-tuning por feedback humano).</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço para praticar teoria da informação?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Calcule a entropia de um dado justo (6 lados) e de um dado viciado onde o 6 sai 50% das vezes. Qual tem mais entropia? Depois, calcule a cross-entropy entre a distribuição real do dado viciado e a predição de um modelo que acha que o dado é justo. A diferença entre as duas é a KL Divergence — e é exatamente isso que minimizamos ao treinar modelos. O livro <strong>Information Theory, Inference and Learning Algorithms</strong> de David MacKay é fantástico e gratuito online.</div>
    </div>
  </div>
</div>

<h2 id="6-geometria-e-topologia-noções">6. Geometria e Topologia (noções)</h2>

<h3 id="distâncias-e-similaridade">Distâncias e Similaridade</h3>

<ul>
  <li><strong>Distância Euclidiana</strong>: distância “em linha reta” entre dois pontos</li>
  <li><strong>Similaridade do Cosseno</strong>: mede o ângulo entre dois vetores, ignorando magnitude</li>
  <li><strong>Distância de Manhattan</strong>: soma das diferenças absolutas</li>
</ul>

<p><strong>Onde aparece</strong>: K-NN, clustering, busca por similaridade em embeddings, RAG.</p>

<h3 id="espaços-de-alta-dimensão">Espaços de Alta Dimensão</h3>

<p>A intuição humana falha em dimensões altas. Fenômenos como a “maldição da dimensionalidade” — onde os dados ficam esparsos e distâncias perdem significado — são reais e afetam o design de modelos.</p>

<p><strong>Onde aparece</strong>: embeddings, redução de dimensionalidade, regularização.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Como faço para praticar conceitos de distância e similaridade?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Pegue 5 frases curtas, gere embeddings com um modelo como o <code>sentence-transformers</code> e calcule a similaridade do cosseno entre todos os pares. Ordene do mais similar ao menos similar e veja se bate com a sua intuição. Depois, tente com a distância euclidiana e compare os rankings. Você vai notar que cosseno é melhor para comparar "direção" (significado) enquanto euclidiana é sensível à magnitude. É assim que sistemas de busca semântica e RAG funcionam na prática.</div>
    </div>
  </div>
</div>

<h2 id="roteiro-de-estudo-sugerido">Roteiro de estudo sugerido</h2>

<p>Para quem quer construir uma base sólida, sugiro esta ordem:</p>

<ol>
  <li><strong>Álgebra Linear</strong> — comece aqui. Vetores, matrizes, multiplicação</li>
  <li><strong>Cálculo</strong> — derivadas, regra da cadeia, gradientes</li>
  <li><strong>Probabilidade</strong> — distribuições, Bayes, MLE</li>
  <li><strong>Otimização</strong> — gradient descent e variações</li>
  <li><strong>Teoria da Informação</strong> — entropia, cross-entropy</li>
</ol>

<h3 id="recursos-recomendados">Recursos recomendados</h3>

<ul>
  <li><strong>Mathematics for Machine Learning</strong> (Deisenroth, Faisal, Ong) — livro gratuito online, cobre tudo com foco em ML</li>
  <li><strong>3Blue1Brown</strong> (YouTube) — as melhores visualizações de álgebra linear e cálculo que existem</li>
  <li><strong>Khan Academy</strong> — para revisar fundamentos que ficaram enferrujados</li>
  <li><strong>Deep Learning Book</strong> (Goodfellow, Bengio, Courville) — capítulos 2-4 cobrem a matemática essencial</li>
</ul>

<h2 id="conclusão">Conclusão</h2>

<p>Você não precisa ser matemático para trabalhar com IA, mas precisa ser <strong>amigo</strong> da matemática. Entender esses conceitos transforma a experiência de “usar frameworks como caixa preta” para “entender o que está acontecendo e por quê”.</p>

<p>No mestrado, percebi que a maioria dos papers e técnicas novas são variações criativas desses mesmos fundamentos. Quem domina a base matemática consegue ler papers, implementar arquiteturas novas e — mais importante — saber <strong>por que</strong> algo funciona ou não funciona.</p>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Valeu pela ajuda, Claude. Acho que ficou um bom mapa para quem está começando.</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Ficou ótimo! E lembra: não precisa dominar tudo de uma vez. Vá praticando conforme os conceitos aparecem nos seus projetos e papers do mestrado. A matemática faz muito mais sentido quando tem um problema real motivando o estudo.</div>
    </div>
  </div>
</div>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Uma última dúvida, Claude. Quais são os pré-requisitos matemáticos para conseguir estudar esses tópicos? O que eu preciso saber antes de mergulhar na matemática da IA?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Boa pergunta — e a resposta é mais acessível do que parece. A base que você precisa ter é a matemática do ensino médio bem consolidada:<br /><br /><strong>1. Aritmética e operações básicas</strong> — frações, potências, raízes, notação científica. Parece trivial, mas é o alicerce de tudo.<br /><br /><strong>2. Funções</strong> — entender o que é uma função, domínio, imagem, gráficos. Funções lineares, quadráticas, exponenciais e logarítmicas. Sem isso, derivadas e gradientes não fazem sentido.<br /><br /><strong>3. Equações e sistemas lineares</strong> — resolver sistemas de equações é literalmente o que álgebra linear generaliza com matrizes. Se você sabe resolver 2 equações com 2 incógnitas, já tem a intuição certa.<br /><br /><strong>4. Trigonometria básica</strong> — seno, cosseno e a noção de ângulo entre vetores. A similaridade do cosseno, usada em embeddings e busca semântica, vem diretamente daqui.<br /><br /><strong>5. Noções de conjuntos e lógica</strong> — união, interseção, pertinência. Ajuda muito na hora de entender probabilidade e teoria da informação.<br /><br /><strong>6. Geometria analítica</strong> — pontos, retas, distância entre pontos no plano cartesiano. É a versão 2D do que depois vira espaços de alta dimensão.<br /><br />Se algum desses pontos está enferrujado, a <strong>Khan Academy</strong> cobre tudo de forma gratuita e progressiva. Dedique 2-3 semanas revisando esses fundamentos e depois os tópicos deste post vão fluir muito melhor. A verdade é que a barreira de entrada para a matemática de IA não é ser um gênio — é ter paciência para solidificar a base.</div>
    </div>
  </div>
</div>

<div class="chat">
  <div class="chat-message jose">
    <div class="chat-avatar">JA</div>
    <div>
      <div class="chat-name">José</div>
      <div class="chat-bubble">Recomenda algum livro em português que cubra todos ou a maior parte desses pré-requisitos?</div>
    </div>
  </div>
  <div class="chat-message claude">
    <div class="chat-avatar">C</div>
    <div>
      <div class="chat-name">Claude</div>
      <div class="chat-bubble">Recomendo o <strong>"Fundamentos de Matemática Elementar"</strong> do Gelson Iezzi e coautores. É uma coleção clássica brasileira em 11 volumes que cobre praticamente tudo: conjuntos, funções, trigonometria, logaritmos, sistemas lineares, geometria analítica e mais. Os volumes mais relevantes para quem quer se preparar para a matemática de IA são:<br /><br />• <strong>Vol. 1</strong> — Conjuntos, Funções<br />• <strong>Vol. 3</strong> — Trigonometria<br />• <strong>Vol. 4</strong> — Sequências, Matrizes, Determinantes e Sistemas<br />• <strong>Vol. 7</strong> — Geometria Analítica<br /><br />É o livro que a maioria dos cursos de exatas no Brasil usa como referência. A linguagem é clara, tem muitos exercícios resolvidos e vai te dar uma base sólida para depois atacar álgebra linear e cálculo com confiança. Se quiser algo mais compacto e direto ao ponto, o <strong>"Pré-Cálculo"</strong> do Valdir Bezerra (editora LTC) condensa tudo que você precisa em um único volume com foco em preparar para cálculo e álgebra linear.</div>
    </div>
  </div>
</div>]]></content><author><name>anchietajunior</name></author><category term="AI" /><category term="Mathematics" /><category term="ai" /><category term="machine-learning" /><category term="deep-learning" /><category term="math" /><summary type="html"><![CDATA[Por que matemática importa para IA?]]></summary></entry><entry xml:lang="pt-br"><title type="html">Meu setup do Claude Code: como uso IA no terminal para programar</title><link href="/2026/02/28/my-claude-code-setup/" rel="alternate" type="text/html" title="Meu setup do Claude Code: como uso IA no terminal para programar" /><published>2026-02-28T00:00:00-03:00</published><updated>2026-02-28T00:00:00-03:00</updated><id>/2026/02/28/my-claude-code-setup</id><content type="html" xml:base="/2026/02/28/my-claude-code-setup/"><![CDATA[<h1 id="claude-code-no-meu-dia-a-dia">Claude Code no meu dia a dia</h1>

<p>Nos últimos meses, o <strong>Claude Code</strong> se tornou uma das ferramentas mais presentes no meu fluxo de trabalho como desenvolvedor. Diferente de assistentes que funcionam dentro de IDEs com autocomplete, o Claude Code roda direto no terminal — e isso muda completamente a forma como eu interajo com ele.</p>

<p>Neste post, vou compartilhar como está meu setup atual, quais plugins uso, como configurei permissões e a status line customizada que me ajuda a monitorar o contexto da conversa.</p>

<h2 id="o-que-é-o-claude-code">O que é o Claude Code?</h2>

<p>O Claude Code é a CLI oficial da Anthropic para o Claude. Ele funciona como um agente de engenharia de software que pode:</p>

<ul>
  <li>Ler, criar e editar arquivos no seu projeto</li>
  <li>Executar comandos no terminal</li>
  <li>Pesquisar na web e buscar documentação</li>
  <li>Navegar pelo codebase com ferramentas como Glob e Grep</li>
  <li>Criar commits, abrir PRs e interagir com o GitHub</li>
  <li>Lançar sub-agentes para tarefas paralelas</li>
</ul>

<p>A versão que estou usando atualmente é a <strong>2.1.61</strong>.</p>

<h2 id="minha-configuração-settingsjson">Minha configuração (<code class="language-plaintext highlighter-rouge">settings.json</code>)</h2>

<p>O coração da configuração fica em <code class="language-plaintext highlighter-rouge">~/.claude/settings.json</code>. Aqui está o meu:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"permissions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"allow"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="s2">"Bash(bin/rails test:*)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"Bash(bin/rails db:migrate*)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"Bash(bundle exec rubocop*)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"Bash(bun:*)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"Bash(foreman start)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"Bash(git:*)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"WebSearch"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"WebFetch(domain:api.stripe.com)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"WebFetch(domain:qbench.net)"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"WebFetch(domain:github.com)"</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"enabledPlugins"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"compound-engineering@every-marketplace"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ralph-loop@claude-plugins-official"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"skipDangerousModePermissionPrompt"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="permissões">Permissões</h3>

<p>Uma das partes mais importantes do setup são as <strong>permissões pré-aprovadas</strong>. Sem elas, o Claude Code pede confirmação para cada comando executado no terminal, o que pode ser bem lento no fluxo de trabalho. Eu configurei permissões para:</p>

<ul>
  <li><strong>Rails</strong>: rodar testes (<code class="language-plaintext highlighter-rouge">bin/rails test:*</code>) e migrações (<code class="language-plaintext highlighter-rouge">bin/rails db:migrate*</code>)</li>
  <li><strong>Rubocop</strong>: executar o linter livremente</li>
  <li><strong>Bun</strong>: gerenciamento de pacotes JavaScript</li>
  <li><strong>Foreman</strong>: iniciar o servidor de desenvolvimento</li>
  <li><strong>Git</strong>: todos os comandos git</li>
  <li><strong>Web</strong>: pesquisa na web e fetch de domínios específicos (Stripe, GitHub)</li>
</ul>

<p>Isso me dá um equilíbrio entre produtividade e segurança — o Claude pode executar comandos comuns sem pedir permissão, mas qualquer coisa fora desse escopo ainda precisa da minha aprovação.</p>

<h3 id="agent-teams-experimental">Agent Teams (experimental)</h3>

<p>A variável <code class="language-plaintext highlighter-rouge">CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS</code> habilita o recurso de times de agentes. Isso permite que o Claude Code lance múltiplos agentes que trabalham em paralelo, se comunicam entre si e dividem tarefas complexas. É útil para projetos maiores onde diferentes partes do trabalho podem ser feitas simultaneamente.</p>

<h3 id="agent-teams--tmux-paralelismo-real">Agent Teams + tmux: paralelismo real</h3>

<p>Uma das combinações mais poderosas que descobri é usar <strong>Agent Teams com tmux</strong>. A ideia é simples: em vez de rodar tudo em um único painel do terminal, eu abro uma sessão tmux com múltiplos painéis e deixo cada agente trabalhando de forma visível.</p>

<p>Na prática, o fluxo funciona assim:</p>

<ol>
  <li>Abro uma sessão tmux e divido a tela em painéis</li>
  <li>No painel principal, inicio o Claude Code como líder do time</li>
  <li>Quando ele lança sub-agentes, cada um aparece em seu próprio processo</li>
  <li>Consigo acompanhar visualmente o que cada agente está fazendo em tempo real</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Criar sessão tmux com layout para agent teams</span>
tmux new-session <span class="nt">-s</span> claude <span class="nt">-d</span>
tmux split-window <span class="nt">-h</span>
tmux split-window <span class="nt">-v</span>
tmux <span class="k">select</span><span class="nt">-pane</span> <span class="nt">-t</span> 0
tmux split-window <span class="nt">-v</span>
tmux attach <span class="nt">-t</span> claude
</code></pre></div></div>

<p>Com esse layout de 4 painéis, consigo monitorar o agente principal e até 3 sub-agentes simultaneamente. É como ter um time de desenvolvedores trabalhando na sua tela.</p>

<p>O que torna isso prático:</p>

<ul>
  <li><strong>Visibilidade</strong>: vejo exatamente o que cada agente está fazendo, sem precisar esperar o resultado final</li>
  <li><strong>Controle</strong>: se um agente tomar um caminho errado, posso identificar rapidamente</li>
  <li><strong>Produtividade</strong>: enquanto um agente pesquisa documentação, outro pode estar editando código e um terceiro rodando testes</li>
</ul>

<p>Um exemplo real: ao importar e traduzir 20 posts para este blog, o Claude Code dividiu o trabalho entre 3 agentes — cada um traduziu um lote de posts em paralelo. O que levaria vários minutos sequencialmente foi resolvido em uma fração do tempo.</p>

<p>Para quem já usa tmux no dia a dia, a integração é natural. E para quem não usa, esse é um ótimo motivo para começar.</p>

<h2 id="plugins">Plugins</h2>

<p>Atualmente uso dois plugins:</p>

<h3 id="compound-engineering-every-marketplace">Compound Engineering (Every Marketplace)</h3>

<p>O plugin <code class="language-plaintext highlighter-rouge">compound-engineering</code> adiciona uma grande quantidade de agentes especializados e skills ao Claude Code. Alguns que uso com frequência:</p>

<ul>
  <li><strong>Agentes de review</strong>: revisão de código com diferentes perspectivas (Rails, segurança, performance, arquitetura)</li>
  <li><strong>Design Iterator</strong>: para refinar interfaces iterativamente</li>
  <li><strong>Framework Docs Researcher</strong>: busca documentação atualizada de libraries</li>
  <li><strong>Context7</strong>: integração com documentação e code examples atualizados de qualquer library</li>
</ul>

<h3 id="ralph-loop">Ralph Loop</h3>

<p>O plugin <code class="language-plaintext highlighter-rouge">ralph-loop</code> permite criar loops autônomos onde o Claude Code continua trabalhando em uma tarefa sem precisar de input constante. Útil para tarefas mais longas.</p>

<h2 id="status-line-customizada">Status Line customizada</h2>

<p>Uma das customizações que mais gosto é a <strong>status line</strong> no terminal. Ela mostra em tempo real:</p>

<ul>
  <li>O <strong>modelo</strong> sendo usado (Opus, Sonnet, Haiku)</li>
  <li>O <strong>diretório</strong> do projeto atual</li>
  <li>O <strong>branch</strong> do git ativo</li>
  <li>Uma <strong>barra de progresso</strong> do contexto utilizado</li>
  <li>O <strong>horário</strong> atual</li>
</ul>

<p>A configuração fica no <code class="language-plaintext highlighter-rouge">settings.json</code>, na chave <code class="language-plaintext highlighter-rouge">statusLine</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"statusLine"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"command"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;conteúdo do script abaixo&gt;"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>O comando em si é um shell script. Para facilitar a leitura, aqui está ele formatado:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">input</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat</span><span class="si">)</span>

<span class="c"># Extrai informações do JSON de input</span>
<span class="nv">model</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$input</span><span class="s2">"</span> | jq <span class="nt">-r</span> <span class="s1">'.model.display_name'</span><span class="si">)</span>
<span class="nv">cwd</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$input</span><span class="s2">"</span> | jq <span class="nt">-r</span> <span class="s1">'.workspace.current_dir'</span><span class="si">)</span>
<span class="nv">dir_name</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="s2">"</span><span class="nv">$cwd</span><span class="s2">"</span><span class="si">)</span>

<span class="c"># Detecta branch do git</span>
<span class="nv">git_info</span><span class="o">=</span><span class="s2">""</span>
<span class="k">if </span>git <span class="nt">-C</span> <span class="s2">"</span><span class="nv">$cwd</span><span class="s2">"</span> rev-parse <span class="nt">--git-dir</span> <span class="o">&gt;</span> /dev/null 2&gt;&amp;1<span class="p">;</span> <span class="k">then
  </span><span class="nv">branch</span><span class="o">=</span><span class="si">$(</span>git <span class="nt">-C</span> <span class="s2">"</span><span class="nv">$cwd</span><span class="s2">"</span> <span class="nt">--no-optional-locks</span> branch <span class="nt">--show-current</span> 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">"detached"</span><span class="si">)</span>
  <span class="nv">git_info</span><span class="o">=</span><span class="s2">" - 🌿 </span><span class="k">${</span><span class="nv">branch</span><span class="k">}</span><span class="s2">"</span>
<span class="k">fi</span>

<span class="c"># Monta barra de progresso do contexto</span>
<span class="nv">progress_bar</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">remaining</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$input</span><span class="s2">"</span> | jq <span class="nt">-r</span> <span class="s1">'.context_window.remaining_percentage // empty'</span><span class="si">)</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$remaining</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nv">used</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$input</span><span class="s2">"</span> | jq <span class="nt">-r</span> <span class="s1">'.context_window.used_percentage // 0'</span><span class="si">)</span>
  <span class="nv">used_int</span><span class="o">=</span><span class="k">${</span><span class="nv">used</span><span class="p">%.*</span><span class="k">}</span>
  <span class="nv">bar_length</span><span class="o">=</span>20
  <span class="nv">filled</span><span class="o">=</span><span class="k">$((</span>used_int <span class="o">*</span> bar_length <span class="o">/</span> <span class="m">100</span><span class="k">))</span>
  <span class="nv">empty</span><span class="o">=</span><span class="k">$((</span>bar_length <span class="o">-</span> filled<span class="k">))</span>
  <span class="nv">bar</span><span class="o">=</span><span class="s2">""</span>
  <span class="k">for</span> <span class="o">((</span><span class="nv">i</span><span class="o">=</span>0<span class="p">;</span> i&lt;filled<span class="p">;</span> i++<span class="o">))</span><span class="p">;</span> <span class="k">do </span><span class="nv">bar</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">bar</span><span class="k">}</span><span class="s2">█"</span><span class="p">;</span> <span class="k">done
  for</span> <span class="o">((</span><span class="nv">i</span><span class="o">=</span>0<span class="p">;</span> i&lt;empty<span class="p">;</span> i++<span class="o">))</span><span class="p">;</span> <span class="k">do </span><span class="nv">bar</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">bar</span><span class="k">}</span><span class="s2">░"</span><span class="p">;</span> <span class="k">done
  </span><span class="nv">progress_bar</span><span class="o">=</span><span class="si">$(</span><span class="nb">printf</span> <span class="s2">"[</span><span class="se">\0</span><span class="s2">33[32m%s</span><span class="se">\0</span><span class="s2">33[0m] %d%%"</span> <span class="s2">"</span><span class="nv">$bar</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$used_int</span><span class="s2">"</span><span class="si">)</span>
<span class="k">fi

</span><span class="nb">time</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%H:%M:%S<span class="si">)</span>

<span class="c"># Output: linha 1 = modelo + diretório + branch</span>
<span class="c">#         linha 2 = barra de contexto + horário</span>
<span class="nb">printf</span> <span class="s2">"%s - 📁 %s%s</span><span class="se">\n</span><span class="s2">%s - %s"</span> <span class="se">\</span>
  <span class="s2">"</span><span class="nv">$model</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$dir_name</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$git_info</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$progress_bar</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$time</span><span class="s2">"</span>
</code></pre></div></div>

<p>O Claude Code passa um JSON via stdin com informações sobre o modelo, diretório e uso de contexto. O script lê esse JSON com <code class="language-plaintext highlighter-rouge">jq</code>, monta uma barra visual e exibe tudo na status line.</p>

<p>A barra de contexto é especialmente útil: conforme a conversa cresce, o Claude Code consome mais da janela de contexto. Quando a barra está quase cheia, sei que é hora de iniciar uma nova conversa ou comprimir o contexto.</p>

<h2 id="como-uso-no-dia-a-dia">Como uso no dia a dia</h2>

<p>Meu fluxo típico com o Claude Code:</p>

<ol>
  <li><strong>Abro o terminal no diretório do projeto</strong> e digito <code class="language-plaintext highlighter-rouge">claude</code></li>
  <li><strong>Descrevo a tarefa</strong> em linguagem natural — pode ser em português ou inglês</li>
  <li><strong>Reviso as mudanças</strong> que ele propõe antes de aprovar</li>
  <li><strong>Peço commits e PRs</strong> quando estou satisfeito com o resultado</li>
  <li><strong>Uso <code class="language-plaintext highlighter-rouge">/help</code></strong> quando preciso lembrar de funcionalidades</li>
</ol>

<h3 id="dicas-práticas">Dicas práticas</h3>

<ul>
  <li><strong>Seja específico</strong>: quanto mais contexto você der, melhor o resultado</li>
  <li><strong>Use o modo plan</strong> para tarefas complexas — ele analisa o codebase antes de fazer mudanças</li>
  <li><strong>Aproveite os sub-agentes</strong>: para pesquisa ou tarefas paralelas, o Claude Code pode lançar agentes especializados</li>
  <li><strong>Configure permissões</strong>: poupa muito tempo não ter que aprovar cada <code class="language-plaintext highlighter-rouge">git status</code></li>
  <li><strong>Monitore o contexto</strong>: conversas muito longas perdem qualidade. Comece uma nova quando necessário</li>
</ul>

<h2 id="conclusão">Conclusão</h2>

<p>O Claude Code mudou significativamente como eu trabalho. A combinação de acesso ao terminal, leitura do codebase e capacidade de executar comandos faz dele muito mais do que um chatbot — é um par de programação que realmente entende o projeto.</p>

<p>Se você é desenvolvedor e ainda não experimentou, recomendo fortemente. E se já usa, invista tempo configurando as permissões e a status line — faz toda a diferença na produtividade.</p>]]></content><author><name>anchietajunior</name></author><category term="AI" /><category term="Tools" /><category term="claude-code" /><category term="ai" /><category term="productivity" /><category term="setup" /><summary type="html"><![CDATA[Claude Code no meu dia a dia]]></summary></entry><entry xml:lang="pt-br"><title type="html">Mergulho profundo no CurrentAttributes do Rails</title><link href="/2025/12/18/current_attributes/" rel="alternate" type="text/html" title="Mergulho profundo no CurrentAttributes do Rails" /><published>2025-12-18T13:00:00-03:00</published><updated>2025-12-18T13:00:00-03:00</updated><id>/2025/12/18/current_attributes</id><content type="html" xml:base="/2025/12/18/current_attributes/"><![CDATA[<h1 id="entendendo-contexto-de-execução-no-rails">Entendendo Contexto de Execução no Rails</h1>

<p>Estou criando alguns posts de estudos particulares com ajuda de IA e decidi começar a compartlhar um pouco do que tenho aprendido utilizando IA.</p>

<h2 id="processos-threads-fibers-e-activesupportcurrentattributes">Processos, Threads, Fibers e ActiveSupport::CurrentAttributes</h2>

<p>Quem trabalha com Rails há algum tempo inevitavelmente esbarra em conceitos como Current.user, concorrência, vazamento de contexto e comportamentos estranhos que só aparecem em produção. Muitas vezes esses problemas são atribuídos a “Rails magic”, quando na verdade eles nascem de um entendimento incompleto sobre como o código Rails é executado em tempo de execução.</p>

<p>Este texto tem um objetivo simples: explicar, de forma progressiva e conectada, como processos, threads e fibers se relacionam no Rails moderno e como o framework usa essa estrutura para compartilhar contexto com segurança através do ActiveSupport::CurrentAttributes.</p>

<h2 id="processos-o-primeiro-nível-de-isolamento">Processos: o primeiro nível de isolamento</h2>

<p>Um processo é a unidade mais isolada de execução fornecida pelo sistema operacional. Quando um processo é criado, ele recebe seu próprio espaço de memória, seu próprio identificador (PID) e não compartilha variáveis, objetos ou estado com outros processos.</p>

<p>No contexto de Rails, isso significa que cada worker de um servidor web em modo cluster (como Puma) ou cada processo do Sidekiq roda em um processo separado. Mesmo que dois processos estejam executando o mesmo código Rails, eles não enxergam a memória um do outro. Se um processo altera um objeto em memória, os demais processos não sabem que isso aconteceu.</p>

<p>Uma boa analogia é pensar no processo como uma casa: tudo o que está dentro dela pertence apenas àquela casa. Outras casas podem ser idênticas, mas não compartilham móveis.</p>

<p>Essa característica é o motivo pelo qual nenhum estado em memória — incluindo CurrentAttributes — pode atravessar processos. Se algo precisa ser compartilhado entre processos, ele precisa ser persistido em um banco, cache externo ou fila.</p>

<h2 id="threads-concorrência-dentro-do-mesmo-processo">Threads: concorrência dentro do mesmo processo</h2>

<p>Dentro de um processo, o sistema operacional permite a criação de threads. Uma thread é um fluxo de execução independente, mas que compartilha a mesma memória do processo que a criou.</p>

<p>No Rails, cada request é atribuído a uma thread do servidor web. Em um processo Puma com múltiplas threads, vários requests podem estar sendo processados simultaneamente, todos compartilhando as mesmas classes, constantes e heap de memória.</p>

<p>A grande diferença entre processo e thread é que threads compartilham memória, mas não compartilham execução. Cada thread tem sua própria stack, seu próprio contador de instruções e seu próprio Thread.current. Além disso, threads são preemptivas: o sistema operacional pode interromper uma thread a qualquer momento para dar espaço a outra.</p>

<p>Uma analogia útil é pensar na thread como pessoas andando dentro da mesma casa. Elas compartilham os mesmos móveis, mas cada uma tem sua própria linha de pensamento e pode ser interrompida no meio de uma ação.</p>

<p>Essa característica é essencial para entender por que o Rails não pode usar variáveis globais simples para armazenar dados como current_user. Em um ambiente multi-thread, isso causaria vazamento de dados entre requests.</p>

<h2 id="fibers-concorrência-cooperativa-e-previsível">Fibers: concorrência cooperativa e previsível</h2>

<p>Fibers são frequentemente confundidas com threads, mas elas resolvem um problema diferente. Uma fiber é uma unidade leve de execução gerenciada pelo próprio Ruby, não pelo sistema operacional. Diferente das threads, fibers não são preemptivas: elas só pausam quando o código explicitamente permite.</p>

<p>Fibers vivem dentro de uma thread e nunca executam em paralelo. O que elas permitem é que múltiplas operações façam progresso enquanto aguardam I/O, sem bloquear a thread inteira. Isso é especialmente útil para servidores web e sistemas que lidam com muitas operações de rede.</p>

<p>No Rails moderno, fibers são usadas para suportar I/O assíncrono, ActionCable, Turbo Streams e outras formas de concorrência cooperativa. O modelo real de execução passa a ser: um processo contendo várias threads, e cada thread podendo alternar entre múltiplas fibers.</p>

<p>Uma boa analogia é imaginar uma thread como um trilho de trem e fibers como vagões. Apenas um vagão anda por vez, mas vários podem se revezar no mesmo trilho de forma organizada e previsível.</p>

<h2 id="o-ciclo-de-vida-de-um-request-no-rails">O ciclo de vida de um request no Rails</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sistema Operacional
 └── Processo (PID)
      ├── Thread 1
      ├── Thread 2
      ├── Thread 3
      └── ...
</code></pre></div></div>

<p>Com esses conceitos em mente, fica mais fácil entender o request lifecycle no Rails. Quando um request chega ao servidor web, ele é atribuído a uma thread específica. Todo o código do Rails — middlewares, controllers, models, services e callbacks — executa dentro dessa thread.</p>

<p>Durante a execução, o Rails pode suspender e retomar a execução usando fibers, especialmente quando há operações de I/O. Mesmo assim, o contexto do request permanece associado à execução correta.</p>

<p>Esse detalhe é fundamental: todo request Rails sempre tem uma thread associada, mesmo quando fibers entram em cena. Fibers não eliminam threads; elas apenas permitem um uso mais eficiente delas.</p>

<h2 id="activesupportcurrentattributes-contexto-atrelado-à-execução">ActiveSupport::CurrentAttributes: contexto atrelado à execução</h2>

<p>O ActiveSupport::CurrentAttributes existe para resolver um problema prático: como disponibilizar dados de contexto — como usuário atual, conta, fuso horário ou request ID — em qualquer camada da aplicação sem passar parâmetros por toda parte e sem usar variáveis globais perigosas?</p>

<p>A solução do Rails é tratar contexto como algo associado à execução, não a objetos específicos. Internamente, o Rails armazena esses dados em um storage ligado à thread e, no Rails moderno, também integrado ao contexto de fibers por meio do ActiveSupport::ExecutionContext.</p>

<p>Quando você define Current.user = current_user no início do request, esse valor fica disponível em qualquer ponto do código que esteja executando naquele mesmo contexto. Models, services e callbacks conseguem acessá-lo porque estão rodando dentro da mesma thread (e fibers associadas).</p>

<p>Esse contexto é automaticamente limpo ao final do request. Isso é essencial porque threads são reutilizadas. Sem esse reset, dados de um request poderiam vazar para o próximo.</p>

<h2 id="até-onde-vai-o-contexto">Até onde vai o contexto</h2>

<p>É importante entender os limites desse mecanismo. CurrentAttributes não atravessa processos. Ele também não sobrevive a background jobs, filas ou qualquer fronteira temporal. Quando um job começa, ele roda em outro processo ou thread, com um contexto completamente novo.</p>

<p>Por isso, qualquer dado necessário fora do request lifecycle deve ser passado explicitamente. O Current é uma ferramenta poderosa, mas apenas dentro do escopo correto.</p>

<h2 id="leituras">Leituras</h2>

<p>O ciclo de vida de uma request por exemplo vive dentro de uma thread. O contexto é preservado no Rails através de <a href="https://github.com/rails/rails/blob/3d2510359c37b2d84a8b33b0b3964ae60f6653f0/activesupport/lib/active_support/execution_context.rb">ExecutionContext</a></p>

<h2 id="conclusão">Conclusão</h2>

<p>Entender processos, threads e fibers não é um detalhe acadêmico: é o que permite escrever aplicações Rails previsíveis, seguras e escaláveis. ActiveSupport::CurrentAttributes funciona porque o Rails trata contexto como parte do fluxo de execução, respeitando os limites impostos pelo sistema operacional e pelo Ruby.</p>

<p>Quando esses conceitos estão claros, desaparecem os mistérios do tipo “funciona local, quebra em produção”. O que sobra é arquitetura, concorrência e decisões conscientes — exatamente o tipo de entendimento que diferencia quem apenas usa Rails de quem realmente o domina.</p>]]></content><author><name>anchietajunior</name></author><category term="Rails" /><category term="ActiveSupport" /><category term="rails" /><category term="activesupport" /><summary type="html"><![CDATA[Entendendo Contexto de Execução no Rails]]></summary></entry><entry xml:lang="en"><title type="html">Deploying a Ruby on Rails app to DigitalOcean with a managed Database.</title><link href="/2024/12/08/kamal-app-managed-db/" rel="alternate" type="text/html" title="Deploying a Ruby on Rails app to DigitalOcean with a managed Database." /><published>2024-12-08T01:00:00-03:00</published><updated>2024-12-08T01:00:00-03:00</updated><id>/2024/12/08/kamal-app-managed-db</id><content type="html" xml:base="/2024/12/08/kamal-app-managed-db/"><![CDATA[<p><img src="https://i.ibb.co/WcjMWr2/3c6700b4-2080-4b38-aa87-1f105c2bb47c.webp" alt="Kamal Managed" /></p>

<p>Exploring Kamal (2) and how to deploy Ruby on Rails applications to DigitalOcean (or any other cloud service) using a managed database service, i started testing some configurations/deploy and my idea now is definitely create more posts about Kamal and share more information here in my personal blog about it.</p>

<h2 id="what-is-kamal">What is Kamal?</h2>

<p><a href="https://kamal-deploy.org/">The official Kamal website</a> says:</p>

<p>“Kamal offers zero-downtime deploys, rolling restarts, asset bridging, remote builds, accessory service management, and everything else you need to deploy and manage your web app in production with Docker. Originally built for Rails apps, Kamal will work with any type of web app that can be containerized.”</p>

<h2 id="why-i-like-this">Why i like this?</h2>

<p>Honestly i used Capistrano for a long time and i liked that. When I first heard Kamal described as a Capistrano-like tool for deploying containerized applications, I got pretty excited about the idea. But in my view, that’s not the best way to define it. For me, Kamal is a great tool for anyone looking to deploy containerized apps without having to rely on Kubernetes—or who just doesn’t need it.</p>

<h2 id="things-to-start-understanding-kamal-before-using-it">Things to start understanding Kamal before using it</h2>

<p>Kamal is now in version 2, here are some things i think are very important to understand.</p>

<ul>
  <li>Docker (Containers, Orchestration)</li>
  <li>SSH (SSHKit)</li>
  <li>Reverse proxies (Kamal used Traefik in version 1 but now it uses Kamal-proxy)</li>
  <li>Load balances</li>
  <li>YAML files syntax</li>
</ul>

<h3 id="managed-databases-vs-database-container">Managed databases vs Database container</h3>

<p>Using Kamal you can choose adding accessories (containers) or use a managed database service we can find in multiple cloud services like AWS, GCP or DigitalOcean which i’m using right now for my apps.</p>

<p>I actually choose a managed database service because it’s easier to create backups, setup connections and etc, it’s all handled by the cloud service you choose. That’s the reason for me and my small apps i’ve being creating.</p>

<h2 id="the-example">The example</h2>

<p>When using Ruby on Rails 8, Kamal is by default in your app and you can check some files to see how it works.</p>

<ul>
  <li>config/deploy.yml</li>
  <li>.kamal/secrets</li>
  <li>.kamal/hooks</li>
  <li>Dockerfile</li>
  <li>bin/docker-entrypoint</li>
</ul>

<p>And this is my current config/deploy.yml file to deploy an app to Digital Ocean using the Digital Ocean managed database service, off course you need to create the database and droplet first. I added some comments sou you can use the file and understand what’s happening out there.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Name of the Project</span>
<span class="na">service</span><span class="pi">:</span> <span class="s">nice_project</span>

<span class="c1"># Name of the image, by default it will use DockerHub</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">joseanchieta/nice_project</span>

<span class="c1"># IP Servers, you can set just one or how many you want</span>
<span class="na">servers</span><span class="pi">:</span>
  <span class="na">web</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">192.168.0.1</span>

<span class="c1"># Your DockerHub/Other service username</span>
<span class="na">registry</span><span class="pi">:</span>
  <span class="na">username</span><span class="pi">:</span> <span class="s">joseanchieta</span>
  <span class="c1"># You can find this variable inside .kamal/secrets</span>
  <span class="na">password</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">KAMAL_REGISTRY_PASSWORD</span>

<span class="c1"># Secrets come from .kamal/secrets.</span>
<span class="na">env</span><span class="pi">:</span>
  <span class="na">secret</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">RAILS_MASTER_KEY</span>
    <span class="pi">-</span> <span class="s">PRODUCTION_DB_HOST</span>
    <span class="pi">-</span> <span class="s">KAMAL_REGISTRY_PASSWORD</span>
  <span class="na">clear</span><span class="pi">:</span>
    <span class="na">SOLID_QUEUE_IN_PUMA</span><span class="pi">:</span> <span class="kc">true</span>

    <span class="c1"># IP or address of the database service, this is an example using</span>
    <span class="c1"># Digital Ocean</span>
    <span class="c1"># In database.yml &gt;&gt; production you should have something like this:</span>
    <span class="c1"># production:</span>
    <span class="c1">#   adapter: postgresql</span>
    <span class="c1">#   encoding: unicode</span>
    <span class="c1">#   pool: &lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&gt;</span>
    <span class="c1">#   database: production_database_name</span>
    <span class="c1">#   url: &lt;%= ENV["PRODUCTION_DB_HOST"] %&gt;</span>
    <span class="na">DB_HOST</span><span class="pi">:</span> <span class="s">db-postgresql-nyc3-something123.ondigitalocean.com</span>

<span class="c1"># These commands are helpful, you can type in your terminal:</span>
<span class="c1"># kamal console</span>
<span class="c1"># kamal shell</span>
<span class="c1"># kamal logs</span>
<span class="na">aliases</span><span class="pi">:</span>
  <span class="na">console</span><span class="pi">:</span> <span class="s">app exec --interactive --reuse "bin/rails console"</span>
  <span class="na">shell</span><span class="pi">:</span> <span class="s">app exec --interactive --reuse "bash"</span>
  <span class="na">logs</span><span class="pi">:</span> <span class="s">app logs -f</span>
  <span class="na">dbc</span><span class="pi">:</span> <span class="s">app exec --interactive --reuse "bin/rails dbconsole"</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s2">"</span><span class="s">nice_project:/rails/storage"</span>

<span class="na">asset_path</span><span class="pi">:</span> <span class="s">/rails/public/assets</span>

<span class="na">builder</span><span class="pi">:</span>
  <span class="na">arch</span><span class="pi">:</span> <span class="s">amd64</span>
</code></pre></div></div>]]></content><author><name>joseanchieta</name></author><category term="Ruby" /><category term="Kamal" /><category term="DigitalOcean" /><category term="Deploy" /><category term="PostgreSQL" /><category term="ruby" /><category term="kamal" /><category term="deploy" /><category term="postgresql" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://i.ibb.co/WcjMWr2/3c6700b4-2080-4b38-aa87-1f105c2bb47c.webp" /><media:content medium="image" url="https://i.ibb.co/WcjMWr2/3c6700b4-2080-4b38-aa87-1f105c2bb47c.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry xml:lang="pt-br"><title type="html">Criando iOS apps utilizando Rails e Turbo Native</title><link href="/2023/11/29/creating-turbo-native-apps/" rel="alternate" type="text/html" title="Criando iOS apps utilizando Rails e Turbo Native" /><published>2023-11-29T13:00:00-03:00</published><updated>2023-11-29T13:00:00-03:00</updated><id>/2023/11/29/creating-turbo-native-apps</id><content type="html" xml:base="/2023/11/29/creating-turbo-native-apps/"><![CDATA[<p><a href="https://postimg.cc/Q9dkj5q4"><img src="https://i.postimg.cc/V6j7BW08/ios.png" alt="ios.png" /></a></p>

<h2 id="um-novo-horizonte-para-o-que-pode-ser-feito-com-rails">Um novo horizonte para o que pode ser feito com Rails.</h2>

<p>Desde o início dos anúncios sobre Hotwire e Turbo, venho acompanhando e formando minhas opiniões a respeito. Confesso que de primeira, fiquei cético e achei que seria apenas mais uma coisa de Javascript para aprender, <code class="language-plaintext highlighter-rouge">eu estava terrivelmente enganado</code>.</p>

<p>Após assistir as palestras do RailsWorld realizado em Berlim no mês passado, percebi de vez que a forma
como vejo o ecossistema Rails, mudou muito. Hoje existe um horizonte de possibilidades para criação de 
aplicações web com Rails com um cardápio grande de opções: Esbuild, ImportMaps, Hotwire,
Kamal e etc. Estou bem animado com todas as alternativas que temos e que teremos em breve no ecossistema Rails.</p>

<h2 id="turbo-native">Turbo Native</h2>

<p>Há a algum tempo, venho acompanhando o perfil no Twitter (X) do Joe Masilotti, The turbo native guy como ele
se auto proclama. Esse era o ponto no qual eu mais duvidava, “aplicativos de celular com Rails?, não me parece uma boa ideia”. Esse pensamento surgiu em mim diante de tantas discussões a respeito de React Native e Flutter, parecem duas torcidas rivais e fervorosas para provar qual a melhor tecnologia.</p>

<p>O Joe foi um dos palestrantes do Rails World e na minha opinião, entregou uma das melhores palestras do evento. Este post basicamente replica e acrescenta algumas considerações ao que foi visto na palestra do Joe. Deixarei o link da palestra ao final deste post e o link para o Twitter do Joe também, vale bastante a pena segui-lo.</p>

<h2 id="como-funciona-isso-e-por-que">Como funciona isso e por que?</h2>

<p>Bom, pra quem conhece o conceito de webview em aplicações mobile, não é nada revolucionário. Resumindo de forma muito simples, é um app mobile que exibe um app web. Ou seja, teremos uma aplicação web Rails sendo exibida dentro de um app mobile, neste caso específico iOS.</p>

<p>Já ouvi várias coisas negativas sobre a abordagem de webview, desde que ouvi pela primeira vez sobre ferramentas como Ionic lá em 2014. Concordo com grande parte dos pontos negativos que ouvi naquela época, porém temos de levar em consideração que não estamos mais em 2014 e os devices mudaram muito, principalmente em capacidade de processamento, memória e etc.</p>

<p>Tendo tudo isso em mente, tenho a impressão que estamos entrando em uma nova era do desenvolvimento de software em que as pessoas estão cada vez mais pragmáticas e simplistas, isso faz parte de um conjunto de teorias, pensamentos e opiniões que venho acompanhando e que faz sentido (para mim). Acredito que muito vem dos resultados falhos de overengineering que obtivemos nos últimos anos com separação de back e front, microservices e etc sem que haja de fato um motivo real para aderir a estas técnicas.</p>

<p>Joe cita na palestra um outro exemplo, empresas/pessoas criando apps nativos visando performance e extrair o máximo dos devices sem que haja a necessidade real disso, ou seja, grande parte dos apps mobile não precisam extrair o máximo do aparelho, elas precisam renderizar algumas views, realizar algumas operações de crud e etc. Mais uma vez, estou de acordo com isso e pra mim o uso do Turbo Native faz sentido neste momento.</p>

<h2 id="criando-o-projeto">Criando o projeto</h2>

<p>Vamos começar criando um projeto simples em Rails, o intuito do post é criar um app rails e fazer com que o mesmo apareça em um iOS simulator (infelizmente apenas para usuários de Mac, mas sinta-se a vontade para fazer o mesmo para apps Android).</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails new iosapp <span class="nt">-T</span> <span class="nt">-css</span> tailwind
</code></pre></div></div>
<p>Vamos adicionar apenas um scaffold ao projeto para que possamos ver o resultado no simulador do iOS.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails g scaffold Post title body:text
</code></pre></div></div>

<p>Execute as migrações:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails db:migrate
</code></pre></div></div>

<p>Adicione um root_path (root_url) no arquivo <code class="language-plaintext highlighter-rouge">routes.rb</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">root</span> <span class="s2">"posts#index"</span>
</code></pre></div></div>
<p>Execute o app:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./bin/dev
</code></pre></div></div>

<p>Agora acesse o seu browser em <code class="language-plaintext highlighter-rouge">localhost:3000</code> e crie alguns posts.</p>

<p>Assim está o meu app no browser após adicionar alguns estilos utilizando Tailwind:</p>

<p><img src="https://i.postimg.cc/662TcqFT/Screenshot-2023-11-30-at-13-49-00.png" alt="iosturbo index" /></p>

<h2 id="criando-o-app-ios">Criando o app iOS</h2>

<p>Neste passo é necessário ter o XCode instalado e estar utilizando um Mac. Abra o XCode, no menu principal clique em File, New, Project.</p>

<p><a href="https://postimg.cc/Y4Jb8wsx"><img src="https://i.postimg.cc/QtMw88gv/Screenshot-2023-11-30-at-14-04-20.png" alt="Criando o projeto" /></a></p>

<p>Na caixa de seleção “Choose a template for your new project” selecione iOS » App e clique em Next. Aqui você terá algumas opções para preencher que são obrigatórias, no nosso caso pode desmarcar a opção <code class="language-plaintext highlighter-rouge">Include Tests</code> e na caixa de Interface, selecione StoryBoard.</p>

<p><a href="https://postimg.cc/rDRh4mhp"><img src="https://i.postimg.cc/3xnzP0m2/Screenshot-2023-11-30-at-14-09-32.png" alt="Configurações" /></a></p>

<h3 id="acrescentando-o-turbo-native-como-dependência-do-projeto">Acrescentando o Turbo Native como dependência do projeto</h3>

<p>Clique no menu File novamente, encontre a opção <code class="language-plaintext highlighter-rouge">Add Package Dependecies</code>, clique nesta opção e selecione o Turbo Native, caso o Turbo Native não apareça para você, digite a url do github no campo <code class="language-plaintext highlighter-rouge">Enter Package URL</code>.</p>

<p>URL: <code class="language-plaintext highlighter-rouge">github.com/hotwired/turbo-ios</code></p>

<p><a href="https://postimg.cc/G9TyPyFG"><img src="https://i.postimg.cc/wx0Qp53f/Screenshot-2023-11-30-at-14-24-27.png" alt="Dependência Turbo Native" /></a></p>

<p>O único passo necessário por agora é alterar o arquivo swift <code class="language-plaintext highlighter-rouge">SceneDelegate</code> que se encontra na árvore de arquivos exibida no Xcode.</p>

<p>O código ficará da seguinte forma:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">Turbo</span>
<span class="kd">import</span> <span class="kt">WebKit</span>
<span class="kd">import</span> <span class="kt">UIKit</span>

<span class="k">let</span> <span class="nv">rootUrl</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"http://127.0.0.1:3000/"</span><span class="p">)</span><span class="o">!</span>

<span class="kd">class</span> <span class="kt">SceneDelegate</span><span class="p">:</span> <span class="kt">UIResponder</span><span class="p">,</span> <span class="kt">UIWindowSceneDelegate</span> <span class="p">{</span>

    <span class="k">var</span> <span class="nv">window</span><span class="p">:</span> <span class="kt">UIWindow</span><span class="p">?</span>
    
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">navigationController</span> <span class="o">=</span> <span class="kt">UINavigationController</span><span class="p">()</span>

    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">session</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">config</span> <span class="o">=</span> <span class="kt">WKWebViewConfiguration</span><span class="p">()</span>
        <span class="n">config</span><span class="o">.</span><span class="n">applicationNameForUserAgent</span> <span class="o">=</span> <span class="s">"Turbo Native iOS"</span>
        
       <span class="k">let</span> <span class="nv">session</span> <span class="o">=</span> <span class="kt">Session</span><span class="p">(</span><span class="nv">webViewConfiguration</span><span class="p">:</span> <span class="n">config</span><span class="p">)</span>
       <span class="n">session</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
       <span class="k">return</span> <span class="n">session</span>
    <span class="p">}()</span>

    <span class="c1">// When the app launches</span>
    <span class="kd">func</span> <span class="nf">scene</span><span class="p">(</span><span class="n">_</span> <span class="nv">scene</span><span class="p">:</span> <span class="kt">UIScene</span><span class="p">,</span> <span class="n">willConnectTo</span> <span class="nv">session</span><span class="p">:</span> <span class="kt">UISceneSession</span><span class="p">,</span> <span class="n">options</span> <span class="nv">connectionOptions</span><span class="p">:</span> <span class="kt">UIScene</span><span class="o">.</span><span class="kt">ConnectionOptions</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">window</span><span class="p">?</span><span class="o">.</span><span class="n">rootViewController</span> <span class="o">=</span> <span class="n">navigationController</span>
        
        <span class="k">let</span> <span class="nv">proposal</span> <span class="o">=</span> <span class="kt">VisitProposal</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">rootUrl</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="kt">VisitOptions</span><span class="p">())</span>
        <span class="nf">visit</span><span class="p">(</span><span class="nv">proposal</span><span class="p">:</span> <span class="n">proposal</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="c1">// 1. Create Screen</span>
    <span class="c1">// 2. Push the Screen</span>
    <span class="c1">// 3. Render the HTML</span>
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">visit</span><span class="p">(</span><span class="nv">proposal</span><span class="p">:</span> <span class="kt">VisitProposal</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">visitable</span> <span class="o">=</span> <span class="kt">VisitableViewController</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">proposal</span><span class="o">.</span><span class="n">url</span><span class="p">)</span>
        <span class="n">navigationController</span><span class="o">.</span><span class="nf">pushViewController</span><span class="p">(</span><span class="n">visitable</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
        <span class="n">session</span><span class="o">.</span><span class="nf">visit</span><span class="p">(</span><span class="n">visitable</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="n">proposal</span><span class="o">.</span><span class="n">options</span><span class="p">)</span>
    <span class="p">}</span>
 <span class="p">}</span>

<span class="kd">extension</span> <span class="kt">SceneDelegate</span><span class="p">:</span> <span class="kt">SessionDelegate</span><span class="p">{</span>
    <span class="c1">// Web view dies</span>
    <span class="kd">func</span> <span class="nf">sessionWebViewProcessDidTerminate</span><span class="p">(</span><span class="n">_</span> <span class="nv">session</span><span class="p">:</span> <span class="kt">Session</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">session</span><span class="o">.</span><span class="nf">reload</span><span class="p">()</span>
    <span class="p">}</span>
    
    <span class="c1">// Link is clicked</span>
    <span class="kd">func</span> <span class="nf">session</span><span class="p">(</span><span class="n">_</span> <span class="nv">session</span><span class="p">:</span> <span class="kt">Session</span><span class="p">,</span> <span class="n">didProposeVisit</span> <span class="nv">proposal</span><span class="p">:</span> <span class="kt">VisitProposal</span><span class="p">){</span>
        <span class="nf">visit</span><span class="p">(</span><span class="nv">proposal</span><span class="p">:</span> <span class="n">proposal</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="c1">// Link fails</span>
    <span class="kd">func</span> <span class="nf">session</span><span class="p">(</span><span class="n">_</span> <span class="nv">session</span><span class="p">:</span> <span class="kt">Session</span><span class="p">,</span> <span class="n">didFailRequestForVisitable</span> <span class="nv">visitable</span><span class="p">:</span> <span class="kt">Visitable</span><span class="p">,</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">){</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Failed to visit: </span><span class="se">\(</span><span class="n">error</span><span class="o">.</span><span class="n">localizedDescription</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
    
<span class="p">}</span>
</code></pre></div></div>

<p>Aqui, temos algumas coisas relacionadas a navegação do app (que ficam muito bem explicadas pelo Joe no vídeo na Palestra) e logo no começo é possível ver que estamos apontando para algo no endereço <code class="language-plaintext highlighter-rouge">localhost:3000</code>. Sim, é aqui que indicamos ao app iOS onde buscar o seu <code class="language-plaintext highlighter-rouge">conteúdo</code>.</p>

<p>Agora vamos executar o simulador, isso pode ser feito com o atalho: <code class="language-plaintext highlighter-rouge">Command + R</code></p>

<p>E olha só que maravilha!</p>

<p><a href="https://postimg.cc/fVHdHYBK"><img src="https://i.postimg.cc/43G1x5cr/Screenshot-2023-11-30-at-14-31-47.png" alt="iOS simulador" /></a></p>

<p>Nossa aplicação Rails sendo executada como um app mobile iOS. Sensacional!</p>

<h2 id="conclusão">Conclusão</h2>

<p>Este foi um pequeno exemplo de como é rápido criar um app iOS rodando uma aplicação Rails e isso é só o começo, imagina que para algumas atualizações basta apenas atualizar a aplicação Rails e isso já será refletido no App.</p>

<h3 id="considerações">Considerações</h3>

<p>Como eu disse anteriormente, isso não é a solução para todos os problemas. Mas é uma boa solução para vários.
Reitero que é importante assistir a palestra do Joe, onde ele passa mais detalhes e passa até outras coisas como upload de imagens.</p>

<p>É isso, espero que gostem!</p>

<h2 id="referências">Referências</h2>

<p>Palestra: <a href="https://www.youtube.com/watch?v=hAq05KSra2g&amp;t=339s&amp;ab_channel=RubyonRails" target="_blank">Joe Masilotti - Just enough Turbo Native to be dangerous - Rails World 2023</a></p>

<p>Link para o Workshop do Joe Masilotti: <a href="https://masilotti.com/turbo-native-workshop/">Workshop</a></p>

<p>Link para Post no blog do Joe: <a href="https://masilotti.com/hide-web-rendered-content-on-turbo-native-apps/">Masilotti</a></p>]]></content><author><name>joséanchieta</name></author><category term="Turbo" /><category term="rails" /><category term="turbo" /><category term="ios" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://i.postimg.cc/V6j7BW08/ios.png" /><media:content medium="image" url="https://i.postimg.cc/V6j7BW08/ios.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry xml:lang="pt-br"><title type="html">A atenção é minha ferramenta mais valiosa para a produtividade como desenvolvedor de software</title><link href="/2020/11/08/attention-and-productivity/" rel="alternate" type="text/html" title="A atenção é minha ferramenta mais valiosa para a produtividade como desenvolvedor de software" /><published>2020-11-08T13:00:00-03:00</published><updated>2020-11-08T13:00:00-03:00</updated><id>/2020/11/08/attention-and-productivity</id><content type="html" xml:base="/2020/11/08/attention-and-productivity/"><![CDATA[<p><img src="https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;auto=format&amp;fit=crop&amp;w=2070&amp;q=80" alt="Image" /></p>

<h3 id="essa-é-uma-tradução-e-adaptação-desse-artigo">Essa é uma tradução e adaptação desse <a href="https://zwbetz.com/attention-is-my-most-valuable-asset-for-productivity-as-a-software-developer/">artigo</a>.</h3>

<p>Como apreciador das obras que inspiraram esse post e como uma pessoa que sempre está em busca de um melhor desempenho, gostei bastante desse artigo que cita inclusive algumas medidas que tomei em minha vida em busca de produtividade e resolvi traduzi-lo. Espero que gostem.</p>

<hr />

<p>Como uma função bem escrita, prefiro parar de executar cedo se nenhuma condição for válida. Então, se você discorda dessas definições e suposições, agora é um bom momento para parar de ler.</p>

<ul>
  <li>
    <p><strong><em>Produtividade sustentável:</em></strong> A taxa máxima de produção de trabalho de qualidade, sem prejuízo para o bem-estar do desenvolvedor.</p>
  </li>
  <li>
    <p><strong><em>Trabalho de qualidade:</em></strong> software que atende aos requisitos, é valioso para os usuários, pode ser mantido e é o mais livre de bugs possível.</p>
  </li>
  <li>
    <p><strong><em>Atenção:</em></strong> A capacidade mental limitada de se concentrar em uma tarefa.</p>
  </li>
  <li>
    <p>A produtividade sustentável é desejada.</p>
  </li>
  <li>
    <p>Atenção é essencial para a produtividade sustentável.</p>
  </li>
</ul>

<p>Meu fluxo mais produtivo de trabalho é mais ou menos assim: identificar o problema a ser resolvido; pense no problema e deixe as idéias se infiltrarem; pesquise, discuta e experimente essas idéias; implementar e testar a solução; entregar e manter a solução.</p>

<p>Este ciclo pode se repetir muitas vezes ao dia. Ou eu poderia passar dias preso em uma única etapa do ciclo. Cada etapa deste ciclo requer atenção. Quanto mais atenção posso dedicar, mais ciclos posso concluir e mais produtivo sou.</p>

<p>Por quanto tempo você pode se concentrar em uma tarefa varia de pessoa para pessoa. Algumas pessoas são muito boas logo de cara, outras nem tanto. Independente das suas características, acredito que o foco (o ato de priorizar sua atenção) é uma habilidade e, como qualquer habilidade, pode ser melhorada com a prática.</p>

<p>Então, como você pode aumentar suas reservas de atenção? O melhor retorno para seu investimento é organizar seu mundo exterior de forma que seja o mais livre possível de distrações. Depois de fazer isso, você terá mais tempo para praticar e, portanto, mais tempo para melhorar.</p>

<p><strong><em>Construa força física.</em></strong> O dano causado por ficar sentado 8 horas ou mais por dia é subestimado. Você precisa encontrar uma maneira de compensar esses danos, especialmente se planeja trabalhar nessa área por décadas. Se você não fizer mais nada, faça levantamento de peso. Existem poucos movimentos mais primitivos do que pegar um objeto pesado do chão e levantá-lo. Você pode aprender a técnica correta em minutos. Eu gosto mais de levantamento de peso porque você pode fazê-los com segurança, com pesos elevados, até ficar cansado. Também gosto da força das mãos, das costas e do quadril que eles proporcionam, para dificultar ainda mais os danos ao sentar-se.</p>

<p><strong><em>Torne o seu local de trabalho enfadonho e organizado.</em></strong> Meu escritório é um quarto extra. As paredes estão vazias. Não há TV. Há uma mesa, cadeira, laptop, suporte para laptop, teclado, mouse e mouse pad. Há uma janela que permite a entrada de luz suficiente para que eu não sinta que estou perdendo um dia lindo, mas não muita luz para causar brilho na tela. Se eu precisar trabalhar com papel, ele é imediatamente arquivado em algum lugar quando terminar. Como eu disse, chato e arrumado.</p>

<p><strong><em>Deixe seu smartphone mudo.</em></strong> Meu telefone está com todas as notificações desabilitadas, exceto para chamadas e mensagens de texto. Bem, e alertas do National Hurricane Center, já que moro na Louisiana. A menos que você seja minha esposa, você sabe que eu não respondo a mensagens de texto imediatamente, é assim que as coisas são. Desativei minhas contas de mídia social há algum tempo. Mas, se você os tiver, desligar as notificações deve ajudar a diminuir o desejo de verificá-los compulsivamente.</p>

<p><strong><em>Tenha um sistema operacional minimalista.</em></strong> Os aplicativos que uso com menos frequência estão a uma combinação de teclas de distância. Diante disso, meu dock tem apenas os aplicativos que uso diariamente:</p>

<ul>
  <li>Sistema de arquivos</li>
  <li>Browser</li>
  <li>Terminal</li>
  <li>Editor de texto para código de front-end e notas</li>
  <li>IDE para código de back-end</li>
  <li>IDE para banco de dados</li>
  <li>Visualizador de diferenças em arquivos para controle de versão</li>
  <li>Cliente de email</li>
  <li>Cliente de mensagem instantânea</li>
</ul>

<p>Minha área de trabalho alterna entre os estados limpo e sujo. Arquivos com os quais estou trabalhando no momento na área de trabalho. Em seguida, eles são arquivados em pastas adequadas quando concluídos.</p>

<p><strong><em>Organize os favoritos do seu navegador.</em></strong> Quando leio algo útil que talvez precise consultar mais tarde, eu deixo arquivado em uma pasta geral. Em seguida, itens mais específicos obtêm suas próprias pastas. Os links acessados ​​com frequência ficam visíveis na minha barra de favoritos em sua própria pasta.</p>

<p><strong><em>Minimize as reuniões.</em></strong> Olha, eu sei que algumas coisas fazem sentido discutir cara a cara, ou voz a voz. Mas se não, você não precisa de uma reunião. Um e-mail ou mensagem instantânea será suficiente.</p>

<p><strong><em>Finalmente, use o Método Eisenhower para categorizar suas tarefas.</em></strong> Imagine 4 categorias:</p>

<ul>
  <li>Importante e Urgente</li>
  <li>Importante e Não Urgente</li>
  <li>Não Importante e Urgente</li>
  <li>Não Importante e Não Urgente</li>
</ul>

<p><strong><em>Tarefas Importantes e Urgentes precisam ser tratadas.</em></strong> Para mim, esses são geralmente os principais problemas de produção.</p>

<p><strong><em>Tarefas Importantes e não Urgentes devem absorver a maior parte do seu tempo.</em></strong> Para mim, este é o velho trabalho de desenvolvimento de implementar recursos, corrigir bugs e tornar o código existente mais fácil de manter e melhorar seu desempenho. Também estão incluídos a construção de relacionamentos com outras pessoas e o planejamento futuro.</p>

<p><strong><em>Tarefas Não Importantes e Urgentes são ladrões de atenção desagradáveis.</em></strong> Eles gritam com você imediatamente, mas oferecem pouco valor em troca. Você deve saber do que estou falando. Para mim, essas são na maioria das vezes perguntas mal feitas por outras pessoas, que exigem mais trabalho para determinar o que está realmente sendo perguntado, em vez de descobrir qual deveria ser a resposta. Também estão incluídas reuniões de última hora e colegas de trabalho muito faladores.</p>

<p><strong><em>As tarefas Não Importantes e Não Urgentes geralmente não são conhecidas por seus usuários.</em></strong> Ler atualizações da documentação de um projeto por exemplo. O fato é que eles são um investimento em você mesmo, o que significa um “você” mais produtivo no futuro. Portanto, não se esqueça de se dedicar a eles um pouco em seus momentos de folga.</p>

<p><strong><em>Leitura adicional.</em></strong> Se você não sabe quem é Cal Newport, está perdendo. Ele tem um blog inteiro dedicado a esse tipo de coisa, e já escreveu livros como Deep Work e Digital Minimalism. Aqui estão alguns dos meus artigos favoritos dele:</p>

<ul>
  <li><a href="https://www.calnewport.com/blog/2009/02/04/have-we-lost-our-tolerance-for-a-little-boredom/">Have We Lost Our Tolerance For a Little Boredom?</a></li>
  <li><a href="https://www.calnewport.com/blog/2010/06/10/is-allowing-your-child-to-study-while-on-facebook-morally-equivalent-to-drinking-while-pregnant/">Is Allowing Your Child to Study While on Facebook Morally Irresponsible?</a></li>
  <li><a href="https://www.calnewport.com/blog/2008/04/07/monday-master-class-how-to-reduce-stress-and-get-more-done-by-building-an-autopilot-schedule/">Monday Master Class: How to Reduce Stress and Get More Done By Building an Autopilot Schedule</a></li>
  <li><a href="https://www.calnewport.com/blog/2009/11/24/are-passions-serendipitously-discovered-or-painstakingly-constructed/">Are Passions Serendipitously Discovered or Painstakingly Constructed?</a></li>
  <li><a href="https://www.calnewport.com/blog/2018/06/08/jerry-seinfelds-closed-door/">Jerry Seinfeld’s Closed Door</a></li>
</ul>]]></content><author><name>anchietajunior</name></author><category term="Miscellaneous" /><category term="productivity" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;auto=format&amp;fit=crop&amp;w=2070&amp;q=80" /><media:content medium="image" url="https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&amp;auto=format&amp;fit=crop&amp;w=2070&amp;q=80" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry xml:lang="pt-br"><title type="html">O lado humano do code review</title><link href="/2020/09/22/human-code-review/" rel="alternate" type="text/html" title="O lado humano do code review" /><published>2020-09-22T13:00:00-03:00</published><updated>2020-09-22T13:00:00-03:00</updated><id>/2020/09/22/human-code-review</id><content type="html" xml:base="/2020/09/22/human-code-review/"><![CDATA[<p><img class="post-image" src="https://d33x644h9xoqir.cloudfront.net/wp-content/uploads/sites/2/2019/02/como-construir-relacoes-produtivas-com-seus-colegas-de-trabalho.jpg" /></p>

<p>Em primeiro lugar, pensar no lado humano não é desencorajar a fazer code review, pelo contrário,
estou apenas alertando para possíveis problemas causados pelo code review quando feito sem estar consciente
de alguns fatores. Fatores estes que estão mais ligados a parte humana do que a parte técnica.</p>

<p>Algumas premissas do code review ficaram fixas na minha cabeça depois de passar por algumas equipes
muito boas em fazer code review e outras nem tanto, de qualquer forma o meu objetivo em relação aos
code reviews e várias outras coisas da vida é fugir da superficialidade e dar contexto ou um sentido
para as minhas ações, então vamos a algumas lições que aprendi:</p>

<h2 id="o-principal-objetivo-e-a-razão">O principal objetivo e a razão</h2>

<p>Algumas vezes podemos esquecer disso, mas o principal objetivo de um code review é manter a qualidade
do código, consequentemente melhorando a qualidade/confiabilidade do projeto. Quando se está fixado na
mente que o principal objetivo é a qualidade do projeto, cria-se uma missão em comum com os seus colegas
de equipe, que gera companheirismo e empatia.</p>

<p>Sempre que for revisar o código de alguém, lembre-se desse objetivo, é comum o code review acabar virando
apenas uma crítica sem um objetivo, ou seja, quando uma crítica é feita com uma razão forte, provavelmente,
será melhor aceita, já quando feita apenas pensando em código e sem empatia alguma por quem fez, existe
uma grande probabilidade que se torne uma coisa desagradável, até mesmo tenha um tom ríspido ou agressivo,
o que não traz benefício algum para a equipe.</p>

<p>Assim penso, acima de code reviews e lutas de ego entre programadores, está o bom relacionamento e sintonia
entre a equipe. Hoje tenho uma certeza, prefiro trabalhar em empresas que se importam mais com o bom
relacionamento dos colaboradores do que com as críticas feitas nos code reviews, já que tenho uma impressão
(quase certeza) de que esses ambientes acabam entregando melhores produtos aos seus clientes e são ambientes
muito melhores para se trabalhar e crescer como pessoa, além de fazer com o que os colaboradores sintam vontade
de permanecer por anos e anos nesses lugares.</p>

<h2 id="empatia-enviando-a-mensagem">Empatia, enviando a mensagem</h2>

<p>A empatia é o elo que liga uma crítica a um ambiente cordial e respeitoso entre colegas de equipe.
Tive companheiros de equipe que faziam críticas tão boas e de uma forma tão sutil que eu tinha vontade de recebe-las
todos os dias (várias vezes aos dias), por sentir que aquilo estava me fazendo evoluir como programador e
como pessoa. Ao ver a cordialidade embutida na crítica, aquilo acabava me contagiando e criando um efeito dominó,
eu sentia vontade de tratar os meus colegas com toda aquela gentileza que recebi através de críticas. Isso não tem preço.</p>

<p>Por outro lado, quando a empatia não faz parte das peças do jogo, o mais comum é que se crie um ambiente de
disputa, nada cordial e até agressivo. Não consigo enxergar de que forma isso traz benefícios para a equipe ou
empresa, já que o maior patrimônio das empresas, são as pessoas e não o código. Se as pessoas não estão
pensando no seu colega antes de fazer uma crítica, já existe um erro que deveria ser revisado antes mesmo
do code review.</p>

<h2 id="foco-no-problema-recebendo-a-mensagem">Foco no problema, recebendo a mensagem</h2>

<p>Do outro lado da ponta está o receptor, quem recebe a mensagem precisa estar preparado e adaptado ao pensamento
de que críticas construtivas te fazem evoluir, e isso é tão importante quanto a forma com a mensagem é enviada.
Não adianta enviar uma mensagem cordial e gentil se do outro lado é entendida como um “jab de direita”.
Focar sempre nos problemas e não em pessoas é um bom exercício para se tornar um expert em receber críticas e
transforma-las em um degrau na sua evolução.</p>

<h2 id="conlusão">Conlusão</h2>

<p>Sempre estou me fazendo algumas perguntas como:</p>

<ul>
  <li>Estou fazendo o meu melhor?</li>
  <li>Estou pensando no meu colega?</li>
  <li>Estou usando as melhores palavras?</li>
  <li>Estou pensando em todo o contexto?</li>
</ul>

<p>Essa auto-reflexão te deixa mais consciente para não enviar uma mensagem ou receber recebe-la
de uma forma errada. Cautela sempre é bom, pensar no próximo é melhor ainda, esses são ingredientes
de um bolo que sempre corre riscos de não ficar bom, porém se utilizar os ingredientes certos e fizer
com carinho, a chance de dar errado diminui muito. Graças a minha esposa, aprendi isso.</p>

<p>Pra mim, a mesma coisa vale para code reviews, os ingredientes são bem conhecidos e são fáceis de encontrar,
basta saber dosar e sempre jogar duas xícaras de chá cheias de empatia que o risco do bolo ficar murcho ou sem sabor,
é bem menor.</p>

<p>É isso, dattebayo.</p>]]></content><author><name>anchietajunior</name></author><category term="Miscellaneous" /><category term="Code Review" /><category term="development" /><category term="code-review" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://d33x644h9xoqir.cloudfront.net/wp-content/uploads/sites/2/2019/02/como-construir-relacoes-produtivas-com-seus-colegas-de-trabalho.jpg" /><media:content medium="image" url="https://d33x644h9xoqir.cloudfront.net/wp-content/uploads/sites/2/2019/02/como-construir-relacoes-produtivas-com-seus-colegas-de-trabalho.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>