<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-BR"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://822992a2.lab-jekyll.pages.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://822992a2.lab-jekyll.pages.dev/" rel="alternate" type="text/html" hreflang="pt-BR" /><updated>2026-05-29T18:58:19+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/feed.xml</id><title type="html">The Lab Gazette</title><subtitle>Estabelecida 2026 — Laboratório de IA &amp; Laboratório de Desenvolvimento de Software</subtitle><author><name>Lab IA/SW</name></author><entry><title type="html">O que muda com o GPT-5: análise técnica para engenheiros</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/26/gpt5-analise-tecnica/" rel="alternate" type="text/html" title="O que muda com o GPT-5: análise técnica para engenheiros" /><published>2026-05-26T00:00:00+00:00</published><updated>2026-05-26T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/26/gpt5-analise-tecnica</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/26/gpt5-analise-tecnica/"><![CDATA[<p>O lançamento do GPT-5 em março de 2026 trouxe três mudanças que importam mais do que os benchmarks de marketing: <strong>contexto de 10M tokens</strong>, <strong>modalidade visual + áudio nativa</strong> e <strong>redução de 60% no custo por token de output</strong>. O que isso muda na arquitetura de quem já roda IA em produção?</p>

<h2 id="contexto-de-10m-tokens--o-que-realmente-significa">Contexto de 10M tokens — o que realmente significa</h2>

<p>10M tokens equivale a aproximadamente:</p>
<ul>
  <li><strong>15.000 páginas</strong> de texto</li>
  <li>O Linux kernel <strong>inteiro</strong> + comentários</li>
  <li>~80 horas de transcrição de podcast</li>
</ul>

<p>Mas atenção: <strong>latência cresce não-linearmente</strong> com contexto. Em testes internos no laboratório:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Contexto    Latência média (p95)
4K          820 ms
32K         1.4 s
128K        4.8 s
512K        18 s
2M          76 s
</code></pre></div></div>

<p>Para a maioria dos casos, RAG bem feito com 32K de contexto ainda bate carregar 2M de tokens “para garantir”. O ganho é em <strong>casos específicos</strong>: análise de codebase inteira, processamento de documentos legais longos, debugging de logs corporativos.</p>

<h2 id="multimodal-nativo">Multimodal nativo</h2>

<p>O grande salto não é “aceita imagem” — isso o GPT-4 já fazia. É que <strong>o modelo raciocina nativamente sobre o conteúdo visual e textual no mesmo passe</strong>. Em prática:</p>

<ul>
  <li>Você manda um screenshot de erro de UI + o componente React + o stack trace</li>
  <li>O modelo conecta os três num único contexto de raciocínio</li>
  <li>Diagnóstico cai de minutos (humano vendo cada parte) para 4 segundos</li>
</ul>

<h2 id="custo-60-mais-barato-mas-com-pegadinhas">Custo: 60% mais barato, mas com pegadinhas</h2>

<p>Output tokens caíram 60%. Input tokens caíram só 25%. Para workloads que são <strong>input-heavy</strong> (RAG, busca semântica), o ganho é menor do que parece. Para <strong>output-heavy</strong> (geração de código, escrita longa), o ganho é real.</p>

<blockquote>
  <p>A regra prática que estamos usando: se 70%+ do custo do seu pipeline está em output tokens, migre para GPT-5 hoje. Se está em input, espere o GPT-5 Mini sair em julho.</p>
</blockquote>

<h2 id="quando-não-migrar">Quando NÃO migrar</h2>

<ul>
  <li>Pipelines críticos com prompt engineering muito específico ao GPT-4 (vai precisar re-tunar)</li>
  <li>Workloads onde Claude 4.6 ainda é melhor (tarefas de raciocínio matemático e código longo)</li>
  <li>Aplicações com SLA agressivo de latência (GPT-5 é, em média, 15% mais lento que GPT-4o em prompts curtos)</li>
</ul>

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

<p>GPT-5 é evolução, não revolução. Mas a economia de output + multimodal nativo justifica a migração para a maioria dos pipelines do laboratório. Migramos 3 dos nossos 7 pipelines até agora — os outros 4 ficam no GPT-4o ou Claude até a próxima geração.</p>]]></content><author><name>Pedro Tsutomu</name></author><category term="Tech" /><category term="tech" /><summary type="html"><![CDATA[Multimodal nativo, contexto de 10M tokens, custo 60% menor por token. Quando faz sentido migrar e quando segurar.]]></summary></entry><entry><title type="html">Aula prática: containerizar uma API Python em produção</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/22/aula-containerizar-api-python/" rel="alternate" type="text/html" title="Aula prática: containerizar uma API Python em produção" /><published>2026-05-22T00:00:00+00:00</published><updated>2026-05-22T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/22/aula-containerizar-api-python</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/22/aula-containerizar-api-python/"><![CDATA[<p>Containerizar uma API Python parece simples — até você precisar de logs estruturados, healthcheck, graceful shutdown e multi-stage build para uma imagem final &lt; 100MB. Esta aula reproduz o template que usamos no lab.</p>

<h2 id="o-dockerfile-final-que-vamos-construir">O Dockerfile final (que vamos construir)</h2>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ===== Stage 1: build =====</span>
<span class="k">FROM</span><span class="w"> </span><span class="s">python:3.12-slim</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span>
<span class="k">WORKDIR</span><span class="s"> /build</span>
<span class="k">COPY</span><span class="s"> requirements.txt .</span>
<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--user</span> <span class="nt">--no-cache-dir</span> <span class="nt">-r</span> requirements.txt

<span class="c"># ===== Stage 2: runtime =====</span>
<span class="k">FROM</span><span class="s"> python:3.12-slim</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="c"># Non-root user</span>
<span class="k">RUN </span>useradd <span class="nt">-m</span> <span class="nt">-u</span> 1000 <span class="nt">-s</span> /bin/bash app

<span class="c"># Copy installed deps from builder</span>
<span class="k">COPY</span><span class="s"> --from=builder /root/.local /home/app/.local</span>
<span class="k">ENV</span><span class="s"> PATH=/home/app/.local/bin:$PATH</span>

<span class="c"># Copy app code</span>
<span class="k">COPY</span><span class="s"> --chown=app:app . .</span>
<span class="k">USER</span><span class="s"> app</span>

<span class="k">EXPOSE</span><span class="s"> 8000</span>
<span class="k">HEALTHCHECK</span><span class="s"> --interval=30s --timeout=3s --start-period=5s --retries=3 \</span>
  CMD curl -f http://localhost:8000/health || exit 1

<span class="k">CMD</span><span class="s"> ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", \</span>
     "--bind", "0.0.0.0:8000", "--access-logfile", "-", \
     "main:app"]
</code></pre></div></div>

<p>Resultado: imagem final de ~92MB.</p>

<h2 id="passo-1--multi-stage-cuts-your-image-in-half">Passo 1 — Multi-stage cuts your image in half</h2>

<p>O truque do multi-stage é instalar dependências em um stage descartável (<code class="language-plaintext highlighter-rouge">builder</code>), e copiar <strong>apenas o resultado</strong> para o stage final. Sem isso, sua imagem carrega <code class="language-plaintext highlighter-rouge">gcc</code>, headers do Python, cache do pip — tudo desnecessário em runtime.</p>

<table>
  <thead>
    <tr>
      <th>Approach</th>
      <th>Tamanho final</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">python:3.12</code> + pip install</td>
      <td>980 MB</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">python:3.12-slim</code> + pip install</td>
      <td>280 MB</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">python:3.12-slim</code> + multi-stage</td>
      <td><strong>92 MB</strong></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">python:3.12-alpine</code> + multi-stage</td>
      <td>68 MB ⚠️</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>Alpine é menor, mas dá problemas com libs que dependem de glibc (numpy, scipy, pandas). Para APIs simples, vale. Para data-science, fique no slim.</p>
</blockquote>

<h2 id="passo-2--por-que-gunicorn--uvicorn-workers">Passo 2 — Por que <code class="language-plaintext highlighter-rouge">gunicorn</code> + <code class="language-plaintext highlighter-rouge">uvicorn</code> workers?</h2>

<p>FastAPI é ASGI (async). Uvicorn é o servidor ASGI de referência. Mas em produção:</p>

<ul>
  <li><strong>Uvicorn sozinho:</strong> processo único. Se ele crashar, sua API morre.</li>
  <li><strong>Gunicorn como process manager:</strong> fork de N workers, restart automático, graceful shutdown.</li>
  <li><strong>Workers do tipo <code class="language-plaintext highlighter-rouge">UvicornWorker</code>:</strong> gunicorn gerencia, uvicorn executa o async.</li>
</ul>

<p>A combinação dá robustez de process manager + performance async. É o padrão recomendado pela própria documentação do FastAPI.</p>

<h2 id="passo-3--healthcheck-que-faz-sentido">Passo 3 — Healthcheck que faz sentido</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@app.get</span><span class="p">(</span><span class="sh">"</span><span class="s">/health</span><span class="sh">"</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">health</span><span class="p">():</span>
    <span class="c1"># 1. Processo está respondendo? Sim (a função executou)
</span>    <span class="c1"># 2. Conexão com banco está ok?
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="k">await</span> <span class="n">db</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="sh">"</span><span class="s">SELECT 1</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
        <span class="k">return</span> <span class="nc">Response</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">503</span><span class="p">)</span>
    <span class="k">return</span> <span class="p">{</span><span class="sh">"</span><span class="s">status</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">ok</span><span class="sh">"</span><span class="p">}</span>
</code></pre></div></div>

<p>Esse <code class="language-plaintext highlighter-rouge">/health</code> é usado tanto pelo <code class="language-plaintext highlighter-rouge">HEALTHCHECK</code> do Dockerfile quanto pelo liveness/readiness do Kubernetes. <strong>Não retorne sempre 200</strong>: o healthcheck precisa falhar se o banco cair, senão o orchestrator nunca remove o pod doente do load balancer.</p>

<h2 id="passo-4--logs-estruturados">Passo 4 — Logs estruturados</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">logging</span><span class="p">,</span> <span class="n">json</span>

<span class="k">class</span> <span class="nc">JSONFormatter</span><span class="p">(</span><span class="n">logging</span><span class="p">.</span><span class="n">Formatter</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">format</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="nf">dumps</span><span class="p">({</span>
            <span class="sh">"</span><span class="s">ts</span><span class="sh">"</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">formatTime</span><span class="p">(</span><span class="n">record</span><span class="p">),</span>
            <span class="sh">"</span><span class="s">level</span><span class="sh">"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">levelname</span><span class="p">,</span>
            <span class="sh">"</span><span class="s">msg</span><span class="sh">"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="nf">getMessage</span><span class="p">(),</span>
            <span class="sh">"</span><span class="s">module</span><span class="sh">"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">module</span><span class="p">,</span>
        <span class="p">})</span>

<span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">StreamHandler</span><span class="p">()</span>
<span class="n">handler</span><span class="p">.</span><span class="nf">setFormatter</span><span class="p">(</span><span class="nc">JSONFormatter</span><span class="p">())</span>
<span class="n">logging</span><span class="p">.</span><span class="nf">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="p">.</span><span class="n">INFO</span><span class="p">,</span> <span class="n">handlers</span><span class="o">=</span><span class="p">[</span><span class="n">handler</span><span class="p">])</span>
</code></pre></div></div>

<p>Logs JSON são indispensáveis para qualquer ferramenta de observability (CloudWatch, Datadog, Grafana Loki) extrair campos.</p>

<h2 id="passo-5--graceful-shutdown">Passo 5 — Graceful shutdown</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">contextlib</span> <span class="kn">import</span> <span class="n">asynccontextmanager</span>

<span class="nd">@asynccontextmanager</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
    <span class="c1"># startup
</span>    <span class="k">yield</span>
    <span class="c1"># shutdown: close connections, flush logs
</span>    <span class="k">await</span> <span class="n">db</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

<span class="n">app</span> <span class="o">=</span> <span class="nc">FastAPI</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
</code></pre></div></div>

<p>Sem isso, ao escalar para baixo, conexões DB ficam abertas no servidor, requests em vôo são abortadas.</p>

<h2 id="resultado">Resultado</h2>

<p>Imagem final pronta para:</p>
<ul>
  <li>ECS Fargate</li>
  <li>Cloud Run</li>
  <li>Kubernetes</li>
  <li>Docker Compose em produção (com Caddy/Nginx na frente)</li>
</ul>

<p>E roda em qualquer plataforma sem mudança.</p>]]></content><author><name>Helena Marques</name></author><category term="Aula" /><category term="aula" /><summary type="html"><![CDATA[Dockerfile multi-stage, gunicorn + uvicorn workers, healthcheck. O passo-a-passo que usamos no laboratório.]]></summary></entry><entry><title type="html">Tóquio em 5 dias: guia gastronômico para devorar a cidade</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/20/toquio-cinco-dias/" rel="alternate" type="text/html" title="Tóquio em 5 dias: guia gastronômico para devorar a cidade" /><published>2026-05-20T00:00:00+00:00</published><updated>2026-05-20T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/20/toquio-cinco-dias</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/20/toquio-cinco-dias/"><![CDATA[<p>Tóquio é uma cidade que recompensa a curiosidade gastronômica como poucas. Em cinco dias, é possível atravessar uma régua impressionante: do sushi premiado no Tsukiji ao ramen de meia-noite em becos de Shinjuku, passando por izakayas que cabem oito pessoas e cafés especializados que tratam o coado como cerimônia.</p>

<h2 id="dia-1--tsukiji-outer-market">Dia 1 — Tsukiji Outer Market</h2>

<p>Chegue cedo. Antes das 7h, antes de pensar em café. O mercado externo do antigo Tsukiji ainda funciona, e é ali que você prova um sushi de atum que reconfigura sua escala interna de qualidade. Aposte em <strong>Sushi Dai</strong> ou <strong>Daiwa Sushi</strong> — se a fila assustar, ande mais uns 200 metros: tem sempre um balcão menor com peixe igualmente fresco.</p>

<h2 id="dia-2--ramen-tour-por-shinjuku">Dia 2 — Ramen tour por Shinjuku</h2>

<p>Não existe “o melhor ramen de Tóquio”. Existe o ramen que você prova nessa noite específica, depois de 14 km de caminhada e duas garrafas de chu-hi. Para começar, <strong>Fuunji</strong> (tsukemen) e <strong>Menya Musashi</strong> (tonkotsu). Vá com fome e roupa que não te incomode no calor do vapor.</p>

<h2 id="dia-3--mercados-de-bairro">Dia 3 — Mercados de bairro</h2>

<p>Pegue um trem para Yanaka. Caminhe sem destino. Você vai encontrar:</p>

<ul>
  <li>Tofu artesanal preparado naquela manhã</li>
  <li>Croquetes que custam menos de R$ 5</li>
  <li>Cafés escondidos em casas antigas</li>
</ul>

<blockquote>
  <p>O segredo de Tóquio não está na lista de restaurantes premiados. Está em virar uma esquina e descobrir uma soba house de quatro lugares que existe há 70 anos.</p>
</blockquote>

<h2 id="dia-4--café-de-especialidade">Dia 4 — Café de especialidade</h2>

<p>Tóquio é a capital silenciosa do café de especialidade. <strong>Glitch Coffee</strong> (Kanda), <strong>Koffee Mameya</strong> (Omotesando) e <strong>Bear Pond Espresso</strong> (Shimokitazawa) são paradas obrigatórias. Reserve o dia: cada um é uma experiência longa.</p>

<h2 id="dia-5--izakaya-em-golden-gai">Dia 5 — Izakaya em Golden Gai</h2>

<p>O bairro de Shinjuku conhecido como Golden Gai concentra ~200 bares em vielas estreitas. Muitos cabem seis pessoas. Pague o cover, peça uma highball, converse com o dono. É o adeus que Tóquio merece.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Gastos médios por dia (jan/2026):
- Refeições:      ¥ 4.500 – 8.000
- Transporte:     ¥ 800 – 1.200
- Atrações:       ¥ 1.000 – 3.000
Total:            R$ 350 – 600/dia
</code></pre></div></div>]]></content><author><name>Marina Oliveira</name></author><category term="Viagem" /><category term="viagem" /><summary type="html"><![CDATA[Do sushi do mercado aos ramens das 3 da manhã — um roteiro para quem viaja de garfo na mão.]]></summary></entry><entry><title type="html">Por que Rust está virando o padrão na AWS (e o que isso significa para nós)</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/18/rust-aws-padrao/" rel="alternate" type="text/html" title="Por que Rust está virando o padrão na AWS (e o que isso significa para nós)" /><published>2026-05-18T00:00:00+00:00</published><updated>2026-05-18T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/18/rust-aws-padrao</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/18/rust-aws-padrao/"><![CDATA[<p>Em 2018, a AWS abriu o Firecracker (a VM minimalista que roda o Lambda e o Fargate) em <strong>Rust</strong>. Em 2022, o S3 começou a migrar componentes-chave para Rust. Em 2025, o time do EC2 anunciou que o novo controlador de placement está sendo escrito em Rust do zero. Em 2026, a Amazon publicou que <strong>40% do código novo de infra core é Rust</strong>.</p>

<p>Não é hype. É decisão técnica.</p>

<h2 id="o-que-rust-resolve-que-gocjava-não-resolviam">O que Rust resolve que Go/C++/Java não resolviam</h2>

<p><strong>Memória sem garbage collector.</strong> Lambda precisa de cold start de &lt; 100ms — GC pause de Go (mesmo do Go 1.21+) pode estourar isso em workloads sensíveis.</p>

<p><strong>Concorrência sem data races em compile-time.</strong> O borrow checker do Rust <em>impede</em> que você compile código com race condition. Em sistemas distribuídos críticos (S3 servindo milhões de req/s), isso elimina uma classe inteira de bugs.</p>

<p><strong>Performance C/C++, segurança Java.</strong> Sem segfault, sem buffer overflow, sem dangling pointer — e com o mesmo overhead de runtime que C.</p>

<h2 id="o-custo-real">O custo real</h2>

<table>
  <thead>
    <tr>
      <th>Aspecto</th>
      <th>Go</th>
      <th>Rust</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tempo para um eng. virar produtivo</td>
      <td>2 semanas</td>
      <td>3-6 meses</td>
    </tr>
    <tr>
      <td>Linhas de código (mesma funcionalidade)</td>
      <td>1.0x</td>
      <td>1.3x</td>
    </tr>
    <tr>
      <td>Tempo de compilação</td>
      <td>5s</td>
      <td>90s</td>
    </tr>
    <tr>
      <td>Bugs em produção (memória/concorrência)</td>
      <td>médio</td>
      <td>quase zero</td>
    </tr>
  </tbody>
</table>

<p>Rust é mais <strong>caro de escrever</strong>. A AWS aceita esse custo porque o ganho operacional (menos pages às 3 da manhã) compensa em escala.</p>

<h2 id="o-que-isso-significa-para-times-menores">O que isso significa para times menores</h2>

<p>Se você tem 5 engineers e está escrevendo uma API CRUD, <strong>Rust é a escolha errada</strong>. A produtividade cai mais do que o ganho de performance compensa.</p>

<p>Se você está escrevendo:</p>
<ul>
  <li>Hot path de proxy / load balancer</li>
  <li>Serializer/parser que processa GB/s</li>
  <li>Sistema embarcado (IoT, edge)</li>
  <li>Service mesh / data plane</li>
</ul>

<p>…Rust passa a fazer sentido econômico.</p>

<h2 id="a-migração-no-laboratório">A migração no laboratório</h2>

<p>Estamos migrando um único serviço para Rust: o <strong>API gateway interno</strong> que processa autenticação de todos os pipelines de ML. Volume: ~50k req/s no pico. Razão: o serviço atual em Node consome 8 GB de RAM em pico e o cold start em deploy custa 2-3 min de degradação.</p>

<p>Resultado parcial (semana 3 de 12):</p>
<ul>
  <li>RAM em pico: 8GB → 600MB</li>
  <li>Cold start: 2-3 min → ~3 segundos</li>
  <li>Custo do time: 1 engineer Rust em formação por 3 meses</li>
</ul>

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

<p>Rust não é “o novo Go”. É <strong>a linguagem certa para sistemas onde overhead e correctness importam mais do que velocidade de desenvolvimento</strong>. Para o resto, Python/Go/TypeScript seguem sendo as escolhas certas.</p>

<p>A AWS migra porque a conta fecha. Para a maioria dos times, ainda não fecha.</p>]]></content><author><name>Lucas Pavan</name></author><category term="Tech" /><category term="tech" /><summary type="html"><![CDATA[Lambda, S3, Firecracker, EC2 controlador — pedaços críticos da AWS migraram para Rust. Análise técnica e o que aprender com isso.]]></summary></entry><entry><title type="html">Os 6 cafés de Florianópolis que mudaram nossa rotina de trabalho remoto</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/15/cafes-florianopolis/" rel="alternate" type="text/html" title="Os 6 cafés de Florianópolis que mudaram nossa rotina de trabalho remoto" /><published>2026-05-15T00:00:00+00:00</published><updated>2026-05-15T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/15/cafes-florianopolis</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/15/cafes-florianopolis/"><![CDATA[<p>Trabalhar remoto em Floripa parece fácil — até o terceiro dia, quando você descobre que nem todo café tem tomada disponível, que algumas pousadas cortam o wi-fi às 22h e que o “espresso” de quiosque pode ser uma surpresa amarga.</p>

<p>Depois de seis meses morando entre Lagoa, Centro e Campeche, montamos uma lista honesta dos seis cafés que viraram nossa base de operações.</p>

<h2 id="1-café-cultura-lagoa-da-conceição">1. Café Cultura (Lagoa da Conceição)</h2>

<p>O clássico. Wi-fi rápido, mesa grande comunal e um cold brew que segura uma manhã inteira. Pode ficar lotado no fim de semana, mas das 9h às 11h em dia útil, é praticamente um co-working com bolo de cenoura.</p>

<h2 id="2-bracarense-centro">2. Bracarense (Centro)</h2>

<p>Mais tradicional, com menos tomadas, mas com <strong>o melhor pão de queijo da ilha</strong>. Ideal para reuniões curtas de 1-2 horas.</p>

<h2 id="3-origens-coffee-lab-campeche">3. Origens Coffee Lab (Campeche)</h2>

<p>Quem é fissurado em coado vai amar. Trabalham com grão de origem mapeada e tem um barista que sabe nome de fazenda da Mantiqueira de cor.</p>

<h2 id="4-box-32-mercado-público">4. Box 32 (Mercado Público)</h2>

<p>Não é exatamente um café — é um café-bar dentro do Mercado. Mas tem mesa, tomada e um cortado bem feito. Funciona até as 18h.</p>

<h2 id="5-coffee-lab-daniel-brito-trindade">5. Coffee Lab Daniel Brito (Trindade)</h2>

<p>Próximo da UFSC, lotado de estudantes, mas com pelo menos quatro mesas reservadas para quem leva notebook. Atmosfera de biblioteca-com-cheiro-de-grão.</p>

<h2 id="6-casa-aberta-itacorubi">6. Casa Aberta (Itacorubi)</h2>

<p>O mais escondido. Cardápio menor, mas o espresso tem corpo de cinema. Vai cedo: ele fecha às 14h.</p>

<blockquote>
  <p>Critério principal: tomada <strong>acessível</strong> ao lado da mesa. Café bom sem energia não dura uma standup matinal.</p>
</blockquote>]]></content><author><name>Pedro Tsutomu</name></author><category term="Comida" /><category term="comida" /><summary type="html"><![CDATA[Wi-fi decente, tomada acessível, café de origem. A lista que a gente queria ter encontrado em janeiro.]]></summary></entry><entry><title type="html">Kubernetes vs Serverless em 2026: o framework de decisão que usamos no lab</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/14/k8s-vs-serverless-2026/" rel="alternate" type="text/html" title="Kubernetes vs Serverless em 2026: o framework de decisão que usamos no lab" /><published>2026-05-14T00:00:00+00:00</published><updated>2026-05-14T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/14/k8s-vs-serverless-2026</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/14/k8s-vs-serverless-2026/"><![CDATA[<p>A pergunta volta a cada novo serviço que vamos provisionar: <strong>K8s ou serverless?</strong> Depois de 3 anos rodando os dois em paralelo no laboratório, sistematizamos a decisão num framework de 5 dimensões.</p>

<h2 id="a-regra-rápida-pra-quem-só-quer-o-palpite">A regra rápida (pra quem só quer o palpite)</h2>

<blockquote>
  <p>Se você tem <strong>menos de 4 microserviços</strong> ou <strong>não tem time dedicado de plataforma</strong>, comece em serverless. Migre para K8s só quando a conta da serverless ficar maior que o custo de manter um cluster.</p>
</blockquote>

<p>Esse é o ponto onde a maioria dos times decide errado — começa em K8s “porque é o futuro”, passa 6 meses configurando ingress, secrets, monitoring, helm, e nunca chega a entregar o produto.</p>

<h2 id="as-5-dimensões">As 5 dimensões</h2>

<h3 id="1-custo-em-escala">1. Custo em escala</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                Lambda      Cloud Run       K8s (EKS)
1 req/s         $0.20/mês   $0.50/mês       $73/mês (cluster)
100 req/s       $20/mês     $35/mês         $73/mês
10k req/s       $2000/mês   $1200/mês       $400/mês (3 nodes)
100k req/s      $20k/mês    $8k/mês         $1200/mês
</code></pre></div></div>

<p>O ponto de inflexão fica entre <strong>1k e 5k req/s</strong> dependendo do payload. Abaixo disso, serverless ganha. Acima, K8s ganha.</p>

<h3 id="2-cold-start-tolerância">2. Cold start tolerância</h3>

<table>
  <thead>
    <tr>
      <th>Workload</th>
      <th>Tolera cold start?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Webhook receiver</td>
      <td>✅ sim, raro</td>
    </tr>
    <tr>
      <td>API pública pra usuário</td>
      <td>⚠️ depende (300ms tolerável, 3s não)</td>
    </tr>
    <tr>
      <td>Processamento batch</td>
      <td>✅ totalmente</td>
    </tr>
    <tr>
      <td>Inferência de IA em tempo real</td>
      <td>❌ K8s/SageMaker</td>
    </tr>
    <tr>
      <td>Conexão WebSocket persistente</td>
      <td>❌ K8s</td>
    </tr>
  </tbody>
</table>

<h3 id="3-estado-e-conexões-persistentes">3. Estado e conexões persistentes</h3>

<p><strong>Serverless não mantém estado entre invocações.</strong> Se você precisa:</p>
<ul>
  <li>Conexão pool de DB persistente</li>
  <li>Cache em memória</li>
  <li>WebSocket de longa duração</li>
  <li>Subscriptions de eventos com state</li>
</ul>

<p>K8s é o caminho. Lambda + ElastiCache + DynamoDB resolve, mas com mais glue code.</p>

<h3 id="4-equipe-disponível">4. Equipe disponível</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Setup mínimo para K8s saudável:
- 1 SRE/Platform engineer dedicado
- CI/CD configurado (ArgoCD ou Flux)
- Observability stack (Prometheus + Grafana + Loki)
- Política de RBAC + secrets management
- Backup/disaster recovery do etcd

Setup mínimo para Lambda saudável:
- AWS SAM ou Serverless Framework
- CloudWatch alarms básicos
</code></pre></div></div>

<p>Tempo de setup inicial: <strong>2-4 semanas K8s vs 1 dia Lambda.</strong></p>

<h3 id="5-lock-in-com-provedor">5. Lock-in com provedor</h3>

<p>Serverless = lock-in alto. AWS Lambda + API Gateway + DynamoDB é difícil migrar pra GCP.</p>

<p>K8s = portável (em teoria). Na prática, manifests rodam em qualquer cluster, mas operadores específicos (AWS Load Balancer Controller, EFS CSI driver, etc.) prendem ao cloud.</p>

<h2 id="casos-reais-do-laboratório">Casos reais do laboratório</h2>

<h3 id="pipeline-de-embedding-jobs-assíncronos-batch-grande">Pipeline de embedding (jobs assíncronos, batch grande)</h3>
<p><strong>Escolha:</strong> Lambda + SQS. Cold start de 800ms é irrelevante quando o job inteiro leva 2 minutos. Custo cai pra ~$3/mês fora dos picos.</p>

<h3 id="api-de-inferência-em-tempo-real-latência-crítica-p99--100ms">API de inferência em tempo real (latência crítica, p99 &lt; 100ms)</h3>
<p><strong>Escolha:</strong> EKS + nodes com GPU. Modelos carregados em memória, sem cold start.</p>

<h3 id="webhooks-de-integrações-10-50-reqs-latência-não-crítica">Webhooks de integrações (10-50 req/s, latência não-crítica)</h3>
<p><strong>Escolha:</strong> Cloud Run. Auto-scaling até zero quando ninguém está chamando, paga só pelo que usa.</p>

<h3 id="dashboard-interno-10-users-simultâneos-baixíssima-escala">Dashboard interno (10 users simultâneos, baixíssima escala)</h3>
<p><strong>Escolha:</strong> Cloud Run (free tier cobre tudo). K8s seria caro pra esse volume.</p>

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

<p>Não existe escolha “futura-prova”. Existe <strong>escolha certa para o estágio atual do produto e tamanho do time</strong>. Comece simples, migre quando a conta ou a complexidade da serverless exceder o custo de operar um cluster.</p>]]></content><author><name>Rafael Andrade</name></author><category term="Aula" /><category term="aula" /><summary type="html"><![CDATA[Quando vale a complexidade do K8s, quando Lambda/Cloud Run resolve. Análise por dimensão de custo, escala e equipe.]]></summary></entry><entry><title type="html">Lisboa em 7 dias: o roteiro que evita o turismo cansado</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/10/lisboa-sete-dias/" rel="alternate" type="text/html" title="Lisboa em 7 dias: o roteiro que evita o turismo cansado" /><published>2026-05-10T00:00:00+00:00</published><updated>2026-05-10T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/10/lisboa-sete-dias</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/10/lisboa-sete-dias/"><![CDATA[<p>Lisboa é uma cidade que vive de luz. A primeira impressão é sempre o brilho dourado batendo nas fachadas de azulejo. Mas depois das fotos no mirante de Santa Catarina, a maioria dos viajantes cai na mesma armadilha: três horas de fila no Pastéis de Belém, dois dias no Chiado, e Alfama virou um cartão postal sem alma.</p>

<p>Esse roteiro de 7 dias tenta fugir disso.</p>

<h2 id="dias-1-2-centro-e-chiado-cedo">Dias 1-2: Centro e Chiado (cedo)</h2>

<p>Faça a turistada inicial, mas <strong>antes das 10h</strong>. Praça do Comércio, Rua Augusta, Chiado. Almoce na Cantinho do Avillez (reservar) ou no Time Out Market (sem reserva).</p>

<h2 id="dia-3-alfama-de-noite">Dia 3: Alfama de noite</h2>

<p>Alfama de manhã é Disneylândia. De noite, depois das 21h, vira o que sempre foi: bairro de gente que mora ali, fado nas tascas e luz de poste no calçamento. Vá ao <strong>A Baiuca</strong> e peça o vinho da casa.</p>

<h2 id="dia-4-belém-sem-o-pastéis-de-belém">Dia 4: Belém <strong>sem</strong> o Pastéis de Belém</h2>

<p>Sim. Vá ao Mosteiro dos Jerónimos, à Torre de Belém, ao MAAT. E coma o pastel na <strong>Manteigaria</strong> mesmo (no Chiado) — tem gente que prefere. Diferença é mínima e a fila é zero.</p>

<h2 id="dia-5-lx-factory--time-out">Dia 5: LX Factory + Time Out</h2>

<p>Manhã na LX Factory (bookshop <strong>Ler Devagar</strong> vale a viagem). Almoço/tarde no Time Out Market.</p>

<h2 id="dia-6-sintra">Dia 6: Sintra</h2>

<p>Pegue um Uber, não o trem. Saia das 8h. Faça <strong>Quinta da Regaleira</strong> primeiro (poço inverso!), depois Palácio da Pena. Volte tomando vinho na Quinta da Cevada.</p>

<h2 id="dia-7-cascais-ou-setúbal">Dia 7: Cascais ou Setúbal</h2>

<p>Praia. Marisco. Final de viagem. <strong>Setúbal</strong> tem o melhor choco frito do planeta. Cascais é mais fácil de chegar.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Custo médio total (7 dias, casal):
- Hospedagem (Alfama, Airbnb):  R$ 4.500
- Comida + bebida:               R$ 4.000
- Transporte:                    R$ 1.200
Total:                           R$ 9.700
</code></pre></div></div>]]></content><author><name>Helena Marques</name></author><category term="Viagem" /><category term="viagem" /><summary type="html"><![CDATA[Pastel de Belém, mirantes, fado em Alfama — sem cair na armadilha das filas de duas horas.]]></summary></entry><entry><title type="html">Edge computing: o que aprendemos rodando IA na borda em 6 meses</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/08/edge-computing-ia-borda/" rel="alternate" type="text/html" title="Edge computing: o que aprendemos rodando IA na borda em 6 meses" /><published>2026-05-08T00:00:00+00:00</published><updated>2026-05-08T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/08/edge-computing-ia-borda</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/08/edge-computing-ia-borda/"><![CDATA[<p>Em 2026, “rodar IA na borda” deixou de ser pesquisa experimental e virou opção real de arquitetura. Em 6 meses no laboratório, testamos três cenários reais. O resumo: <strong>muito mais difícil do que vendem, e muito mais útil do que parece em alguns casos específicos.</strong></p>

<h2 id="cenário-1-classificação-de-texto-em-cloudflare-workers-ai">Cenário 1: classificação de texto em Cloudflare Workers AI</h2>

<p><strong>Modelo:</strong> Llama-3.2-3B quantizado para 4-bit
<strong>Tarefa:</strong> classificar tickets de suporte em 12 categorias
<strong>Volume:</strong> ~80k requests/dia</p>

<h3 id="resultado">Resultado</h3>

<ul>
  <li>Latência média: <strong>120 ms</strong> (vs 800 ms chamando OpenAI)</li>
  <li>Custo: <strong>$8/mês</strong> (vs $240/mês na OpenAI no mesmo volume)</li>
  <li>Acurácia: 91% (vs 96% no GPT-4o)</li>
</ul>

<p>Trade-off claro: 5% menos preciso, 30x mais barato, 7x mais rápido. Para classificação de ticket onde o humano revisa de qualquer jeito, vale demais.</p>

<h3 id="o-que-não-documentavam">O que não documentavam</h3>

<ul>
  <li><strong>Tamanho do modelo no edge:</strong> Cloudflare Workers AI cobra por modelo ativo. Modelos &gt; 8GB têm cold start de 5-8s na primeira request da hora.</li>
  <li><strong>Determinismo:</strong> quantização 4-bit introduz não-determinismo perceptível. Mesmo input pode dar saídas diferentes em 1-2% das requests.</li>
</ul>

<h2 id="cenário-2-onnx-no-navegador-busca-semântica-client-side">Cenário 2: ONNX no navegador (busca semântica client-side)</h2>

<p><strong>Modelo:</strong> <code class="language-plaintext highlighter-rouge">all-MiniLM-L6-v2</code> (22 MB após quantização)
<strong>Tarefa:</strong> busca semântica em um catálogo de 5k itens
<strong>Onde roda:</strong> direto no navegador do usuário, via WebAssembly</p>

<h3 id="resultado-1">Resultado</h3>

<ul>
  <li>Latência de inferência: 40-80 ms (M1 Mac), 200 ms (Android médio)</li>
  <li>Custo de servidor: <strong>$0</strong> (tudo no client)</li>
  <li>Privacidade: dados nunca saem do device</li>
</ul>

<h3 id="o-que-não-documentavam-1">O que não documentavam</h3>

<ul>
  <li><strong>Download inicial de 22MB</strong> trava o primeiro paint. Solução: lazy-load com <code class="language-plaintext highlighter-rouge">IntersectionObserver</code> na busca, não na home.</li>
  <li><strong>Memória:</strong> modelos em WebAssembly não liberam memória depois de carregados — chrome chega a 800MB de RAM no tab. Não dá pra carregar 3 modelos diferentes.</li>
  <li><strong>iOS Safari</strong> tem implementação WASM limitada. Em iPhones antigos, a inferência leva 2-3s.</li>
</ul>

<h2 id="cenário-3-llamacpp-em-raspberry-pi-5-lab-only-off-grid">Cenário 3: llama.cpp em Raspberry Pi 5 (lab-only, off-grid)</h2>

<p><strong>Modelo:</strong> Llama-3.2-1B quantizado em 4-bit (700 MB)
<strong>Tarefa:</strong> assistant para o sistema de monitoramento de sensores do lab
<strong>Onde roda:</strong> Raspberry Pi 5 8GB conectado direto aos sensores, sem internet</p>

<h3 id="resultado-2">Resultado</h3>

<ul>
  <li>Tokens/segundo: <strong>8-12</strong> (suficiente para resposta de 100 tokens em ~10s)</li>
  <li>RAM: 1.8 GB em uso constante</li>
  <li>Funciona <strong>offline</strong>, sem dependência de API externa</li>
</ul>

<h3 id="custo">Custo</h3>

<ul>
  <li>Raspberry Pi 5 8GB: R$ 900</li>
  <li>SD card 128GB: R$ 80</li>
  <li>Cooler ativo: R$ 60</li>
  <li><strong>Total: R$ 1040 hardware único</strong> vs ~R$ 100/mês de API perpétua</li>
</ul>

<p>Em 11 meses se paga. Útil para cenários <strong>air-gapped</strong> (laboratórios sensíveis, ambientes sem internet).</p>

<h2 id="quando-edge-não-compensa">Quando edge NÃO compensa</h2>

<ul>
  <li>Modelos &gt; 8 GB (Llama 70B, GPT-5) — edge não tem RAM/GPU pra isso</li>
  <li>Workloads com baixíssimo volume (&lt; 100 req/dia) — overhead de operação não compensa</li>
  <li>Aplicações onde 5% de queda na precisão é inaceitável (saúde, jurídico, financeiro)</li>
</ul>

<h2 id="checklist-para-considerar-edge-no-seu-caso">Checklist para considerar edge no seu caso</h2>

<ol>
  <li>Volume &gt; 10k req/dia? ✅ sim, vale avaliar</li>
  <li>Modelo tem versão quantizada de qualidade conhecida? ✅ vale avaliar</li>
  <li>Latência atual é gargalo? ✅ vale avaliar</li>
  <li>Custo de API hoje &gt; R$ 500/mês? ✅ vale avaliar</li>
  <li>Time tem alguém disposto a aprender quantização? ✅ vale avaliar</li>
</ol>

<p>Se respondeu sim em 3+, faça um POC de 1 semana.</p>

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

<p>Edge AI é tecnologia real e útil — mas apenas em <strong>casos específicos</strong> onde o trade-off de precisão por latência/custo/privacidade compensa. Para 80% dos workloads, chamar OpenAI ainda é a escolha certa.</p>

<p>A pergunta correta não é “podemos colocar IA na borda?” mas “esse caso específico paga o custo operacional de manter modelo na borda?”</p>]]></content><author><name>Marina Oliveira</name></author><category term="Tech" /><category term="tech" /><summary type="html"><![CDATA[Modelos quantizados em Cloudflare Workers, ONNX no navegador, llama.cpp em Raspberry Pi. Onde compensa, onde ainda não.]]></summary></entry><entry><title type="html">Bolonha: por que essa cidade é a capital mundial da pasta</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/05/bolonha-pasta/" rel="alternate" type="text/html" title="Bolonha: por que essa cidade é a capital mundial da pasta" /><published>2026-05-05T00:00:00+00:00</published><updated>2026-05-05T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/05/bolonha-pasta</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/05/bolonha-pasta/"><![CDATA[<p>Bolonha tem três apelidos: <strong>La Dotta</strong> (a culta — por causa da universidade mais antiga do mundo), <strong>La Rossa</strong> (a vermelha — pelas telhas) e <strong>La Grassa</strong> (a gorda — pela comida). É esse terceiro nome que nos interessa.</p>

<h2 id="a-pasta-fresca-como-religião">A pasta fresca como religião</h2>

<p>Em Bolonha, pasta seca é coisa de gente apressada. A pasta de verdade é fresca, feita ovo + farinha 00, esticada à mão ou com cilindro manual. As <strong>sfogline</strong> (mulheres que fazem pasta em vitrines de rua) são uma instituição local — você passa e vê tagliatelle nascendo na sua frente.</p>

<h2 id="o-ragù-bolonhês-de-verdade">O ragù bolonhês de verdade</h2>

<p>Esqueça espaguete à bolonhesa. Em Bolonha, o ragù é servido com <strong>tagliatelle</strong> (massa larga, que carrega o molho) ou em <strong>lasanha verde</strong>. Os ingredientes não incluem alho. Incluem:</p>

<ul>
  <li>Carne de boi moída grossa</li>
  <li>Pancetta</li>
  <li>Sofrito de cebola, cenoura e aipo</li>
  <li>Vinho branco (não tinto)</li>
  <li>Leite no final (a parte controversa)</li>
  <li>Tomate em <strong>pouca quantidade</strong></li>
</ul>

<blockquote>
  <p>Quem coloca alho em ragù em Bolonha é deportado simbolicamente.</p>
</blockquote>

<h2 id="tortellini-in-brodo">Tortellini in brodo</h2>

<p>O outro prato-bandeira. Tortellini minúsculos recheados de mortadela, lombo, parmigiano e noz-moscada, servidos em um caldo de carne claro e perfeito. É comida de inverno, comida de domingo, comida de avó.</p>

<h2 id="por-que-bolonha-não-tem-pizza">Por que Bolonha não tem pizza?</h2>

<p>A pizza é napolitana. Bolonha tem <strong>piadina</strong> — pão fino, achatado, recheado com mortadela e squacquerone. Não é pizza, mas resolve.</p>

<h2 id="onde-comer-não-erra">Onde comer (não erra)</h2>

<ol>
  <li><strong>Trattoria Anna Maria</strong> — tagliatelle al ragù exemplar</li>
  <li><strong>Sfoglia Rina</strong> — pasta fresca para levar</li>
  <li><strong>Osteria dell’Orsa</strong> — tortellini in brodo + crostini</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Tip prático:
Reserve sempre antes das 13h ou 20h.
Bolonha almoça no horário, jantar começa 19h30.
</code></pre></div></div>]]></content><author><name>Lucas Pavan</name></author><category term="Comida" /><category term="comida" /><summary type="html"><![CDATA[Tagliatelle al ragù, tortellini in brodo e o motivo de Bolonha não ter pizza.]]></summary></entry><entry><title type="html">Aula: observability em Go do zero — traces, metrics e logs em 1 hora</title><link href="https://822992a2.lab-jekyll.pages.dev/2026/05/02/aula-observability-go/" rel="alternate" type="text/html" title="Aula: observability em Go do zero — traces, metrics e logs em 1 hora" /><published>2026-05-02T00:00:00+00:00</published><updated>2026-05-02T00:00:00+00:00</updated><id>https://822992a2.lab-jekyll.pages.dev/2026/05/02/aula-observability-go</id><content type="html" xml:base="https://822992a2.lab-jekyll.pages.dev/2026/05/02/aula-observability-go/"><![CDATA[<p>Esta aula é um passo-a-passo prático: pegamos uma API Go básica e instrumentamos os três pilares de observability (<strong>logs, metrics, traces</strong>) em 60 minutos. No final, você tem um serviço pronto para produção com visibilidade completa.</p>

