<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Leo Cavalcante]]></title><description><![CDATA[Developer Experience @ PicPay]]></description><link>https://leocavalcante.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 11 May 2026 17:30:54 GMT</lastBuildDate><atom:link href="https://leocavalcante.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Por que sua IA falha? O segredo não está no modelo, mas no "Agent Harness"]]></title><description><![CDATA[Você já deve ter passado por isso: em um momento, o GPT ou o Claude resolve um problema complexo de código em segundos; no momento seguinte, a mesma IA esquece o contexto básico ou inventa uma informa]]></description><link>https://leocavalcante.dev/por-que-sua-ia-falha-o-segredo-n-o-est-no-modelo-mas-no-agent-harness</link><guid isPermaLink="true">https://leocavalcante.dev/por-que-sua-ia-falha-o-segredo-n-o-est-no-modelo-mas-no-agent-harness</guid><category><![CDATA[AI]]></category><category><![CDATA[harness-engineering]]></category><category><![CDATA[agent-harness]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 04 May 2026 11:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/60f177f63a0aaa311ef8ed29/7cf9487b-a473-4afd-a66c-beb573839d5d.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Você já deve ter passado por isso: em um momento, o GPT ou o Claude resolve um problema complexo de código em segundos; no momento seguinte, a mesma IA esquece o contexto básico ou inventa uma informação inexistente. Se o "cérebro" (o modelo) é o mesmo, por que os resultados são tão inconsistentes?</p>
<p>Em 2026, a fronteira da tecnologia deixou de ser a busca pelo modelo mais "inteligente". O diferencial de desempenho agora reside no Agent Harness (ou "arreio" do agente). A falha da sua IA raramente é uma questão de "burrice" do modelo, mas sim uma falha de engenharia no ambiente onde ele opera, o seu substrato de execução.</p>
<h2>O modelo é o motor, o harness é o carro</h2>
<p>Para entender a diferença entre capacidade cognitiva bruta e trabalho útil, imagine um cavalo selvagem. Ele possui força bruta, mas, sem um arreio (harness), pode correr para qualquer lado ou se assustar com um ruído. O harness não torna o cavalo mais forte; ele permite que sua força seja direcionada de forma confiável.</p>
<p>Na IA, o modelo é o motor, mas o harness é o carro completo. Segundo a arquitetura proposta por WenHao Yu e Martin Fowler, um sistema de agentes robusto é composto por seis camadas de engenharia:</p>
<ol>
<li><p><strong>Loop</strong>: ciclos de observação, decisão, ação e verificação.</p>
</li>
<li><p><strong>Tools</strong>: interfaces para ler arquivos, rodar código e acessar APIs.</p>
</li>
<li><p><strong>Context</strong>: curadoria do que a IA deve ver para evitar sobrecarga de tokens.</p>
</li>
<li><p><strong>Persistence</strong>: manutenção de estado e memória de longo prazo.</p>
</li>
<li><p><strong>Verification</strong>: auto-revisão, testes unitários e linters automáticos.</p>
</li>
<li><p><strong>Constraints</strong>: limites de gastos, segurança e permissões de acesso.</p>
</li>
</ol>
<blockquote>
<p>"O mesmo modelo sob diferentes harnesses parece uma espécie inteiramente diferente." — WenHao Yu</p>
</blockquote>
<h2>O salto de 52% para 66% sem mudar uma linha de código da IA</h2>
<p>A prova definitiva de que a engenharia de sistemas supera a troca de modelos veio de um experimento da LangChain em fevereiro de 2026. Ao testar seu agente de codificação no Terminal Bench 2.0, a pontuação inicial foi de 52,8%.</p>
<p>Sem atualizar o modelo para uma versão mais potente, a equipe modificou apenas o harness. As mudanças incluíram:</p>
<ul>
<li><p><strong>Tuned reasoning budgets</strong>: mais tempo para planejamento e menos para implementação.</p>
</li>
<li><p><strong>Injeção de contexto ambiental</strong>: mapeamento completo do diretório antes da tarefa.</p>
</li>
<li><p><strong>Failure analysis</strong>: análise automatizada de padrões de erro entre execuções.</p>
</li>
<li><p><strong>Anti-drift detection</strong>: identificação de loops repetitivos onde a IA editava o mesmo arquivo sem progresso.</p>
</li>
</ul>
<p>O resultado? A precisão saltou para 66,5%, levando o agente ao Top 5 do ranking global. Isso demonstra que o "teto" de desempenho é definido pelo sistema de suporte, não pela inteligência inferencial do modelo.</p>
<h2>O paradoxo da Vercel: menos ferramentas, mais precisão</h2>
<p>Muitos acreditam que quanto mais ferramentas dermos a um agente, mais capaz ele será. O caso da Vercel prova o contrário através do princípio da <strong>"Least Agency"</strong> (Agência Mínima). Ao reduzir as ferramentas disponíveis de 15 para apenas 2, a empresa otimizou drasticamente a camada de Constraints (Restrições).</p>
<p>Os ganhos foram brutais:</p>
<ul>
<li><p><strong>Precisão</strong>: subiu de 80% para 100%.</p>
</li>
<li><p><strong>Custo</strong>: redução de 37% no uso de tokens.</p>
</li>
<li><p><strong>Velocidade</strong>: o sistema tornou-se 3,5 vezes mais rápido.</p>
</li>
</ul>
<p>Ao restringir o espaço de decisão, você remove o "ruído" que causa alucinações. Um harness bem projetado reduz a superfície de ataque e de erro, garantindo que o modelo foque apenas na execução essencial.</p>
<h2>Harness Engineering: O fim da "Engenharia de Prompt"</h2>
<p>Em 2026, o foco mudou. Se antes o objetivo era descobrir a "palavra mágica" no prompt, hoje o foco é estruturar o sistema. O modelo CAR (Control, Agency, Runtime), detalhado no paper da preprints.org, define como os arquitetos projetam sistemas hoje:</p>
<ul>
<li><p><strong>Control</strong>: onde o julgamento humano vira restrição legível por máquina. Inclui arquivos como AGENTS.md, Repository Maps e políticas de linter.</p>
</li>
<li><p><strong>Agency</strong>: a interface mediada de ação. Define como a IA interage com o substrato de execução (APIs, browsers, gRPC).</p>
</li>
<li><p><strong>Runtime</strong>: gerenciamento de estado sobre o tempo. Envolve State Compaction (compactação de memória), Checkpoints e políticas de Rollback para recuperação de erros.</p>
</li>
</ul>
<h2>Segurança arquitetônica: quem pensa não deve agir</h2>
<p>Um dos maiores riscos atuais é a "Lethal Trifecta": acesso a dados privados, exposição a conteúdo não confiável e um vetor de exfiltração. O paper Parallax argumenta que confiar em "prompts de segurança" é uma falácia, pois eles compartilham o mesmo substrato computacional que as ameaças.</p>
<p>A solução é a Separação Cognitivo-Executiva (CES). Nela, o sistema que "pensa" (LLM) é estruturalmente incapaz de agir. Toda proposta de ação passa pelo Shield, uma camada de validação independente com 4 níveis de determinismo:</p>
<ul>
<li><p><strong>Tier 0 (Policy)</strong>: regras determinísticas e imutáveis (Ex: "Nunca apague a pasta /root").</p>
</li>
<li><p><strong>Tier 1 (Classifier)</strong>: heurísticas e modelos fixos para detectar injeção de prompt e ofuscação.</p>
</li>
<li><p><strong>Tier 2 (LLM Eval)</strong>: um segundo modelo, isolado e com orçamento limitado, que julga a intenção da ação usando canary tokens.</p>
</li>
<li><p><strong>Tier 3 (Human)</strong>: aprovação humana em tempo real para ações de alto risco.</p>
</li>
</ul>
<p>Essa hierarquia garante que, mesmo que a IA seja "enganada" por um prompt malicioso, o sistema de execução permaneça íntegro através de Privilege Separation.</p>
<h2>O harness na sua vida: terceirizando a função executiva</h2>
<p>O conceito de harness é uma lente poderosa para a produtividade pessoal. WenHao Yu sugere que olhemos para o nosso cérebro como o "modelo" e para o nosso ambiente como o harness.</p>
<p>Em vez de tentar um "upgrade no cérebro" via força de vontade (que é inconstante e cara), você deve terceirizar sua função executiva para o ambiente.</p>
<ul>
<li><p>Quer foco? Mude a camada de Constraints: coloque o celular em outro cômodo.</p>
</li>
<li><p>Quer consistência? Crie um Loop: uma rotina matinal que não dependa de decisão, apenas de execução.</p>
</li>
</ul>
<p>Mudar o harness ambiental é sempre mais rápido e eficaz do que tentar mudar a biologia.</p>
<h2>O futuro é dos engenheiros de sistemas de IA</h2>
<p>A fronteira da inteligência artificial em 2026 não está mais na escala trilionária dos modelos, mas na sofisticação da engenharia ao redor deles. A confiabilidade da IA depende de quão bem projetado é o seu Agent Harness.</p>
<p>Se você quer que sua IA pare de falhar, pare de ajustar o prompt e comece a projetar o sistema. A inteligência é inferencial, mas a segurança e a eficácia devem ser determinísticas.</p>
<p>Se você pudesse mudar apenas uma peça do "harness" que envolve sua rotina hoje, qual delas teria o maior impacto na sua produtividade?</p>
]]></content:encoded></item><item><title><![CDATA[Construindo Agentes de IA de Verdade com o Copilot SDK em Go]]></title><description><![CDATA[Se você acompanha o ecossistema do GitHub Copilot, provavelmente já ouviu falar dos arquivos *.agent.md. Eles são legais para coisas simples, basicamente um prompt turbinado que roda dentro do Copilot]]></description><link>https://leocavalcante.dev/construindo-agentes-de-ia-de-verdade-com-o-copilot-sdk-em-go</link><guid isPermaLink="true">https://leocavalcante.dev/construindo-agentes-de-ia-de-verdade-com-o-copilot-sdk-em-go</guid><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Wed, 22 Apr 2026 17:03:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/60f177f63a0aaa311ef8ed29/846504af-5ad2-4889-92f7-3cc6c1df9c4a.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se você acompanha o ecossistema do GitHub Copilot, provavelmente já ouviu falar dos arquivos <code>*.agent.md</code>. Eles são legais para coisas simples, basicamente um prompt turbinado que roda dentro do Copilot. Mas quando você precisa de um agente <strong>de verdade</strong>, que chama APIs, consulta bancos de dados, aplica políticas de permissão, injeta contexto via RAG e roda em produção como um microserviço... Markdown não dá conta.</p>
<p>É aí que entra o <a href="https://github.com/github/copilot-sdk">GitHub Copilot SDK</a>.</p>
<p>Neste post, vou mostrar como construir um agente de resposta a incidentes, do zero até um container pronto para deploy, usando o SDK em Go. A ideia é que, ao final, você tenha uma visão completa de tudo que o SDK oferece e <strong>por que</strong> ele existe.</p>
<h2>O que o Copilot SDK faz que um <code>agent.md</code> não faz?</h2>
<p>Antes de mergulhar no código, vale entender o gap:</p>
<table>
<thead>
<tr>
<th>Capacidade</th>
<th><code>*.agent.md</code></th>
<th>Copilot SDK</th>
</tr>
</thead>
<tbody><tr>
<td>Ferramentas customizadas em código nativo (sem o overhead de um MCP externo)</td>
<td>⚠️ via MCP server separado</td>
<td>✅</td>
</tr>
<tr>
<td>Hooks (interceptar prompts, tool calls, erros)</td>
<td>⚠️ <code>PostToolUse</code> via shell (Preview no VS Code)</td>
<td>✅</td>
</tr>
<tr>
<td>Controle de permissões (aprovar/negar por tipo)</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Streaming com delta events</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>UI de elicitação (formulários, seleções)</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Sessões infinitas com compactação automática</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Controle granular do system prompt (por seção)</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Orquestração programática multi-sessão</td>
<td>⚠️ subagents + handoffs declarativos no VS Code</td>
<td>✅</td>
</tr>
<tr>
<td>BYOK (Bring Your Own Key)</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Telemetria (OpenTelemetry nativo)</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Embutir em um backend/CLI/worker</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Slash commands customizados</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Override de ferramentas built-in</td>
<td>❌</td>
<td>✅</td>
</tr>
</tbody></table>
<p>O <code>agent.md</code> é ótimo para instruções estáticas. O SDK é para quando você precisa de <strong>lógica de verdade</strong>.</p>
<h2>Arquitetura: como o SDK funciona</h2>
<p>O Copilot SDK segue uma arquitetura de dois processos. Sua aplicação fala com o SDK, que se comunica com o Copilot CLI via JSON-RPC:</p>
<pre><code class="language-plaintext">Sua Aplicação (API/Worker/CLI)
       ↓
  SDK Client
       ↓ JSON-RPC (stdio ou TCP)
  Copilot CLI (modo headless)
       ↓
  ☁️ GitHub Copilot / Provedor de modelo
</code></pre>
<p>O Go SDK tem um diferencial importante em relação aos outros SDKs: ele consegue <strong>embutir o binário do CLI</strong> diretamente no seu binário Go usando <code>//go:embed</code>. Isso significa que seu artefato final é um <strong>único binário estático</strong> sem dependências externas, perfeito para containers distroless.</p>
<h2>O que vamos construir</h2>
<p>Um <strong>Incident Commander Agent</strong>, um agente que engenheiros de plantão podem acionar durante um incidente de produção. Ele:</p>
<ul>
<li><p>Consulta métricas no Prometheus automaticamente</p>
</li>
<li><p>Busca runbooks de uma base de conhecimento</p>
</li>
<li><p>Injeta contexto de incidentes passados via RAG</p>
</li>
<li><p>Escala serviços no Kubernetes e faz rollback de deploys</p>
</li>
<li><p>Exige confirmação antes de ações destrutivas</p>
</li>
<li><p>Registra tudo em um audit log</p>
</li>
<li><p>Exporta traces via OpenTelemetry</p>
</li>
</ul>
<p>Tudo isso empacotado em um único binário Go dentro de um container de ~60MB.</p>
<h2>Configurando o projeto</h2>
<pre><code class="language-shell">mkdir incident-commander &amp;&amp; cd incident-commander
go mod init github.com/seu-usuario/incident-commander

# Adiciona o SDK
go get github.com/github/copilot-sdk/go

# Adiciona o bundler como ferramenta (uma vez só)
go get -tool github.com/github/copilot-sdk/go/cmd/bundler
</code></pre>
<h2>Estrutura do projeto</h2>
<pre><code class="language-plaintext">incident-commander/
├── main.go                       # Entry point + HTTP server
├── agent.go                      # Sessão, permissões, hooks
├── tools.go                      # Ferramentas customizadas
├── webhooks.go                   # Webhooks (PagerDuty, AM)
├── slack.go                      # Integração com Slack
├── cron.go                       # Health checks proativos
├── go.mod
├── go.sum
├── Dockerfile
├── docker-compose.yml
├── zcopilot_*_linux_amd64.zst      # ← gerado pelo bundler
├── zcopilot_*_linux_amd64.license  # ← gerado pelo bundler
└── zcopilot_linux_amd64.go         # ← gerado pelo bundler
</code></pre>
<h2>Ferramentas customizadas</h2>
<p>Aqui é onde o SDK brilha. Cada ferramenta é uma função Go tipada que o modelo pode chamar quando precisar:</p>
<pre><code class="language-go">package main

import (
	"fmt"
	copilot "github.com/github/copilot-sdk/go"
)

type QueryPrometheusParams struct {
	Query    string `json:"query" jsonschema:"PromQL query string"`
	Duration string `json:"duration,omitempty" jsonschema:"Time range (e.g. 5m, 1h). Default: 5m"`
}

type GetRunbookParams struct {
	Service string `json:"service" jsonschema:"Service name to look up runbook for"`
}

type ScaleServiceParams struct {
	Service  string `json:"service" jsonschema:"Kubernetes service/deployment name"`
	Replicas int    `json:"replicas" jsonschema:"Target replica count"`
}

type RollbackDeployParams struct {
	Service string `json:"service" jsonschema:"Service to rollback"`
	Version string `json:"version,omitempty" jsonschema:"Target version. If empty, rolls back to previous."`
}

func incidentTools() []copilot.Tool {
	queryPrometheus := copilot.DefineTool("query_prometheus",
		"Query Prometheus for metrics. Use for CPU, memory, error rates, latency percentiles, and Kafka consumer lag.",
		func(params QueryPrometheusParams, inv copilot.ToolInvocation) (any, error) {
			duration := params.Duration
			if duration == "" {
				duration = "5m"
			}
			result, err := prometheusQuery(params.Query, duration)
			if err != nil {
				return nil, fmt.Errorf("prometheus query failed: %w", err)
			}
			return result, nil
		})
	queryPrometheus.SkipPermission = true // read-only, auto-approve

	getRunbook := copilot.DefineTool("get_runbook",
		"Retrieve the incident runbook for a service from the knowledge base.",
		func(params GetRunbookParams, inv copilot.ToolInvocation) (any, error) {
			runbook, err := fetchRunbook(params.Service)
			if err != nil {
				return nil, err
			}
			return runbook, nil
		})
	getRunbook.SkipPermission = true

	scaleService := copilot.DefineTool("scale_service",
		"Scale a Kubernetes deployment. Requires engineer confirmation.",
		func(params ScaleServiceParams, inv copilot.ToolInvocation) (any, error) {
			if err := kubeScale(params.Service, params.Replicas); err != nil {
				return nil, err
			}
			return fmt.Sprintf("Scaled %s to %d replicas", params.Service, params.Replicas), nil
		})

	rollbackDeploy := copilot.DefineTool("rollback_deploy",
		"Rollback a service deployment via the CD pipeline. DESTRUCTIVE, requires confirmation.",
		func(params RollbackDeployParams, inv copilot.ToolInvocation) (any, error) {
			version, err := triggerRollback(params.Service, params.Version)
			if err != nil {
				return nil, err
			}
			return fmt.Sprintf("Rollback of %s to %s initiated", params.Service, version), nil
		})

	return []copilot.Tool{queryPrometheus, getRunbook, scaleService, rollbackDeploy}
}
</code></pre>
<p>Repare no <code>SkipPermission = true</code> nas ferramentas de leitura. Ferramentas que apenas consultam dados não precisam pedir permissão. Já <code>scale_service</code> e <code>rollback_deploy</code> vão passar pelo handler de permissões, que veremos a seguir.</p>
<p>O <code>DefineTool</code> usa generics e o pacote <code>jsonschema-go</code> para gerar o JSON Schema automaticamente a partir das structs Go. As tags <code>jsonschema:"..."</code> viram as descrições dos parâmetros para o modelo.</p>
<h2>Permissões: controle fino sobre o que o agente pode fazer</h2>
<p>O handler de permissões é <strong>obrigatório</strong> no SDK. Você precisa decidir explicitamente o que aprovar ou negar. Isso é um design intencional: agentes em produção precisam de guardrails.</p>
<pre><code class="language-go">package main

import (
	"log"
	copilot "github.com/github/copilot-sdk/go"
)

func incidentPermissionHandler() copilot.PermissionHandlerFunc {
	return func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (copilot.PermissionRequestResult, error) {
		switch req.Kind {
		case "shell":
			// Nunca permitir execução de comandos shell
			log.Printf("[PERMISSION DENIED] Shell command blocked: %v", req.FullCommandText)
			return copilot.PermissionRequestResult{
				Kind: copilot.PermissionRequestResultKindDeniedByRules,
			}, nil

		case "custom_tool":
			toolName := ""
			if req.ToolName != nil {
				toolName = *req.ToolName
			}

			switch toolName {
			case "query_prometheus", "get_runbook":
				return copilot.PermissionRequestResult{
					Kind: copilot.PermissionRequestResultKindApproved,
				}, nil
			case "scale_service", "rollback_deploy":
				// Aprovar, mas logar para audit trail
				log.Printf("[AUDIT] Destructive tool approved: %s (call: %v)", toolName, req.ToolCallID)
				return copilot.PermissionRequestResult{
					Kind: copilot.PermissionRequestResultKindApproved,
				}, nil
			}
		}

		// Default: approve
		return copilot.PermissionRequestResult{
			Kind: copilot.PermissionRequestResultKindApproved,
		}, nil
	}
}
</code></pre>
<p>Isso é algo impossível com <code>agent.md</code>. Lá, o agente roda com as permissões padrão do Copilot. Aqui, <strong>você</strong> define a política.</p>
<h2>Hooks: RAG, auditoria e tratamento de erros</h2>
<p>Os hooks são interceptadores que rodam em momentos específicos do ciclo de vida da sessão. Vamos usar três:</p>
<ol>
<li><p><code>OnUserPromptSubmitted</code>: injeta contexto de incidentes passados (RAG) antes do modelo processar</p>
</li>
<li><p><code>OnPostToolUse</code>: loga cada execução de ferramenta</p>
</li>
<li><p><code>OnErrorOccurred</code>: define estratégia de retry/skip/abort</p>
</li>
</ol>
<pre><code class="language-go">package main

import (
	"fmt"
	"log"
	copilot "github.com/github/copilot-sdk/go"
)

func incidentHooks() *copilot.SessionHooks {
	return &amp;copilot.SessionHooks{
		// RAG: enriquece cada prompt com incidentes passados similares
		OnUserPromptSubmitted: func(input copilot.UserPromptSubmittedHookInput, inv copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) {
			context, err := ragSearch(input.Prompt)
			if err != nil {
				log.Printf("[RAG] Search failed (non-fatal): %v", err)
				return &amp;copilot.UserPromptSubmittedHookOutput{
					ModifiedPrompt: input.Prompt,
				}, nil
			}

			enriched := fmt.Sprintf(
				"%s\n\n&lt;past_incidents&gt;\n%s\n&lt;/past_incidents&gt;",
				input.Prompt, context,
			)
			return &amp;copilot.UserPromptSubmittedHookOutput{
				ModifiedPrompt: enriched,
			}, nil
		},

		// Audit: loga cada execução de ferramenta
		OnPostToolUse: func(input copilot.PostToolUseHookInput, inv copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) {
			log.Printf("[TOOL] %s completed (session: %s)", input.ToolName, inv.SessionID)
			return &amp;copilot.PostToolUseHookOutput{}, nil
		},

		// Erros: retry automático em falhas transientes
		OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, inv copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) {
			log.Printf("[ERROR] %s in context %s", input.Error, input.ErrorContext)
			return &amp;copilot.ErrorOccurredHookOutput{
				ErrorHandling: "retry",
			}, nil
		},
	}
}

func ragSearch(query string) (string, error) {
	// Na prática: consulte seu vector DB (pgvector, Pinecone, Weaviate, etc.)
	return "Incidente similar (INC-2024-0312): spike de latência p99 causado por exaustão do connection pool. Resolvido escalando para 8 réplicas e reiniciando pods.", nil
}
</code></pre>
<p>O hook de RAG é particularmente poderoso. Toda vez que o engenheiro envia uma mensagem, o hook intercepta o prompt <strong>antes</strong> de chegar ao modelo, consulta um banco vetorial de post-mortems passados e injeta os resultados mais relevantes como contexto adicional. O modelo recebe a mensagem do engenheiro <strong>junto</strong> com incidentes similares, sem que o engenheiro precise buscar manualmente. Isso é completamente transparente para quem está usando.</p>
<h2>Montando a sessão do agente</h2>
<p>Agora juntamos tudo: ferramentas, hooks, permissões e configuração do system prompt:</p>
<pre><code class="language-go">package main

import (
	"context"
	copilot "github.com/github/copilot-sdk/go"
)

