Agentic Development e por que Go é uma excelente escolha

Principais Pontos
  • A simplicidade do Go diminui a quantidade de opções, facilitando a vida dos agentes ao reduzir o espaço de soluções possíveis.
  • Código escrito em 2012 ainda é válido, tornando o treinamento dos modelos de IA mais duradouro e confiável.
  • Para desenvolvimento assistido por agentes, priorize simplicidade.

Se você está construindo sistemas com agentes de IA (ou planejando fazer isso), a escolha da stack de tecnologia faz toda a diferença. A Anthropic propôs uma definição que achei bem interessante:

O que são agentes?

O termo “agente” pode ser definido de várias maneiras. Alguns clientes o veem como um sistema totalmente autônomo que opera de forma independente por longos períodos e utiliza diferentes ferramentas para executar tarefas complexas. Outros usam a palavra para descrever implementações mais prescritivas, que seguem fluxos de trabalho predefinidos. Na Anthropic, consideramos todas essas variações como sistemas agênticos, mas fazemos uma distinção arquitetural crucial entre fluxos de trabalho e agentes:

  • Fluxos de trabalho (Workflows) são sistemas em que LLMs e ferramentas são orquestrados por caminhos predefinidos.
  • Agentes são sistemas em que LLMs direcionam dinamicamente seus próprios processos e o uso de ferramentas, mantendo controle sobre como realizam as tarefas.

Ao desenvolver software com assistência de LLMs, a questão passa a não ser mais qual linguagem/stack tem mais recursos, mas qual oferece o caminho mais claro e previsível para que agentes de IA consigam operar com mais efetividade.

Go é intecionalmente "boring" (e isso é perfeito para IA)

"Choose Boring Technology" não é apenas um slogan, é uma filosofia estratégica. Quando você está construindo sistemas agênticos, você já está gastando suas "fichas de inovação" em IA, LLMs e orquestração complexa. A última coisa que você quer é lutar com a linguagem de programação.

Já explorei anteriormente em detalhes como Go favorece o minimalismo através de sua filosofia de simplicidade radical, eliminando abstrações e complexidade desnecessárias.

Vamos dar uma olhada em um exemplo:

func ProcessRequest(ctx context.Context, req Request) (*Response, error) {
    if err := validateRequest(req); err != nil {
        return nil, fmt.Errorf("invalid request: %w", err)
    }
    
    result, err := callProvider(ctx, req.input)
    if err != nil {
        return nil, fmt.Errorf("provider call failed: %w", err)
    }
    
    return &Response{Data: result}, nil
}

Código escrito com a sintaxe de versões antigas, ainda compila e roda sem problemas. Isso por que Go não tem metaprogramação misteriosa, sem sintaxe experimental, sem mágica implícita.

Linguagens "excitantes" vêm com custos operacionais que dificultam e muito o trabalho dos LLMs:

  • Documentação fragmentada
  • Ecossistema instável
  • Debugging complexo
  • Curva de aprendizado íngreme

Rob Pike, um dos criadores do Go, descreveu a linguagem como sendo adequada para desenvolvedores que não estão equipados para lidar com uma linguagem complexa. A frase pode ser controversa, mas é reveladora. Agora substitua "desenvolvedores" por "agentes de IA".

A simplicidade do Go é uma virtude para a geração de código por IA:

  • Uma única maneira de fazer as coisas: Laços for são a única estrutura de repetição. A formatação é padronizada com gofmt.
  • Sintaxe concisa e clara: Não há herança, construtores complexos, anotações ou exceções.
  • Gerenciamento de erros explícito: O padrão if err != nil força o agente a lidar com erros no local.

O problema da paralisia por opções

Para uma LLM, uma gramática menor e menos "açúcar sintático" significam uma probabilidade menor de gerar código bizarro (a famosa alucinação), ineficiente ou simplesmente incorreto:

# Python oferece múltiplas maneiras de fazer a mesma coisa
# Um agente pode escolher qualquer uma delas, nem sempre a melhor

# Opção 1: Lista tradicional
users = []
for user in get_users():
    if user.is_active:
        users.append(user.name)

# Opção 2: List comprehension
users = [user.name for user in get_users() if user.is_active]

# Opção 3: Filter + map
users = list(map(lambda u: u.name, filter(lambda u: u.is_active, get_users())))

# Opção 4: Com walrus operator
users = [name for user in get_users() if user.is_active and (name := user.name)]

Go elimina essas decisões desnecessárias:

// Em Go, há essencialmente uma maneira idiomática de fazer isso
var users []string
for _, user := range getUsers() {
    if user.IsActive {
        users = append(users, user.Name)
    }
}

Estabilidade ao longo do tempo

Um dos maiores desafios para LLMs é a "deterioração do conhecimento". O modelo é treinado com dados de um determinado ponto no tempo. Em ecossistemas de rápida evolução, como JavaScript, um tutorial ou uma resposta do Stack Overflow de dois anos atrás pode estar completamente obsoleta.

Go possui milhões de projetos públicos no GitHub que foram usados para treinar modelos de IA. Crucialmente:

  • Código escrito para Go 1.1 ainda funciona no Go 1.22
  • Em JavaScript ou Python, frameworks e padrões mudam drasticamente a cada poucos anos

Essa estabilidade significa que o vasto corpo de código Go disponível na internet permanece relevante por muito mais tempo. Resultado:

  • Agentes geram código com maior probabilidade de estarem corretos
  • Padrões aprendidos continuam válidos
  • Menos erros por usar APIs obsoletas
  • Maior confiança no código gerado

Conclusão

A cultura Go de evitar breaking changes beneficia diretamente a qualidade do código gerado por IA. Enquanto outras linguagens competem em elegância ou expressividade, Go lidera por conta de sua simplicidade e previsibilidade e por isso eu apostaria nesta linguagem ao construir software com assistência de IA hoje em dia.


Receba mais conteúdo como este

Inscreva-se na newsletter para receber links, insights e análises sobre engenharia de software, arquitetura e liderança técnica diretamente no seu e-mail.

Assinar newsletter →