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:
- dependências que uma busca por palavra-chave encontra;
- dependências alcançáveis seguindo a cadeia de imports;
- 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:
| Pattern | Arquivos do fluxo de login | Linhas | Total com a base comum |
|---|---|---|---|
| MVVM | 3 (View, ViewModel, FlowView) | 124 | 10 arquivos, 287 linhas |
| MVVM-C | 6 (View, ViewModel, Coordinator, evento, rota, protocolo) | 180 | 13 arquivos, 343 linhas |
| VIP | 6 (View, Presenter, Interactor, Models, Router, FlowView) | 208 | 13 arquivos, 371 linhas |
| TCA | 3 (Feature, Client, View) | 254 | 10 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.swiftA 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.swiftNa 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 loginno 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:
- Fronteira de módulo no Swift Package Manager. Se
Sharednã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. - Controle de acesso.
internalpor padrão,publicsó no que é contrato. O agente não chama o que não enxerga. - 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.
- Testes que o agente roda. Critério de aceite executável vale mais que parágrafo de instrução.
- 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ério | Pergunta prática | Sinal de alerta |
|---|---|---|
| Saltos por caso de uso | Quantos arquivos eu abro para entender uma ação do usuário? | Mais de 6 ou 7 para um fluxo simples |
| Descobribilidade léxica | Buscar o nome da feature acha tudo que participa dela? | Ligação feita por container, reflexão ou configuração remota |
| Protocolos com uma implementação | Quantos existem sem segunda implementação real? | "Todo use case tem protocolo" |
| Tamanho do maior arquivo | Cabe folgado na janela junto com os vizinhos? | Reducer ou ViewModel de várias centenas de linhas |
| Homônimos | Quantos métodos com o mesmo nome em tipos diferentes? | execute, handle, load em dezenas de tipos |
| Regras que o compilador garante | O 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
- Legibilidade para agente e para humano vão divergir? Hoje andam juntas. Não sei se continuam.
- 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.
- 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.
- 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:
- Colocação sobre camadas. O comportamento inteiro em um lugar: pasta por feature, módulo por domínio.
- Chamada estática sobre indireção de runtime. Protocolo só quando existe mais de uma implementação real.
- 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
- 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.
- PAIPURU, T. CodeCompass: Navigating the Navigation Paradox in Agentic Code Intelligence. arXiv:2602.20048, 2026. Disponível em: https://arxiv.org/abs/2602.20048.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- MA, D. et al. LLM Agents Can See Code Repositories. arXiv:2606.14061, 2026. Disponível em: https://arxiv.org/abs/2606.14061.
- 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.
- 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
