Pular para o conteúdo principal
Voltar para Artigos

Quanto custa a arquitetura do seu app para um agente de IA

Categoria
Arquitetura
Autor
Michel Lütz
Tópicos
Arquitetura de Software, Mobile

Camadas, protocolos e injeção de dependência têm um preço em tokens e em janela de contexto. O que os papers de 2026 mostram, o que eu medi em um projeto Swift com MVVM, MVVM-C, VIP e TCA, e o que pesar antes de escolher um pattern.

Durante anos eu escolhi pattern de apresentação pensando em três leitores: quem escreve a feature hoje, quem vai dar manutenção daqui a seis meses e quem vai revisar o pull request. Em 2026 entrou um quarto leitor na sala, e ele lê de um jeito muito diferente dos outros três.

Um agente de código não tem "go to implementation". Ele tem busca por texto, leitura de arquivo e uma janela de contexto que enche. Cada protocolo que eu criei "para ficar testável", cada camada que eu adicionei "porque é o padrão do time", vira um arquivo a mais que ele precisa abrir para entender o que a tela de login faz. E arquivo aberto é token gasto.

Este post nasceu de uma leitura. O pessoal da Tech Leads Club publicou Quanto Custa Sua Arquitetura para a IA?, um levantamento dos papers recentes sobre como agentes navegam em código, e a discussão lá é de backend. Eu quis trazer a mesma pergunta para o mundo em que eu trabalho: iOS, Swift, SwiftUI, e a eterna conversa sobre MVVM, Coordinator, VIP e TCA. Fui atrás dos papers, conferi os números na fonte e medi o que dava para medir em um projeto meu.

Observação: é um post longo, e o campo tem meses de idade. Onde a evidência é fraca eu digo que é fraca. Desconfie de quem afirmar lei sobre esse assunto hoje, inclusive de mim.


1. Contexto é o recurso escasso

Quando eu abro um projeto no Xcode e quero saber quem implementa AuthRepositoryProtocol, eu seguro o Command, clico e escolho. Custo: dois segundos e zero memória de trabalho, porque a IDE resolve a indireção para mim.

O agente paga esse mesmo salto de outra forma. Ele busca o nome, recebe uma lista de ocorrências, abre os arquivos que parecem promissores e lê. Tudo que ele leu fica na janela de contexto até o fim da tarefa, tenha sido útil ou não.

O paper SWE-Edit deu nome a isso: context coupling. A interface padrão de edição mistura inspeção de código, planejamento da mudança e execução do edit na mesma janela. Os autores separaram essas responsabilidades em subagentes e relataram 2,1 pontos percentuais a mais de tarefas resolvidas, com 17,9% a menos de custo de inferência. O ganho não veio de um modelo melhor, veio de sujar menos o contexto.

A consequência prática é simples de dizer e difícil de engolir: o agente falha onde a relevância não pode ser descoberta lendo e buscando.

[PRINT 1] Captura de uma sessão do agente explorando o projeto: a sequência de buscas e leituras de arquivo antes do primeiro edit. Vale mostrar quantos arquivos ele abriu para uma mudança de uma linha.


2. O paradoxo da navegação

A resposta óbvia seria "é só esperar a janela de contexto crescer". O paper CodeCompass argumenta que não: janela maior não elimina o problema de navegação, só troca o modo de falha. O agente não falha porque o arquivo não cabe. Falha porque nunca descobre que aquele arquivo importava.

O experimento separou as tarefas em três grupos:

  1. dependências que uma busca por palavra-chave encontra;
  2. dependências alcançáveis seguindo a cadeia de imports;
  3. dependências escondidas, sem nenhuma sobreposição de vocabulário entre quem chama e quem executa.

O terceiro grupo é exatamente o que protocolo e injeção de dependência produzem. Nas tarefas desse grupo, o agente sem ajuda completou 76,2%. Com acesso a um grafo estrutural de dependências, 99,4%. A busca por BM25 ficou em 78,2%, quase empatada com o agente puro, o que diz muito: melhorar a busca textual não resolve o que não é textual.

O detalhe que mais me chamou atenção: em 58% das execuções em que a ferramenta de grafo estava disponível, o agente não a chamou nenhuma vez. Foi preciso instrução explícita no prompt para ele largar o hábito de buscar texto. Ferramenta instalada não é ferramenta usada.