func createIncidentSession(ctx context.Context, client *copilot.Client, sessionID, prompt string) (*copilot.Session, error) {
	return client.CreateSession(ctx, &amp;copilot.SessionConfig{
		SessionID: sessionID,
		Model:     "gpt-4.1",
		Streaming: true,
		Tools:     incidentTools(),
		Hooks:     incidentHooks(),
		SystemMessage: &amp;copilot.SystemMessageConfig{
			Mode: "customize",
			Sections: map[string]copilot.SectionOverride{
				copilot.SectionIdentity: {
					Action:  "replace",
					Content: "You are an Incident Commander assistant. You help on-call engineers diagnose and resolve production incidents.",
				},
				copilot.SectionCodeChangeRules: {Action: "remove"},
				copilot.SectionGuidelines: {
					Action: "append",
					Content: `
* Always check metrics before suggesting a remediation.
* Never execute a rollback without explicit engineer confirmation.
* Log every destructive action.`,
				},
			},
			Content: "Focus on incident triage, diagnosis, and remediation for a microservices platform.",
		},
		OnPermissionRequest: incidentPermissionHandler(),
		InfiniteSessions: &amp;copilot.InfiniteSessionConfig{
			Enabled:                       copilot.Bool(true),
			BackgroundCompactionThreshold: copilot.Float64(0.80),
		},
	})
}
</code></pre>
<p>Repare no <code>SystemMessage</code> com <code>Mode: "customize"</code>. Em vez de substituir o prompt inteiro ou apenas concatenar texto no final, o SDK permite <strong>controlar seção por seção</strong>: substituir a identidade, remover regras de código (irrelevantes para esse agente), e adicionar guidelines específicas. As outras seções (safety, tool instructions, etc.) são preservadas automaticamente.</p>
<p>O <code>InfiniteSessions</code> com <code>BackgroundCompactionThreshold: 0.80</code> faz com que, quando o contexto atingir 80% da janela, o SDK automaticamente compacte o histórico em background, sem que o engenheiro perceba. Isso é essencial para incidentes longos com centenas de mensagens.</p>
<h2>Triggers: quando e como o agente é acionado?</h2>
<p>O agente é um servidor HTTP que fica sempre rodando. A questão é: <strong>quem chama ele, e quando?</strong> Existem vários padrões, e na prática você combina mais de um.</p>
<h3>Webhook do PagerDuty (trigger principal)</h3>
<p>Quando um alerta dispara, o PagerDuty envia um webhook. O agente recebe, cria uma sessão, roda o diagnóstico inicial e posta no Slack:</p>
<pre><code class="language-go">package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	copilot "github.com/github/copilot-sdk/go"
)

type PagerDutyWebhook struct {
	Event struct {
		EventType string `json:"event_type"`
		Data      struct {
			ID      string `json:"id"`
			Title   string `json:"title"`
			Service struct {
				Name string `json:"name"`
			} `json:"service"`
			Urgency   string `json:"urgency"`
			Assignees []struct {
				Summary string `json:"summary"`
			} `json:"assignees"`
		} `json:"data"`
	} `json:"event"`
}

func handlePagerDutyWebhook(ctx context.Context, client *copilot.Client, w http.ResponseWriter, r *http.Request) {
	var webhook PagerDutyWebhook
	if err := json.NewDecoder(r.Body).Decode(&amp;webhook); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if webhook.Event.EventType != "incident.triggered" {
		w.WriteHeader(http.StatusOK)
		return
	}

	data := webhook.Event.Data
	sessionID := fmt.Sprintf("incident-%s", data.ID)

	prompt := fmt.Sprintf(
		`A new incident has been triggered:
- **Incident**: %s
- **Service**: %s
- **Urgency**: %s
- **ID**: %s

Please:
1. Query Prometheus for the current health of service "%s" (error rate, p99 latency, CPU, memory)
2. Fetch the runbook for this service
3. Provide an initial triage summary with likely root cause and recommended actions`,
		data.Title, data.Service.Name, data.Urgency, data.ID, data.Service.Name,
	)

	session, err := createIncidentSession(ctx, client, sessionID, prompt)
	if err != nil {
		log.Printf("[WEBHOOK] Failed to create session: %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	response, err := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: prompt})
	if err != nil {
		log.Printf("[WEBHOOK] Agent failed: %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if d, ok := response.Data.(*copilot.AssistantMessageData); ok {
		postToSlack(data.Service.Name, data.ID, sessionID, d.Content)
	}

	w.WriteHeader(http.StatusAccepted)
}
</code></pre>
<h3>Slack Bot (conversa interativa)</h3>
<p>Depois do diagnóstico inicial, o engenheiro continua a conversa pelo Slack. Cada mensagem é um <code>SendAndWait</code> na mesma sessão:</p>
<pre><code class="language-go">package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

	copilot "github.com/github/copilot-sdk/go"
)

var threadToSession sync.Map

type SlackEvent struct {
	Type      string `json:"type"`
	Challenge string `json:"challenge"`
	Event     struct {
		Type     string `json:"type"`
		Text     string `json:"text"`
		Channel  string `json:"channel"`
		ThreadTS string `json:"thread_ts"`
		TS       string `json:"ts"`
		User     string `json:"user"`
	} `json:"event"`
}

func handleSlackEvent(ctx context.Context, client *copilot.Client, w http.ResponseWriter, r *http.Request) {
	var event SlackEvent
	if err := json.NewDecoder(r.Body).Decode(&amp;event); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if event.Type == "url_verification" {
		json.NewEncoder(w).Encode(map[string]string{"challenge": event.Challenge})
		return
	}

	threadTS := event.Event.ThreadTS
	if threadTS == "" {
		threadTS = event.Event.TS
	}

	message := stripBotMention(event.Event.Text)
	if message == "" {
		w.WriteHeader(http.StatusOK)
		return
	}

	sessionIDVal, ok := threadToSession.Load(threadTS)
	if !ok {
		w.WriteHeader(http.StatusOK)
		return
	}
	sessionID := sessionIDVal.(string)

	go func() {
		session, err := client.ResumeSession(ctx, sessionID, &amp;copilot.ResumeSessionConfig{
			OnPermissionRequest: incidentPermissionHandler(),
		})
		if err != nil {
			log.Printf("[SLACK] Failed to resume session %s: %v", sessionID, err)
			return
		}
		defer session.Disconnect()

		response, err := session.SendAndWait(ctx, copilot.MessageOptions{
			Prompt: fmt.Sprintf("[Engineer %s]: %s", event.Event.User, message),
		})
		if err != nil {
			return
		}

		if d, ok := response.Data.(*copilot.AssistantMessageData); ok {
			postToSlackThread(event.Event.Channel, threadTS, d.Content)
		}
	}()

	w.WriteHeader(http.StatusOK)
}

func stripBotMention(text string) string {
	if idx := strings.Index(text, "&gt; "); idx != -1 {
		return strings.TrimSpace(text[idx+2:])
	}
	return strings.TrimSpace(text)
}
</code></pre>
<h3>Webhook do Alertmanager (direto dos alertas)</h3>
<p>Você pode cortar o PagerDuty e reagir diretamente aos alertas do Prometheus:</p>
<pre><code class="language-yaml"># alertmanager.yml
route:
  receiver: incident-commander
  group_by: ['alertname', 'service']
  group_wait: 30s

receivers:
  - name: incident-commander
    webhook_configs:
      - url: 'http://incident-commander:8080/api/webhook/alertmanager'
        send_resolved: true
</code></pre>
<h3>Cron (health checks proativos)</h3>
<p>O agente não precisa ser apenas reativo. Ele pode rodar periodicamente e <strong>encontrar problemas antes que virem incidentes</strong>:</p>
<pre><code class="language-go">package main

import (
	"context"
	"fmt"
	"log"
	"strings"
	"time"

	copilot "github.com/github/copilot-sdk/go"
)

func StartHealthCheckCron(ctx context.Context, client *copilot.Client, interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case &lt;-ctx.Done():
			return
		case &lt;-ticker.C:
			runProactiveCheck(ctx, client)
		}
	}
}

func runProactiveCheck(ctx context.Context, client *copilot.Client) {
	sessionID := fmt.Sprintf("healthcheck-%d", time.Now().Unix())

	session, err := createIncidentSession(ctx, client, sessionID,
		`Run a proactive health check across critical services:
1. Check error rates for the main services
2. Check p99 latency for each
3. Check Kafka consumer lag for payment processing topics
4. Flag anything anomalous with severity and recommended action`)
	if err != nil {
		log.Printf("[CRON] Failed: %v", err)
		return
	}

	response, err := session.SendAndWait(ctx, copilot.MessageOptions{
		Prompt: "Run proactive health check now.",
	})
	if err != nil {
		log.Printf("[CRON] Agent failed: %v", err)
		return
	}

	if d, ok := response.Data.(*copilot.AssistantMessageData); ok {
		if strings.Contains(strings.ToLower(d.Content), "anomal") ||
			strings.Contains(strings.ToLower(d.Content), "elevated") ||
			strings.Contains(strings.ToLower(d.Content), "degraded") {
			postToSlack("platform", "proactive-check", sessionID, d.Content)
		}
	}

	client.DeleteSession(ctx, sessionID)
}
</code></pre>
<h2>O entry point: juntando tudo</h2>
<pre><code class="language-go">package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	copilot "github.com/github/copilot-sdk/go"
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(),
		syscall.SIGINT, syscall.SIGTERM)
	defer cancel()

	// Sem CLIPath: o CLI embutido é usado automaticamente
	client := copilot.NewClient(&amp;copilot.ClientOptions{
		LogLevel: "error",
		Telemetry: &amp;copilot.TelemetryConfig{
			OTLPEndpoint: os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"),
			SourceName:   "incident-commander",
		},
	})

	if err := client.Start(ctx); err != nil {
		log.Fatalf("Failed to start Copilot client: %v", err)
	}
	defer client.Stop()

	log.Println("Copilot client started with embedded CLI")

	mux := http.NewServeMux()

	// Webhooks
	mux.HandleFunc("POST /api/webhook/pagerduty", func(w http.ResponseWriter, r *http.Request) {
		handlePagerDutyWebhook(ctx, client, w, r)
	})

	// Slack
	mux.HandleFunc("POST /api/slack/events", func(w http.ResponseWriter, r *http.Request) {
		handleSlackEvent(ctx, client, w, r)
	})

	// API direta
	mux.HandleFunc("POST /api/incident", func(w http.ResponseWriter, r *http.Request) {
		handleIncident(ctx, client, w, r)
	})
	mux.HandleFunc("POST /api/incident/{sessionID}/message", func(w http.ResponseWriter, r *http.Request) {
		handleMessage(ctx, client, w, r)
	})

	// Health check
	mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
		if _, err := client.Ping(ctx, "health"); err != nil {
			http.Error(w, "unhealthy", 503)
			return
		}
		w.WriteHeader(200)
	})

	// Background: health checks proativos a cada 15 minutos
	go StartHealthCheckCron(ctx, client, 15*time.Minute)

	srv := &amp;http.Server{Addr: ":8080", Handler: mux}
	go func() {
		&lt;-ctx.Done()
		srv.Shutdown(context.Background())
	}()

	log.Println("Incident Commander listening on :8080")
	if err := srv.ListenAndServe(); err != http.ErrServerClosed {
		log.Fatal(err)
	}
}
</code></pre>
<h2>O CLI embutido: como funciona</h2>
<p>O Go SDK tem um <strong>bundler</strong> que automatiza todo o processo de embutir o Copilot CLI no seu binário.</p>
<p>O que acontece quando você roda <code>go tool bundler</code>:</p>
<ol>
<li><p>Lê o <code>go.mod</code> para detectar a versão do SDK</p>
</li>
<li><p>Busca a versão correspondente do CLI no npm registry</p>
</li>
<li><p>Baixa o binário específico para a plataforma alvo (ex: <code>linux/amd64</code>)</p>
</li>
<li><p>Comprime com zstd</p>
</li>
<li><p>Gera um arquivo Go com a diretiva <code>//go:embed</code></p>
</li>
</ol>
<p>O arquivo gerado é mais ou menos assim:</p>
<pre><code class="language-go">// Code generated by copilot-sdk bundler; DO NOT EDIT.
package main

import (
    _ "embed"
    "github.com/github/copilot-sdk/go/embeddedcli"
)

//go:embed zcopilot_0.25.0_linux_amd64.zst
var localEmbeddedCopilotCLI []byte

func init() {
    embeddedcli.Setup(embeddedcli.Config{
        Cli:     cliReader(), // descomprime o zst
        Version: "0.25.0",
        CliHash: mustDecodeBase64("sha256-hash..."),
    })
}
</code></pre>
<p>No runtime, quando <code>NewClient</code> detecta que não tem <code>CLIPath</code> nem <code>COPILOT_CLI_PATH</code>, ele usa o blob embutido: descomprime para um diretório de cache e verifica o SHA-256.</p>
<p>O build completo fica assim:</p>
<pre><code class="language-bash">#!/bin/bash
set -euo pipefail

# 1. Baixa + embute o CLI para a plataforma alvo
go tool bundler --platform linux/amd64

# 2. Compila o binário final
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o incident-commander .
</code></pre>
<h2>Dockerfile: multi-stage com distroless</h2>
<pre><code class="language-dockerfile"># Stage 1: Build
FROM golang:1.24-bookworm AS builder

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Baixa e embute o CLI
RUN go tool bundler --platform linux/amd64

# Compila binário estático
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-s -w" -o /incident-commander .

# Stage 2: Runtime (distroless para superfície de ataque mínima)
FROM gcr.io/distroless/static-debian12:nonroot

ENV HOME=/home/nonroot

COPY --from=builder /incident-commander /incident-commander

# Sessões persistem aqui: monte um volume em produção
VOLUME /home/nonroot/.copilot/session-state

EXPOSE 8080

ENTRYPOINT ["/incident-commander"]
</code></pre>
<p>Por que <code>distroless/static</code>? Como o binário Go é totalmente estático (<code>CGO_ENABLED=0</code>) e o Copilot CLI embutido também é estático, <strong>não existem dependências de SO</strong>. Sem apt, sem bash, sem glibc. A imagem final fica em torno de 60-90MB (seu binário + CLI comprimido). Isso também significa menos CVEs para se preocupar.</p>
<h2>Docker Compose para desenvolvimento local</h2>
<pre><code class="language-yaml">version: "3.8"

services:
  incident-commander:
    build: .
    ports:
      - "8080:8080"
    environment:
      - COPILOT_GITHUB_TOKEN=${COPILOT_GITHUB_TOKEN}
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
    volumes:
      - session-data:/home/nonroot/.copilot/session-state
    depends_on:
      - otel-collector
    restart: unless-stopped

  otel-collector:
    image: otel/opentelemetry-collector-contrib:latest
    ports:
      - "4318:4318"
    volumes:
      - ./otel-config.yaml:/etc/otelcol-contrib/config.yaml

  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"

volumes:
  session-data:
</code></pre>
<h2>Deploy no Kubernetes</h2>
<pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
  name: incident-commander
spec:
  replicas: 2
  selector:
    matchLabels:
      app: incident-commander
  template:
    metadata:
      labels:
        app: incident-commander
    spec:
      containers:
        - name: agent
          image: ghcr.io/seu-usuario/incident-commander:latest
          ports:
            - containerPort: 8080
          env:
            - name: COPILOT_GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: copilot-secrets
                  key: github-token
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: "http://otel-collector.observability:4318"
          volumeMounts:
            - name: session-state
              mountPath: /home/nonroot/.copilot/session-state
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 30
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "1Gi"
              cpu: "500m"
      volumes:
        - name: session-state
          persistentVolumeClaim:
            claimName: incident-commander-sessions
</code></pre>
<p>Você deploya esse agente <strong>exatamente como qualquer outro microserviço Go</strong>. O único detalhe extra é o volume persistente para o estado das sessões.</p>
<h2>CI/CD com GitHub Actions</h2>
<pre><code class="language-yaml">name: Build &amp; Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: "1.24"

      - uses: actions/cache@v4
        with:
          path: zcopilot_*
          key: copilot-cli-${{ hashFiles('go.sum') }}-linux-amd64

      - name: Bundle CLI
        run: go tool bundler --platform linux/amd64

      - name: Test
        run: go test ./...

      - name: Build &amp; Push Container
        run: |
          echo "\({{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u \){{ github.actor }} --password-stdin
          docker build -t ghcr.io/seu-usuario/incident-commander:${{ github.sha }} .
          docker push ghcr.io/seu-usuario/incident-commander:${{ github.sha }}
</code></pre>
<p>O cache do <code>zcopilot_*</code> evita re-download do CLI a cada build. Ele só muda quando o SDK é atualizado (refletido no <code>go.sum</code>).</p>
<h2>Considerações para produção</h2>
<p>Algumas coisas importantes para manter isso rodando de verdade:</p>
<table>
<thead>
<tr>
<th>Preocupação</th>
<th>Recomendação</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Storage</strong></td>
<td>Monte <code>/home/nonroot/.copilot/session-state/</code> em volume persistente</td>
</tr>
<tr>
<td><strong>Secrets</strong></td>
<td>Use K8s Secrets ou Vault para o <code>COPILOT_GITHUB_TOKEN</code></td>
</tr>
<tr>
<td><strong>Health check</strong></td>
<td><code>client.Ping()</code> como liveness probe, reinicie se não responder</td>
</tr>
<tr>
<td><strong>Cleanup de sessões</strong></td>
<td>Cron para deletar sessões mais velhas que 24h (+ auto-cleanup de 30min idle)</td>
</tr>
<tr>
<td><strong>Locking</strong></td>
<td>Redis <code>SETNX</code> se múltiplos pods acessam a mesma sessão</td>
</tr>
<tr>
<td><strong>Observabilidade</strong></td>
<td>OpenTelemetry → OTLP collector → Grafana/Jaeger</td>
</tr>
<tr>
<td><strong>Graceful shutdown</strong></td>
<td>Drene sessões ativas antes de parar o CLI</td>
</tr>
<tr>
<td><strong>Rate limiting</strong></td>
<td>Aplique na sua camada de API, o SDK não limita</td>
</tr>
</tbody></table>
<h2>Conclusão</h2>
<p>O Copilot SDK transforma o Copilot de um assistente de código em uma <strong>runtime de agentes</strong>. Você define o comportamento em código (não em Markdown), controla permissões, intercepta o fluxo com hooks, injeta contexto dinâmico, e deploya como qualquer microserviço.</p>
<p>O Go SDK em particular tem uma proposta muito elegante: o bundler + <code>//go:embed</code> gera um <strong>único binário estático</strong> com o CLI incluso. Sem sidecar, sem Docker-in-Docker, sem dependência externa. <code>go build</code> e pronto.</p>
<p>O agente que construímos aqui é um caso real: recebe webhooks de sistemas de monitoramento, diagnostica incidentes de forma autônoma, mantém conversas com engenheiros via Slack, e executa remediações com gates de permissão. Tudo observável via OpenTelemetry.</p>
<p>Isso não é um "Hello, World!" com IA. É um microserviço de produção que por acaso tem um LLM dentro.</p>
<hr />
<p><em>O código e os exemplos deste post estão baseados na</em> <a href="https://github.com/github/copilot-sdk"><em>documentação do Copilot SDK</em></a><em>. O SDK está em public preview, confira o repositório para a versão mais recente.</em></p>
]]></content:encoded></item><item><title><![CDATA[DORA, SPACE e a Ciência da Performance na Era da Agilidade e da IA]]></title><description><![CDATA[O paradoxo da velocidade moderna
Vivemos o paradoxo da abundância técnica: na era da Inteligência Artificial, geramos código em minutos, mas as organizações nunca lutaram tanto para converter esse vol]]></description><link>https://leocavalcante.dev/dora-space-e-a-ci-ncia-da-performance-na-era-da-agilidade-e-da-ia</link><guid isPermaLink="true">https://leocavalcante.dev/dora-space-e-a-ci-ncia-da-performance-na-era-da-agilidade-e-da-ia</guid><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 23 Feb 2026 17:50:41 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/60f177f63a0aaa311ef8ed29/92a0b071-a882-40dc-b09b-65137e403a5f.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>O paradoxo da velocidade moderna</h2>
<p>Vivemos o paradoxo da abundância técnica: na era da Inteligência Artificial, geramos código em minutos, mas as organizações nunca lutaram tanto para converter esse volume em valor real. O erro fatal da liderança moderna é acreditar que a velocidade de codificação resolve o gargalo da entrega. Como David Rogers articula em sua tese sobre Transformação Digital, o desafio não é tecnológico, mas estratégico. Vivemos sob a máxima de Andy Grove: "apenas o paranoico sobrevive". No cenário atual, a paranoia produtiva deve se voltar para a ciência da performance.</p>
<p>A agilidade real não é medida pelo volume de linhas de código cuspida por uma IA, mas pela capacidade sistêmica de mover uma ideia do conceito para o mercado sem atritos. Se o seu pipeline de entrega não for <strong>"frictionless"</strong>, a IA apenas empilhará mais detritos em um funil já entupido, criando o que chamamos de "pesadelo do gargalo". Para vencer, é preciso dominar os domínios de Clientes, Competição, Dados, Inovação e Valor (o framework CC-DIV de Rogers) sob a ótica do rigor técnico e humano.</p>
<h2>DORA metrics</h2>
<p>A performance de engenharia não é um subproduto da sorte; é uma vantagem competitiva mensurável. O framework DORA (DevOps Research and Assessment), consolidado por Nicole Forsgren e sua equipe no Google através da obra Accelerate, estabeleceu a assinatura de performance que separa líderes de retardatários. No entanto, o estrategista entende que o DORA é, acima de tudo, um reflexo da cultura. Como Patrick Debois propôs em 2009 no nascimento do DevOps: a cultura vem antes das ferramentas.</p>
<p>Para diagnosticar a saúde da sua "máquina" de entrega, olhamos para as quatro métricas de ouro:</p>
<ul>
<li><p>Deployment Frequency (DF): A cadência de lançamentos bem-sucedidos em produção. É o pulso da agilidade.</p>
</li>
<li><p>Lead Time for Changes (LTC): O tempo entre o commit e o código rodando. Mede a eficiência do fluxo.</p>
</li>
<li><p>Change Failure Rate (CFR): O percentual de deploys que exigem remediação. É o termômetro da estabilidade.</p>
</li>
<li><p>Mean Time to Restore (MTTR): A velocidade de recuperação após uma falha. Mede a resiliência sistêmica.</p>
</li>
</ul>
<p>A disparidade é brutal. Dados científicos mostram que os Elite Performers, comparados aos Low Performers, realizam deploys 46 vezes mais frequentes, com um lead time 2555 vezes mais rápido, uma taxa de falha em mudanças 7 vezes menor e um tempo de recuperação de incidentes 2604 vezes superior. Essa performance é sustentada por segurança psicológica e alta cobertura de testes, permitindo inovação sem medo.</p>
<h2>O framework SPACE e porque o "fator humano" é o novo diferencial</h2>
<p>A obsessão míope por métricas de vazão técnica ignora o subsistema fundamental: a cognição do desenvolvedor. O framework SPACE (Satisfação, Performance, Atividade, Comunicação e Eficiência) é a evolução necessária à visão estritamente técnica do DORA. Ele foca na Developer Experience (DevEx) como o motor silencioso da estratégia CC-DIV.</p>
<p>Como bem afirmou Linus Torvalds: "Grande software vem de grande colaboração." A produtividade é um fenômeno biopsicossocial. Estudos da Sentry revelam que <strong>desenvolvedores apenas 10% mais felizes são 10% mais rápidos na conclusão de tarefas complexas</strong>. O estrategista prioriza o "flow", o estado de trabalho sem interrupções. Quando ignoramos a satisfação e o bem-estar, as métricas DORA inevitavelmente entram em colapso devido ao burnout e à rotatividade de talentos.</p>
<h2>O custo invisível da "fricção" no desenvolvimento</h2>
<p>A fricção operacional é o dreno invisível da inovação. Deploys lentos e sistemas frágeis não são apenas incômodos técnicos; são riscos catastróficos aos negócios. Em Frictionless, Nicole Forsgren e Abi Noda diagnosticam como essa barreira impede as empresas de superarem a concorrência.</p>
<p>A dívida técnica e a fricção operacional custam às empresas americanas assustadores US$ 1,52 trilhão anualmente.</p>
<p>O impacto prático: cerca de 70% dos desenvolvedores perdem ao menos três horas por semana devido a loops de feedback ineficientes. Remover essa fricção transforma trajetórias. O LinkedIn, por exemplo, mudou radicalmente sua capacidade competitiva ao migrar de lançamentos mensais para múltiplas entregas diárias, provando que a saúde do ecossistema de engenharia é o que permite a agilidade de mercado.</p>
<h2>Plataformas e "co-opetição"</h2>
<p>Na economia digital, as fronteiras das indústrias são fluidas. A Tesla é uma montadora ou uma empresa de utilidade elétrica? Essa indefinição é a marca dos Competidores Assimétricos. Para navegar nesse cenário, o líder deve entender o modelo de plataforma (Airbnb, Uber), onde o valor é criado facilitando interações diretas, gerando poderosos Efeitos de Rede.</p>
<p>Entramos na era da "co-opetição": a necessidade estratégica de competir e colaborar simultaneamente. Apple e Samsung competem por dispositivos, mas colaboram em Cadeias de Valor Competitivas de suprimentos. Sua empresa não deve apenas construir produtos; deve construir ecossistemas que reduzam a fricção para parceiros e usuários. No domínio da competição de Rogers, a pergunta não é como vencer o rival, mas como ganhar influência no ecossistema global.</p>
<h2>O próximo passo</h2>
<p>A transformação digital não é sobre tecnologia; é sobre atualizar seu mindset estratégico nos domínios de clientes, competição, dados, inovação e valor. A agilidade da IA só terá retorno se o seu ecossistema for capaz de absorvê-la.</p>
<p>Takeaway Final: Você está apenas medindo a velocidade da sua "máquina" (DORA) ou está investindo na saúde e na fluidez do seu "ecossistema" (SPACE e Plataformas)? No novo jogo da performance, a vitória pertence a quem elimina a fricção antes que ela se torne o seu maior custo operacional.</p>
]]></content:encoded></item><item><title><![CDATA[A Ilusão da Sequência: Porque a Hierarquia é o Próximo Salto da Inferência de IA]]></title><description><![CDATA[O paradoxo da escala
Existe um abismo de eficiência entre a arquitetura biológica e o silício. Enquanto uma criança de 12 anos já domina as complexidades da linguagem humana, modelos como o GPT-3 exig]]></description><link>https://leocavalcante.dev/a-ilusao-da-sequencia-porque-a-hierarquia-e-o-proximo-salto-da-inferencia-de-ia</link><guid isPermaLink="true">https://leocavalcante.dev/a-ilusao-da-sequencia-porque-a-hierarquia-e-o-proximo-salto-da-inferencia-de-ia</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[RLM]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Thu, 19 Feb 2026 17:16:18 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/60f177f63a0aaa311ef8ed29/65ae4fab-008f-4363-849f-1a567f789519.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>O paradoxo da escala</h2>
<p>Existe um abismo de eficiência entre a arquitetura biológica e o silício. Enquanto uma criança de 12 anos já domina as complexidades da linguagem humana, modelos como o GPT-3 exigem cerca de <strong>2.000 vezes mais dados</strong> para atingir uma proficiência comparável. Esse "Paradoxo da Escala" revela uma verdade incômoda: estamos tentando compensar a falta de lógica estrutural e de um <strong>viés indutivo</strong> eficiente com a força bruta de dados massivos.</p>
<p>Atualmente, os <strong>Large Language Models (LLMs)</strong> operam sob uma visão linear, processando informações como sequências de tokens em uma janela de contexto que, por maior que seja, acaba sofrendo com o "apodrecimento do contexto" (<strong>context rot</strong>) e a degradação da atenção. A tese que emerge nos laboratórios de elite é que a próxima fronteira não reside em janelas de 10 milhões de tokens, mas em modelos que compreendam a <strong>estrutura hierárquica</strong> da informação, tratando a linguagem como ela realmente é: uma árvore aninhada, e não uma linha infinita.</p>
<h2>Recursivo vs. Recorrente (RvNN vs. RNN)</h2>
<p>A transição do processamento linear para o estruturado exige revisitarmos a distinção fundamental entre as Redes Neurais Recursivas (<strong>RvNN</strong>) e as Redes Neurais Recorrentes (<strong>RNN</strong>). Enquanto as RNNs são otimizadas para a continuidade temporal, as RvNNs são arquitetadas para a profundidade organizacional.</p>
<table style="min-width:75px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p>Característica</p></td><td><p>Redes Neurais Recursivas (RvNN)</p></td><td><p>Redes Neurais Recorrentes (RNN)</p></td></tr><tr><td><p><strong>Arquitetura</strong></p></td><td><p>Hierárquica (Árvore/Aninhada).</p></td><td><p>Em cadeia (Sequencial).</p></td></tr><tr><td><p><strong>Processamento</strong></p></td><td><p>Modela relações aninhadas e componentes.</p></td><td><p>Séries temporais e dependências lineares.</p></td></tr><tr><td><p><strong>Complexidade de Treino</strong></p></td><td><p>Alta: exige algoritmos de travessia de árvore.</p></td><td><p>Padrão: usa <strong>Backpropagation Through Time (BPTT)</strong>.</p></td></tr><tr><td><p><strong>Casos de Uso</strong></p></td><td><p>Análise sintática e parsing de imagens.</p></td><td><p>Reconhecimento de fala e tradução básica.</p></td></tr></tbody></table>

