allanlotta
~/blog/agent-native-software

Software agent-native

Criamos frameworks porque escrever código era caro. Criamos infraestrutura como assinatura porque operar servidores era caro. A IA está deixando as duas coisas dramaticamente mais baratas.

Então por que ainda desenhamos software como se cada linha e cada operação de servidor precisassem ser feitas à mão?

Talvez a stack AI-native não seja mais abstração. Talvez seja menos.

Passamos uma década otimizando developer experience. A próxima década é sobre agent experience. Agent-native não significa software escrito com IA. Significa software cuja arquitetura, infraestrutura, segurança e operação foram deliberadamente desenhadas para que um agente consiga compreender e operar o sistema inteiro com pouca ambiguidade e pouca dependência humana.

Contexto é o recurso escasso

Para um humano, escrever código é caro. Para um agente, gerar código é barato. O que ficou caro é o contexto necessário para alterar o sistema corretamente.

Pegue duas aplicações que fazem a mesma coisa. A primeira é:

HTML
CSS
TypeScript
Bun
Postgres

A segunda é:

Next.js       Prisma      Clerk
React         tRPC        Supabase
React Query   Zod         Vercel
Zustand       Tailwind    Sentry
shadcn                    PostHog

A segunda provavelmente tem menos código escrito pela empresa. Ela também exige que um agente entenda muito mais conceitos antes de conseguir mudar qualquer coisa com segurança.

Linhas de código deixaram de ser uma boa medida de complexidade.

Superfície semântica

Chame de superfície semântica a quantidade de conhecimento que um agente precisa carregar para fazer uma mudança corretamente: arquivos, dependências, APIs de framework, convenções, configuração, saltos entre abstrações, serviços externos, regras implícitas.

Uma feature que exige entender

page → component → hook → provider → service
     → repository → ORM → serviço externo

tem superfície muito maior que

customers.ts → db.ts

mesmo que o segundo tenha mais linhas. O objetivo de uma arquitetura agent-native é minimizar essa superfície sem abrir mão de corretude, segurança ou requisitos reais do produto.

Ou seja: minimizar conceitos, não código. Cinquenta linhas explícitas podem ser melhores que quinze linhas que dependem de cinco abstrações. Em um mundo AI-native, código pode ser mais barato que abstração.

Localidade, e nada de mágica

Uma alteração deve tocar poucos lugares. Uma feature, um a três arquivos. Arquivos são fronteiras de contexto, e os nomes deveriam revelar a arquitetura:

src/
  app.ts        api.ts       state.ts     types.ts
  pages/        dashboard.ts customers.ts reports.ts
  components/   table.ts     modal.ts
server/
  server.ts     db.ts        auth.ts
AGENTS.md

Agentes funcionam melhor quando causa e efeito estão próximos. Prefira

router.get("/customers", getCustomers);

a comportamentos que só se descobrem perseguindo decorators, plugins, metaprogramação ou convenções espalhadas pelo projeto. Mágica economiza digitação e gasta compreensão.

Depois, reduza a quantidade de maneiras de resolver o mesmo problema. Não useState e Redux e Context e Zustand e signals e React Query e stores próprias, tudo ao mesmo tempo. Uma decisão, escrita:

HTTP           → api.ts
Estado comum   → state.ts
Páginas        → pages/
UI reutilizada → components/
Banco          → db.ts

Menos liberdade arquitetural pode produzir mais autonomia do agente.

Plataforma primeiro, dependências depois

Antes de instalar algo, pergunte se a plataforma já resolve. HTML, CSS, DOM APIs, ES modules, fetch, URL, FormData, WebSocket, JSON, HTTP. No servidor: arquivos, processos, SQL, cron, logs. Na infraestrutura: Linux, SSH, systemd, Docker, Caddy, PostgreSQL.

Essas coisas têm uma propriedade que importa enormemente para agentes: são estáveis, textuais, previsíveis e universalmente compreendidas.

Toda dependência adiciona uma nova linguagem ao sistema — uma API, configuração, versões, documentação, breaking changes, advisories, edge cases, comportamento implícito. Então a regra não é "todo projeto precisa disso". A regra é: uma dependência precisa remover mais complexidade do que introduz.

Isso não é um argumento de que React, Next, Supabase ou Vercel são ruins. Eles resolvem problemas reais. O que muda é que deixam de ser default e passam a precisar se justificar. Se removêssemos esse framework, o sistema ficaria realmente mais difícil para um agente operar? Se não, provavelmente não precisamos dele.