A ressalva honesta: é um estudo de autor único, com 30 tarefas e 258 execuções. É evidência forte do mecanismo, não uma constante da natureza. Eu não sairia por aí dizendo que "uma em cada quatro tarefas falha".

Como isso aparece em Swift

No iOS, a dependência escondida tem cara conhecida. Este trecho é do módulo TCA do meu projeto de estudo:

@Reducer
public struct AuthFeature {
    @Dependency(\.authRepository) private var authRepository
    // ...
}

Quem lê AuthFeature não vê nenhuma chamada a AuthRepository(). A ligação acontece em outro arquivo, por uma chave registrada em DependencyValues:

private enum AuthRepositoryClientKey: DependencyKey {
    static let liveValue = AuthRepositoryClient.live
}

public extension DependencyValues {
    var authRepository: AuthRepositoryClient {
        get { self[AuthRepositoryClientKey.self] }
        set { self[AuthRepositoryClientKey.self] = newValue }
    }
}

Aqui ainda há uma pista léxica, porque o key path se chama authRepository e a busca acha. Troque esse nome por \.session ou \.apiClient e o fio some: o reducer e a implementação deixam de compartilhar vocabulário. O mesmo vale para container de injeção com resolve(AuthRepositoryProtocol.self) registrado no AppDelegate, para @Environment com chave customizada e para qualquer coisa montada por reflexão ou por JSON vindo do servidor.

Esse último caso eu conheço de perto. No portal de notícias com Server-Driven UI, a tela não existe no repositório do app: ela é descrita pelo backend e montada em tempo de execução. Para o usuário isso é ótimo. Para um agente que recebe "mude o card da home", a busca no projeto iOS volta vazia, porque a resposta mora em outro repositório. É a dependência escondida levada ao limite, e é uma decisão que continua valendo a pena, desde que eu saiba o preço e deixe o caminho escrito em algum lugar que o agente leia.


3. Camadas contra slices: o que dá para afirmar

A pergunta que todo mundo quer respondida é se arquitetura em camadas atrapalha o agente e se organizar por feature ajuda.

Até onde encontrei, ninguém rodou o experimento controlado. Não existe paper comparando as duas abordagens e medindo taxa de sucesso do agente. O que existe é evidência de mecanismo apontando para o mesmo lado:

  • Camadas espalham um caso de uso por vários arquivos, e o agente precisa carregar todos para entender um comportamento.
  • Um slice vertical põe o comportamento inteiro em um lugar: menos saltos, menos tokens, menos chance de perder o fio.
  • Quando o modelo tem liberdade estrutural total, ele centraliza. No estudo de geração de software do zero, com 94 repositórios e 7 modelos, todos mostraram preferência forte por estrutura monolítica, com mediana entre 1 e 3 arquivos, enquanto os repositórios humanos equivalentes tinham distribuição ampla. Os autores atribuem isso a evitar erro de dependência entre arquivos e a manter o estado inteiro dentro da janela. Colocação é o modo nativo do modelo.

Vale a nota: nesse mesmo estudo, a melhor taxa de sucesso foi 43,8%. Preferir monolito não significa fazer monolito bem feito.

O que eu medi

Eu tenho um projeto que serve bem para essa conversa. O swift-patterns-studies implementa a mesma jornada de login e cadastro quatro vezes, com MVVM, MVVM-C, VIP e TCA, sobre a mesma base de domínio e rede. É o projeto do post MVVM não é arquitetura.

Fiz a pergunta que um agente faria: quantos arquivos eu preciso abrir para entender o que acontece quando o usuário toca em "Entrar"?

A base comum (Modules/Shared) custa o mesmo para todos: protocolo do repositório, implementação, endpoint, DTO, credenciais, token e erro. São 7 arquivos e 163 linhas. A diferença está na camada de apresentação:

PatternArquivos do fluxo de loginLinhasTotal com a base comum
MVVM3 (View, ViewModel, FlowView)12410 arquivos, 287 linhas
MVVM-C6 (View, ViewModel, Coordinator, evento, rota, protocolo)18013 arquivos, 343 linhas
VIP6 (View, Presenter, Interactor, Models, Router, FlowView)20813 arquivos, 371 linhas
TCA3 (Feature, Client, View)25410 arquivos, 417 linhas