<p>As RvNNs operam decompondo estruturas complexas em componentes simples, do <strong>nível folha</strong> até a raiz. Essa abordagem permite uma compreensão contextual onde o significado de uma frase é construído a partir da composição de seus constituintes, permitindo que o modelo "enxergue" a hierarquia lógica antes mesmo de processar a sequência final.</p>
<h2>Tree-Planted Transformers (TPT): Plantando Árvores na Atenção</h2>
<p>Uma das inovações mais elegantes nesta área é o método de <strong>tree-planting</strong> (plantio de árvores) aplicado a Transformers unidirecionais. Em vez de forçar o modelo a gerar estruturas sintáticas explícitas (o que destruiria a velocidade de inferência), os <strong>Tree-Planted Transformers (TPT)</strong> injetam um viés estrutural diretamente nos pesos de atenção.</p>
<p>O segredo técnico reside na <strong>Syntactic Distance Matrix</strong>: uma matriz 2D que mapeia o número de arestas entre cada par de palavras na árvore sintática. O modelo é treinado para que seus pesos de atenção decaiam exponencialmente conforme a distância sintática aumenta.</p>
<p>"Os Tree-Planted Transformers (TPT) herdam a eficiência de treinamento dos Modelos de Linguagem Sintática (SLM) sem alterar a eficiência de inferência dos seus modelos base."</p>
<p>Testes no benchmark <strong>SyntaxGym</strong> mostraram que a supervisão baseada em <strong>Dependency Structures</strong> (estruturas de dependência) é superior à de constituintes. A razão é lógica: em uma estrutura de dependência, o núcleo do sujeito está sempre matematicamente mais próximo do verbo principal. Em estruturas de constituintes, elementos irrelevantes (como determinantes) podem aparecer na mesma distância gramatical, diluindo o foco do modelo.</p>
<h2>A Otimização Invisível: FSM e Árvores PQ</h2>
<p>Para que essas redes dinâmicas sejam viáveis em produção, precisamos resolver o pesadelo computacional do <em>batching</em>. O framework <strong>ED-Batch</strong> resolve isso através de duas frentes de engenharia sofisticada:</p>
<ol>
<li><p><strong>Algoritmo baseado em FSM (Finite State Machines):</strong> Em vez de usar heurísticas simples, ele utiliza <strong>Aprendizado por Reforço</strong> para encontrar políticas de loteamento ideais. A FSM atua representando o conjunto de tipos de operadores na <strong>fronteira</strong> do grafo de fluxo de dados, aprendendo a agrupar operações ao identificar regularidades na topologia da rede.</p>
</li>
<li><p><strong>Planejamento de Memória via Árvore PQ:</strong> Para minimizar o movimento de dados, o grande vilão da latência, este algoritmo de complexidade <strong>quase linear</strong> resolve a "consecutive ones property", garantindo que os operandos estejam contíguos no hardware.</p>
</li>
</ol>
<p>Os ganhos são expressivos: acelerações de <strong>1.15x</strong> em cadeias, <strong>1.39x</strong> em árvores e impressionantes <strong>2.45x</strong> em redes baseadas em treliças (<strong>lattice-based</strong>), permitindo que modelos estruturados rodem com a mesma agilidade de seus pares lineares.</p>
<h2>Modelos de Linguagem Recursivos (RLM): O Contexto como Ambiente</h2>
<p>O <strong>Recursive Language Model (RLM)</strong> não é uma nova arquitetura que exige retreinamento, mas sim um <strong>padrão de orquestração em tempo de inferência</strong>. Ele muda fundamentalmente a relação do modelo com a informação.</p>
<p>Enquanto o <strong>RAG (Retrieval-Augmented Generation)</strong> trata o contexto como um depósito de busca estático, o RLM trata o contexto como um <strong>ambiente externo</strong> que o modelo explora. Em vez de tentar "ler" 1 milhão de tokens de uma vez, o modelo chama a si mesmo recursivamente para resolver sub-tarefas focadas, decompondo problemas massivos em micro-análises estruturadas.</p>
<p>Essa abordagem permite que a IA mantenha o raciocínio afiado em escalas que superam <strong>10 milhões de tokens</strong>, eliminando a degradação de atenção. É a diferença entre tentar decorar um livro inteiro em cinco minutos ou ter um pesquisador que sabe exatamente quais capítulos consultar e como sintetizar cada parágrafo de forma lógica.</p>
<h2>Conclusão: O Futuro é Estruturado</h2>
<p>A corrida armamentista por modelos com trilhões de parâmetros está encontrando retornos decrescentes. O futuro da inteligência de nível <em>enterprise</em> não depende de janelas de contexto infinitas, mas de processos de <strong>inferência inteligente</strong> que respeitem a hierarquia natural da informação.</p>
<p>Ao unir orquestração recursiva com viés sintático, estamos finalmente saindo da "era da sequência" para entrar na era da compreensão estrutural. Se o cérebro humano processa a linguagem de forma hierárquica para economizar energia e maximizar o sentido, por que ainda insistimos em alimentar nossas máquinas com sequências infinitas e desestruturadas? A resposta, ao que tudo indica, está plantada nas árvores.</p>
]]></content:encoded></item><item><title><![CDATA[3 princípios não tão intuitivos na Experiência de Desenvolvimento (DX)]]></title><description><![CDATA[O paradoxo da produtividade
Líderes de engenharia e desenvolvedores estão em uma busca constante por mais produtividade. A pressão para entregar mais rápido é implacável, mas os caminhos para alcançar essa velocidade raramente são claros. Investimos ...]]></description><link>https://leocavalcante.dev/3-principios-nao-tao-intuitivos-na-experiencia-de-desenvolvimento-dx</link><guid isPermaLink="true">https://leocavalcante.dev/3-principios-nao-tao-intuitivos-na-experiencia-de-desenvolvimento-dx</guid><category><![CDATA[developer experience]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 05 Jan 2026 12:09:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764597044912/25feb18f-ef3f-4849-a793-c48e27d156ea.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-o-paradoxo-da-produtividade">O paradoxo da produtividade</h2>
<p>Líderes de engenharia e desenvolvedores estão em uma busca constante por mais produtividade. A pressão para entregar mais rápido é implacável, mas os caminhos para alcançar essa velocidade raramente são claros. Investimos em novas ferramentas e processos, muitas vezes sem entender se estamos realmente resolvendo os gargalos certos ou apenas adicionando complexidade.</p>
<p>Com base em anos de experiência na otimização da produtividade em empresas como Google, LinkedIn e Capital One, o especialista Max argumenta que a excelência na experiência de desenvolvimento (DX) não é uma lista interminável de melhorias. Na verdade, para a experiência interativa do desenvolvedor, existem apenas três coisas que realmente importam. Esses princípios são surpreendentes, muitas vezes contra-intuitivos, e formam um framework poderoso para focar onde o impacto é maior.</p>
<h2 id="heading-o-maior-ladrao-de-tempo-nao-e-a-codificacao-e-a-observacao">O maior ladrão de tempo não é a codificação, é a observação</h2>
<p>Para otimizar a velocidade, precisamos primeiro entender o "Tempo de Ciclo" (<em>Cycle Time</em>): o tempo entre o momento em que um desenvolvedor tem uma intenção e o momento em que ele vivencia o resultado dessa intenção. Os ciclos de desenvolvimento são como um fractal: não importa o quanto você se aproxime, a estrutura parece ser feita de si mesma. Ciclos grandes, como entregar uma feature em três meses, são compostos por inúmeros ciclos menores, como escrever uma linha de código.</p>
<p>A abordagem comum de pedir à equipe para "ir mais rápido" em ciclos grandes é um erro fatal. Isso apenas diminui a qualidade, o que, por sua vez, ironicamente, diminui a velocidade a longo prazo. O segredo é que a parte mais lenta de qualquer ciclo quase nunca é a fase de "agir" (digitar o código). É quase sempre a fase de "observar": o tempo gasto coletando as informações necessárias para tomar a próxima decisão.</p>
<p>Essa fase de observação se torna um gargalo <em>porque</em> o desenvolvedor é forçado a investigar em vez de agir. Exemplos disso incluem:</p>
<ul>
<li><p>Mensagens de erro vagas que forçam uma caça ao tesouro para entender o problema.</p>
</li>
<li><p>Requisitos de negócio ambíguos que paralisam a tomada de decisão.</p>
</li>
<li><p>Falhas de teste que exigem 20 minutos de depuração para serem compreendidas.</p>
</li>
</ul>
<p>Isso é impactante porque <em>exige</em> uma mudança de foco: pare de otimizar a velocidade de digitação e comece a otimizar a clareza da informação. O objetivo é criar um ambiente onde o próximo passo seja sempre óbvio.</p>
<p>Como eu poderia fazer com que os desenvolvedores nunca precisassem pensar ou adivinhar qual é o próximo passo?</p>
<h2 id="heading-ferramentas-frustrantes-quebram-o-foco-da-mesma-forma-que-reunioes">Ferramentas frustrantes quebram o foco da mesma forma que reuniões</h2>
<p>O desenvolvimento de software exige um estado de concentração profunda, ou "fluxo" (<em>flow</em>). É como construir um "palácio mental" complexo, onde cada peça precisa se encaixar perfeitamente. Interrupções, ou trocas de contexto, são devastadoras. Pode levar até 15 minutos para reconstruir esse palácio mental após uma interrupção que durou apenas 30 segundos ou dois minutos.</p>
<p>A visão contra-intuitiva é que as interrupções não são apenas reuniões ou mensagens no Slack. Interrupções emocionais, como a frustração causada por uma ferramenta que trava sem dar feedback, são igualmente prejudiciais. A atenção do desenvolvedor se desvia da tarefa para a sua irritação, quebrando o fluxo da mesma forma que uma interrupção direta faria. A complexidade da interrupção também importa: um erro de compilação complexo que leva dois minutos para depurar é muito mais danoso ao foco do que uma mensagem simples. A falta de clareza sobre o "porquê" de uma tarefa também é um assassino do foco, pois a indecisão constante força o cérebro a sair do modo de resolução de problemas.</p>
<p>A solução para isso exige uma franqueza que muitos evitam. Nas palavras diretas de Max:</p>
<blockquote>
<p>Você precisa parar de convidar engenheiros de linha de frente para reuniões.</p>
</blockquote>
<h2 id="heading-para-acelerar-o-desenvolvimento-voce-precisa-remover-escolhas">Para acelerar o desenvolvimento, você precisa remover escolhas</h2>
<p>A seguir, a ideia que soa controversa, mas que, com base em anos de experiência, é inquestionavelmente verdadeira. "Carga Cognitiva" (<em>Cognitive Load</em>) é a quantidade de coisas que um desenvolvedor precisa saber e o número de decisões que ele precisa tomar para realizar uma tarefa. Muitas empresas, na tentativa de melhorar a DX, criam dezenas de ferramentas internas. Cada uma exige tempo de aprendizado e adiciona complexidade.</p>
<p>O problema é devastador quando quantificado. Se sua empresa tem 50 ferramentas internas e cada uma exige apenas uma hora por mês para aprender sobre atualizações, você está exigindo <strong>50 horas por mês de cada desenvolvedor</strong> apenas para entender como trabalhar em seu ecossistema. Uma solução comum — criar um portal web para abrigar todas as 50 ferramentas — resolve o problema de descoberta, mas não o de carga cognitiva. Você ainda tem 50 ferramentas para aprender.</p>
<p>A solução radical não é apenas tornar as ferramentas mais intuitivas, mas sim eliminar a necessidade de elas existirem, através da automação. Em vez de um processo com três ferramentas para validar, enviar e gerenciar um arquivo de configuração, o ideal é um sistema onde o desenvolvedor apenas faz o check-in do arquivo, e todo o resto acontece automaticamente. O verdadeiro avanço é remover decisões sobre infraestrutura — qual provedor de nuvem, qual ferramenta de build, qual sistema de CI — para que os desenvolvedores possam focar exclusivamente nos problemas de negócio.</p>
<p>A distinção crucial está em remover decisões sobre <em>como</em> o trabalho é feito (infraestrutura) para liberar o desenvolvedor para focar no <em>que</em> está sendo feito (valor de negócio).</p>
<ul>
<li><p><strong>Boas restrições</strong> simplificam o ecossistema. Padronizar um único sistema de CI ou uma única plataforma de monitoramento acelera a colaboração e a resolução de incidentes.</p>
</li>
<li><p><strong>Más restrições</strong> limitam a capacidade do desenvolvedor de resolver o problema. Forçar o uso de uma única linguagem de programação para todos os casos de uso ou proibir editores de código específicos prejudica a produtividade. A chave é entender quais problemas os desenvolvedores realmente precisam resolver.</p>
</li>
</ul>
<h2 id="heading-o-desafio-real-e-humano-nao-tecnico">O desafio real é humano, não técnico</h2>
<p>A busca por uma experiência de desenvolvimento top se resume a três pilares: otimizar o tempo de <em>observação</em> para acelerar os ciclos de desenvolvimento, proteger o <em>foco</em> de frustrações e interrupções, e reduzir a <em>carga cognitiva</em> removendo escolhas desnecessárias sobre infraestrutura. Depois de otimizar esses aspectos técnicos, os maiores desafios que restam são fundamentalmente humanos, não tecnológicos.</p>
<p>A verdadeira transformação acontece quando mudamos a cultura. Depois de resolver a tecnologia, como você planeja lidar com a aversão à mudança, aprender a dizer "não" e transformar a cultura de desenvolvimento da sua equipe?</p>
]]></content:encoded></item><item><title><![CDATA[Levando AI Engineering para o próximo nível com RAG]]></title><description><![CDATA[Se você está começando a se aventurar no mundo de AI Engineering e quer ir além do básico, precisa entender profundamente o que é Retrieval-Augmented Generation (RAG). Essa técnica é, sem dúvida, um divisor de águas para quem quer construir agentes d...]]></description><link>https://leocavalcante.dev/levando-ai-engineering-para-o-proximo-nivel-com-rag</link><guid isPermaLink="true">https://leocavalcante.dev/levando-ai-engineering-para-o-proximo-nivel-com-rag</guid><category><![CDATA[AI]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[RAG ]]></category><category><![CDATA[chunking]]></category><category><![CDATA[reranking]]></category><category><![CDATA[AI Engineering]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Fri, 28 Nov 2025 18:58:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764356222861/61be2507-7227-4af8-947c-01255d7e10df.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se você está começando a se aventurar no mundo de AI Engineering e quer ir além do básico, precisa entender profundamente o que é <strong>Retrieval-Augmented Generation (RAG)</strong>. Essa técnica é, sem dúvida, um divisor de águas para quem quer construir agentes de IA que realmente entregam resultados bacanas com dados específicos de usuários e empresas.</p>
<h2 id="heading-por-que-rag-importa">Por que RAG importa?</h2>
<p>Vamos ser diretos: treinar um Large Language Model (LLM) do zero é absurdamente caro. Estamos falando de milhões de dólares em infraestrutura, energia e tempo. Para a maioria das empresas, isso simplesmente não é viável. E é exatamente aqui que o RAG entra como uma solução elegante e prática.</p>
<p>O RAG combina o poder generativo dos LLMs com sistemas de recuperação de informação, permitindo que você "ensine" o modelo sobre seu domínio específico sem precisar retreiná-lo. Imagine que você tem uma base de conhecimento interna com políticas da empresa, documentação técnica ou dados de clientes. Com RAG, você pode conectar essa base diretamente ao LLM, fazendo com que ele responda de forma contextualizada e precisa.</p>
<p>Mas os benefícios vão muito além da economia. LLMs têm um problema sério chamado <strong>hallucination</strong>, onde eles inventam informações que parecem verdadeiras mas são completamente fabricadas. O RAG mitiga drasticamente esse problema porque força o modelo a basear suas respostas em documentos reais que foram recuperados da sua base de conhecimento. O modelo deixa de "inventar" e passa a "citar fontes".</p>
<p>Outro ponto crítico é o <strong>knowledge cutoff</strong>. Os LLMs são treinados até uma data específica e não sabem nada sobre eventos posteriores. Com RAG, você pode alimentar o sistema com informações atualizadas em tempo real, simplesmente adicionando novos documentos ao seu índice. Nada de retreinamento, nada de custos astronômicos.</p>
<h2 id="heading-a-arquitetura-basica-entendendo-o-fluxo">A arquitetura básica: entendendo o fluxo</h2>
<p>Antes de mergulharmos nas técnicas avançadas, precisamos entender como um pipeline RAG funciona. O processo se divide em duas fases principais.</p>
<p>Na primeira fase, chamada de <strong>Indexing</strong>, você prepara seus dados. Isso envolve carregar documentos de diversas fontes como PDFs, páginas web ou bases de dados internas, dividir esses documentos em pedaços menores chamados <strong>chunks</strong>, converter cada chunk em uma representação numérica chamada <strong>embedding</strong> usando um modelo especializado, e finalmente armazenar esses embeddings em um <strong>vector database</strong> como ChromaDB, Pinecone ou Weaviate.</p>
<p>A segunda fase é a <strong>Retrieval-Generation</strong>, que acontece quando o usuário faz uma pergunta. A query do usuário é convertida em um embedding, esse embedding é comparado com os embeddings armazenados para encontrar os chunks mais similares, os chunks recuperados são inseridos no prompt junto com a pergunta original, e o LLM gera uma resposta baseada nesse contexto enriquecido.</p>
<p>Parece simples, certo? E conceitualmente é. Mas a diferença entre um RAG que funciona "mais ou menos" e um que entrega resultados espetaculares está nos detalhes de implementação. E é aí que entram as técnicas avançadas.</p>
<h2 id="heading-avancando-2-casas">Avançando 2 casas</h2>
<p>Quando falamos de <strong>Advanced RAG</strong> ou <strong>Context Engineering</strong>, estamos falando de uma mudança de mentalidade. Não é mais sobre escrever prompts melhores, é sobre arquitetar sistemas inteiros que garantem que o LLM receba exatamente a informação certa, no formato certo, na hora certa.</p>
<p>Uma técnica poderosa é o <strong>Hybrid Retrieval</strong>, que combina busca semântica via vectors com busca por keywords usando algoritmos como BM25. Isso garante que você capture resultados mesmo quando as palavras exatas são diferentes, mas também quando precisam ser exatamente iguais. Estudos mostram que hybrid search pode reduzir erros de resposta em até 40%.</p>
<p>Outra abordagem é o <strong>Query Rewriting</strong>, onde você usa o próprio LLM para transformar perguntas vagas ou complexas em queries mais efetivas para busca. Isso é especialmente útil quando usuários fazem perguntas ambíguas ou mal formuladas.</p>
<p>O <strong>GraphRAG</strong> é uma evolução interessante que converte dados não estruturados em knowledge graphs. Isso permite que o LLM raciocine sobre relacionamentos entre entidades, algo que busca vetorial simples não consegue fazer bem. Imagine perguntar "quais funcionários que reportam ao gerente X trabalharam no projeto Y?" - isso requer entender relações, não apenas similaridade semântica.</p>
<p>E claro, temos o <strong>Agentic RAG</strong>, onde o LLM não é mais passivo. Ele decide dinamicamente quando e como buscar informação, podendo acessar múltiplas fontes como databases SQL, APIs web ou diferentes vector stores. É o primeiro passo para construir agentes verdadeiramente autônomos.</p>
<p>Mas de todas essas técnicas, duas merecem atenção especial porque impactam diretamente a qualidade do seu sistema: <strong>Chunking</strong> e <strong>Re-ranking</strong>.</p>
<h2 id="heading-chunking-a-fundacao-que-define-seu-teto">Chunking: a fundação que define seu teto</h2>
<p>Sendo bem claro: a qualidade do seu chunking define o limite máximo de performance do seu sistema RAG. Não importa quão sofisticado seja seu modelo de embedding ou quão poderoso seja seu LLM, se você alimentar o sistema com chunks mal construídos, o resultado será medíocre. É o clássico "garbage in, garbage out".</p>
<p>Chunking é o processo de dividir documentos grandes em pedaços menores que serão individualmente indexados e recuperados. Parece trivial, mas a forma como você faz isso tem implicações profundas.</p>
<p>Primeiro, existe uma questão técnica: embeddings são representações numéricas de conteúdo, e modelos de embedding têm limites de tokens que podem processar. Segundo, o LLM tem uma context window limitada, então você precisa de chunks que caibam nessa janela. Terceiro, e mais importante, chunks muito grandes diluem a relevância e chunks muito pequenos perdem contexto essencial.</p>
<p>O tamanho ideal geralmente fica entre 256 e 512 tokens, com um overlap de 10 a 20% entre chunks consecutivos. Mas isso é apenas um ponto de partida. O tamanho ótimo depende do seu domínio, do tipo de perguntas que os usuários fazem e da natureza dos seus documentos. É um problema de engenharia iterativa, não uma fórmula mágica.</p>
<p>A estratégia mais básica é o <strong>Fixed-Size Chunking</strong>, onde você simplesmente corta o texto a cada N caracteres. É rápido e simples, mas frequentemente corta frases no meio, separando ideias que deveriam estar juntas. O resultado são chunks fragmentados e incoerentes.</p>
<p>Uma evolução significativa é o <strong>Recursive Character Text Splitting</strong>, que é considerado o método go-to para a maioria dos casos. Ele usa uma hierarquia de separadores como parágrafos, quebras de linha e espaços, tentando manter unidades semânticas juntas. Só quando não consegue respeitar o limite de tamanho usando um separador, ele passa para o próximo na hierarquia.</p>
<p>Para documentos bem estruturados, o <strong>Structure-Aware Chunking</strong> pode ser a maior melhoria de performance com o menor esforço. Se você tem Markdown com headers claros, código organizado em funções ou HTML semântico, faz todo sentido usar esses delimitadores naturais ao invés de ignorá-los.</p>
<p>O <strong>Semantic Chunking</strong> vai um passo além. Ele calcula a similaridade vetorial entre sentenças adjacentes e só quebra o chunk quando detecta uma mudança significativa de tópico. Isso garante alta coesão semântica dentro de cada chunk, o que é especialmente valioso para knowledge bases e papers de pesquisa.</p>
<p>Uma técnica particularmente elegante é o <strong>Small-to-Large Chunking</strong>, também conhecido como Parent Document Retriever. A ideia é usar chunks pequenos e precisos para retrieval, garantindo alta precisão na busca, mas quando um chunk é encontrado, você recupera o chunk "pai" maior para dar contexto rico ao LLM. É o melhor dos dois mundos.</p>
<p>Vamos ver como implementar o Recursive Character Text Splitting usando LangChain:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> langchain.document_loaders <span class="hljs-keyword">import</span> PyPDFLoader
<span class="hljs-keyword">from</span> langchain.text_splitter <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter

<span class="hljs-comment"># Carregando o documento</span>
loader = PyPDFLoader(<span class="hljs-string">"documento_com_dominio_especifico.pdf"</span>)
documents = loader.load()

<span class="hljs-comment"># Configurando o splitter com a estratégia recursiva</span>
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=<span class="hljs-number">500</span>,
    chunk_overlap=<span class="hljs-number">100</span>,
    separators=[<span class="hljs-string">"\n\n"</span>, <span class="hljs-string">"\n"</span>, <span class="hljs-string">" "</span>, <span class="hljs-string">""</span>]
)

<span class="hljs-comment"># Aplicando o splitting</span>
chunks = text_splitter.split_documents(documents)

print(<span class="hljs-string">f"Documento dividido em <span class="hljs-subst">{len(chunks)}</span> chunks"</span>)
</code></pre>
<p>O parâmetro <code>separators</code> é onde a mágica acontece. O algoritmo primeiro tenta dividir por parágrafos duplos, depois por quebras de linha simples, depois por espaços, e só em último caso faz cortes arbitrários. Isso preserva a estrutura semântica do texto original.</p>
<p>O <code>chunk_overlap</code> é crucial para não perder contexto nas bordas. Se uma informação importante está no final de um chunk e é referenciada no início do próximo, o overlap garante que essa conexão seja mantida em pelo menos um dos chunks.</p>
<h2 id="heading-re-ranking-o-filtro-de-qualidade">Re-ranking: o filtro de qualidade</h2>
<p>Se chunking é a fundação, re-ranking é o controle de qualidade que garante que só o melhor material chegue ao LLM. E isso é mais importante do que parece.</p>
<p>Quando você faz uma busca vetorial, o sistema retorna os N documentos mais similares baseado em distância cosine ou outra métrica. Mas aqui está o problema: a similaridade de embedding é apenas uma aproximação grosseira de relevância real. O processo de converter texto em vetores inevitavelmente perde informação, e dois textos podem ter embeddings similares sem serem realmente relevantes para a query específica.</p>
<p>É aí que entra o re-ranking. Depois de recuperar um conjunto inicial amplo de candidatos, digamos 10 ou 20 chunks, um modelo especializado reavalia cada um deles em relação à query original e atribui um score de relevância refinado. Os chunks são então reordenados baseado nesses novos scores, e apenas os melhores, talvez 3 ou 4, são passados para o LLM.</p>
<p>A técnica mais comum para re-ranking usa <strong>Cross-Encoders</strong>. Diferente dos bi-encoders usados para criar embeddings, que processam query e documento separadamente, cross-encoders processam ambos juntos. Isso permite uma análise muito mais profunda da relação entre a pergunta e o conteúdo, resultando em scores de relevância muito mais precisos.</p>
<p>O trade-off é que cross-encoders são mais lentos porque precisam processar cada par query-documento individualmente. Por isso usamos em duas etapas: primeiro uma busca vetorial rápida para pegar muitos candidatos, depois re-ranking mais lento mas preciso para filtrar os melhores.</p>
<p>Veja como implementar isso na prática:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> langchain.embeddings <span class="hljs-keyword">import</span> OpenAIEmbeddings
<span class="hljs-keyword">from</span> langchain_chroma <span class="hljs-keyword">import</span> Chroma
<span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> CrossEncoder

<span class="hljs-comment"># Primeiro, criamos o vectorstore com os chunks</span>
embeddings = OpenAIEmbeddings(model=<span class="hljs-string">"text-embedding-ada-002"</span>)
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory=<span class="hljs-string">"./chroma_db"</span>
)

<span class="hljs-comment"># Query do usuário</span>
query = <span class="hljs-string">"Qual é a política de cancelamento de voos?"</span>

<span class="hljs-comment"># Retrieval inicial com K alto para ter candidatos suficientes</span>
candidatos = vectorstore. similarity_search(query, k=<span class="hljs-number">10</span>)

<span class="hljs-comment"># Agora aplicamos re-ranking com Cross-Encoder</span>
cross_encoder = CrossEncoder(<span class="hljs-string">'cross-encoder/ms-marco-MiniLM-L-6-v2'</span>)

<span class="hljs-comment"># Preparando os pares query-documento</span>
pares = [[query, doc. page_content] <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> candidatos]

<span class="hljs-comment"># Calculando scores refinados</span>
scores = cross_encoder.predict(pares)

<span class="hljs-comment"># Combinando scores com documentos e ordenando</span>
docs_com_score = list(zip(scores, candidatos))
docs_ordenados = sorted(docs_com_score, key=<span class="hljs-keyword">lambda</span> x: x[<span class="hljs-number">0</span>], reverse=<span class="hljs-literal">True</span>)

<span class="hljs-comment"># Selecionando apenas os top para o LLM</span>
top_chunks = [doc <span class="hljs-keyword">for</span> score, doc <span class="hljs-keyword">in</span> docs_ordenados[:<span class="hljs-number">4</span>]]

print(<span class="hljs-string">f"Selecionados <span class="hljs-subst">{len(top_chunks)}</span> chunks após re-ranking"</span>)
</code></pre>
<p>O modelo <code>cross-encoder/ms-marco-MiniLM-L-6-v2</code> é uma escolha sólida para começar. É relativamente pequeno e rápido, mas ainda oferece uma melhoria significativa sobre confiar apenas nos scores de similaridade vetorial.</p>
<h2 id="heading-juntando-tudo">Juntando tudo</h2>
<p>Agora vamos ver como todos esses componentes se conectam em um pipeline RAG completo e funcional:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> langchain. document_loaders <span class="hljs-keyword">import</span> PyPDFLoader
<span class="hljs-keyword">from</span> langchain.text_splitter <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter
<span class="hljs-keyword">from</span> langchain.embeddings <span class="hljs-keyword">import</span> OpenAIEmbeddings
<span class="hljs-keyword">from</span> langchain_chroma <span class="hljs-keyword">import</span> Chroma
<span class="hljs-keyword">from</span> langchain.prompts <span class="hljs-keyword">import</span> ChatPromptTemplate
<span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI
<span class="hljs-keyword">from</span> langchain. chains. combine_documents <span class="hljs-keyword">import</span> create_stuff_documents_chain
<span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> CrossEncoder

<span class="hljs-comment"># FASE 1: Indexing com Advanced Chunking</span>
loader = PyPDFLoader(<span class="hljs-string">"base_conhecimento.pdf"</span>)
documents = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=<span class="hljs-number">500</span>,
    chunk_overlap=<span class="hljs-number">100</span>,
    separators=[<span class="hljs-string">"\n\n"</span>, <span class="hljs-string">"\n"</span>, <span class="hljs-string">" "</span>, <span class="hljs-string">""</span>]
)
chunks = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings(model=<span class="hljs-string">"text-embedding-ada-002"</span>)
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory=<span class="hljs-string">"./chroma_db"</span>
)

<span class="hljs-comment"># FASE 2: Retrieval com Re-ranking</span>
query = <span class="hljs-string">"Como funciona o processo de reembolso?"</span>

candidatos = vectorstore.similarity_search(query, k=<span class="hljs-number">10</span>)

cross_encoder = CrossEncoder(<span class="hljs-string">'cross-encoder/ms-marco-MiniLM-L-6-v2'</span>)
pares = [[query, doc.page_content] <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> candidatos]
scores = cross_encoder. predict(pares)

docs_ordenados = sorted(zip(scores, candidatos), key=<span class="hljs-keyword">lambda</span> x: x[<span class="hljs-number">0</span>], reverse=<span class="hljs-literal">True</span>)
contexto_final = [doc <span class="hljs-keyword">for</span> score, doc <span class="hljs-keyword">in</span> docs_ordenados[:<span class="hljs-number">4</span>]]

<span class="hljs-comment"># FASE 3: Generation</span>
llm = ChatOpenAI(model=<span class="hljs-string">"gpt-4o-mini"</span>, temperature=<span class="hljs-number">0</span>)

prompt = ChatPromptTemplate.from_template(<span class="hljs-string">"""
Responda a pergunta baseando-se APENAS no contexto fornecido.  
Se o contexto não contiver a informação necessária, diga claramente 
que não foi possível encontrar a resposta.

Contexto: {context}

Pergunta: {input}
"""</span>)

chain = create_stuff_documents_chain(llm, prompt)

resposta = chain.invoke({
    <span class="hljs-string">"input"</span>: query,
    <span class="hljs-string">"context"</span>: contexto_final
})