Vale na direção oposta também: zero dependências também é dogma. Uma biblioteca de criptografia madura é mais segura que a sua. Uma boa biblioteca de gráficos economiza semanas. Use dependências deliberadamente, não culturalmente.

Source code é uma interface para o agente

Nunca minifique o source para economizar tokens. Isto

export async function getCustomer(id: string) {
  const response = await fetch(`/api/customers/${id}`);

  if (!response.ok) {
    throw new Error("Failed to load customer");
  }

  return response.json();
}

custa mais tokens que uma versão minificada em uma linha, e vale cada um deles, porque é menos ambíguo. Bem nomeado, formatado, tipado, explícito. Minificação pertence ao artefato de produção, não ao código que o agente precisa ler. Formatação é informação semântica.

Documentação segue a mesma lógica: um mapa, não um livro. Um AGENTS.md, cinquenta a cem linhas, dizendo qual é a arquitetura, onde as coisas ficam, quais são as regras e quais comandos verificam o trabalho. Ele funciona como o system prompt do repositório.

Testes são os sentidos do agente

Em desenvolvimento autônomo, testes deixam de ser só proteção contra regressão. Eles viram o sistema sensorial. O loop é:

entender → alterar → typecheck → testar → rodar
         → inspecionar → corrigir → repetir

Um projeto precisa permitir que o agente descubra sozinho que errou. Isso significa comandos simples

bun typecheck
bun test
bun run security
bun run build

e resultados igualmente simples.

TYPECHECK PASS
TESTS PASS
SECURITY PASS
BUILD PASS

Autonomia não é ausência de restrições. Autonomia vem de feedback confiável.

Possua o simples. Alugue o difícil.

A engenharia moderna não adicionou só frameworks. Ela transformou infraestrutura em assinatura. Uma aplicação pequena frequentemente nasce com oito fornecedores antes de ter qualquer escala real — e com eles oito contas, oito SDKs, oito modelos de autenticação, oito documentações, oito políticas de preço, oito pontos de falha.

A complexidade não desapareceu. Ela se mudou para fora do seu repositório.

Dependência externa é uma decisão arquitetural com quatro custos: dinheiro, contexto que o agente precisa carregar, acoplamento a APIs proprietárias, e outra empresa dentro do seu caminho crítico. Uma stack agent-native também minimiza superfície de dependência econômica.

Então: possua o simples, alugue o difícil. Ou, mais preciso: alugue complexidade, não conveniência.

Provavelmente possuir    Provavelmente alugar
─────────────────────    ────────────────────
servidor da aplicação    pagamentos
banco de dados           mitigação de DDoS
cache                    KYC
cron                     SMS
workers                  reputação de e-mail
arquivos estáticos       integração bancária
monitoramento básico     identidade corporativa

A Stripe é o bom exemplo. Você não está pagando por POST /payment. Está pagando por redes de cartão, prevenção a fraude, PCI, chargebacks, compliance e trilhos globais de pagamento. Isso é complexidade real. Cloudflare na frente da aplicação é o mesmo argumento: uma rede de borda e mitigação de DDoS que você nunca deveria construir. Alugue complexidade adversarial ou especializada.

O que sobra pode ser sem graça, e é justamente esse o ponto:

Internet → Cloudflare → Caddy → app Bun → PostgreSQL

Em uma única VPS. Talvez com Docker. Talvez sem, dependendo do sistema.

Segurança não pode morar no prompt

Essa parte não é negociável. Agent-native não significa dar root para um modelo e pedir para ele tomar cuidado.

Nunca use prompts como fronteira de segurança.

Segurança precisa existir abaixo do agente, na infraestrutura: apenas 443 público, PostgreSQL não exposto, SSH com chave e sem login de root, a aplicação rodando com o próprio usuário, um role de banco que não é superuser, migrations em um role separado, secrets fora do repositório.

E ela deveria ser executável. Um projeto pode ter bun run security verificando o que uma máquina consegue verificar:

✓ aplicação não roda como root
✓ PostgreSQL não está exposto publicamente
✓ nenhum secret commitado
✓ security headers presentes
✓ testes de autenticação e autorização passam
✓ testes de SQL injection passam
✓ backup existe e restaura

Aí segurança deixa de ser uma página de documentação e vira comportamento verificável por máquina. O mesmo vale para backup: backup não verificado é esperança. Um agente pode dumpar produção, restaurar em uma instância temporária, rodar testes de integridade e reportar RESTORE PASS, periodicamente. O que um humano faz ocasionalmente vira rotina contínua.

Agentes também são superfície de ataque