Três leituras, com as ressalvas na frente: contei linhas, não tokens. O projeto é pequeno e didático. E no TCA o reducer junta login e cadastro, então as 254 linhas não são só login.

Primeira: número de arquivos e número de linhas contam histórias diferentes. O TCA tem o maior volume e o menor número de saltos, porque estado, ações e efeitos estão no mesmo arquivo. O VIP tem volume parecido com o do MVVM-C e o dobro de saltos do MVVM. Para o agente, salto custa mais que linha: cada salto é uma busca, uma decisão sobre qual resultado abrir e o risco de abrir o errado.

Segunda: o custo do pattern é pequeno perto do custo da base. Sete dos dez arquivos do MVVM são Shared. Quem quer economizar contexto ganha mais organizando bem o domínio e a rede do que trocando MVVM por outra sigla.

Terceira: o projeto já está organizado por feature dentro de cada módulo (Features/Auth/Login/), e isso ajuda independente do pattern. O agente lista uma pasta e vê o comportamento inteiro.

[PRINT 2] Árvore de pastas do módulo VIP ao lado da do módulo MVVM no Xcode, com os arquivos do fluxo de login destacados.

O mesmo caso de uso, duas organizações

Para deixar concreto, compare duas formas de dispor a mesma feature. A primeira é a que eu vi em muito projeto enterprise:

Presentation/Views/LoginView.swift
Presentation/ViewModels/LoginViewModel.swift
Domain/UseCases/LoginUseCase.swift
Domain/UseCases/LoginUseCaseProtocol.swift
Domain/Repositories/AuthRepositoryProtocol.swift
Data/Repositories/AuthRepository.swift
Data/DTOs/TokenDTO.swift
Data/Mappers/TokenMapper.swift
DI/AuthAssembly.swift

A segunda coloca por feature e só separa o que tem motivo real para ser separado:

Features/Auth/Login/LoginView.swift
Features/Auth/Login/LoginViewModel.swift
Features/Auth/AuthRepository.swift      // protocolo, implementação e DTO
Features/Auth/AuthModels.swift

Na primeira, "o que o login faz" está em nove arquivos e cinco pastas, e nenhuma das pastas diz "login". Na segunda, está em quatro arquivos e uma pasta que grita o nome da feature. A direção das dependências pode ser a mesma nas duas. O que muda é quanto o leitor paga para reconstituir o comportamento.

Por isso a formulação que eu sustento é esta: a variável real é a quantidade de indireção, não o nome do padrão. Uma arquitetura em camadas pragmática, com pouca indireção especulativa, provavelmente se sai parecido com slices. Camadas aplicadas como ritual, com um protocolo para cada classe, são o pior caso.


4. O problema não é o protocolo, é o protocolo especulativo

Swift é uma linguagem orientada a protocolo, e eu não estou propondo abandonar isso. O ponto é distinguir dois usos.

No meu projeto de estudo, AuthRepositoryProtocol tem duas implementações que existem de verdade: AuthRepository, que fala com a API, e MockAuthRepository, usado pelos testes de todos os módulos. E tem quatro consumidores diferentes, um por pattern. Esse protocolo documenta uma fronteira real e vale o custo, para humano e para agente.

Agora o outro caso, que todo mundo já escreveu:

protocol LoginUseCaseProtocol {
    func execute(_ credentials: AuthCredentials) async throws -> AuthToken
}

final class LoginUseCase: LoginUseCaseProtocol {
    private let repository: any AuthRepositoryProtocol

    init(repository: any AuthRepositoryProtocol) {
        self.repository = repository
    }

    func execute(_ credentials: AuthCredentials) async throws -> AuthToken {
        try await repository.login(credentials)
    }
}

Uma implementação, nenhuma regra, um repasse. Esse protocolo existe porque "use case tem protocolo". Ele converte uma chamada estática, que a busca encontra, em uma ligação de runtime, que o agente precisa reconstituir por inferência. É imposto de navegação sem ganho de informação.

Há um segundo efeito, mais sutil. Rode uma busca por func login no projeto e veja quantas ocorrências voltam: o protocolo, a implementação real, o mock, o Interactor do VIP, o método privado de cada ViewModel. Quando o agente navega por nome, cada homônimo é uma chance de abrir o arquivo errado. Trabalhos de localização de falha com agentes, como o LLM4FL, existem justamente porque navegar por um repositório grande só com texto degrada, e eles recorrem a grafo de chamadas para compensar.