print(resposta)
</code></pre>
<p>Esse código representa um sistema RAG robusto que vai muito além do básico. Você tem chunking inteligente que preserva contexto semântico, retrieval que busca candidatos amplos, re-ranking que filtra apenas os melhores, e generation que força o modelo a se basear no contexto fornecido.</p>
<h2 id="heading-ou-seja">Ou seja</h2>
<p>Dominar RAG é essencial para qualquer AI Engineer que quer construir sistemas de produção sérios. Não é mais sobre escrever prompts bonitos, é sobre arquitetar pipelines que garantem qualidade consistente e confiável.</p>
<p>As técnicas que exploramos aqui, especialmente chunking avançado e re-ranking, são o tipo de conhecimento que separa implementações amadoras de sistemas enterprise-grade. E o melhor é que frameworks como LangChain tornam a implementação acessível, permitindo que você foque na arquitetura e na otimização ao invés de reinventar a roda.</p>
<p>O próximo passo natural é explorar técnicas ainda mais avançadas como Agentic RAG, onde o sistema decide autonomamente quando e como buscar informação, e GraphRAG para casos que exigem raciocínio sobre relacionamentos complexos. Mas com a fundação que construímos aqui, você já está preparado para entregar resultados que realmente impressionam.</p>
<p>A jornada de prompt engineering para Context Engineering é uma evolução necessária. E agora você tem as ferramentas para fazer essa transição.</p>
]]></content:encoded></item><item><title><![CDATA[Sistemas Multi-Agentes de IA: Quando Usar, Como Construir e Porque Tantos Falham]]></title><description><![CDATA[Sabe quando você está tentando resolver um problema complexo com IA e parece que um único modelo não dá conta do recado? Passei por isso várias vezes. É tipo quando você pede para um agente de IA fazer várias coisas ao mesmo tempo e ele simplesmente ...]]></description><link>https://leocavalcante.dev/sistemas-multi-agentes-de-ia-quando-usar-como-construir-e-porque-tantos-falham</link><guid isPermaLink="true">https://leocavalcante.dev/sistemas-multi-agentes-de-ia-quando-usar-como-construir-e-porque-tantos-falham</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[#agent]]></category><category><![CDATA[agents]]></category><category><![CDATA[agentic AI]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Tue, 04 Nov 2025 12:33:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ggg_B1MeqQk/upload/7add0378af065a7efa54b70b965cd6f7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sabe quando você está tentando resolver um problema complexo com IA e parece que um único modelo não dá conta do recado? Passei por isso várias vezes. É tipo quando você pede para um agente de IA fazer várias coisas ao mesmo tempo e ele simplesmente trava, demora uma eternidade ou dá respostas medianas pra tudo. Foi aí que mergulhei fundo no mundo dos sistemas multi-agentes, e cara, aprendi que essa área é muito mais complicada do que parece na teoria.</p>
<p>A ideia por trás dos sistemas multi-agentes é simples: ao invés de forçar um único modelo de IA a fazer tudo, você divide o trabalho entre vários agentes especializados. Imagina uma situação real: um passageiro entra em contato porque seu voo atrasou três horas e ele vai perder a conexão para Tóquio. Um sistema com um único agente pode levar vinte segundos e sugerir uma rota com escala de quatorze horas, mesmo tendo opções melhores com apenas duas horas de espera. Pior ainda, ele confirma a remarcação mas esquece de avisar que o upgrade não será transferido para o novo voo.</p>
<p>Agora pensa em dividir isso entre agentes especializados: um cuida do rastreamento de voos e status logísticos, outro gerencia informações de pagamento e reembolsos, e um terceiro lida com recomendações e alternativas. Cada um faz o que faz de melhor, sem perder contexto ou se confundir com múltiplas tarefas ao mesmo tempo.</p>
<h2 id="heading-quando-a-especializacao-realmente-funciona">Quando a Especialização Realmente Funciona</h2>
<p>A grande sacada é entender que nem todo problema precisa de múltiplos agentes. Na verdade, a maioria não precisa. Descobri que sistemas multi-agentes brilham em situações específicas: quando você consegue paralelizar tarefas genuinamente independentes, quando precisa de múltiplas camadas de validação para garantir precisão, ou quando está lidando com volumes enormes de dados que se beneficiam de processamento simultâneo.</p>
<p>Por exemplo, se você está analisando cem relatórios trimestrais de empresas diferentes para insights de investimento, cada agente pode pegar um relatório e extrair métricas chave sem precisar se comunicar com os outros durante o processamento. No final, você agrega tudo numa análise de mercado abrangente. Isso funciona porque as tarefas são realmente independentes e a combinação dos resultados é mecânica.</p>
<p>Mas tem um porém gigantesco que muita gente ignora: a coordenação entre agentes tem um custo real. Não é só sobre tokens ou dinheiro, é sobre complexidade. Dois agentes precisam de um canal de comunicação. Três agentes precisam de três canais. Quatro agentes precisam de seis. Logo, seu sistema fica mais difícil de gerenciar do que é útil. É como aquele projeto em equipe onde você gasta mais tempo coordenando reuniões do que realmente trabalhando.</p>
<h2 id="heading-os-quatro-jeitos-de-organizar-seus-agentes">Os Quatro Jeitos de Organizar Seus Agentes</h2>
<p>Depois de quebrar a cabeça tentando entender qual arquitetura usar, percebi que existem quatro padrões principais, cada um com seus pontos fortes e fracos.</p>
<p>A arquitetura centralizada é tipo ter um maestro regendo uma orquestra. Um agente supervisor poderoso coordena todos os outros, atribui tarefas, monitora progresso e sintetiza resultados. É fácil de entender e debugar porque tudo passa por um único ponto de controle. O problema? Esse orquestrador vira um gargalo quando você escala pra dez ou vinte agentes. Se ele cai, todo o sistema para.</p>
<p>Já a arquitetura descentralizada deixa os agentes conversarem diretamente entre si, sem um chefe central. É mais resiliente porque se um agente falha, os outros continuam funcionando. Mas coordenar comportamento global vira um pesadelo quando nenhum agente tem visão completa do que está acontecendo. É tipo aquele grupo de WhatsApp desorganizado onde todo mundo fala ao mesmo tempo e ninguém sabe quem está fazendo o quê.</p>
<p>A arquitetura hierárquica cria camadas de supervisão, tipo uma árvore organizacional de verdade. Você tem times especializados com líderes de time que reportam pra coordenadores de nível superior. Funciona bem pra problemas complexos que naturalmente se dividem em sub-problemas, mas adiciona overhead de coordenação entre os níveis.</p>
<p>E tem a arquitetura híbrida, que mistura tudo isso. Decisões globais vêm de coordenadores centrais enquanto otimizações locais acontecem através de interações peer-to-peer. É tipo uma empresa de delivery de comida: o centro mantém integridade de pagamentos e pedidos, mas os entregadores negociam rotas entre si localmente sem esperar aprovação central pra cada decisão.</p>
<h2 id="heading-o-grande-vilao-gerenciamento-de-contexto">O Grande Vilão: Gerenciamento de Contexto</h2>
<p>Aqui é onde a maioria dos sistemas multi-agentes quebra na prática, e demorei pra entender isso direito. Contexto não é a mesma coisa que memória. Contexto é tipo a RAM do computador, é a informação imediata que o modelo consegue "ver" agora. Memória é o HD, informação persistente armazenada externamente que sobrevive entre sessões.</p>
<p>O problema é que pra cada token que seus agentes geram, eles processam em média cem tokens de entrada. Essa proporção de cem pra um significa que gerenciamento de contexto é o que determina se seu sistema funciona ou colapsa. A maioria dos times simplesmente joga tudo no contexto e espera que o modelo se vire sozinho. Não funciona.</p>
<p>Tem quatro tipos de contexto que seus agentes precisam lidar: instruções (prompts do sistema e exemplos), conhecimento (fatos, documentos recuperados, preferências do usuário), ferramentas (descrições de ferramentas e resultados de chamadas), e histórico (conversas passadas e decisões anteriores). Cada tipo precisa de tratamento diferente.</p>
<p>E cada tipo pode falhar de um jeito específico. O envenenamento de contexto acontece quando uma alucinação ou erro entra no contexto e fica sendo referenciado repetidamente, compondo o erro ao longo do tempo. A distração de contexto ocorre quando o histórico acumulado fica tão longo que o modelo para de raciocinar sobre a situação atual e só procura padrões do passado pra copiar. A confusão de contexto surge quando você dá acesso a muitas ferramentas, dificultando a seleção confiável da certa. E o confronto de contexto é quando seu contexto acumulado contém informações contraditórias que descarrilham o raciocínio do agente.</p>
<h2 id="heading-metricas-que-realmente-importam">Métricas Que Realmente Importam</h2>
<p>Aprendi que tem três níveis de rastreamento que você precisa dominar. No nível de sessão, você mede se o objetivo geral do cliente foi alcançado. Se alguém pergunta "Por que minha conta está mais alta esse mês?" e o agente apenas confirma que vai checar sem fornecer informações específicas, a ação está incompleta. Uma taxa de conclusão de ação abaixo de oitenta por cento indica que seus agentes podem não estar usando as ferramentas corretamente.</p>
<p>No nível de <em>step (ou task)</em>, você rastreia decisões individuais. A qualidade de seleção de ferramentas mostra se seus agentes escolhem as ferramentas certas com os parâmetros certos. Se o score cai abaixo de oitenta por cento, geralmente indica um de dois problemas: alguns agentes chamam ferramentas desnecessariamente pra perguntas que poderiam responder diretamente, ou outros pulam chamadas de ferramenta quando deveriam estar verificando dados reais.</p>
<p>E no nível de sistema, você monitora latência, falhas de API, e padrões agregados. A visualização de linha do tempo mostra onde o tempo é gasto: operações de chamada de modelo, lógica de transferência, ou recuperação de conhecimento. Isso te ajuda a identificar gargalos e otimizar a experiência do usuário.</p>
<h2 id="heading-quando-vale-a-pena-e-quando-nao-vale">Quando Vale a Pena e Quando Não Vale</h2>
<p>Depois de tudo isso, a conclusão mais importante é: não construa um sistema multi-agente só porque parece legal. Na maioria dos casos, oitenta por cento dos problemas podem ser resolvidos com um único agente bem projetado com bom gerenciamento de contexto. Engenharia de prompt melhor quase sempre bate um sistema multi-agente mal pensado.</p>
<p>Sistemas multi-agentes fazem sentido quando você realmente consegue paralelizar tarefas independentes, quando precisa de múltiplas camadas de validação pra garantir precisão, ou quando está lidando com escala onde o processamento paralelo fornece benefícios exponenciais. Eles não fazem sentido quando você precisa de respostas sub-segundo, quando tem orçamento apertado que não suporta aumento de custo de duas a cinco vezes, ou quando suas tarefas são sequenciais com dependências fortes.</p>
<p>E tem outro ponto crucial que muita gente esquece: os modelos estão melhorando rápido. Aquela arquitetura complexa multi-agente que você passa meses construindo pode se tornar overhead desnecessário quando um modelo melhor surge em seis meses. Times que construíram camadas elaboradas de orquestração pro GPT-4 descobriram que eram desnecessárias com o GPT-5. Cadeias de raciocínio multi-passo desenhadas pro Claude 3 viraram prompts únicos com o Claude 4. A estrutura adicionada pra contornar limitações virou a própria limitação.</p>
<p>Por isso, comece simples. Use estrutura mínima que você pode deletar depois. Teste se seu caso de uso realmente se beneficia de múltiplos agentes antes de construir infraestrutura complexa. E sempre, sempre tenha observabilidade desde o dia um, porque sem ela você está otimizando no escuro.</p>
<p>No fim das contas, sistemas multi-agentes são ferramentas poderosas quando usadas corretamente, mas exigem pensamento cuidadoso sobre arquitetura, gerenciamento de contexto, e melhoria contínua. A diferença entre teoria e sistemas confiáveis na produção se resume a prática deliberada e decisões baseadas em dados reais, não em suposições.</p>
]]></content:encoded></item><item><title><![CDATA[Um Ano de "Golanged"]]></title><description><![CDATA[Há exatamente um ano, um commit com a mensagem "Golanged" marcava o início de uma nova fase na minha carreira como desenvolvedor. Era meu primeiro passo com a linguagem Go em um projeto profissional, dentro de um monorepo que mantenho até hoje. Mal s...]]></description><link>https://leocavalcante.dev/um-ano-de-golanged</link><guid isPermaLink="true">https://leocavalcante.dev/um-ano-de-golanged</guid><category><![CDATA[Go Language]]></category><category><![CDATA[monorepo]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Wed, 24 Sep 2025 12:53:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758718062617/eaf0fc7f-d37d-4e63-a9a4-5cb7ad096e35.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Há exatamente um ano, um commit com a mensagem "Golanged" marcava o início de uma nova fase na minha carreira como desenvolvedor. Era meu primeiro passo com a linguagem Go em um projeto profissional, dentro de um monorepo que mantenho até hoje. Mal sabia eu que aquela palavra, quase uma brincadeira, representaria a descoberta de um universo de possibilidades. Hoje, celebro não só a data, mas a decisão de embarcar nessa jornada.</p>
<p>Go, ou Golang, é uma linguagem que me conquistou por sua <strong>simplicidade</strong>, no sentido mais poderoso da palavra. Em um mundo de frameworks complexos e múltiplas formas de se chegar ao mesmo resultado, Go oferece um caminho mais direto, quase uma filosofia de "o jeito certo de fazer". E o mais incrível é como a comunidade de desenvolvedores abraça essa ideia, criando um ecossistema de engenheiros pragmáticos focados em construir soluções eficientes e legíveis.</p>
<hr />
<h3 id="heading-os-pilares-do-go-por-que-a-linguagem-brilha">Os Pilares do Go: Por que a linguagem brilha?</h3>
<p>Minha experiência ao longo deste ano me permitiu vivenciar na prática os benefícios que tanto lia a respeito. Se eu pudesse resumir os pontos altos de usar Go, destacaria:</p>
<ul>
<li><p><strong>Simplicidade e Legibilidade:</strong> A sintaxe do Go é enxuta e clara. Com poucas palavras-chave e uma formatação padrão (graças ao <code>gofmt</code>), o código se torna incrivelmente fácil de ler e manter. Isso acelera o onboarding de novos desenvolvedores e facilita a colaboração, pois o estilo do código é consistente em toda a base.</p>
</li>
<li><p><strong>Performance Excepcional:</strong> Go é uma linguagem compilada que gera binários nativos e otimizados. O tempo de compilação é surpreendentemente rápido, e o desempenho em tempo de execução é fantástico, aproximando-se de linguagens como C++, mas com uma complexidade muito menor.</p>
</li>
<li><p><strong>Concorrência Nativa:</strong> Este é, talvez, o superpoder do Go. Com <strong>Goroutines</strong> e <strong>Channels</strong>, escrever código concorrente (que executa várias tarefas "ao mesmo tempo") é trivial e seguro. Em vez de lidar com a complexidade de threads e locks, Go oferece um modelo elegante que simplifica o desenvolvimento de sistemas de alta performance e que fazem uso máximo dos processadores modernos.</p>
</li>
<li><p><strong>Biblioteca Padrão Robusta:</strong> Go vem com uma "bateria inclusa". Sua biblioteca padrão é vasta e cobre desde a criação de servidores web (<code>net/http</code>) até tarefas de criptografia e manipulação de dados, reduzindo a necessidade de dependências externas para funcionalidades essenciais.</p>
</li>
</ul>
<hr />
<h3 id="heading-bonus-a-magia-do-monorepo-e-nao-nao-e-um-monolito">Bônus: A Magia do Monorepo (e não, não é um Monolito!)</h3>
<p>Minha jornada com Go aconteceu dentro de um <strong>monorepo</strong>, e essa combinação se provou extremamente poderosa. É crucial desfazer uma confusão comum: <strong>monorepo não é sinônimo de monolito</strong>.</p>
<ul>
<li><p>Um <strong>monolito</strong> é uma <em>arquitetura de software</em> onde toda a aplicação é uma única e grande unidade de código, fortemente acoplada.</p>
</li>
<li><p>Um <strong>monorepo</strong> é uma <em>estratégia de versionamento de código</em> onde múltiplos projetos, bibliotecas e serviços (que podem ser microsserviços, por exemplo) coexistem no mesmo repositório Git.</p>
</li>
</ul>
<p>As vantagens de trabalhar com um monorepo foram claras desde o início:</p>
<ul>
<li><p><strong>Compartilhamento de Código Simplificado:</strong> Reutilizar código entre diferentes serviços é tão simples quanto importar um pacote local. Não há necessidade de publicar versões em um registro de pacotes e gerenciar dependências externas.</p>
</li>
<li><p><strong>Refatoração Atômica:</strong> Precisa mudar uma API que é consumida por vários serviços? Em um monorepo, você pode atualizar a API e todos os seus consumidores em um único commit, garantindo que tudo continue funcionando junto.</p>
</li>
<li><p><strong>Gerenciamento Único de Dependências:</strong> Todos os projetos compartilham a mesma versão das dependências, evitando o "inferno das dependências" e garantindo consistência em todo o ecossistema.</p>
</li>
<li><p><strong>Colaboração Facilitada:</strong> Equipes diferentes têm visibilidade do código umas das outras, o que incentiva a colaboração e a padronização de boas práticas.</p>
</li>
</ul>
<h3 id="heading-pra-finalizar">Pra finalizar</h3>
<p>Olhando para trás, aquele commit "Golanged" foi mais do que apenas o início de um projeto. Foi o começo de uma nova forma de pensar sobre software: mais simples, mais direta e mais pragmática. A combinação da elegância do Go com a eficiência organizacional de um monorepo criou um ambiente de desenvolvimento produtivo e agradável.</p>
<p>Se você busca uma linguagem que valoriza a clareza, a performance e a produtividade do desenvolvedor, minha experiência de um ano grita: dê uma chance ao Go. Você pode acabar se surpreendendo.</p>
]]></content:encoded></item><item><title><![CDATA[A Engenharia de Prompt e a Variação entre LLMs]]></title><description><![CDATA[No universo do desenvolvimento de software, a engenharia de prompts está emergindo como uma disciplina crucial. A habilidade de extrair respostas precisas e úteis de Modelos de Linguagem Grandes (LLMs) tornou-se uma vantagem competitiva. No entanto, ...]]></description><link>https://leocavalcante.dev/a-engenharia-de-prompt-e-a-variacao-entre-llms</link><guid isPermaLink="true">https://leocavalcante.dev/a-engenharia-de-prompt-e-a-variacao-entre-llms</guid><category><![CDATA[AI]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[LLM's ]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Fri, 22 Aug 2025 13:37:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/n1ccr-zVG68/upload/7ba02ea295cf69bcb045013b55ee6edf.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>No universo do desenvolvimento de software, a engenharia de prompts está emergindo como uma disciplina crucial. A habilidade de extrair respostas precisas e úteis de Modelos de Linguagem Grandes (LLMs) tornou-se uma vantagem competitiva. No entanto, um aspecto fundamental dessa nova arte está sendo largamente ignorado: a engenharia de um bom prompt pode, e vai, mudar silenciosamente dependendo do modelo utilizado. Muitos tratam os LLMs como uma entidade monolítica, esperando que um "prompt mestre" funcione universalmente, mas a realidade é muito mais complexa e cheia de nuances.</p>
<p>A verdade é que não existem dois LLMs iguais. Assim como diferentes linguagens de programação têm suas próprias sintaxes e paradigmas, os LLMs possuem "personalidades" distintas, moldadas por suas arquiteturas, dados de treinamento e processos de ajuste fino. Um modelo treinado com um foco massivo em código-fonte de repositórios públicos se comportará de maneira diferente de um que foi otimizado para diálogo criativo. Essas diferenças não são apenas superficiais; elas influenciam a maneira como o modelo "raciocina", interpreta a ambiguidade e estrutura suas respostas. Para um desenvolvedor, isso significa que a forma de pedir a geração de um snippet de código em Python, a explicação de um algoritmo complexo ou a criação de documentação técnica precisa ser adaptada ao "dialeto" específico do LLM em questão.</p>
<p>Essa variação é o ponto cego da engenharia de contexto atual. Vemos uma proliferação de guias e "receitas de bolo" para prompts, mas raramente eles vêm com uma etiqueta especificando para qual modelo foram projetados. Um prompt cuidadosamente elaborado para gerar testes unitários com o modelo A pode produzir resultados medíocres ou verbosos com o modelo B. Essa falta de conscientização pode levar a ciclos de frustração, onde desenvolvedores acreditam que a ferramenta é inadequada, quando, na verdade, a comunicação que está desalinhada. A engenharia de prompt eficaz, portanto, não é sobre encontrar uma fórmula mágica, mas sobre desenvolver a sensibilidade para entender as tendências e particularidades do LLM com o qual se está interagindo.</p>
<p>Para nós, desenvolvedores, isso significa que a experimentação e a adaptação são essenciais. Ao integrar um LLM em um fluxo de trabalho, seja para automação de código, análise de logs ou suporte ao cliente, é preciso investir tempo para "aprender" o modelo. Testar variações de um mesmo prompt, observar como ele lida com a falta de contexto ou com instruções complexas e documentar o que funciona melhor é um passo que não pode ser pulado. A engenharia de prompt no desenvolvimento de software deve evoluir para uma prática mais parecida com a otimização de performance: um processo contínuo de medição, ajuste e refinamento, sempre considerando as características únicas da plataforma que está sendo utilizada. A próxima fronteira não é apenas criar bons prompts, mas criar os prompts certos para o cérebro certo.</p>
<h2 id="heading-nao-determinismo-e-as-personalidades-dos-modelos">Não-Determinismo e as "Personalidades" dos Modelos</h2>
<p><strong>Nem todos os LLMs são criados iguais.</strong> Mas essa afirmação apenas arranha a superfície. Para um desenvolvedor de software, entender o porquê dessa variação é o que transforma a frustração em produtividade. A questão vai muito além dos dados de treinamento; ela reside na arquitetura, na filosofia de fine-tuning e, crucialmente, em uma característica inerente a esses modelos: o não-determinismo controlado.</p>
<p>Imagine o seguinte cenário: você pede a dois LLMs diferentes para "criar uma função em Python que valide um CPF". O Modelo A retorna um código enxuto, idiomático, usando uma expressão regular e sem comentários, assumindo que você é um desenvolvedor experiente. O Modelo B, para o mesmo prompt exato, entrega uma função muito mais longa, com validação passo a passo, try-except blocks, type hints e comentários detalhados explicando a lógica por trás de cada dígito verificador. Nenhum dos dois está "errado", mas eles operam sob filosofias distintas. O Modelo A pode ter sido otimizado para concisão e uso em ambientes de programação competitiva, enquanto o Modelo B foi ajustado com um viés para segurança, clareza e fins educacionais, priorizando um código que seja fácil de entender e manter, mesmo que mais verboso. Essas "personalidades" são o resultado direto de como os modelos foram refinados após o treinamento inicial, um processo que ensina não apenas o que responder, mas como responder.</p>
<p>Agora, vamos adicionar uma camada ainda mais intrigante: o não-determinismo. Você executa o mesmo prompt, no mesmo Modelo A, duas vezes seguidas e obtém duas variações ligeiramente diferentes do código. Isso não é um bug; é uma das características mais poderosas e incompreendidas dos LLMs. A maioria dos modelos expõe um parâmetro chamado "temperatura". Pense na temperatura como um "botão de criatividade" ou um "gerenciador de risco". Com a temperatura em zero, o modelo se torna “determinístico” (ou o mais próximo disso): ele sempre escolherá a palavra ou token seguinte com a maior probabilidade estatística. O resultado é mais previsível e repetitivo, útil para tarefas que exigem consistência absoluta.</p>
<p>No entanto, quando aumentamos a temperatura, introduzimos uma aleatoriedade calculada. O modelo passa a poder escolher palavras que não são as mais prováveis, mas que ainda fazem sentido no contexto. É como um músico de blues improvisando sobre uma melodia. A estrutura básica está lá, mas as notas escolhidas a cada momento podem variar, criando uma peça única a cada execução. Para um desenvolvedor, isso é ouro. Ao pedir para um LLM "sugerir formas de refatorar este trecho de código", uma temperatura maior que zero pode gerar três ou quatro abordagens válidas e criativas, em vez de apenas a mais óbvia e estatisticamente comum. Pode sugerir o uso de um padrão de projeto que você não havia considerado ou uma função de biblioteca mais moderna.</p>
<p>Essa dança entre a "personalidade" fixa de um modelo e a fluidez do seu resultado não-determinístico é o cerne da engenharia de prompt avançada. Significa que nosso trabalho não é apenas enviar uma instrução e receber uma resposta. É entender que estamos iniciando um diálogo com uma ferramenta que tem seus próprios vieses e um elemento de criatividade controlada. O prompt perfeito, portanto, não é estático. Ele pode precisar de ajustes dependendo se você está falando com o "arquiteto verboso" ou com o "codificador minimalista". E, às vezes, a melhor solução vem de fazer a mesma pergunta algumas vezes, permitindo que a "sorte" do não-determinismo lhe mostre um caminho inesperado e inovador.</p>
]]></content:encoded></item><item><title><![CDATA[Go 1.25: As Principais Novidades da Nova Versão]]></title><description><![CDATA[O Go 1.25 foi oficialmente lançado em agosto de 2025, trazendo uma série de melhorias significativas para desenvolvedores. Esta nova versão mantém a tradição de compatibilidade do Go, garantindo que códigos existentes continuem funcionando perfeitame...]]></description><link>https://leocavalcante.dev/go-125</link><guid isPermaLink="true">https://leocavalcante.dev/go-125</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Wed, 13 Aug 2025 17:22:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4wfnB-ux-0s/upload/8fa652f7a46bf8586aa67d577a625598.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O Go 1.25 foi oficialmente lançado em agosto de 2025, trazendo uma série de melhorias significativas para desenvolvedores. Esta nova versão mantém a tradição de compatibilidade do Go, garantindo que códigos existentes continuem funcionando perfeitamente, enquanto introduz otimizações importantes e novos recursos.</p>
<h2 id="heading-principais-destaques">Principais destaques</h2>
<h3 id="heading-garbage-collector-aprimorado">Garbage Collector aprimorado</h3>
<p>Uma das mudanças mais significativas do Go 1.25 é a introdução de um garbage collector completamente reformulado. Esta melhoria promete:</p>
<ul>
<li><p><strong>Menor latência</strong>: redução significativa nas pausas de coleta de lixo.</p>
</li>
<li><p><strong>Melhor performance</strong>: otimizações que beneficiam aplicações com alta carga de trabalho.</p>
</li>
<li><p><strong>Uso mais eficiente de memória</strong>: algoritmos aprimorados para gerenciamento de heap.</p>
</li>
</ul>
<h3 id="heading-pgo-estavel">PGO estável</h3>
<p>O Profile-Guided Optimization (PGO), que estava em desenvolvimento nas versões anteriores, agora está oficialmente estável no Go 1.25. Esta funcionalidade permite:</p>
<ul>
<li><p><strong>Compilação mais inteligente</strong>: o compilador usa dados de profiling para otimizar o código.</p>
</li>
<li><p><strong>Performance superior</strong>: melhorias de 2-14% em performance para aplicações que utilizam PGO.</p>
</li>
<li><p><strong>Fácil implementação</strong>: processo simplificado para habilitar PGO em projetos existentes.</p>
</li>
</ul>
<h3 id="heading-melhorias-no-compilador-e-linker">Melhorias no compilador e linker</h3>
<p>O Go 1.25 traz avanços significativos nas ferramentas de build:</p>
<h4 id="heading-dwarf-5-para-informacoes-de-debug">DWARF 5 para informações de debug</h4>
<ul>
<li><p><strong>Redução no tamanho</strong>: menos espaço ocupado pelas informações de debugging.</p>
</li>
<li><p><strong>Linking mais rápido</strong>: tempo de compilação reduzido.</p>
</li>
<li><p><strong>Melhor compatibilidade</strong>: suporte aprimorado para ferramentas de debug modernas.</p>
</li>
</ul>
<h4 id="heading-performance-de-compilacao">Performance de compilação</h4>
<ul>
<li><p><strong>Compilação mais rápida</strong>: otimizações no compilador resultam em builds mais ágeis.</p>
</li>
<li><p><strong>Uso otimizado de recursos</strong>: melhor aproveitamento de CPU e memória durante a compilação.</p>
</li>
</ul>
<h3 id="heading-novos-pacotes-na-biblioteca-padrao">Novos pacotes na biblioteca padrão</h3>
<h4 id="heading-testingsynctest">testing/synctest</h4>
<p>Um dos grandes destaques é a adição do pacote <code>testing/synctest</code>, especificamente projetado para:</p>
<ul>
<li><p><strong>Testes de código concorrente</strong>: ferramentas especializadas para testar goroutines e sincronização.</p>
</li>
<li><p><strong>Detecção de race conditions</strong>: identificação mais eficaz de problemas de concorrência.</p>
</li>
<li><p><strong>Simulação de cenários</strong>: capacidade de simular diferentes condições de timing.</p>
</li>
</ul>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"testing"</span>
    <span class="hljs-string">"testing/synctest"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestConcurrentCode</span><span class="hljs-params">(t *testing.T)</span></span> {
    synctest.Run(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Seu código de teste concorrente aqui</span>
        <span class="hljs-comment">// O synctest fornece um ambiente controlado</span>
        <span class="hljs-comment">// para testar comportamentos síncronos</span>
    })
}
</code></pre>
<h2 id="heading-compatibilidade-e-migracao">Compatibilidade e migração</h2>
<h3 id="heading-sem-mudancas-na-linguagem">Sem mudanças na linguagem</h3>
<p>Uma excelente notícia para desenvolvedores é que o Go 1.25 <strong>não introduz mudanças que quebrem a compatibilidade</strong>. Isso significa:</p>
<ul>
<li><p><strong>Migração transparente</strong>: códigos existentes funcionam sem modificações.</p>
</li>
<li><p><strong>Atualização segura</strong>: você pode atualizar com confiança.</p>
</li>
<li><p><strong>Foco em estabilidade</strong>: mantém a filosofia de estabilidade do Go.</p>
</li>
</ul>
<h3 id="heading-melhorias-na-especificacao">Melhorias na especificação</h3>
<p>Embora não haja mudanças funcionais, a especificação da linguagem foi refinada:</p>
<ul>
<li><p><strong>Remoção do conceito de "core types"</strong>: simplificação da documentação técnica.</p>
</li>
<li><p><strong>Prosa mais clara</strong>: descrições mais diretas e compreensíveis.</p>
</li>
</ul>
<h2 id="heading-impacto-para-a-comunidade">Impacto para a comunidade</h2>
<p>O Go 1.25 reforça o compromisso da linguagem com:</p>
<ul>
<li><p><strong>Performance</strong>: melhorias contínuas sem sacrificar a simplicidade.</p>
</li>
<li><p><strong>Confiabilidade</strong>: ferramentas melhores para garantir a qualidade do código.</p>
</li>
<li><p><strong>Produtividade</strong>: builds mais rápidos e debugging mais eficiente.</p>
</li>
</ul>
<h2 id="heading-proximos-passos">Próximos passos</h2>
<p>A equipe do Go promete posts detalhados nas próximas semanas, cobrindo aspectos específicos das novidades do Go 1.25. Fique atento ao <a target="_blank" href="https://blog.golang.org">blog oficial</a> para mergulhar mais fundo em cada funcionalidade.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>O Go 1.25 representa um passo sólido na evolução da linguagem, focando em performance, ferramentas de desenvolvimento e qualidade de código. Com melhorias no garbage collector, PGO estável e novos pacotes para testes, esta versão oferece valor tangível para desenvolvedores de todos os níveis.</p>
<p>A ausência de breaking changes torna a atualização uma decisão fácil - você ganha todas as melhorias mantendo a estabilidade do seu código existente. É definitivamente uma atualização recomendada para todos os projetos Go.</p>
]]></content:encoded></item><item><title><![CDATA[Swiss Tables: O Segredo por Trás dos Maps Mais Rápidos do Go 1.24]]></title><description><![CDATA[Os mapas (maps) no Go sempre foram uma das estruturas de dados mais utilizadas e otimizadas da linguagem. Com o lançamento do Go 1.24, eles ficaram ainda mais rápidos, graças à implementação do conceito de Swiss Tables.
Onde vivem e o que comem as Sw...]]></description><link>https://leocavalcante.dev/swiss-tables-go-124</link><guid isPermaLink="true">https://leocavalcante.dev/swiss-tables-go-124</guid><category><![CDATA[swiss tables]]></category><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Wed, 05 Mar 2025 15:16:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/V5XWBdjVWKA/upload/364c347959d743f1cae7cfa5a92daba9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Os mapas (maps) no Go sempre foram uma das estruturas de dados mais utilizadas e otimizadas da linguagem. Com o lançamento do Go 1.24, eles ficaram ainda mais rápidos, graças à implementação do conceito de Swiss Tables.</p>
<h2 id="heading-onde-vivem-e-o-que-comem-as-swiss-tables">Onde vivem e o que comem as Swiss Tables?</h2>
<p>Swiss Tables são uma técnica avançada de hash table desenvolvida pelo Google para otimizar buscas e inserções em estruturas de dados baseadas em tabelas hash. Esse modelo foi originalmente implementado na biblioteca C++ Abseil e, desde então, inspirou melhorias em diversas linguagens e frameworks.</p>
<p>A principal inovação das Swiss Tables está na combinação de técnicas como:</p>
<ul>
<li><p><strong>Open Addressing</strong> (<em>endereçamento aberto</em>): os elementos são armazenados diretamente na tabela, sem ponteiros adicionais.</p>
</li>
<li><p><strong>SIMD Acceleration</strong> (<em>uso de instruções vetoriais</em>): otimiza a busca simultânea em vários slots de uma vez.</p>
</li>
<li><p><strong>Control Word</strong> (<em>uso de metadados para indexação eficiente</em>): melhora a busca e minimiza colisões.</p>
</li>
</ul>
<p>Exemplo:</p>
<pre><code class="lang-plaintext">Control Word: | 89 | 56 | 32 | 21 | - | - | - | - |
Slots:        | X  | X  | X  | X  |   |   |   |   |
</code></pre>
<p>O <em>Control Word</em> armazena os metadados sobre quais slots estão ocupados e quais podem ser utilizados, tornando as operações mais rápidas.</p>
<h2 id="heading-como-funciona-a-estrutura-das-swiss-tables">Como funciona a estrutura das Swiss Tables?</h2>
<p>A estrutura das Swiss Tables difere de uma hash table tradicional ao introduzir um <strong>controle de metadados</strong> que facilita buscas e minimiza colisões. Vamos detalhar seus principais componentes:</p>
<h3 id="heading-1-divisao-em-grupos">1. Divisão em grupos</h3>
<p>As chaves são armazenadas em um array dividido em <strong>grupos de slots</strong> (tipicamente 8 ou 16 slots por grupo). Cada grupo possui um bloco de controle que informa quais slots estão ocupados e quais podem ser usados.</p>
<h3 id="heading-2-control-word-e-sua-relacao-com-a-ram-e-cpu">2. Control Word e sua relação com a RAM e CPU</h3>
<p>Cada grupo de slots tem um <strong>Control Word</strong> de 64 bits, onde cada byte corresponde a um slot e contém informações sobre se o slot está vazio, ocupado ou foi deletado. Essa abordagem otimiza a utilização da memória RAM, pois permite um acesso mais eficiente aos dados, reduzindo <em>cache misses</em> e melhorando a localidade espacial dos dados armazenados.</p>
<p>A implementação do Control Word permite que os processadores modernos utilizem <strong>instruções vetorizadas SIMD</strong> para comparar múltiplos valores simultaneamente. Como resultado, a CPU pode verificar rapidamente quais slots estão disponíveis sem precisar iterar sequencialmente sobre todos os elementos. Esse modelo reduz significativamente a latência de busca e inserção, uma vantagem crucial para aplicações de alta performance.</p>
<h3 id="heading-3-comparacao-com-simd">3. Comparação com SIMD</h3>
<p>As buscas se tornam mais rápidas com <strong>instruções SIMD</strong> (<em>Single Instruction, Multiple Data</em>), que permitem comparar múltiplos valores simultaneamente ao operar sobre vários elementos de um vetor de dados em uma única instrução.</p>
<p>O processo funciona da seguinte forma:</p>
<ol>
<li><p><strong>Carregamento do Control Word</strong>: A CPU carrega um bloco de 64 bits contendo os metadados dos slots.</p>
</li>
<li><p><strong>Criação de um vetor de comparação</strong>: Um vetor contendo o valor de hash buscado é replicado para que todos os bytes possam ser comparados simultaneamente.</p>
</li>
<li><p><strong>Aplicação da operação SIMD</strong>: Uma única instrução realiza a comparação de todos os bytes do <em>Control Word</em> com o valor procurado.</p>
</li>
<li><p><strong>Extração dos resultados</strong>: Um mapa de bits indica quais posições do vetor possuem correspondência com o hash buscado.</p>
</li>
</ol>
<p>Exemplo:</p>
<pre><code class="lang-plaintext">Control Word:   | 89 | 56 | 32 | 21 | -- | -- | -- | -- |
Comparação SIMD | == | == | == | == | == | == | == | == |
                | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 32 |