Esse é o risco genuinamente novo. Um atacante manda uma requisição HTTP, a requisição cai no log, o agente lê o log. O mesmo vale para tickets, e-mails, documentos, issues, páginas web, entrada de usuário. Conteúdo parecido com instrução chega por dados.

Dado não confiável nunca pode virar autoridade confiável.

O que leva à frase que eu manteria se só pudesse manter uma: o agente toma decisões, a infraestrutura define autoridade. Um agente de monitoramento recebe somente leitura. Um agente de deploy recebe deploy, restart, rollback. Um agente de migration recebe permissão de migration. Um agente de backup recebe leitura, escrita de backup e restore em um banco isolado. Nenhum agente precisa de root, DNS, superuser de produção, owner do GitHub e todos os secrets.

Quanto mais autonomia damos, mais importam menor privilégio, isolamento, rollback, backups imutáveis, audit logs e aprovação para ações catastróficas. A meta não é limitar produtividade. É limitar o dano de um erro. Autônomo não significa onipotente.

Os dois testes

Antes de adicionar uma biblioteca, framework ou serviço:

  1. Qual problema concreto isso resolve?
  2. Existe uma primitiva da plataforma que resolve suficientemente?
  3. Quantos conceitos novos isso adiciona?
  4. Quanta superfície operacional — configs, tokens, dashboards?
  5. Quanto custa hoje, em 10x, em 100x?
  6. Quão difícil seria remover?
  7. Um agente conseguiria operar a alternativa simples de forma confiável?
  8. Estamos alugando segurança real ou só conveniência?
  9. O que acontece quando esse fornecedor cai?
  10. Isso remove mais complexidade do que introduz?

E antes de adicionar uma abstração, uma pergunta: isso reduz a quantidade de contexto que um agente futuro precisa para alterar este sistema corretamente? Se não, provavelmente estamos apenas deslocando complexidade.

Refatorar nessa direção

Um agente encarregado de tornar um projeto existente agent-native procura abstrações usadas uma única vez para inlinar, wrappers de wrappers para achatar, dependências triviais para trocar por APIs da plataforma, sistemas de estado concorrentes para consolidar, comportamento implícito para tornar explícito, hierarquias profundas de diretórios para achatar, SaaS usado por conveniência para avaliar self-hosting, testes frágeis para tornar determinísticos, documentação longa para comprimir em um mapa operacional.

Com uma restrição acima de todas as outras: preserve o comportamento primeiro. Agent-native não é licença para reescrever.

A medida que falta

Para deixar de ser opinião, isso precisa de um número. Algo como custo de modificação por agente:

tokens consumidos + arquivos inspecionados
+ arquivos modificados + dependências consultadas
+ tool calls + iterações de teste
+ tempo até resultado verificado

Rodar as mesmas tarefas contra uma arquitetura Next com serviços gerenciados e contra uma agent-native, e comparar. Eu ainda não tenho esse dataset. Acho que é o benchmark mais interessante que ninguém está publicando.

A diretiva

A metade operacional disso cabe em um repositório. Este é o bloco que eu colocaria no AGENTS.md:

# Agent-Native Software Directive

This repository is designed primarily for autonomous AI
development and operation.

The goal is NOT to minimize lines of code. The goal is to
minimize the context, concepts, dependencies and external
knowledge required to safely understand, modify, test,
deploy and operate the system.

Principles
- Minimize semantic surface area.
- Prefer platform-native primitives.
- Prefer explicit behavior over implicit behavior.
- Prefer locality over distributed abstractions.
- Prefer one obvious implementation path.
- No abstraction used once without strong reason.
- Every dependency must remove more complexity than
  it introduces.
- Make architecture discoverable from filenames.
- Use tests as feedback for autonomous agents.
- Make validation deterministic.
- Keep documentation short and operational.
- Optimize source for comprehension, not size.
- Do not adopt frameworks because they are defaults.
- Own simple infrastructure. Rent specialized,
  adversarial or regulatory complexity.
- Never use prompts as security boundaries.
- The infrastructure, not the model, defines authority.

Validation
Every meaningful change ends with typecheck, test,
security and build. A change is not complete because
code was generated. It is complete when behavior has
been verified.

Cinco frases

Código está ficando barato. Contexto está ficando caro.

Possua o simples. Alugue o difícil.

Toda abstração precisa reduzir mais complexidade do que introduz.

O agente toma decisões. A infraestrutura define autoridade.

Construa sistemas pequenos o bastante para um agente entender, operar, testar e reparar de ponta a ponta.