A regra que eu adotei:

  • Protocolo com mais de uma implementação real, incluindo um mock que os testes usam de verdade: mantém.
  • Protocolo com uma implementação, criado por hábito: apaga e usa o tipo concreto. No dia em que a segunda implementação aparecer, extrair o protocolo leva um minuto.

[PRINT 3] Resultado da busca por func login no projeto, mostrando as ocorrências homônimas em arquivos diferentes.


5. O outro lado: o agente também escreve, e escreve mal sozinho

Seria confortável parar aqui e concluir "arquitetura simples, deixa o agente voar". A evidência sobre o que agentes produzem não deixa.

O estudo Speed at the Cost of Quality comparou 806 repositórios que adotaram o Cursor com 1.380 repositórios de controle. O volume de código adicionado subiu cerca de 28,6%, mas o ganho só foi significativo nos dois primeiros meses. Os avisos de análise estática subiram 30,3% e a complexidade do código 41,6%, e esses dois ficaram. O agente cava o buraco em que ele mesmo vai cair depois.

O paper Microskill Architecture retoma um termo que já circulava, a miragem modular: código gerado que parece modular, com classes distribuídas em arquivos bem nomeados, mas com acoplamento semântico severo e dependências cíclicas. Modular só no nome. O mesmo paper cita o estudo Are We SOLID Yet? (ASE 2025), segundo o qual a capacidade dos modelos de reconhecer e aplicar princípios como aberto/fechado e inversão de dependência cai conforme a complexidade do código ao redor aumenta.

Traduzindo para o nosso mundo: slice sem guarda-corpo vira um ViewModel de 900 linhas que chama URLSession direto. Arquitetura para agentes não é só ajudar o agente a ler, é restringir o que ele escreve.

Guarda-corpos que funcionam em Swift

O que eu uso, do mais forte para o mais fraco:

  1. Fronteira de módulo no Swift Package Manager. Se Shared não declara dependência de um módulo de apresentação, o agente não consegue importar na direção errada: não compila. É a única regra arquitetural que o compilador garante.
  2. Controle de acesso. internal por padrão, public só no que é contrato. O agente não chama o que não enxerga.
  3. Lint com limite de tamanho. Regra de comprimento de arquivo e de função no SwiftLint transforma "não faça ViewModel gigante" em erro de build.
  4. Testes que o agente roda. Critério de aceite executável vale mais que parágrafo de instrução.
  5. Arquivo de contexto do repositório, o AGENTS.md.

Sobre o quinto item a evidência é dividida, e eu acho isso saudável. Um estudo com 10 repositórios e 124 pull requests mediu 28,64% a menos de tempo mediano de execução e 16,58% a menos de tokens de saída com AGENTS.md presente. Outro, mais amplo, concluiu que arquivos de contexto em geral não aumentam a taxa de sucesso e ainda sobem o custo de inferência em cerca de 20%, porque o agente obedece e faz mais coisa.

Minha leitura das duas: AGENTS.md que repete o que o código já diz é custo puro. AGENTS.md que registra o que não dá para descobrir lendo é o que paga. No repositório deste site, o arquivo tem uma seção com quatro armadilhas, e a primeira é que escrever images/ no caminho de uma imagem gera uma URL duplicada que quebra em produção sem o build reclamar. Nenhuma busca revela isso. É exatamente o tipo de relevância escondida de que o CodeCompass fala, só que resolvida com uma frase em vez de um grafo.

Esse é o mesmo raciocínio do Spec-Driven Development: a spec e o arquivo de contexto existem para carregar a intenção que o código não carrega.


6. O que pesar na hora de escolher o pattern

Juntando tudo, estes são os critérios que eu passei a colocar na mesa, ao lado dos de sempre (tamanho do time, complexidade do fluxo, testabilidade):