Resultado       |  0 |  0 |  1 |  0 |  0 |  0 |  0 |  0 |
</code></pre>
<p>Neste exemplo, a posição 3 contém o valor desejado, permitindo que a busca finalize rapidamente sem necessidade de iteração tradicional.</p>
<p>Essa técnica aproveita a capacidade das CPUs modernas de processar dados em paralelo, tornando buscas e inserções significativamente mais rápidas em comparação a tabelas hash tradicionais.</p>
<h3 id="heading-4-carga-maxima-ajustada">4. Carga máxima ajustada</h3>
<p>Como as buscas são mais eficientes, a Swiss Table pode operar com <strong>uma carga maior antes da necessidade de redimensionamento</strong>, o que melhora a eficiência de memória e reduz realocações.</p>
<h2 id="heading-a-implementacao-das-swiss-tables-no-go-124">A implementação das Swiss Tables no Go 1.24</h2>
<p>A introdução das Swiss Tables no Go 1.24 trouxe uma reestruturação completa da implementação dos maps. O novo modelo mantém a semântica dos mapas do Go, mas agora cada mapa é composto por <strong>múltiplas tabelas independentes</strong>, permitindo um crescimento mais eficiente.</p>
<h3 id="heading-melhorias-na-implementacao-do-go">Melhorias na implementação do Go:</h3>
<ul>
<li><p><strong>Busca mais rápida:</strong> Graças ao <em>Control Word</em>, a busca por um item se tornou até <strong>60% mais eficiente</strong> em microbenchmarks.</p>
</li>
<li><p><strong>Menos realocações:</strong> O uso de grupos de slots reduz a necessidade de redimensionamento.</p>
</li>
<li><p><strong>Melhor iteração:</strong> Diferente de outras implementações, o Go permite modificar um mapa enquanto se itera sobre ele.</p>
</li>
</ul>
<p>Exemplo prático:</p>
<pre><code class="lang-go">m := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">int</span>]<span class="hljs-keyword">string</span>)
m[<span class="hljs-number">1</span>] = <span class="hljs-string">"Gopher"</span>
m[<span class="hljs-number">2</span>] = <span class="hljs-string">"Swiss Tables"</span>
<span class="hljs-built_in">delete</span>(m, <span class="hljs-number">1</span>)
fmt.Println(m[<span class="hljs-number">1</span>]) <span class="hljs-comment">// Saída: ""</span>
</code></pre>
<h2 id="heading-por-que-isso-torna-o-map-em-go-mais-rapido">Por que isso torna o <code>map</code> em Go mais rápido?</h2>
<p>O impacto da adoção das Swiss Tables no Go 1.24 pode ser resumido em três grandes vantagens:</p>
<ol>
<li><p><strong>Maior eficiência nas buscas:</strong> Graças ao <em>Control Word</em>, várias comparações são feitas simultaneamente.</p>
</li>
<li><p><strong>Menos colisões e realocações:</strong> A estrutura melhora a distribuição de chaves.</p>
</li>
<li><p><strong>Uso otimizado de CPU:</strong> O design das Swiss Tables aproveita ao máximo os processadores modernos, minimizando acessos desnecessários à memória RAM e reduzindo o tempo de execução das operações.</p>
</li>
</ol>
<h2 id="heading-ou-seja">Ou seja…</h2>
<p>A implementação das Swiss Tables no Go 1.24 é um avanço significativo para a performance da linguagem. Com essa nova abordagem, os mapas se tornaram ainda mais eficientes, garantindo ganhos de velocidade para diversas aplicações.</p>
<p>Se você ainda não testou essa nova versão, vale a pena explorar e otimizar suas aplicações para aproveitar esses ganhos de performance!</p>
<hr />
<p>Referência: <a target="_blank" href="https://go.dev/blog/swisstable">https://go.dev/blog/swisstable</a></p>
]]></content:encoded></item><item><title><![CDATA[Weak Pointers no Go 1.24: Entendendo e Aplicando na Prática]]></title><description><![CDATA[O Go 1.24 introduziu um novo pacote chamado weak, trazendo suporte para weak pointers (ponteiros fracos). Esses ponteiros permitem referenciar memória sem impedir sua coleta pelo garbage collector (GC), tornando-se particularmente úteis para otimizaç...]]></description><link>https://leocavalcante.dev/weak-pointers-no-go-124-entendendo-e-aplicando-na-pratica</link><guid isPermaLink="true">https://leocavalcante.dev/weak-pointers-no-go-124-entendendo-e-aplicando-na-pratica</guid><category><![CDATA[weak pointer]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[pointers in Go]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 24 Feb 2025 14:30:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FlPc9_VocJ4/upload/7fb579239a3384f892d5a9fe9814373b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O <strong>Go 1.24</strong> introduziu um novo pacote chamado <code>weak</code>, trazendo suporte para <strong>weak pointers</strong> (ponteiros fracos). Esses ponteiros permitem referenciar memória sem impedir sua coleta pelo <strong>garbage collector (GC)</strong>, tornando-se particularmente úteis para otimização de caches e redução de <strong>memory leaks</strong>.</p>
<p>Se você é um desenvolvedor experiente e quer entender como os <strong>weak pointers</strong> funcionam e quando utilizá-los, este artigo é para você.</p>
<hr />
<h2 id="heading-o-que-sao-weak-pointers"><strong>O que são Weak Pointers?</strong></h2>
<p>Um <strong>weak pointer</strong> é um tipo de referência para um objeto na memória que <strong>não impede que o GC o remova</strong> quando não há mais referências fortes a ele. Se o objeto for coletado, o ponteiro fraco se torna <strong>nil</strong> automaticamente.</p>
<h3 id="heading-as-principais-caracteristicas-sao"><strong>As principais características são:</strong></h3>
<ul>
<li><strong>Evita vazamentos de memória</strong> ao permitir que objetos sejam coletados pelo GC mesmo se ainda existirem referências fracas.</li>
<li><strong>Elimina riscos de ponteiros pendentes</strong> (dangling pointers), pois quando um objeto é coletado, a referência fraca é automaticamente anulada.</li>
<li><strong>Diferenciação entre referência forte e fraca:</strong><ul>
<li><strong>Referências fortes:</strong> Impedem a coleta do objeto.</li>
<li><strong>Referências fracas:</strong> Permitem que o objeto seja coletado se não houver referências fortes.</li>
</ul>
</li>
</ul>
<h2 id="heading-quando-usar-weak-pointers"><strong>Quando usar Weak Pointers?</strong></h2>
<p>Os weak pointers não são necessários para a maioria dos casos no Go, pois o garbage collector gerencia a memória eficientemente. Mas... existem cenários onde eles são extremamente úteis:</p>
<ol>
<li><strong>Otimização de caches:</strong> evita que objetos permaneçam na memória quando não são mais usados.</li>
<li><strong>Gerenciamento de recursos compartilhados:</strong> reduz o consumo de memória sem exigir controle manual.</li>
<li><strong>Prevenção de vazamentos de memória:</strong> permite que objetos não referenciados sejam removidos automaticamente.</li>
</ol>
<p>Agora, vamos aplicar esse conceito na prática.</p>
<hr />
<h2 id="heading-implementando-um-cache-com-e-sem-weak-pointers"><strong>Implementando um Cache com e sem Weak Pointers</strong></h2>
<p>Vamos implementar um cache simples para entender como o uso de weak pointers impacta o gerenciamento de memória.</p>
<h3 id="heading-cache-sem-weak-pointers-com-vazamento-de-memoria"><strong>Cache sem Weak Pointers (com vazamento de memória)</strong></h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"runtime"</span>
    <span class="hljs-string">"strings"</span>
)

<span class="hljs-keyword">type</span> Data <span class="hljs-keyword">struct</span> {
    Value <span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> Cache <span class="hljs-keyword">struct</span> {
    Items <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Data
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewCache</span><span class="hljs-params">()</span> *<span class="hljs-title">Cache</span></span> {
    <span class="hljs-keyword">return</span> &amp;Cache{
        Items: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*Data),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Cache)</span> <span class="hljs-title">Set</span><span class="hljs-params">(k <span class="hljs-keyword">string</span>, d *Data)</span></span> {
    c.Items[k] = d
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Cache)</span> <span class="hljs-title">Get</span><span class="hljs-params">(k <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">Data</span></span> {
    <span class="hljs-keyword">return</span> c.Items[k]
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    d := &amp;Data{
        Value: strings.Repeat(<span class="hljs-string">"Go"</span>, <span class="hljs-number">1</span>_000_000),
    }

    cache := NewCache()
    cache.Set(<span class="hljs-string">"data"</span>, d)

    memUsage()

    d = <span class="hljs-literal">nil</span>
    runtime.GC()

    useData(cache.Get(<span class="hljs-string">"data"</span>))

    memUsage()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">useData</span><span class="hljs-params">(d *Data)</span></span> {
    <span class="hljs-comment">// Ponto que irá utilizar o dado</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">memUsage</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> m runtime.MemStats
    runtime.ReadMemStats(&amp;m)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"Alloc = "</span>, m.Alloc)
}
</code></pre>
<h3 id="heading-saida-esperada"><strong>Saída esperada:</strong></h3>
<p>Mesmo após chamar <code>runtime.GC()</code>, a memória alocada continua a mesma, pois a referência ainda existe no cache.</p>
<pre><code class="lang-text">Alloc =  2197336
Alloc =  2209696
</code></pre>
<hr />
<h2 id="heading-cache-com-weak-pointers-sem-vazamento-de-memoria"><strong>Cache com Weak Pointers (sem vazamento de memória)</strong></h2>
<p>Agora, vamos modificar nosso cache para usar weak pointers.</p>
<pre><code class="lang-shell">git diff memoryleak..weakpointer
</code></pre>
<pre><code class="lang-diff">import (
        "runtime"
        "strings"
<span class="hljs-addition">+       "weak"</span>
)

type Cache struct {
<span class="hljs-deletion">-       Items map[string]*Data</span>
<span class="hljs-addition">+       Items map[string]weak.Pointer[Data]</span>
}

func NewCache() *Cache {
        return &amp;Cache{
<span class="hljs-deletion">-               Items: make(map[string]*Data),</span>
<span class="hljs-addition">+               Items: make(map[string]weak.Pointer[Data]),</span>
        }
}

func (c *Cache) Set(k string, d *Data) {
<span class="hljs-deletion">-       c.Items[k] = d</span>
<span class="hljs-addition">+       c.Items[k] = weak.Make(d)</span>
}

func (c *Cache) Get(k string) *Data {
<span class="hljs-deletion">-       return c.Items[k]</span>
<span class="hljs-addition">+       return c.Items[k].Value()</span>
}
</code></pre>
<p>Resultado final</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"runtime"</span>
    <span class="hljs-string">"strings"</span>
    <span class="hljs-string">"weak"</span>
)

<span class="hljs-keyword">type</span> Data <span class="hljs-keyword">struct</span> {
    Value <span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> Cache <span class="hljs-keyword">struct</span> {
    Items <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]weak.Pointer[Data]
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewCache</span><span class="hljs-params">()</span> *<span class="hljs-title">Cache</span></span> {
    <span class="hljs-keyword">return</span> &amp;Cache{
        Items: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]weak.Pointer[Data]),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Cache)</span> <span class="hljs-title">Set</span><span class="hljs-params">(k <span class="hljs-keyword">string</span>, d *Data)</span></span> {
    c.Items[k] = weak.Make(d)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Cache)</span> <span class="hljs-title">Get</span><span class="hljs-params">(k <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">Data</span></span> {
    <span class="hljs-keyword">return</span> c.Items[k].Value()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    d := &amp;Data{
        Value: strings.Repeat(<span class="hljs-string">"Go"</span>, <span class="hljs-number">1</span>_000_000),
    }

    cache := NewCache()
    cache.Set(<span class="hljs-string">"data"</span>, d)

    memUsage()

    d = <span class="hljs-literal">nil</span>
    runtime.GC()

    useData(cache.Get(<span class="hljs-string">"data"</span>))

    memUsage()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">useData</span><span class="hljs-params">(d *Data)</span></span> {
    <span class="hljs-comment">// Ponto que irá utilizar o dado</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">memUsage</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> m runtime.MemStats
    runtime.ReadMemStats(&amp;m)
    <span class="hljs-built_in">println</span>(<span class="hljs-string">"Alloc = "</span>, m.Alloc)
}
</code></pre>
<h3 id="heading-saida-esperada-1"><strong>Saída esperada:</strong></h3>
<p>Agora, após a coleta do GC, a memória é reduzida, pois o <code>data</code> foi corretamente removido do cache.</p>
<pre><code class="lang-text">Alloc =  2197336
Alloc =  202544
</code></pre>
<hr />
<h2 id="heading-resumindo"><strong>Resumindo</strong></h2>
<p>O <strong>pacote weak no Go 1.24</strong> permite criar <strong>weak pointers</strong>, ajudando a reduzir vazamentos de memória sem precisar gerenciar manualmente os objetos referenciados. No exemplo acima, vimos como isso impacta caches, um dos principais casos de uso dessa funcionalidade.</p>
<h3 id="heading-boas-praticas-ao-usar-weak-pointers"><strong>Boas práticas ao usar Weak Pointers:</strong></h3>
<ul>
<li><strong>Sempre verifique se o valor é nil</strong> antes de acessá-lo.</li>
<li><strong>Evite overuse</strong>: use weak pointers apenas quando necessário para evitar complexidade desnecessária.</li>
<li><strong>Remova chaves de cache que apontam para valores coletados pelo GC</strong> para evitar referências mortas.</li>
</ul>
<p>Com essas práticas, você pode melhorar a eficiência da memória em aplicações Go!</p>
<hr />
<p>Se você quer se aprofundar ainda mais, explore a documentação oficial do Go 1.24 e teste os exemplos no seu próprio código!
https://pkg.go.dev/weak</p>
]]></content:encoded></item><item><title><![CDATA[Novidades do Go 1.24]]></title><description><![CDATA[A versão 1.24 da linguagem Go foi lançada em fevereiro de 2025, trazendo uma série de melhorias e novas funcionalidades que prometem aumentar a produtividade dos desenvolvedores e otimizar o desempenho das aplicações. Neste artigo, vamos destacar alg...]]></description><link>https://leocavalcante.dev/novidades-do-go-124</link><guid isPermaLink="true">https://leocavalcante.dev/novidades-do-go-124</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Wed, 12 Feb 2025 18:57:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739385825858/165da7a9-9c83-4a4c-8d2a-dfd97ca42f22.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A versão 1.24 da linguagem Go foi lançada em fevereiro de 2025, trazendo uma série de melhorias e novas funcionalidades que prometem aumentar a produtividade dos desenvolvedores e otimizar o desempenho das aplicações. Neste artigo, vamos destacar algumas das principais novidades, como a nova diretiva <code>tool</code>, o suporte a generics em type aliases, a função <code>Loop</code> para testes de benchmark, melhorias de performance, suporte aprimorado ao WebAssembly e a nova função <code>AddCleanup</code>.</p>
<h2 id="heading-1-nova-diretiva-tool-para-gerenciamento-de-dependencias-de-ferramentas">1. Nova Diretiva <code>tool</code> para Gerenciamento de Dependências de Ferramentas</h2>
<p>Uma das novidades mais interessantes do Go 1.24 é a introdução da diretiva <code>tool</code> no <code>go.mod</code>, que permite gerenciar dependências de ferramentas de forma mais eficiente. Anteriormente, era necessário adicionar ferramentas como imports em branco em um arquivo <code>tools.go</code>, o que podia ser um tanto incômodo. Agora, com a nova diretiva <code>tool</code>, você pode adicionar ferramentas diretamente no <code>go.mod</code> usando o comando <code>go get -tool</code>.</p>
<p>Por exemplo, para adicionar uma ferramenta chamada <code>golangci-lint</code>, você pode executar:</p>
<pre><code class="lang-bash">go get -tool github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.4
</code></pre>
<p>Isso adicionará uma diretiva <code>tool</code> ao seu <code>go.mod</code>, permitindo que você execute a ferramenta com o comando <code>go tool golangci-lint</code>. Além disso, todas as ferramentas declaradas no módulo podem ser atualizadas de uma só vez com <code>go get tool</code>.</p>
<p>Essa mudança simplifica o gerenciamento de ferramentas e torna o processo mais integrado ao fluxo de trabalho do Go.</p>
<h2 id="heading-2-suporte-a-generics-em-type-aliases">2. Suporte a Generics em Type Aliases</h2>
<p>O Go 1.24 traz uma melhoria significativa no suporte a generics, permitindo que <strong>type aliases</strong> sejam parametrizados, assim como os tipos definidos. Isso significa que você pode agora criar aliases para tipos genéricos, o que aumenta a flexibilidade e a reutilização de código.</p>
<p>Por exemplo, você pode definir um alias para um tipo genérico da seguinte forma:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> MyList[T any] = []T
</code></pre>
<p>Isso permite que você use <code>MyList</code> como um alias para uma lista genérica, simplificando a sintaxe e melhorando a legibilidade do código.</p>
<h2 id="heading-3-nova-funcao-loop-em-testes-de-benchmark">3. Nova Função <code>Loop</code> em Testes de Benchmark</h2>
<p>Outra adição importante no Go 1.24 é a nova função <code>Loop</code> para testes de benchmark. Anteriormente, os benchmarks eram escritos usando um loop <code>for</code> com <code>b.N</code>, o que podia ser propenso a erros e menos eficiente. Agora, você pode usar a função <code>Loop</code> para simplificar a execução de iterações de benchmark:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> b.Loop() {
    <span class="hljs-comment">// Código do benchmark</span>
}
</code></pre>
<p>Essa abordagem é mais segura e eficiente, pois garante que o código de benchmark seja executado exatamente uma vez por iteração, evitando problemas comuns relacionados ao uso de <code>b.N</code>. Além disso, a função <code>Loop</code> mantém os parâmetros e resultados da função vivos, o que impede que o compilador otimize completamente o corpo do loop.</p>
<h2 id="heading-4-melhorias-de-performance">4. Melhorias de Performance</h2>
<p>O Go 1.24 traz várias melhorias de performance, especialmente no runtime. Entre as principais mudanças estão:</p>
<ul>
<li><p><strong>Nova implementação de mapas baseada em Swiss Tables</strong>: Essa nova implementação reduz o overhead de CPU em 2-3% em benchmarks representativos.</p>
</li>
<li><p><strong>Alocação de memória mais eficiente para pequenos objetos</strong>: Isso resulta em uma redução no consumo de memória e em uma execução mais rápida para aplicações que lidam com muitos objetos pequenos.</p>
</li>
<li><p><strong>Nova implementação de mutex interna</strong>: O novo mutex é mais eficiente e menos propenso a erros, melhorando a concorrência em aplicações que dependem de sincronização.</p>
</li>
</ul>
<p>Essas melhorias tornam o Go 1.24 uma escolha ainda mais atraente para aplicações de alto desempenho.</p>
<h2 id="heading-5-suporte-aprimorado-ao-webassembly">5. Suporte Aprimorado ao WebAssembly</h2>
<p>O suporte ao WebAssembly (Wasm) foi significativamente aprimorado no Go 1.24. Agora, você pode exportar funções Go para o host WebAssembly usando a nova diretiva <code>go:wasmexport</code>. Além disso, o Go 1.24 suporta a construção de programas Go como bibliotecas ou reatores no WebAssembly System Interface (WASI) usando o flag <code>-buildmode=c-shared</code>.</p>
<p>Essa melhoria facilita a integração de código Go em ambientes WebAssembly, permitindo que desenvolvedores criem aplicações web mais eficientes e interoperáveis.</p>
<h2 id="heading-6-nova-funcao-addcleanup-para-finalizacao-de-objetos">6. Nova Função <code>AddCleanup</code> para Finalização de Objetos</h2>
<p>A função <code>runtime.AddCleanup</code> foi introduzida no Go 1.24 como uma alternativa mais flexível e eficiente ao <code>runtime.SetFinalizer</code>. Com <code>AddCleanup</code>, você pode anexar funções de limpeza a objetos que serão executadas quando o objeto não for mais alcançável.</p>
<p>A principal vantagem de <code>AddCleanup</code> é que ela permite múltiplas funções de limpeza para um único objeto, e essas funções podem ser anexadas a ponteiros internos. Além disso, <code>AddCleanup</code> não causa vazamentos de memória quando objetos formam ciclos, e não atrasa a liberação de memória.</p>
<p>Exemplo de uso:</p>
<pre><code class="lang-go">obj := &amp;MyObject{}
runtime.AddCleanup(obj, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"Cleanup executed"</span>)
})
</code></pre>
<p>Essa nova função é uma adição valiosa para gerenciamento de recursos e limpeza de memória em aplicações Go.</p>
<h2 id="heading-em-resumo">Em resumo</h2>
<p>O Go 1.24 traz uma série de melhorias que reforçam a linguagem como uma das melhores escolhas para desenvolvimento de software moderno. Desde o gerenciamento simplificado de ferramentas com a diretiva <code>tool</code>, passando pelo suporte a generics em type aliases, até as melhorias de performance e suporte ao WebAssembly, essa versão oferece ferramentas poderosas para desenvolvedores.</p>
<p>A nova função <code>Loop</code> para benchmarks e a função <code>AddCleanup</code> para finalização de objetos são exemplos de como o Go continua evoluindo para atender às necessidades dos desenvolvedores, tornando a linguagem mais segura, eficiente e fácil de usar.</p>
<p>Se você ainda não experimentou o Go 1.24, agora é a hora de atualizar seu ambiente e explorar essas novas funcionalidades. A comunidade Go está mais ativa do que nunca, e com essas melhorias, a linguagem está pronta para enfrentar os desafios do desenvolvimento de software moderno.</p>
<p><a target="_blank" href="https://go.dev/blog/go1.24">https://go.dev/blog/go1.24</a><br /><a target="_blank" href="https://go.dev/doc/go1.24">https://go.dev/doc/go1.24</a></p>
]]></content:encoded></item><item><title><![CDATA[Why Go Should Sometimes Be a No-Go?]]></title><description><![CDATA[A linguagem Go foi alvo de críticas por sua simplicidade e filosofia de design, em um artigo recente chamado "Why Go Should Sometimes Be a No-Go", onde o autor levanta alguns pontos negativos sobre a linguagem, argumentando que sua abordagem minimali...]]></description><link>https://leocavalcante.dev/why-go-should-sometimes-be-a-no-go</link><guid isPermaLink="true">https://leocavalcante.dev/why-go-should-sometimes-be-a-no-go</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 03 Feb 2025 12:00:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xit3LjRvKvM/upload/1051578ed56f7933af35b289ce3360b6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A linguagem Go foi alvo de críticas por sua simplicidade e filosofia de design, em um artigo recente chamado <em>"Why Go Should Sometimes Be a No-Go"</em>, onde o autor levanta alguns pontos negativos sobre a linguagem, argumentando que sua abordagem minimalista pode ser uma limitação em certos contextos. No entanto, esses argumentos podem ser contrapostos com uma visão mais ampla sobre os benefícios e propósitos do Go.</p>
<h2 id="heading-1-go-e-entediante-eficiencia-acima-de-tudo">1. Go é entediante? Eficiência acima de tudo</h2>
<p>O artigo argumenta que Go é "entediante" por não oferecer construções como <code>map</code>, <code>filter</code> e <code>reduce</code>, exigindo que os desenvolvedores implementem essas operações manualmente com loops. No entanto, essa escolha de design não é uma falha, mas sim uma decisão consciente para manter a legibilidade e previsibilidade do código.</p>
<p>Ao evitar abstrações excessivas, Go facilita a manutenção e a depuração de código. Em muitas linguagens, o uso intensivo de construções funcionais pode gerar dificuldades para novos desenvolvedores compreenderem rapidamente um código legado. Além disso, ao forçar uma abordagem mais explícita, Go reduz a complexidade cognitiva e minimiza erros inesperados causados por comportamentos implícitos em funções complexas.</p>
<p>Outro ponto relevante é que, em sistemas de alto desempenho e aplicações distribuídas, loops explícitos frequentemente superam abordagens baseadas em funções de ordem superior em termos de eficiência, uma vez que evitam a alocação desnecessária de memória e overheads relacionados à abstração excessiva.</p>
<h2 id="heading-2-go-desencoraja-principios-de-clean-code-na-verdade-ele-os-reforca">2. Go desencoraja princípios de Clean Code? Na verdade, ele os reforça</h2>
<p>A simplicidade do Go incentiva o uso de práticas claras e diretas de desenvolvimento. Em vez de permitir múltiplas formas de resolver o mesmo problema, Go impõe uma padronização que reduz complexidade e facilita a colaboração em equipe. O foco no código explícito evita o uso excessivo de abstrações desnecessárias, tornando o código mais compreensível para todos os membros de um time.</p>
<p>Ao adotar convenções rígidas, como a formatação automática pelo <code>gofmt</code>, Go remove discussões sobre estilo e formatação, permitindo que os desenvolvedores foquem no que realmente importa: a lógica do programa. Além disso, a ausência de exceções (substituídas pelo tratamento explícito de erros) obriga os desenvolvedores a lidar de forma estruturada com falhas, tornando os sistemas mais robustos e previsíveis.</p>
<h2 id="heading-3-go-e-pequeno-e-forca-a-filosofia-do-it-yourself-modularidade-e-um-beneficio">3. Go é pequeno e força a filosofia Do It Yourself? Modularidade é um benefício</h2>
<p>O artigo menciona que bibliotecas externas são amplamente utilizadas para suprir funcionalidades que a linguagem não oferece nativamente. Esse argumento sugere que Go é "incompleto", mas a realidade é que essa modularidade é uma vantagem.</p>
<p>A filosofia do Go segue o princípio UNIX: "faça uma coisa e faça bem feito". Em vez de inflar a linguagem com funcionalidades que nem todos os projetos precisam, Go permite que desenvolvedores escolham ferramentas externas conforme a necessidade. Isso dá liberdade para adaptar a linguagem a diferentes contextos sem sobrecarregar seu núcleo.</p>
<p>Ao invés de obrigar todos os desenvolvedores a carregar uma grande quantidade de recursos embutidos na linguagem, Go permite um ecossistema enxuto e eficiente, onde pacotes externos podem ser usados conforme a necessidade específica do projeto. Esse modelo mantém a base da linguagem leve e otimizada, permitindo um melhor desempenho e menor tempo de compilação.</p>
<h2 id="heading-4-em-go-ha-apenas-uma-forma-de-fazer-as-coisas-e-isso-e-ruim">4. Em Go, há apenas uma forma de fazer as coisas? E isso é ruim?</h2>
<p>A existência de um único modo de resolver problemas comuns evita debates desnecessários e simplifica a leitura e manutenção do código. Isso não significa falta de flexibilidade, mas sim um design pragmático que prioriza eficiência. Muitas linguagens que oferecem múltiplas formas de realizar uma mesma tarefa acabam gerando código inconsistente e difícil de padronizar dentro de uma equipe.</p>
<p>Além disso, a previsibilidade do código em Go reduz a curva de aprendizado para novos desenvolvedores, permitindo que equipes ampliem sua força de trabalho de forma mais eficiente. O código escrito por um engenheiro pode ser rapidamente compreendido e mantido por outro, sem a necessidade de navegar por múltiplas abordagens de implementação.</p>
<p>Outro benefício é que, ao restringir a linguagem a um conjunto essencial de construções, Go facilita a otimização e análise estática do código, permitindo melhores práticas de engenharia de software e maior controle sobre o desempenho da aplicação.</p>
<h2 id="heading-5-debugging-no-go-nao-e-divertido-ferramentas-modernas-ajudam">5. Debugging no Go não é divertido? Ferramentas modernas ajudam</h2>
<p>Embora o depurador nativo do Go tenha sido básico no passado, hoje há ferramentas avançadas como Delve, que oferecem um ambiente poderoso para debugging. Além disso, a própria simplicidade da linguagem reduz a necessidade de depuração intensiva, pois erros tendem a ser mais óbvios e fáceis de rastrear.</p>
<p>O modelo explícito de tratamento de erros do Go também ajuda a evitar problemas silenciosos. Como os desenvolvedores precisam lidar diretamente com os erros, é menos provável que falhas passem despercebidas, reduzindo a necessidade de um debugging aprofundado. Além disso, a combinação de <code>panic/recover</code> e profiling embutido na linguagem oferece mecanismos eficazes para análise de falhas e otimização de desempenho.</p>
<p>Outro ponto importante é que a compilação rápida e a tipagem estática do Go ajudam a identificar erros antes mesmo da execução, minimizando a incidência de falhas em tempo de execução e reduzindo a necessidade de debugging extensivo.</p>
<hr />
<p>Go não é perfeito para todos os cenários, assim como qualquer linguagem. No entanto, suas características — simplicidade, estabilidade e modularidade — fazem dela uma escolha estratégica para muitos projetos, especialmente aqueles que valorizam desempenho, escalabilidade e facilidade de manutenção. O que pode parecer uma limitação à primeira vista muitas vezes se traduz em benefícios a longo prazo.</p>
<p>A filosofia do Go prioriza eficiência, clareza e previsibilidade, eliminando complexidades desnecessárias e garantindo um ambiente de desenvolvimento produtivo e confiável. Embora algumas decisões da linguagem possam parecer rígidas, elas promovem boas práticas de engenharia de software e resultam em sistemas mais robustos e fáceis de manter.</p>
]]></content:encoded></item><item><title><![CDATA[Programação Funcional com Golang]]></title><description><![CDATA[A programação funcional (FP, do inglês Functional Programming) tem ganhado popularidade nos últimos anos devido à sua capacidade de criar código mais expressivo, modular e com menos efeitos colaterais. Go, apesar de ser uma linguagem projetada princi...]]></description><link>https://leocavalcante.dev/programacao-funcional-com-golang</link><guid isPermaLink="true">https://leocavalcante.dev/programacao-funcional-com-golang</guid><category><![CDATA[Go Language]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Tue, 28 Jan 2025 20:23:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738095494147/9a4fe551-19d1-48db-a787-a21fc387d934.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A programação funcional (FP, do inglês <em>Functional Programming</em>) tem ganhado popularidade nos últimos anos devido à sua capacidade de criar código mais expressivo, modular e com menos efeitos colaterais. Go, apesar de ser uma linguagem projetada principalmente para o paradigma imperativo, possui diversas características que permitem adotar conceitos da programação funcional. Este artigo explora como aplicar princípios da programação funcional no desenvolvimento com Go.</p>
<hr />
<h2 id="heading-o-que-e-programacao-funcional">O que é Programação Funcional?</h2>
<p>A programação funcional é um paradigma de desenvolvimento de software que enfatiza o uso de funções puras, imutabilidade e composição de funções. Diferentemente de paradigmas imperativos, onde o foco está na mutação de estado e no uso de comandos sequenciais, o paradigma funcional incentiva o uso de construções declarativas e a minimização de efeitos colaterais.</p>
<p>Os principais conceitos da programação funcional incluem:</p>
<ol>
<li><strong>Funções Puras</strong>: Uma função pura retorna sempre o mesmo resultado para os mesmos argumentos e não possui efeitos colaterais.</li>
<li><strong>Imutabilidade</strong>: Os dados não são modificados após serem criados.</li>
<li><strong>Composição de Funções</strong>: Combinação de funções menores para criar funcionalidades mais complexas.</li>
<li><strong>Funções de Alta Ordem</strong>: Funções que aceitam outras funções como argumento ou retornam funções.</li>
<li><strong>Avaliação Preguiçosa (Lazy Evaluation)</strong>: Avaliação de expressões apenas quando necessário (não diretamente suportado pelo Go).</li>
</ol>
<hr />
<h2 id="heading-programacao-funcional-em-go">Programação Funcional em Go</h2>
<p>Embora Go não seja uma linguagem funcional pura, ela suporta muitos dos princípios do paradigma funcional. Vamos explorar como isso pode ser feito na prática.</p>
<h3 id="heading-funcoes-de-alta-ordem">Funções de Alta Ordem</h3>
<p>Go permite que funções sejam tratadas como cidadãs de primeira classe. Isso significa que podemos passar funções como argumentos, retorná-las de outras funções ou armazená-las em variáveis.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
)