<h2 id="o-serviço-base-5-minutos">O serviço base (5 minutos)</h2>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"net/http"</span>
    <span class="s">"github.com/go-chi/chi/v5"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">r</span> <span class="o">:=</span> <span class="n">chi</span><span class="o">.</span><span class="n">NewRouter</span><span class="p">()</span>
    <span class="n">r</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="s">"/users/{id}"</span><span class="p">,</span> <span class="n">getUser</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="s">":8080"</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">getUser</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">id</span> <span class="o">:=</span> <span class="n">chi</span><span class="o">.</span><span class="n">URLParam</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="s">"id"</span><span class="p">)</span>
    <span class="c">// ... lógica de fetch user</span>
    <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">`{"id":"`</span> <span class="o">+</span> <span class="n">id</span> <span class="o">+</span> <span class="s">`","name":"Maria"}`</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Funcional, mas cego. Vamos instrumentar.</p>

<h2 id="pilar-1-logs-estruturados-com-slog-10-min">Pilar 1: logs estruturados com <code class="language-plaintext highlighter-rouge">slog</code> (10 min)</h2>

<p>A stdlib Go ganhou <code class="language-plaintext highlighter-rouge">log/slog</code> em 1.21 — não precisa mais de Zap, Logrus, etc.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">(</span>
    <span class="s">"log/slog"</span>
    <span class="s">"os"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">init</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">logger</span> <span class="o">:=</span> <span class="n">slog</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">slog</span><span class="o">.</span><span class="n">NewJSONHandler</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Stdout</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">slog</span><span class="o">.</span><span class="n">HandlerOptions</span><span class="p">{</span>
        <span class="n">Level</span><span class="o">:</span> <span class="n">slog</span><span class="o">.</span><span class="n">LevelInfo</span><span class="p">,</span>
    <span class="p">}))</span>
    <span class="n">slog</span><span class="o">.</span><span class="n">SetDefault</span><span class="p">(</span><span class="n">logger</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">getUser</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">id</span> <span class="o">:=</span> <span class="n">chi</span><span class="o">.</span><span class="n">URLParam</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="s">"id"</span><span class="p">)</span>
    <span class="n">slog</span><span class="o">.</span><span class="n">Info</span><span class="p">(</span><span class="s">"user fetch"</span><span class="p">,</span>
        <span class="s">"user_id"</span><span class="p">,</span> <span class="n">id</span><span class="p">,</span>
        <span class="s">"method"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Method</span><span class="p">,</span>
        <span class="s">"path"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">URL</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="c">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Output:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"2026-05-02T10:00:00Z"</span><span class="p">,</span><span class="nl">"level"</span><span class="p">:</span><span class="s2">"INFO"</span><span class="p">,</span><span class="nl">"msg"</span><span class="p">:</span><span class="s2">"user fetch"</span><span class="p">,</span><span class="nl">"user_id"</span><span class="p">:</span><span class="s2">"42"</span><span class="p">,</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"GET"</span><span class="p">,</span><span class="nl">"path"</span><span class="p">:</span><span class="s2">"/users/42"</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Já consumível por CloudWatch, Loki, Datadog sem parser custom.</p>

<h2 id="pilar-2-metrics-com-prometheus-15-min">Pilar 2: metrics com Prometheus (15 min)</h2>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="s">"github.com/prometheus/client_golang/prometheus/promhttp"</span>
<span class="k">import</span> <span class="s">"github.com/prometheus/client_golang/prometheus"</span>

<span class="k">var</span> <span class="p">(</span>
    <span class="n">requestsTotal</span> <span class="o">=</span> <span class="n">prometheus</span><span class="o">.</span><span class="n">NewCounterVec</span><span class="p">(</span>
        <span class="n">prometheus</span><span class="o">.</span><span class="n">CounterOpts</span><span class="p">{</span>
            <span class="n">Name</span><span class="o">:</span> <span class="s">"http_requests_total"</span><span class="p">,</span>
            <span class="n">Help</span><span class="o">:</span> <span class="s">"Total de requests HTTP"</span><span class="p">,</span>
        <span class="p">},</span>
        <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"method"</span><span class="p">,</span> <span class="s">"endpoint"</span><span class="p">,</span> <span class="s">"status"</span><span class="p">},</span>
    <span class="p">)</span>
    <span class="n">requestDuration</span> <span class="o">=</span> <span class="n">prometheus</span><span class="o">.</span><span class="n">NewHistogramVec</span><span class="p">(</span>
        <span class="n">prometheus</span><span class="o">.</span><span class="n">HistogramOpts</span><span class="p">{</span>
            <span class="n">Name</span><span class="o">:</span> <span class="s">"http_request_duration_seconds"</span><span class="p">,</span>
            <span class="n">Buckets</span><span class="o">:</span> <span class="p">[]</span><span class="kt">float64</span><span class="p">{</span><span class="m">0.01</span><span class="p">,</span> <span class="m">0.05</span><span class="p">,</span> <span class="m">0.1</span><span class="p">,</span> <span class="m">0.3</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">10</span><span class="p">},</span>
        <span class="p">},</span>
        <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"endpoint"</span><span class="p">},</span>
    <span class="p">)</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">init</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">prometheus</span><span class="o">.</span><span class="n">MustRegister</span><span class="p">(</span><span class="n">requestsTotal</span><span class="p">,</span> <span class="n">requestDuration</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">// Middleware</span>
<span class="k">func</span> <span class="n">metricsMiddleware</span><span class="p">(</span><span class="n">next</span> <span class="n">http</span><span class="o">.</span><span class="n">Handler</span><span class="p">)</span> <span class="n">http</span><span class="o">.</span><span class="n">Handler</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">http</span><span class="o">.</span><span class="n">HandlerFunc</span><span class="p">(</span><span class="k">func</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">start</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
        <span class="n">ww</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">statusWriter</span><span class="p">{</span><span class="n">ResponseWriter</span><span class="o">:</span> <span class="n">w</span><span class="p">,</span> <span class="n">status</span><span class="o">:</span> <span class="m">200</span><span class="p">}</span>
        <span class="n">next</span><span class="o">.</span><span class="n">ServeHTTP</span><span class="p">(</span><span class="n">ww</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
        <span class="n">requestsTotal</span><span class="o">.</span><span class="n">WithLabelValues</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Method</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">URL</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%d"</span><span class="p">,</span> <span class="n">ww</span><span class="o">.</span><span class="n">status</span><span class="p">))</span><span class="o">.</span><span class="n">Inc</span><span class="p">()</span>
        <span class="n">requestDuration</span><span class="o">.</span><span class="n">WithLabelValues</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">URL</span><span class="o">.</span><span class="n">Path</span><span class="p">)</span><span class="o">.</span><span class="n">Observe</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Since</span><span class="p">(</span><span class="n">start</span><span class="p">)</span><span class="o">.</span><span class="n">Seconds</span><span class="p">())</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="c">// main()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Use</span><span class="p">(</span><span class="n">metricsMiddleware</span><span class="p">)</span>
<span class="n">r</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="s">"/metrics"</span><span class="p">,</span> <span class="n">promhttp</span><span class="o">.</span><span class="n">Handler</span><span class="p">())</span>
</code></pre></div></div>