CritérioPergunta práticaSinal de alerta
Saltos por caso de usoQuantos arquivos eu abro para entender uma ação do usuário?Mais de 6 ou 7 para um fluxo simples
Descobribilidade léxicaBuscar o nome da feature acha tudo que participa dela?Ligação feita por container, reflexão ou configuração remota
Protocolos com uma implementaçãoQuantos existem sem segunda implementação real?"Todo use case tem protocolo"
Tamanho do maior arquivoCabe folgado na janela junto com os vizinhos?Reducer ou ViewModel de várias centenas de linhas
HomônimosQuantos métodos com o mesmo nome em tipos diferentes?execute, handle, load em dezenas de tipos
Regras que o compilador garanteO que impede o agente de furar a fronteira?Arquitetura que só existe no README

E como os quatro patterns do projeto de estudo se saem, na minha avaliação:

  • MVVM: o mais barato de ler. Poucos saltos, navegação visível na própria View. O risco é do lado da escrita: sem limite, o ViewModel engorda.
  • MVVM-C: paga três arquivos a mais pela navegação desacoplada. Vale quando o fluxo tem ramificação de verdade. Para login e cadastro, é imposto.
  • VIP: o mais caro em saltos. Em compensação, os papéis são tão rígidos que o agente erra menos o lugar de cada coisa. É o pattern em que a restrição de escrita é mais forte e a leitura é mais cara.
  • TCA: poucos saltos e alta colocação, com estado e ações explícitos e greppáveis. Os custos são o arquivo que cresce e a dependência resolvida por key path. Dê nomes óbvios às dependências.

Nenhum deles ganha em tudo. O que muda é que agora "custo de leitura para o agente" entra na conta, e antes não entrava.

Um aviso sobre janela de contexto, que é a pergunta que mais recebo: um projeto que cabe inteiro na janela não está resolvido. Há indício de que contexto longo degrada o desempenho do modelo mesmo quando a informação certa está lá dentro, e o CodeCompass mostra que o gargalo é descobrir o que importa, não fazer caber. Tratar a janela como orçamento, e não como depósito, continua sendo a postura certa.

[PRINT 4] Painel de uso de tokens de duas sessões com a mesma tarefa: uma no módulo MVVM, outra no módulo VIP. Se os números não mostrarem diferença relevante, o print entra do mesmo jeito e o texto acima é ajustado.


7. Arquitetura sempre otimizou para o leitor mais caro

O enquadramento que mais me ajudou vem do texto da Tech Leads Club, e eu concordo com ele: por décadas, o leitor mais caro do código foi o desenvolvedor humano. Construímos camadas, abstrações e indireção para gerenciar o que humanos acham difícil, que é mudança, time grande e escala.

Agora uma parte crescente da leitura e da escrita é feita por um modelo com custos invertidos:

  • indireção é cara;
  • duplicação é barata;
  • colocação vale ouro;
  • obviedade ganha de elegância.

Isso não quer dizer que Clean Architecture estava errada. Quer dizer que o modelo de custo para o qual ela otimizava mudou. E há uma ironia boa nisso: quase tudo que ajuda o agente é coisa que bons engenheiros já defendiam. YAGNI, baixo acoplamento, alta coesão, estrutura de pastas que grita o domínio. Os agentes não inventaram princípio novo, só aumentaram o preço de violar os antigos.


8. O que continua em aberto

  1. Legibilidade para agente e para humano vão divergir? Hoje andam juntas. Não sei se continuam.
  2. A solução é convenção ou ferramenta? Há evidência dos dois lados. O estudo LLM Agents Can See Code Repositories deu ao agente uma visão gráfica do repositório e relatou 26% a menos de tokens de entrada, mantendo ou melhorando a acurácia. O Theory of Code Space mostra que os modelos ainda têm dificuldade de formar um mapa coerente da arquitetura enquanto exploram. Meu palpite é que as duas coisas se somam: organização que reduz saltos e ferramenta que mostra o grafo.
  3. Decisão de arquitetura tomada por prompt merece governança? O paper Architecture Without Architects chama de vibe architecting o que acontece quando o agente escolhe framework e estrutura em segundos e ninguém revisa aquilo como decisão arquitetural. Eu acho que merece, e é mais um motivo para manter ADRs no repositório.
  4. E o experimento controlado? Camadas contra slices, mesmo app, mesmas tarefas, taxa de sucesso e tokens medidos. O projeto de estudo com os quatro patterns é um bom candidato, e é o que eu quero rodar em seguida.

Conclusão