<span class="hljs-comment">// Função que aceita outra função como argumento</span>
def applyOperation(x <span class="hljs-keyword">int</span>, y <span class="hljs-keyword">int</span>, operation <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span>) <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> operation(x, y)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    sum := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> a + b
    }

    product := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> a * b
    }

    fmt.Println(<span class="hljs-string">"Soma:"</span>, applyOperation(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, sum))
    fmt.Println(<span class="hljs-string">"Produto:"</span>, applyOperation(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, product))
}
</code></pre>
<p>Nesse exemplo, a função <code>applyOperation</code> aceita uma função como parâmetro e a utiliza para aplicar diferentes operações aos valores fornecidos.</p>
<h3 id="heading-funcoes-puras">Funções Puras</h3>
<p>Funções puras são aquelas que não dependem ou alteram o estado externo. Isso facilita o teste e a previsibilidade do código.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-comment">// Função pura: sempre retorna o mesmo resultado para os mesmos inputs</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">add</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>))
    fmt.Println(add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>)) <span class="hljs-comment">// Sempre o mesmo resultado</span>
}
</code></pre>
<h3 id="heading-imutabilidade">Imutabilidade</h3>
<p>Embora Go não tenha suporte nativo para imutabilidade como algumas linguagens funcionais (ex.: Haskell), é possível adotar boas práticas para evitar alterações desnecessárias no estado.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">immutableIncrement</span><span class="hljs-params">(numbers []<span class="hljs-keyword">int</span>)</span> []<span class="hljs-title">int</span></span> {
    newNumbers := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-built_in">len</span>(numbers))
    <span class="hljs-keyword">for</span> i, v := <span class="hljs-keyword">range</span> numbers {
        newNumbers[i] = v + <span class="hljs-number">1</span>
    }
    <span class="hljs-keyword">return</span> newNumbers
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    nums := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}
    newNums := immutableIncrement(nums)

    fmt.Println(<span class="hljs-string">"Original:"</span>, nums)
    fmt.Println(<span class="hljs-string">"Novo:"</span>, newNums)
}
</code></pre>
<p>Neste exemplo, ao invés de modificar o slice original, criamos uma nova cópia com as alterações desejadas.</p>
<h3 id="heading-composicao-de-funcoes">Composição de Funções</h3>
<p>Embora Go não tenha suporte nativo para composição de funções como outras linguagens (ex.: <code>pipe</code> em Elixir), podemos implementar nossa própria função de composição.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">compose</span><span class="hljs-params">(f, g <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span>) <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> f(g(x))
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    double := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> { <span class="hljs-keyword">return</span> x * <span class="hljs-number">2</span> }
    square := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> { <span class="hljs-keyword">return</span> x * x }

    composed := compose(square, double)
    fmt.Println(composed(<span class="hljs-number">3</span>)) <span class="hljs-comment">// Resultado: 36 (square(double(3)))</span>
}
</code></pre>
<h3 id="heading-uso-de-closures">Uso de Closures</h3>
<p>Closures são funções que "lembram" o ambiente em que foram criadas. Em Go, closures são úteis para encapsular lógica.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeMultiplier</span><span class="hljs-params">(factor <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> x * factor
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    double := makeMultiplier(<span class="hljs-number">2</span>)
    triple := makeMultiplier(<span class="hljs-number">3</span>)

    fmt.Println(double(<span class="hljs-number">5</span>)) <span class="hljs-comment">// 10</span>
    fmt.Println(triple(<span class="hljs-number">5</span>)) <span class="hljs-comment">// 15</span>
}
</code></pre>
<h3 id="heading-trabalhando-com-map-filter-e-reduce">Trabalhando com Map, Filter e Reduce</h3>
<p>Embora Go não tenha funções nativas como <code>map</code>, <code>filter</code> e <code>reduce</code> disponíveis em linguagens como Python ou JavaScript, podemos implementá-las.</p>
<h4 id="heading-map">Map</h4>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">mapSlice</span><span class="hljs-params">(slice []<span class="hljs-keyword">int</span>, f <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span>) []<span class="hljs-title">int</span></span> {
    result := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-built_in">len</span>(slice))
    <span class="hljs-keyword">for</span> i, v := <span class="hljs-keyword">range</span> slice {
        result[i] = f(v)
    }
    <span class="hljs-keyword">return</span> result
}
</code></pre>
<h4 id="heading-filter">Filter</h4>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">filterSlice</span><span class="hljs-params">(slice []<span class="hljs-keyword">int</span>, predicate <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span>) []<span class="hljs-title">int</span></span> {
    result := []<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> slice {
        <span class="hljs-keyword">if</span> predicate(v) {
            result = <span class="hljs-built_in">append</span>(result, v)
        }
    }
    <span class="hljs-keyword">return</span> result
}
</code></pre>
<h4 id="heading-reduce">Reduce</h4>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">reduceSlice</span><span class="hljs-params">(slice []<span class="hljs-keyword">int</span>, f <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span>, <span class="hljs-title">initial</span> <span class="hljs-title">int</span>) <span class="hljs-title">int</span></span> {
    result := initial
    <span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> slice {
        result = f(result, v)
    }
    <span class="hljs-keyword">return</span> result
}
</code></pre>
<hr />
<h2 id="heading-trabalhando-com-o-pacote-githubcomsamberlo">Trabalhando com o pacote <code>github.com/samber/lo</code></h2>
<p>O pacote <a target="_blank" href="https://github.com/samber/lo"><code>github.com/samber/lo</code></a> é uma biblioteca que estende as capacidades funcionais do Go, oferecendo utilitários para trabalhar com <em>map</em>, <em>filter</em>, <em>reduce</em> e outras operações funcionais de forma mais simples e eficiente. A seguir, veremos como instalar e usar este pacote.</p>
<h3 id="heading-instalacao">Instalação</h3>
<p>Para instalar o pacote, use o seguinte comando:</p>
<pre><code class="lang-sh">go get github.com/samber/lo
</code></pre>
<h3 id="heading-exemplos-de-uso">Exemplos de Uso</h3>
<h4 id="heading-map-1">Map</h4>
<p>O <code>lo.Map</code> permite transformar um slice aplicando uma função em cada elemento:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"github.com/samber/lo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>}
    squared := lo.Map(numbers, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>, _ <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> x * x
    })

    fmt.Println(squared) <span class="hljs-comment">// [1, 4, 9, 16]</span>
}
</code></pre>
<h4 id="heading-filter-1">Filter</h4>
<p>Com <code>lo.Filter</code>, podemos filtrar elementos de um slice com base em uma condição:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"github.com/samber/lo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>}
    even := lo.Filter(numbers, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>, _ <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span></span> {
        <span class="hljs-keyword">return</span> x%<span class="hljs-number">2</span> == <span class="hljs-number">0</span>
    })

    fmt.Println(even) <span class="hljs-comment">// [2, 4]</span>
}
</code></pre>
<h4 id="heading-reduce-1">Reduce</h4>
<p>O <code>lo.Reduce</code> permite acumular valores em um slice:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"github.com/samber/lo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>}
    sum := lo.Reduce(numbers, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(agg <span class="hljs-keyword">int</span>, x <span class="hljs-keyword">int</span>, _ <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> agg + x
    }, <span class="hljs-number">0</span>)

    fmt.Println(sum) <span class="hljs-comment">// 10</span>
}
</code></pre>
<h4 id="heading-outras-funcionalidades">Outras Funcionalidades</h4>
<p>O pacote também oferece utilitários como <code>lo.Uniq</code> para remover duplicatas, <code>lo.FlatMap</code> para transformar e achatar slices, entre outros. Consulte a <a target="_blank" href="https://github.com/samber/lo">documentação oficial</a> para explorar todas as funcionalidades disponíveis.</p>
<hr />
<p>Embora Go não seja uma linguagem funcional pura, ela fornece ferramentas suficientes para incorporar elementos da programação funcional. Adotar esses princípios pode levar a um código mais limpo, reutilizável e com menos bugs. Com práticas como uso de funções puras, composição e imutabilidade, e o auxílio de bibliotecas como <code>github.com/samber/lo</code>, desenvolvedores podem aproveitar o melhor dos dois mundos: a simplicidade do Go e a expressividade da programação funcional.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Estilo, Decisões e Melhores Práticas]]></title><description><![CDATA[O guia de estilo, decisões e melhores práticas da Google para a linguagem Go é um conjunto de recomendações que visa promover a clareza, simplicidade e eficiência no desenvolvimento de código.
Essas diretrizes ajudam os desenvolvedores a escreverem c...]]></description><link>https://leocavalcante.dev/go-estilo-decisoes-e-melhores-praticas</link><guid isPermaLink="true">https://leocavalcante.dev/go-estilo-decisoes-e-melhores-praticas</guid><category><![CDATA[Go Language]]></category><category><![CDATA[styleguide]]></category><category><![CDATA[best practices]]></category><category><![CDATA[Google]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 20 Jan 2025 11:00:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Im7lZjxeLhg/upload/7317f4d05640115de28bf4486c457851.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O guia de estilo, decisões e melhores práticas da Google para a linguagem Go é um conjunto de recomendações que visa promover a clareza, simplicidade e eficiência no desenvolvimento de código.</p>
<p>Essas diretrizes ajudam os desenvolvedores a escreverem código legível e sustentável, essencial para projetos de longo prazo. Neste artigo, exploramos os principais pontos abordados nesses documentos, com ênfase na importância da consistência, da manutenção e da colaboração dentro das equipes de desenvolvimento.</p>
<h2 id="heading-1-estilo-de-codigo">1. Estilo de Código</h2>
<p>Manter um estilo de código consistente é essencial para a colaboração em equipe e a manutenção de projetos. Isso também facilita revisões de código e onboarding de novos membros na equipe.</p>
<h3 id="heading-11-formatacao">1.1 Formatação</h3>
<p>Use <code>gofmt</code> para garantir formatação padronizada. O uso dessa ferramenta elimina ambiguidades sobre espaçamento, indentacão e organização de código. Além disso, integra-se facilmente com IDEs e pipelines de CI/CD.</p>
<p><strong>Boas práticas adicionais:</strong></p>
<ul>
<li><p>Evite linhas com mais de 80 caracteres para melhorar a legibilidade.</p>
</li>
<li><p>Use espaços em vez de tabulações para garantir compatibilidade em diferentes editores.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Formatação correta</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Add</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}

<span class="hljs-comment">// Formatação incorreta</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Add</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
<span class="hljs-keyword">return</span> a + b <span class="hljs-comment">// Errado: indentacão incorreta</span>
}
</code></pre>
<h3 id="heading-12-convencoes-de-nomes">1.2 Convenções de Nomes</h3>
<p>Nomes de variáveis, funções e pacotes devem ser claros e descritivos, refletindo suas responsabilidades. Isso melhora a manutenção e reduz a necessidade de comentários extensivos.</p>
<p><strong>Regras principais:</strong></p>
<ul>
<li><p><strong>camelCase:</strong> Use para variáveis, funções e argumentos internos.</p>
</li>
<li><p><strong>PascalCase:</strong> Use para funções, estruturas e constantes exportadas.</p>
</li>
<li><p><strong>snake_case:</strong> Evite este padrão em código Go.</p>
</li>
<li><p>Pacotes devem usar nomes curtos e em minúsculas, sem underscores.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Correto</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CalculateSum</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    total := a + b
    <span class="hljs-keyword">return</span> total
}

<span class="hljs-comment">// Errado</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculatesum</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    total := a + b
    <span class="hljs-keyword">return</span> total
}
</code></pre>
<p><strong>Nomes de pacotes:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Correto</span>
<span class="hljs-keyword">package</span> mathutils

<span class="hljs-comment">// Errado</span>
<span class="hljs-keyword">package</span> math_utils
</code></pre>
<h3 id="heading-13-comentarios">1.3 Comentários</h3>
<p>Adicione comentários explicativos que descrevam o "por quê" e não o "como". Comentários excessivos ou redundantes devem ser evitados. Para funções exportadas, siga o formato padrão do Go, onde o comentário começa com o nome da função.</p>
<p><strong>Melhores práticas:</strong></p>
<ul>
<li><p>Use docstrings para pacotes e funções importantes.</p>
</li>
<li><p>Comentários devem ser atualizados sempre que o comportamento do código mudar.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// CalculateSum soma dois inteiros e retorna o resultado.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CalculateSum</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}

<span class="hljs-comment">// Comentário redundante e desnecessário</span>
<span class="hljs-comment">// A função retorna a soma de dois inteiros</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Sum</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}
</code></pre>
<hr />
<h2 id="heading-2-decisoes-de-projeto">2. Decisões de Projeto</h2>
<p>Decisões claras sobre organização de projetos e uso de recursos da linguagem ajudam a evitar confusões, bugs e retrabalho. Um design bem pensado também facilita a expansão do projeto no futuro.</p>
<h3 id="heading-21-estrutura-de-projetos">2.1 Estrutura de Projetos</h3>
<p>A organização do código deve ser modular, intuitiva e refletir os objetivos principais do sistema. Adote uma estrutura de diretórios que favoreça a separação de preocupações.</p>
<p><strong>Diretrizes:</strong></p>
<ul>
<li><p>Evite pacotes genéricos como <code>utils</code> sempre que possível. Prefira nomes específicos.</p>
</li>
<li><p>Mantenha o <code>main.go</code> limpo e delegue a lógica para outros pacotes.</p>
</li>
<li><p>Utilize padrões como <code>internal/</code> para código que não deve ser acessado fora do repositório.</p>
</li>
</ul>
<p><strong>Estrutura sugerida:</strong></p>
<pre><code class="lang-plaintext">project/
    cmd/
        app/
            main.go
    mathutils/
        math.go
    internal/
        db/
            connection.go
</code></pre>
<h3 id="heading-22-interfaces">2.2 Interfaces</h3>
<p>Interfaces são mais eficazes quando são pequenas e representam uma única responsabilidade. Isso permite maior flexibilidade na implementação e facilita testes.</p>
<p><strong>Princípios:</strong></p>
<ul>
<li><p>Evite adicionar métodos à interface antes de necessários.</p>
</li>
<li><p>Use composição para criar interfaces mais complexas.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Correto</span>
<span class="hljs-keyword">type</span> Reader <span class="hljs-keyword">interface</span> {
    Read(p []<span class="hljs-keyword">byte</span>) (n <span class="hljs-keyword">int</span>, err error)
}

<span class="hljs-comment">// Composição de interfaces</span>
<span class="hljs-keyword">type</span> ReadWriter <span class="hljs-keyword">interface</span> {
    Reader
    Write(p []<span class="hljs-keyword">byte</span>) (n <span class="hljs-keyword">int</span>, err error)
}

<span class="hljs-comment">// Errado: Interface genérica e inflexível</span>
<span class="hljs-keyword">type</span> Service <span class="hljs-keyword">interface</span> {
    Start()
    Stop()
    Restart()
}
</code></pre>
<h3 id="heading-23-controle-de-dependencias">2.3 Controle de Dependências</h3>
<p>O gerenciamento de dependências é crucial para manter a segurança e a estabilidade do projeto. Use <code>go mod</code> para controlar dependências de forma eficiente.</p>
<p><strong>Boas práticas:</strong></p>
<ul>
<li><p>Atualize dependências regularmente e teste cuidadosamente antes de fazer o merge.</p>
</li>
<li><p>Use ferramentas como <code>dependabot</code> para monitorar atualizações.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-plaintext">module example.com/project

go 1.20

require (
    github.com/pkg/errors v0.9.1
)
</code></pre>
<hr />
<h2 id="heading-3-melhores-praticas">3. Melhores Práticas</h2>
<h3 id="heading-31-tratamento-de-erros">3.1 Tratamento de Erros</h3>
<p>A gestão de erros em Go é projetada para ser simples e explícita. Sempre trate erros imediatamente ou propague-os com contexto adicional.</p>
<p><strong>Dicas:</strong></p>
<ul>
<li><p>Use <code>errors.Is</code> e <code>errors.As</code> para verificar e desembrulhar erros.</p>
</li>
<li><p>Padronize mensagens de erro para facilitar a depuração.</p>
</li>
</ul>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">if</span> err := doSomething(); err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">if</span> errors.Is(err, ErrSpecific) {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"erro especifico: %w"</span>, err)
    }
    <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"erro genérico: %w"</span>, err)
}
</code></pre>
<h3 id="heading-32-concorrencia">3.2 Concorrência</h3>
<p>O suporte embutido a concorrência é uma das maiores forças do Go. No entanto, erros comuns como vazamentos de Goroutines ou acessos concorrentes não sincronizados devem ser evitados.</p>
<p><strong>Ferramentas úteis:</strong></p>
<ul>
<li><p><code>sync.WaitGroup</code> para esperar várias Goroutines.</p>
</li>
<li><p><code>sync.Mutex</code> para proteger seções críticas.</p>
</li>
<li><p><code>context.Context</code> para cancelar Goroutines.</p>
</li>
</ul>
<p><strong>Exemplo com Context:</strong></p>
<pre><code class="lang-go">ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">2</span>*time.Second)
<span class="hljs-keyword">defer</span> cancel()

ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)

<span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(ch)
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++ {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-ctx.Done():
            <span class="hljs-keyword">return</span>
        <span class="hljs-keyword">case</span> ch &lt;- i:
        }
    }
}()