<p>Agora <code class="language-plaintext highlighter-rouge">curl :8080/metrics</code> retorna formato Prometheus pronto pra scrape.</p>

<h2 id="pilar-3-traces-com-opentelemetry-25-min">Pilar 3: traces com OpenTelemetry (25 min)</h2>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">(</span>
    <span class="s">"go.opentelemetry.io/otel"</span>
    <span class="s">"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"</span>
    <span class="n">sdktrace</span> <span class="s">"go.opentelemetry.io/otel/sdk/trace"</span>
    <span class="s">"go.opentelemetry.io/otel/sdk/resource"</span>
    <span class="n">semconv</span> <span class="s">"go.opentelemetry.io/otel/semconv/v1.21.0"</span>
    <span class="s">"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">initTracer</span><span class="p">(</span><span class="n">ctx</span> <span class="n">context</span><span class="o">.</span><span class="n">Context</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">sdktrace</span><span class="o">.</span><span class="n">TracerProvider</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">exporter</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">otlptracegrpc</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span>
        <span class="n">otlptracegrpc</span><span class="o">.</span><span class="n">WithEndpoint</span><span class="p">(</span><span class="s">"localhost:4317"</span><span class="p">),</span>
        <span class="n">otlptracegrpc</span><span class="o">.</span><span class="n">WithInsecure</span><span class="p">(),</span>
    <span class="p">)</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span> <span class="p">}</span>

    <span class="n">tp</span> <span class="o">:=</span> <span class="n">sdktrace</span><span class="o">.</span><span class="n">NewTracerProvider</span><span class="p">(</span>
        <span class="n">sdktrace</span><span class="o">.</span><span class="n">WithBatcher</span><span class="p">(</span><span class="n">exporter</span><span class="p">),</span>
        <span class="n">sdktrace</span><span class="o">.</span><span class="n">WithResource</span><span class="p">(</span><span class="n">resource</span><span class="o">.</span><span class="n">NewWithAttributes</span><span class="p">(</span>
            <span class="n">semconv</span><span class="o">.</span><span class="n">SchemaURL</span><span class="p">,</span>
            <span class="n">semconv</span><span class="o">.</span><span class="n">ServiceName</span><span class="p">(</span><span class="s">"users-api"</span><span class="p">),</span>
        <span class="p">)),</span>
    <span class="p">)</span>
    <span class="n">otel</span><span class="o">.</span><span class="n">SetTracerProvider</span><span class="p">(</span><span class="n">tp</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">tp</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ctx</span> <span class="o">:=</span> <span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">()</span>
    <span class="n">tp</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">initTracer</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
    <span class="k">defer</span> <span class="n">tp</span><span class="o">.</span><span class="n">Shutdown</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>

    <span class="n">r</span> <span class="o">:=</span> <span class="n">chi</span><span class="o">.</span><span class="n">NewRouter</span><span class="p">()</span>
    <span class="n">r</span><span class="o">.</span><span class="n">Use</span><span class="p">(</span><span class="n">metricsMiddleware</span><span class="p">)</span>
    <span class="n">r</span><span class="o">.</span><span class="n">Method</span><span class="p">(</span><span class="s">"GET"</span><span class="p">,</span> <span class="s">"/users/{id}"</span><span class="p">,</span>
        <span class="n">otelhttp</span><span class="o">.</span><span class="n">NewHandler</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">HandlerFunc</span><span class="p">(</span><span class="n">getUser</span><span class="p">),</span> <span class="s">"users.get"</span><span class="p">),</span>
    <span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="s">":8080"</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">getUser</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ctx</span><span class="p">,</span> <span class="n">span</span> <span class="o">:=</span> <span class="n">otel</span><span class="o">.</span><span class="n">Tracer</span><span class="p">(</span><span class="s">"users"</span><span class="p">)</span><span class="o">.</span><span class="n">Start</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Context</span><span class="p">(),</span> <span class="s">"fetch_from_db"</span><span class="p">)</span>
    <span class="k">defer</span> <span class="n">span</span><span class="o">.</span><span class="n">End</span><span class="p">()</span>
    <span class="c">// ... fetch user, e cada query passa ctx pra trazer no trace</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Spans aparecem em qualquer backend OTel: Jaeger, Tempo, Datadog APM, Honeycomb.</p>

<h2 id="stack-docker-compose-pra-testar-localmente">Stack docker-compose pra testar localmente</h2>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">prometheus</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">prom/prometheus</span>
    <span class="na">ports</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">9090:9090"</span><span class="pi">]</span>
    <span class="na">volumes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">./prometheus.yml:/etc/prometheus/prometheus.yml"</span><span class="pi">]</span>

  <span class="na">grafana</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">grafana/grafana</span>
    <span class="na">ports</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">3000:3000"</span><span class="pi">]</span>

  <span class="na">tempo</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">grafana/tempo</span>
    <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">-config.file=/etc/tempo.yaml"</span><span class="pi">]</span>
    <span class="na">ports</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">4317:4317"</span><span class="pi">]</span>
</code></pre></div></div>

<p>Em 1 hora você tem:</p>
<ul>
  <li>Dashboard de RPS, latência, erro por endpoint</li>
  <li>Distributed tracing entre serviços</li>
  <li>Logs estruturados queryable</li>
</ul>

<h2 id="custo-operacional">Custo operacional</h2>

<ul>
  <li><strong>Self-hosted (lab):</strong> 1 VM 4GB roda Prometheus + Grafana + Loki + Tempo sem suar.</li>
  <li><strong>Managed:</strong> Grafana Cloud free tier (10k metrics series, 50GB logs, 50GB traces) cobre serviços médios.</li>
  <li><strong>Datadog:</strong> caro mas turn-key. Considere para times sem SRE.</li>
</ul>

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

<p>Observability não é luxo. É o que te avisa antes do usuário reclamar. Em Go, com a stack acima, o custo de instrumentar é ~200 linhas pra um serviço médio — e o ganho é noites dormindo durante incidentes.</p>]]></content><author><name>Yuki Tanaka</name></author><category term="Aula" /><category term="aula" /><summary type="html"><![CDATA[OpenTelemetry, Prometheus, slog estruturado. Setup completo que você pode replicar no próximo serviço.]]></summary></entry></feed>