Se eu tivesse que comprimir em três princípios, seriam os mesmos do texto que me provocou, traduzidos para Swift:

  1. Colocação sobre camadas. O comportamento inteiro em um lugar: pasta por feature, módulo por domínio.
  2. Chamada estática sobre indireção de runtime. Protocolo só quando existe mais de uma implementação real.
  3. Descobribilidade léxica sobre fiação escondida. Se a busca não acha, o agente provavelmente também não. E o que não dá para achar, escreva no AGENTS.md.

E um quarto, que vale para o post inteiro: escreva suas conclusões com o nível de confiança que a evidência sustenta. Metade dos números acima vem de estudos pequenos, e a minha medição é contagem de linhas em um projeto didático.

Na próxima vez que alguém propuser um pattern no seu time, acrescente uma pergunta às de sempre: quantos arquivos um leitor sem IDE precisa abrir para entender um toque no botão? A resposta serve para o agente, e serve para a pessoa que entra no time na segunda-feira.


Referências

  1. TECH LEADS CLUB. Quanto Custa Sua Arquitetura para a IA? Disponível em: https://www.techleads.club/c/blog/quanto-custa-sua-arquitetura-para-a-ia. Acesso em: 2 out. 2026.
  1. PAIPURU, T. CodeCompass: Navigating the Navigation Paradox in Agentic Code Intelligence. arXiv:2602.20048, 2026. Disponível em: https://arxiv.org/abs/2602.20048.
  1. ZHANG, Y. et al. SWE-Edit: Rethinking Code Editing for Efficient SWE-Agent. arXiv:2604.26102, 2026. Disponível em: https://arxiv.org/abs/2604.26102.
  1. HU, R. et al. Evaluating LLM-Based 0-to-1 Software Generation in End-to-End CLI Tool Scenarios. arXiv:2604.06742, 2026. Disponível em: https://arxiv.org/abs/2604.06742.
  1. HE, H. et al. Speed at the Cost of Quality: How Cursor AI Increases Short-Term Velocity and Long-Term Complexity in Open-Source Projects. arXiv:2511.04427, 2025. Disponível em: https://arxiv.org/abs/2511.04427.
  1. ZARE, M.; ABDOLRAHMANI, O. Microskill Architecture: A Modular Skill-Driven Framework for AI-Native Code Generation. arXiv:2606.05720, 2026. Disponível em: https://arxiv.org/abs/2606.05720.
  1. PEHLIVAN, F. et al. Are We SOLID Yet? An Empirical Study on Prompting LLMs to Detect Design Principle Violations. Proceedings of the 40th IEEE/ACM International Conference on Automated Software Engineering (ASE), p. 3958-3970, 2025.
  1. KONRAD, P. M. et al. Architecture Without Architects: How AI Coding Agents Shape Software Architecture. arXiv:2604.04990, 2026. Disponível em: https://arxiv.org/abs/2604.04990.
  1. GLOAGUEN, T. et al. Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents? arXiv:2602.11988, 2026. Disponível em: https://arxiv.org/abs/2602.11988.
  1. LULLA, J. L. et al. On the Impact of AGENTS.md Files on the Efficiency of AI Coding Agents. arXiv:2601.20404, 2026. Disponível em: https://arxiv.org/abs/2601.20404.
  1. SAPUNOV, G. Theory of Code Space: Do Code Agents Understand Software Architecture? arXiv:2603.00601, 2026. Disponível em: https://arxiv.org/abs/2603.00601.
  1. MA, D. et al. LLM Agents Can See Code Repositories. arXiv:2606.14061, 2026. Disponível em: https://arxiv.org/abs/2606.14061.
  1. RAFI, M. N. et al. A Multi-Agent Approach to Fault Localization via Graph-Based Retrieval and Reflexion (LLM4FL). arXiv:2409.13642, 2024. Disponível em: https://arxiv.org/abs/2409.13642.
  1. MICHEL TLUTZ. swift-patterns-studies: a mesma jornada de autenticação em MVVM, MVVM-C, VIP e TCA. Disponível em: https://github.com/micheltlutz/swift-patterns-studies. Acesso em: 2 out. 2026.

Tags

  • arquitetura
  • ia
  • agentes
  • swift
  • swiftui
  • ios
  • patterns
  • mobile