<span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> ch {
    fmt.Println(val)
}
</code></pre>
<h3 id="heading-33-logging">3.3 Logging</h3>
<p>Logs estruturados ajudam na monitoração e diagnóstico de problemas em produção. Prefira bibliotecas como <code>log/slog</code> para logs mais ricos e configuráveis.</p>
<p><strong>Exemplo:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"log/slog"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Criando um logger padrão</span>
    logger := slog.Default()

    <span class="hljs-comment">// Registrando uma mensagem de informação com campos</span>
    logger.Info(<span class="hljs-string">"iniciando operação"</span>,
        slog.Int(<span class="hljs-string">"valor"</span>, <span class="hljs-number">42</span>),
        slog.String(<span class="hljs-string">"status"</span>, <span class="hljs-string">"inicializado"</span>))
}
</code></pre>
<h3 id="heading-34-testes">3.4 Testes</h3>
<p>Adote TDD (Test-Driven Development) sempre que possível. Utilize subtestes para dividir testes complexos em partes menores e módulos de mocks para simular dependências externas.</p>
<p><strong>Exemplo com mocks:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> MockService <span class="hljs-keyword">struct</span> {}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(m *MockService)</span> <span class="hljs-title">Process</span><span class="hljs-params">(data <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> data == <span class="hljs-string">"error"</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"mock error"</span>)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestService</span><span class="hljs-params">(t *testing.T)</span></span> {
    service := &amp;MockService{}
    err := service.Process(<span class="hljs-string">"error"</span>)
    <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
        t.Fatalf(<span class="hljs-string">"esperava erro, mas obteve nil"</span>)
    }
}
</code></pre>
<hr />
<p>Em resumo, os guias de estilo, decisões e melhores práticas recomendados pela Google para Go são fundamentais para garantir que o código seja claro, conciso e fácil de manter. A adesão a essas orientações não só melhora a qualidade do código, mas também facilita a colaboração em equipes de desenvolvimento. Seguir essas práticas promove a consistência e a legibilidade, elementos essenciais para projetos de longo prazo bem-sucedidos. Para mais informações, consulte os recursos completos no <a target="_blank" href="https://google.github.io/styleguide/go/">site oficial</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Análise do Go Developer Survey 2024]]></title><description><![CDATA[O Go Developer Survey é uma ferramenta essencial para compreender a evolução e os desafios enfrentados pelos desenvolvedores que utilizam a linguagem Go. Os resultados da segunda metade de 2024 (H2), conduzidos entre os dias 9 e 23 de setembro, com 4...]]></description><link>https://leocavalcante.dev/analise-do-go-developer-survey-2024</link><guid isPermaLink="true">https://leocavalcante.dev/analise-do-go-developer-survey-2024</guid><category><![CDATA[Go Language]]></category><category><![CDATA[survey]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Mon, 13 Jan 2025 11:00:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3n3Or1UMnVQ/upload/1b838999c1d3500e08c22c789e8ed85d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O <strong>Go Developer Survey</strong> é uma ferramenta essencial para compreender a evolução e os desafios enfrentados pelos desenvolvedores que utilizam a linguagem Go. Os resultados da segunda metade de 2024 (H2), conduzidos entre os dias 9 e 23 de setembro, com 4.156 respondentes, trazem informações valiosas sobre a satisfação dos desenvolvedores, preferências de ferramentas, tendências tecnológicas e os desafios do dia a dia.</p>
<h3 id="heading-satisfacao-geral-uma-comunidade-engajada-e-feliz"><strong>Satisfação Geral: Uma Comunidade Engajada e Feliz</strong></h3>
<p>Um dos dados mais marcantes é o alto nível de satisfação: <strong>93% dos desenvolvedores</strong> declararam estar satisfeitos com sua experiência usando Go no último ano. Esse índice se mantém consistente com levantamentos anteriores, destacando a estabilidade e maturidade da linguagem no cenário tecnológico.</p>
<h4 id="heading-o-que-torna-os-desenvolvedores-tao-satisfeitos"><strong>O que torna os desenvolvedores tão satisfeitos?</strong></h4>
<ul>
<li><strong>Simplicidade e clareza</strong>: Go é reconhecido por sua sintaxe simples e por eliminar abstrações complexas.</li>
<li><strong>Performance</strong>: O código em Go é altamente performático, tornando-o ideal para aplicações escaláveis e sistemas distribuídos.</li>
<li><strong>Ecosistema robusto</strong>: Ferramentas como <code>go test</code>, <code>go mod</code> e bibliotecas padrão consolidadas ajudam a reduzir a complexidade do desenvolvimento.</li>
</ul>
<h3 id="heading-ambientes-e-ferramentas-de-desenvolvimento"><strong>Ambientes e Ferramentas de Desenvolvimento</strong></h3>
<h4 id="heading-sistemas-operacionais"><strong>Sistemas Operacionais</strong></h4>
<p>A preferência por <strong>Linux</strong> (61%) e <strong>macOS</strong> (59%) como plataformas de desenvolvimento se mantém constante, com uma representatividade menor de desenvolvedores no <strong>Windows</strong> (16%). No entanto, quando analisados os dados por ferramentas, surgem algumas diferenças interessantes:</p>
<ul>
<li>Usuários de <strong>VS Code</strong> e <strong>GoLand</strong> mostram maior propensão a usar Windows (33% e 36%, respectivamente), talvez devido à maior integração dessas ferramentas com o sistema.</li>
</ul>
<h4 id="heading-editores-de-codigo"><strong>Editores de Código</strong></h4>
<ul>
<li><strong>VS Code</strong> lidera com 43% de adoção, destacando-se pela facilidade de configuração e extensões como o plugin oficial de Go.</li>
<li><strong>GoLand</strong>, com 33%, é popular entre desenvolvedores que buscam funcionalidades mais avançadas, como refatoramento e análise estática robusta.</li>
</ul>
<p>Esses números sugerem um ecossistema de ferramentas bem equilibrado, onde os desenvolvedores escolhem com base em suas preferências pessoais e necessidades do projeto.</p>
<h3 id="heading-go-na-nuvem-um-parceiro-de-confianca"><strong>Go na Nuvem: Um Parceiro de Confiança</strong></h3>
<p>A integração de Go com provedores de nuvem continua sendo uma área de destaque. <strong>APIs fáceis de usar e a simplicidade de deploy</strong> são os principais pontos positivos apontados pelos desenvolvedores.</p>
<h4 id="heading-exemplos-de-uso"><strong>Exemplos de uso:</strong></h4>
<ul>
<li>Desenvolvedores destacaram a facilidade de implementar microserviços usando Go e frameworks como <strong>Gin</strong> ou <strong>Echo</strong>, que se integram perfeitamente com <strong>AWS Lambda</strong> ou <strong>Google Cloud Run</strong>.</li>
<li>Ferramentas como o <code>kubectl</code> também reforçam o papel de Go como escolha ideal para operações em Kubernetes.</li>
</ul>
<h3 id="heading-assistencia-de-ia-o-futuro-ja-chegou"><strong>Assistência de IA: O Futuro Já Chegou</strong></h3>
<p>Uma das áreas mais empolgantes é o crescente uso de <strong>assistentes de IA</strong>. <strong>70% dos desenvolvedores</strong> relataram empregar IA em suas atividades diárias de desenvolvimento.</p>
<h4 id="heading-principais-aplicacoes"><strong>Principais aplicações:</strong></h4>
<ol>
<li><strong>Autocompletar baseado em LLMs</strong>: Ferramentas como o GitHub Copilot ajudam a reduzir o tempo de escrita de código.</li>
<li><strong>Geração de testes</strong>: A IA facilita a criação de testes automatizados com cobertura abrangente.</li>
<li><strong>Tradução de linguagem natural para código</strong>: Permite transformar requisitos em protótipos rápidos.</li>
<li><strong>Brainstorming</strong>: Resolvendo problemas ou otimizando soluções.</li>
</ol>
<p>É interessante notar que, apesar de os assistentes de IA já terem sido amplamente adotados, o uso real difere das expectativas iniciais, sinalizando um ajuste dinâmico às necessidades dos desenvolvedores.</p>
<h3 id="heading-desafios-de-colaboracao-em-equipe"><strong>Desafios de Colaboração em Equipe</strong></h3>
<p>O maior desafio apontado foi <strong>manter padrões consistentes de código</strong>, especialmente em equipes com diferentes níveis de experiência em Go. Algumas das soluções adotadas incluem:</p>
<ul>
<li><strong>Code Reviews</strong>: Adotar ferramentas como <strong>Gerrit</strong> ou integrações do GitHub para garantir conformidade.</li>
<li><strong>Linters</strong>: Uso de <code>golangci-lint</code> para identificar problemas comuns.</li>
<li><strong>Treinamentos</strong>: Promover workshops internos para alinhar os conhecimentos da equipe.</li>
</ul>
<hr />
<p>O <strong>Go Developer Survey 2024 (H2)</strong> reafirma que Go está em um momento de grande maturidade e expansão. A satisfação dos desenvolvedores, a estabilidade nas ferramentas e a crescente adoção de IA são indicativos de um ecossistema vibrante e inovador. Por outro lado, desafios como consistência em padrões de código indicam áreas onde a comunidade pode continuar evoluindo.</p>
<p>Se você ainda não experimentou Go, este pode ser o momento ideal para explorar uma linguagem moderna, robusta e com uma comunidade extremamente acolhedora.</p>
<p>https://go.dev/blog/survey2024-h2-results</p>
]]></content:encoded></item><item><title><![CDATA[Os Valores de Go]]></title><description><![CDATA[Desde o seu lançamento em 2009, a linguagem de programação Go tem conquistado desenvolvedores em todo o mundo, especialmente na área de sistemas distribuídos, cloud computing e DevOps. Mas o que exatamente faz do Go uma linguagem tão atrativa? Além d...]]></description><link>https://leocavalcante.dev/os-valores-de-go</link><guid isPermaLink="true">https://leocavalcante.dev/os-valores-de-go</guid><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Tue, 07 Jan 2025 11:00:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ObpCE_X3j6U/upload/3f52f8d1ab3fc41babc7d86bfc4f1810.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Desde o seu lançamento em 2009, a linguagem de programação Go tem conquistado desenvolvedores em todo o mundo, especialmente na área de sistemas distribuídos, cloud computing e DevOps. Mas o que exatamente faz do Go uma linguagem tão atrativa? Além de suas características técnicas, é sua filosofia subjacente que realmente define o que o Go representa. <a target="_blank" href="https://www.reddit.com/r/golang/comments/1hs1yx3/what_are_gos_values/">Essa thread</a> no Reddit reuniu os valores centrais que moldam a linguagem e como eles impactam a experiência dos desenvolvedores.</p>
<h2 id="heading-simplicidade-como-fundamento">Simplicidade como fundamento</h2>
<p>Uma das características mais marcantes do Go é sua simplicidade. A sintaxe da linguagem é direta e fácil de aprender, mesmo para desenvolvedores que vêm de outras linguagens. Por exemplo, enquanto outras linguagens permitem formas elaboradas de definição de funções ou estruturas, o Go opta por um padrão claro e conciso:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">add</span><span class="hljs-params">(a <span class="hljs-keyword">int</span>, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}
</code></pre>
<p>Essa simplicidade não é apenas uma questão de design estético. Ela tem um impacto direto na produtividade, tornando mais fácil para novos membros de equipes entrarem em projetos existentes e reduzindo o risco de erros complexos no código.</p>
<h2 id="heading-ortogonalidade-e-consistencia">Ortogonalidade e consistência</h2>
<p>Go segue o princípio de ortogonalidade, no qual cada recurso da linguagem é projetado para funcionar bem em combinação com outros sem sobreposição desnecessária. Por exemplo, o Go não possui herança clássica de classes, como em Java ou C++. Em vez disso, utiliza interfaces implícitas, que promovem composição em vez de herança:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Shape <span class="hljs-keyword">interface</span> {
    Area() <span class="hljs-keyword">float64</span>
}

<span class="hljs-keyword">type</span> Rectangle <span class="hljs-keyword">struct</span> {
    Width, Height <span class="hljs-keyword">float64</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r Rectangle)</span> <span class="hljs-title">Area</span><span class="hljs-params">()</span> <span class="hljs-title">float64</span></span> {
    <span class="hljs-keyword">return</span> r.Width * r.Height
}

<span class="hljs-comment">// Rectangle implementa Shape sem declaração explícita.</span>
</code></pre>
<p>Isso elimina ambiguidades e torna o código mais previsível e legível.</p>
<h2 id="heading-minimalismo-intencional">Minimalismo intencional</h2>
<p>Go evita deliberadamente incluir recursos que possam aumentar a complexidade desnecessária. Por exemplo, enquanto muitas linguagens incluem suporte para sobrecarga de funções ou herança múltipla, o Go opta por evitar esses recursos, priorizando a simplicidade e a previsibilidade.</p>
<p>Outro exemplo claro é o tratamento de erros. Em vez de usar exceções, o Go adota um modelo explícito de retorno de erros:</p>
<pre><code class="lang-go">file, err := os.Open(<span class="hljs-string">"example.txt"</span>)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Fatal(err)
}
</code></pre>
<p>Embora isso possa parecer verboso, garante que o desenvolvedor lide com os erros de forma consciente e clara, reduzindo bugs silenciosos.</p>
<h2 id="heading-concorrencia">Concorrência</h2>
<p>A concorrência é uma necessidade para muitas aplicações modernas, e Go foi projetado desde o início para lidar com isso de forma eficiente. O modelo baseado em goroutines e canais simplifica a escrita de código concorrente:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">worker</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>, jobs &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, results <span class="hljs-keyword">chan</span>&lt;- <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">for</span> j := <span class="hljs-keyword">range</span> jobs {
        results &lt;- j * <span class="hljs-number">2</span>
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    jobs := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">100</span>)
    results := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">100</span>)

    <span class="hljs-keyword">for</span> w := <span class="hljs-number">1</span>; w &lt;= <span class="hljs-number">3</span>; w++ {
        <span class="hljs-keyword">go</span> worker(w, jobs, results)
    }

    <span class="hljs-keyword">for</span> j := <span class="hljs-number">1</span>; j &lt;= <span class="hljs-number">10</span>; j++ {
        jobs &lt;- j
    }
    <span class="hljs-built_in">close</span>(jobs)

    <span class="hljs-keyword">for</span> a := <span class="hljs-number">1</span>; a &lt;= <span class="hljs-number">10</span>; a++ {
        fmt.Println(&lt;-results)
    }
}
</code></pre>
<p>O modelo permite que os desenvolvedores lidem com tarefas concorrentes de forma intuitiva e eficiente, sem precisar recorrer a bibliotecas complexas ou abstrações externas.</p>
<h2 id="heading-desempenho-e-eficiencia">Desempenho e eficiência</h2>
<p>Como uma linguagem compilada, Go oferece desempenho semelhante ao de linguagens como C e C++, enquanto fornece segurança de memória e um garbage collector para reduzir o risco de erros comuns.</p>
<p>Por exemplo, Go é amplamente utilizado no Kubernetes, que gerencia milhares de contêineres em tempo real com alta eficiência. Sua capacidade de lidar com <em>workloads</em> intensivas de forma confiável é um testemunho de sua performance.</p>
<h2 id="heading-ferramentas-integradas-e-ecosistema-rico">Ferramentas integradas e ecosistema rico</h2>
<p>O Go vem com ferramentas integradas que facilitam a vida dos desenvolvedores. O comando <code>go fmt</code>, por exemplo, garante que todo o código seja formatado de maneira consistente:</p>
<pre><code class="lang-bash">go fmt ./...
</code></pre>
<p>Outras ferramentas, como <code>go test</code> para testes e <code>go mod</code> para gerenciamento de dependências, fazem parte do ecossistema padrão e eliminam a necessidade de configurar ferramentas externas.</p>
<h2 id="heading-estabilidade-e-retrocompatibilidade">Estabilidade e retrocompatibilidade</h2>
<p>Desde o lançamento do Go 1.0, a equipe de desenvolvimento da linguagem se comprometeu com a retrocompatibilidade. Isso significa que códigos escritos em versões anteriores do Go continuam funcionando nas versões mais recentes. Esse compromisso reduz significativamente os custos de manutenção a longo prazo.</p>
<h2 id="heading-design-opinativo">Design opinativo</h2>
<p>Go é uma linguagem opinativa, o que significa que ele favorece convenções fortes e evita ambiguidade. Por exemplo, o uso de <code>gofmt</code> não é opcional. Isso promove um estilo uniforme em toda a base de código, independentemente de quem está escrevendo.</p>
<p>Além disso, a linguagem adota uma abordagem sem compromissos em relação à inclusão de recursos. Isso pode frustrar alguns desenvolvedores, mas também evita a complexidade excessiva que pode surgir com múltiplas maneiras de fazer a mesma coisa.</p>
<h2 id="heading-produtividade">Produtividade</h2>
<p>Todos os valores do Go convergem para um objetivo principal: maximizar a produtividade do desenvolvedor. Desde sua compilação rápida até sua simplicidade e ferramentas integradas, tudo no Go foi projetado para ajudar os desenvolvedores a criarem aplicações robustas e escaláveis rapidamente.</p>
<p>Go é muito mais do que apenas uma linguagem de programação; é uma manifestação de valores que colocam a simplicidade, a eficiência e a colaboração em primeiro lugar. Sua filosofia de design clara e coerente não apenas facilita a criação de software de alta qualidade, mas também promove uma cultura de código legível e manutenível. Essas são as razões pelas quais o Go continua a crescer em popularidade e a influenciar o design de linguagens futuras.</p>
]]></content:encoded></item><item><title><![CDATA[Cobertura de Código em Testes de Go: Usando o covdata para Combinar Perfis]]></title><description><![CDATA[Acho que uma das minhas maiores aventuras com Go tem sido trabalhar com testes. Foi com eles que mudei uma opinião com relação ao pragmatismo da linguagem, ela continua sendo super prática sim, ainda é bem simples e gostoso de programar, mas eu levav...]]></description><link>https://leocavalcante.dev/cobertura-de-codigo-em-testes-de-go-usando-o-covdata-para-combinar-perfis</link><guid isPermaLink="true">https://leocavalcante.dev/cobertura-de-codigo-em-testes-de-go-usando-o-covdata-para-combinar-perfis</guid><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Tue, 26 Nov 2024 14:52:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/dC6Pb2JdAqs/upload/7f2a0c64bfff804ca01745df15d2d5ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Acho que uma das minhas maiores aventuras com Go tem sido trabalhar com testes. Foi com eles que mudei uma opinião com relação ao pragmatismo da linguagem, ela continua sendo super prática sim, ainda é bem simples e gostoso de programar, mas eu levava essa ideia de forma leviana a ponto de fazer as coisas de qualquer jeito e sem respeitar princípios da engenharia de software, que como o nome diz, são princípios, não dependem da linguagem e Go não está de fora dessa.</p>
<p><strong>Quem sabe exploro mais essa ideia em um outro artigo, mas nesse quero mostrar como foi conseguir combinar a cobertura de testes unitários e testes de integração.</strong></p>
<p>Acontece que a ferramenta que analisa a cobertura de código não separa as suítes de testes, então código exclusivo de infraestrutura, que seria coberto por um teste de integração, ainda entra na mesma análise dos testes unitários.
O que eu queria evitar é ter testes de integração sendo executados em conjunto de testes unitários por causa de uma limitação de uma ferramenta terceira (<em>Sonar, cof, cof...</em>).</p>
<p>E o ecossistema de Go surpreendendo mais uma vez: existe uma ferramenta do próprio Go que ajuda nesses casos, a <code>covdata</code>: https://pkg.go.dev/cmd/covdata</p>
<p>O primeiro passo foi separar o que era teste unitário de teste de integração, tenho usado o <code>make</code> pra ter essas execuções à mão:</p>
<pre><code class="lang-Makefile"><span class="hljs-meta"><span class="hljs-meta-keyword">.PHONY</span>: unit-test</span>
<span class="hljs-section">unit-test:</span>
    go test -short -cover ./contract/... ./internal/cli/... ./internal/http/... -test.gocoverdir=<span class="hljs-string">"${PWD}/test/cover/unit"</span>

<span class="hljs-meta"><span class="hljs-meta-keyword">.PHONY</span>: integration-test</span>
<span class="hljs-section">integration-test:</span>
    go test -short -cover ./internal/infra/... -test.gocoverdir=<span class="hljs-string">"${PWD}/test/cover/integration"</span>
</code></pre>
<p>Depois incluir um passo que faz o <em>merge</em> das duas coberturas:</p>
<pre><code class="lang-Makefile"><span class="hljs-meta"><span class="hljs-meta-keyword">.PHONY</span>: cover</span>
<span class="hljs-section">cover:</span>
    go tool covdata textfmt -i=./test/cover/unit,./test/cover/integration -o cover.out
</code></pre>
<p>Agora sim a gente pode enviar o arquivo <code>cover.out</code> para a análise do Sonar:</p>
<pre><code class="lang-properties">sonar.go.coverage.reportPaths=cover.out
</code></pre>
<p>Ele combina a cobertura dos testes unitários e dos testes de integração.</p>
<p>Os testes de integração usam Testcontainers BTW, fica a dica de uma outra ótima ferramenta: https://testcontainers.com.</p>
]]></content:encoded></item><item><title><![CDATA[O que é versionamento semântico?]]></title><description><![CDATA[O versionamento semântico, também chamado de SemVer, é um padrão usado por desenvolvedores para numerar versões de softwares de maneira clara e previsível. Ele facilita o entendimento de quais mudanças ocorreram no software e qual o impacto dessas al...]]></description><link>https://leocavalcante.dev/semver</link><guid isPermaLink="true">https://leocavalcante.dev/semver</guid><category><![CDATA[Git]]></category><category><![CDATA[version control]]></category><category><![CDATA[semantic versioning]]></category><dc:creator><![CDATA[Leo Cavalcante]]></dc:creator><pubDate>Thu, 24 Oct 2024 13:01:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/wX2L8L-fGeA/upload/e2d95e9397f323c7f9a39fb96e9b7101.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O versionamento semântico, também chamado de <strong>SemVer</strong>, é um padrão usado por desenvolvedores para numerar versões de softwares de maneira clara e previsível. Ele facilita o entendimento de quais mudanças ocorreram no software e qual o impacto dessas alterações. Basicamente, o SemVer usa uma notação de três números no formato <strong>X.Y.Z</strong>, onde cada número tem um significado específico.</p>
<h2 id="heading-estrutura-do-semver">Estrutura do SemVer</h2>
<p>A estrutura do versionamento semântico é composta por três partes:</p>
<ol>
<li><p><strong>X (Major)</strong>: Versão maior. Esse número muda quando há alterações que quebram a compatibilidade com versões anteriores (ou seja, mudanças que afetam o comportamento esperado). Exemplos disso são mudanças de API que podem exigir que outros desenvolvedores modifiquem seus códigos.</p>
</li>
<li><p><strong>Y (Minor)</strong>: Versão menor. Esse número é incrementado quando são adicionadas funcionalidades novas, mas que são compatíveis com as versões anteriores. Por exemplo, novas funcionalidades ou melhorias que não afetam o funcionamento existente.</p>
</li>
<li><p><strong>Z (Patch)</strong>: Correção de bugs. Esse número é usado para atualizações que consertam problemas ou erros, sem adicionar novas funcionalidades ou quebrar a compatibilidade com as versões anteriores.</p>
</li>
</ol>
<p>Por exemplo, se você tem a versão <strong>1.4.2</strong>:</p>
<ul>
<li><p><strong>1</strong> indica uma versão maior (major),</p>
</li>
<li><p><strong>4</strong> indica que quatro conjuntos de novas funcionalidades foram adicionadas,</p>
</li>
<li><p><strong>2</strong> indica que houve duas correções de bugs desde a última mudança.</p>
</li>
</ul>
<h2 id="heading-por-que-usar-o-versionamento-semantico">Por que usar o versionamento semântico?</h2>
<p>O SemVer facilita muito o gerenciamento de versões de software, especialmente quando o código é usado por outras pessoas (como bibliotecas ou APIs). Ele deixa claro quando uma atualização vai exigir mudanças no código de quem está usando o software, ou quando é seguro fazer uma atualização sem preocupações. Veja alguns benefícios:</p>
<ul>
<li><p><strong>Transparência</strong>: Saber o tipo de mudança (bug fix, funcionalidade ou quebra de compatibilidade) de forma clara.</p>
</li>
<li><p><strong>Facilidade de atualização</strong>: Quem utiliza o software sabe se a atualização vai exigir mudanças no próprio código.</p>
</li>
<li><p><strong>Colaboração</strong>: Quando várias pessoas trabalham no mesmo projeto, o versionamento facilita a organização das contribuições.</p>
</li>
</ul>
<h2 id="heading-como-aplicar-o-versionamento-semantico">Como aplicar o versionamento semântico?</h2>
<p>Para usar o SemVer corretamente, siga essas diretrizes:</p>
<ol>
<li><p><strong>Comece com a versão 1.0.0</strong> quando o software for lançado publicamente. Versões abaixo disso, como 0.x.x, são para fases experimentais ou de testes, onde não há garantias de estabilidade.</p>
</li>
<li><p><strong>Altere o número Major</strong> (X) se as mudanças não forem compatíveis com versões anteriores.</p>
</li>
<li><p><strong>Altere o número Minor</strong> (Y) se você adicionar novas funcionalidades, mas mantendo a compatibilidade.</p>
</li>
<li><p><strong>Altere o número Patch</strong> (Z) quando corrigir bugs ou problemas menores.</p>
</li>
</ol>
<h2 id="heading-exemplo-pratico">Exemplo prático</h2>
<p>Vamos dizer que você lançou uma API na versão <strong>2.3.1</strong>. Se você corrigir um pequeno bug, a nova versão será <strong>2.3.2</strong>. Agora, se você adicionar uma nova funcionalidade sem quebrar a compatibilidade, seria <strong>2.4.0</strong>. No caso de uma alteração grande que muda o funcionamento da API, como remover ou alterar endpoints, você teria <strong>3.0.0</strong>.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>O versionamento semântico é essencial para manter a organização de projetos e facilitar a vida tanto dos desenvolvedores que mantêm o software quanto de quem utiliza. Ele cria uma linguagem comum para que todos entendam rapidamente o impacto de uma atualização e ajudem a evitar surpresas indesejadas.</p>
<p>Se quiser saber mais detalhes, confira o <a target="_blank" href="https://semver.org/">site oficial do SemVer</a>.</p>
]]></content:encoded></item></channel></rss>