7 min

Mantenha o código gerado manutenível: a regra da arquitetura sem firulas

Aprenda a manter código gerado manutenível com a regra da arquitetura sem firulas: limites de pastas claros, nomes consistentes e defaults simples que reduzem retrabalho.

Mantenha o código gerado manutenível: a regra da arquitetura sem firulas

Por que a manutenibilidade é mais difícil com código gerado

Código gerado altera o trabalho do dia a dia. Você não está apenas construindo funcionalidades; está guiando um sistema que pode criar muitos arquivos rapidamente. A velocidade é real, mas pequenas inconsistências se multiplicam rápido.

O resultado gerado costuma parecer OK isoladamente. O custo aparece na segunda ou terceira mudança: você não sabe onde uma parte pertence, corrige o mesmo comportamento em dois lugares, ou evita mexer em um arquivo porque não sabe o que mais ele afeta.

Estruturas “espertas” ficam caras porque são difíceis de prever. Padrões personalizados, mágicas ocultas e abstrações pesadas fazem sentido no primeiro dia. Na semana seis, a próxima mudança desacelera porque é preciso reaprender o truque antes de atualizar com segurança. Com geração assistida por IA, essa esperteza também pode confundir gerações futuras e levar a lógica duplicada ou camadas novas empilhadas por cima.

Arquitetura sem firulas é o oposto: limites claros, nomes óbvios e padrões simples. Não se trata de perfeição. Trata-se de escolher um layout que um colega cansado (ou você no futuro) consiga entender em 30 segundos.

Um objetivo simples: facilitar a próxima mudança, não impressionar. Normalmente isso significa um lugar claro para cada tipo de código (UI, API, dados, utilitários compartilhados), nomes previsíveis que descrevem o que o arquivo faz e o mínimo de “mágica” como auto-wiring, globais ocultos ou metaprogramação.

Exemplo: se você pedir ao Koder.ai para adicionar “convites de equipe”, quer que a UI vá para a área de UI, que haja uma rota de API na área de API e que os dados de convite fiquem na camada de dados, sem inventar uma pasta ou padrão novo só para essa funcionalidade. Essa consistência sem firulas é o que mantém edições futuras baratas.

A regra da arquitetura sem firulas

Código gerado fica caro quando oferece muitas maneiras de fazer a mesma coisa. A regra da arquitetura sem firulas é simples: torne a próxima mudança previsível, mesmo que a primeira versão pareça menos engenhosa.

Você deve conseguir responder isso rapidamente:

  • Onde essa funcionalidade vive?
  • Onde eu coloco um novo arquivo?
  • Como devo nomeá-lo?
  • Qual é o caminho mais simples da UI até os dados?

A regra

Escolha uma estrutura simples e siga-a em todo lugar. Quando uma ferramenta (ou colega) sugerir um padrão chique, a resposta padrão é “não”, a menos que isso remova uma dor real.

Defaults práticos que se sustentam com o tempo:

  • Uma responsabilidade por pasta e por arquivo. Se um arquivo tem dois motivos para mudar, divida-o.
  • Previsibilidade vence flexibilidade. O mesmo tipo de coisa vai para o mesmo lugar, sempre.
  • Prefira a abordagem padrão da sua stack a mini-frameworks customizados.
  • Torne o caminho feliz óbvio. Novos contribuidores devem adivinhar a localização certa sem perguntar.
  • Rejeite “mágica”. Evite comportamentos ocultos, truques baseados em reflexão e abstrações inteligentes.

Um teste mental rápido

Imagine que um desenvolvedor novo abre seu repositório e precisa adicionar um botão “Cancelar assinatura”. Ele não deveria ter que aprender uma arquitetura customizada primeiro. Deve encontrar uma área de funcionalidade clara, um componente de UI evidente, um único local de cliente de API e um único caminho de acesso a dados.

Essa regra funciona especialmente bem com ferramentas de vibe-coding como o Koder.ai: você pode gerar rápido, mas ainda direciona o resultado para os mesmos limites sem firulas sempre.

Limites simples de pastas que escalam

Código gerado tende a crescer rápido. A maneira mais segura de mantê-lo manutenível é um mapa de pastas sem firulas, onde qualquer um pode adivinhar onde uma mudança pertence.

Um layout pequeno no nível superior que serve para muitos apps web:

  • app/ telas, roteamento e estado em nível de página
  • components/ peças de UI reaproveitáveis
  • features/ uma pasta por funcionalidade (billing, projects, settings)
  • api/ código do cliente de API e helpers de requisição
  • server/ handlers de backend, serviços e regras de negócio

Isso torna os limites óbvios: UI vive em app/ e components/, chamadas de API vivem em api/ e lógica de backend vive em server/.

O acesso a dados também deve ser sem firulas. Mantenha queries SQL e código de repositório perto do backend, não espalhados por arquivos de UI. Em uma stack Go + PostgreSQL, uma regra simples é: handlers HTTP chamam serviços, serviços chamam repositórios, repositórios falam com o banco.

Tipos e utilitários compartilhados merecem um lar claro, mas mantenha pequeno. Coloque tipos cross-cutting em types/ (DTOs, enums, interfaces compartilhadas) e pequenos helpers em utils/ (formatação de datas, validadores simples). Se utils/ começar a parecer um segundo app, o código provavelmente pertence a uma pasta de feature.

Código gerado vs código escrito à mão

Trate pastas geradas como substituíveis.

  • Coloque o output gerado em generated/ (ou gen/) e evite editá-lo diretamente.
  • Mantenha a lógica customizada em features/ ou server/ para que a regeneração não a sobrescreva.
  • Se for preciso corrigir um comportamento gerado, envolva-o (arquivo adapter) em vez de modificar a fonte.

Exemplo: se o Koder.ai gerar um cliente de API, armazene-o em generated/api/, depois escreva wrappers finos em api/ onde você possa adicionar retries, logs ou mensagens de erro mais claras sem tocar nos arquivos gerados.

Convenções de nomeação que evitam confusão

Código gerado é fácil de criar e fácil de acumular. Nomear é o que o mantém legível um mês depois.

Escolha um estilo de nomeação e não misture:

  • Pastas e arquivos: kebab-case (user-profile-card.tsx, billing-settings)
  • Componentes React: PascalCase (UserProfileCard)
  • Funções e variáveis: camelCase (getUserProfile)
  • Constantes: SCREAMING_SNAKE_CASE (MAX_RETRY_COUNT)

Nomeie pela função, não por como funciona hoje. user-repository.ts é um papel. postgres-user-repository.ts é um detalhe de implementação que pode mudar. Use sufixos de implementação só quando realmente houver múltiplas implementações.

Evite gavetas de lixo como misc, helpers ou um utils gigante. Se uma função é usada por apenas uma feature, mantenha-a perto dessa feature. Se for compartilhada, faça o nome descrever a capacidade (date-format.ts, money-format.ts, id-generator.ts) e mantenha o módulo pequeno.

Convenções que aceleram a navegação

Quando rotas, handlers e componentes seguem um padrão, você encontra coisas sem procurar:

  • Rotas: routes/users.ts com paths como /users/:userId
  • Handlers (HTTP): handlers/users.get.ts, handlers/users.update.ts
  • Serviços (regras de negócio): services/user-profile-service.ts
  • Acesso a dados: repositories/user-repository.ts
  • Componentes UI: components/user/UserProfileCard.tsx

Se você usa Koder.ai (ou qualquer gerador), coloque essas regras no prompt e mantenha-as consistentes durante as edições. O objetivo é previsibilidade: se você consegue adivinhar o nome do arquivo, mudanças futuras continuam mais baratas.

Defaults sem esperteza (regras práticas)

Crie uma base CRM sem firulas
Transforme uma especificação clara em um app React com backend Go e PostgreSQL no Koder.ai.

Código gerado pode parecer impressionante no primeiro dia e doloroso no trigésimo. Escolha defaults que tornem o código óbvio, mesmo que um pouco repetitivo.

Comece reduzindo a mágica. Evite carregamento dinâmico, truques de reflexão e auto-wiring a menos que haja uma necessidade medida. Esses recursos escondem de onde as coisas vêm, o que torna debugging e refatoração mais lentos.

Prefira imports explícitos e dependências claras. Se um arquivo precisa de algo, importe diretamente. Se módulos precisam ser ligados, faça isso em um lugar visível (por exemplo, um único arquivo de composição). Quem lê não deve ter que adivinhar o que roda primeiro.

Mantenha configuração entediante e centralizada. Coloque variáveis de ambiente, flags de recurso e configurações globais em um módulo com um esquema de nomes único. Não espalhe config por arquivos aleatórios porque parece conveniente.

Regras práticas que mantêm a equipe consistente:

  • Escolha explícito sobre implícito (imports, roteamento, DI, efeitos colaterais).
  • Se economiza 10 linhas mas adiciona um novo conceito, pule.
  • Mantenha uma maneira de fazer as coisas (uma ferramenta de logging, um módulo de config).
  • Prefira fluxo de dados simples a observadores escondidos ou cadeias de eventos.
  • Ao depurar, remova o código esperto primeiro.

Tratamento de erros é onde a esperteza mais atrapalha. Escolha um padrão e use-o em todo lugar: retorne erros estruturados da camada de dados, mapeie-os para respostas HTTP em um lugar só e traduza-os em mensagens para o usuário na borda da UI. Não lance três tipos diferentes de erro dependendo do arquivo.

Se você gerar um app com Koder.ai, peça esses defaults desde o início: wiring explícito de módulos, config centralizada e um padrão único de erros.

Limites entre UI, API e dados

Linhas claras entre UI, API e dados mantêm mudanças contidas. A maioria dos bugs misteriosos acontece quando uma camada começa a fazer o trabalho de outra.

UI: mostrar estado, coletar entrada

Trate a UI (frequentemente React) como um lugar para renderizar telas e gerenciar estado só da UI: qual aba está aberta, erros de formulário, spinners de carregamento e manipulação básica de inputs.

Mantenha estado do servidor separado: listas buscadas, perfis em cache e tudo que deve bater com o backend. Quando componentes de UI começam a calcular totais, validar regras complexas ou decidir permissões, a lógica se espalha pelas telas e fica caro mudar.

API: formas finas e estáveis

Mantenha a camada de API previsível. Deve traduzir requisições HTTP em chamadas ao código de negócio e traduzir resultados de volta em formas de request/response estáveis. Evite enviar modelos de banco de dados diretamente pela rede. Respostas estáveis permitem refatorar internals sem quebrar a UI.

Um caminho simples que funciona bem:

  • UI chama um cliente de API com objetos request/response tipados.
  • Handlers de API validam input e chamam um método de serviço.
  • Serviços guardam regras de negócio e fluxos de trabalho.
  • Repositórios escondem queries de banco por trás de métodos pequenos.

Dados: esconda queries atrás de repositórios

Coloque SQL (ou lógica ORM) atrás de um limite de repositório para que o resto do app não “saiba” como os dados são armazenados. Em Go + PostgreSQL, isso geralmente significa repositórios tipo UserRepo ou InvoiceRepo com métodos pequenos e claros (GetByID, ListByAccount, Save).

Exemplo concreto: adicionar códigos de desconto. A UI renderiza um campo e mostra o preço atualizado. A API aceita code e retorna {total, discount}. O serviço decide se o código é válido e como descontos se combinam. O repositório busca e persiste as linhas necessárias.

Passo a passo: configurar código gerado manutenível

Apps gerados podem parecer “prontos” rapidamente, mas é a estrutura que mantém mudanças baratas depois. Decida regras sem firulas primeiro, então gere só código suficiente para prová-las.

Fluxo prático de setup

Comece com uma breve passagem de planejamento. Se usar o Koder.ai, o Planning Mode é um bom lugar para escrever um mapa de pastas e algumas regras de nomes antes de gerar qualquer coisa.

Depois siga esta sequência:

  1. Defina o mapa e as regras por escrito. Escolha limites (por exemplo: ui/, api/, data/, features/) e um punhado de regras de nomeação.
  2. Gere uma única fatia vertical fina. Escolha uma pequena feature que toque UI, API e armazenamento, como “criar um contato.” O objetivo é o caminho de ponta a ponta, não completude.
  3. Refatore imediatamente para casar com os limites. Mova código para as pastas planejadas, renomeie arquivos pouco claros e delete duplicatas. Separe funções que “fazem tudo” em UI, handler e acesso a dados.
  4. Adicione uma segunda feature para testar a forma. Pegue algo parecido, como “listar contatos.” Se você se sentir forçado a quebrar suas regras, os limites provavelmente estão errados.
  5. Trave as convenções cedo. Adicione um curto CONVENTIONS.md e trate-o como um contrato. Quando a base cresce, mudar nomes e padrões de pastas fica caro.

Checagem de realidade: se uma pessoa nova não consegue adivinhar onde colocar “editar contato” sem perguntar, a arquitetura ainda não é sem firulas o suficiente.

Cenário de exemplo: adicionar uma feature sem criar bagunça

Envie uma fatia vertical limpa
Gere uma única funcionalidade fina de ponta a ponta e reaplique a mesma estrutura em todas as mudanças.

Pense em um CRM simples: página de lista de contatos e um formulário de edição de contato. Você constrói a primeira versão rápido e, uma semana depois, precisa adicionar “tags” aos contatos.

Trate o app como três caixas sem firulas: UI, API e dados. Cada caixa tem limites claros e nomes literais para que a mudança de “tags” permaneça pequena.

Um layout limpo poderia ser assim:

  • web/src/pages/ContactsPage.tsx e web/src/components/ContactForm.tsx
  • server/internal/http/contacts_handlers.go
  • server/internal/service/contacts_service.go
  • server/internal/repo/contacts_repo.go
  • server/migrations/

Agora “tags” fica previsível. Atualize o esquema (nova tabela contact_tags ou uma coluna tags), depois toque uma camada de cada vez: repo lê/grava tags, service valida, handler expõe o campo, UI renderiza e edita. Não coloque SQL em handlers nem regras de negócio em componentes React.

Se o produto depois pedir “filtrar por tag”, você vai trabalhar principalmente em ContactsPage.tsx (estado da UI e query params) e no handler HTTP (parsing da requisição), enquanto o repo cuida da query.

Para testes e fixtures, mantenha as coisas pequenas e próximas ao código:

  • server/internal/service/contacts_service_test.go para regras como “nomes de tag devem ser únicos por contato”
  • server/internal/repo/testdata/ para fixtures mínimas
  • web/src/components/__tests__/ContactForm.test.tsx para comportamento do formulário

Se você está gerando isso com Koder.ai, a mesma regra vale após a exportação: mantenha pastas sem firulas, nomes literais e as edições param de parecer arqueologia.

Erros comuns que encarecem mudanças futuras

Código gerado pode parecer limpo no primeiro dia e ainda assim custar caro depois. O culpado usual não é “código ruim”, é inconsistência.

Um hábito caro é deixar o gerador inventar estrutura a cada vez. Uma feature chega com suas próprias pastas, estilo de nomes e helper functions, e você acaba com três maneiras de fazer a mesma coisa. Escolha um padrão, escreva-o e trate qualquer novo padrão como uma mudança consciente, não como default.

Outra armadilha é misturar camadas. Quando um componente UI fala direto com o banco, ou um handler de API constrói SQL, pequenas mudanças viram edições arriscadas por toda a app. Mantenha o limite: UI chama API, API chama serviço, serviço chama acesso a dados.

Abstrações genéricas usadas cedo demais também adicionam custo. Um “BaseService” ou “Repository” universal parece elegante, mas abstrações iniciais são palpites. Quando a realidade muda, você briga com seu próprio framework em vez de entregar.

Renomeações e reorganizações constantes são uma forma mais silenciosa de dívida. Se arquivos se movem toda semana, as pessoas deixam de confiar no layout e correções rápidas caem em lugares aleatórios. Estabilize o mapa de pastas primeiro, depois refatore em blocos planejados.

Por fim, cuidado com “código de plataforma” que não tem usuário real. Bibliotecas compartilhadas e ferramentas internas só valem a pena quando há uma necessidade repetida comprovada. Até lá, mantenha defaults diretos.

Um checklist rápido antes de enviar

Seja recompensado por ensinar
Ganhe créditos compartilhando o que você construiu no Koder.ai e as convenções que usou.

Se alguém novo abrir o repo, deve conseguir responder uma pergunta rápido: “Onde eu adiciono isso?”

Teste de navegação de 2 minutos

Entregue o projeto a um colega (ou a você no futuro) e peça para adicionar uma pequena feature, como “adicionar um campo no formulário de signup”. Se ele não achar o lugar certo rapidamente, a estrutura não está cumprindo seu papel.

Cheque três lares claros:

  • Mudanças de UI vivem em um único lugar óbvio.
  • Rotas/handlers de API são fáceis de encontrar a partir da UI.
  • Modelo de dados e mudanças de banco têm uma localização clara.

Regras que valem a pena impor em revisão

  • UI, API e dados têm um lar, exceções são raras.
  • Nomes leem como rótulos, não enigmas.
  • Quebras de camada são sinalizadas (como UI acessando banco).
  • Atalhos espertos são rejeitados por padrão.

Se sua plataforma suportar, mantenha um caminho de rollback. Snapshots e rollback são especialmente úteis quando você experimenta com estrutura e quer um jeito seguro de voltar.

Próximos passos: mantenha sem firulas, mantenha barato

A manutenibilidade melhora mais rápido quando você para de debater estilo e começa a tomar algumas decisões que permanecem. Escreva um pequeno conjunto de convenções que removam hesitação diária: onde os arquivos vão, como são nomeados e como erros e config são tratados. Mantenha curto o suficiente para ler em um minuto.

Depois faça uma única passada de limpeza para alinhar o código a essas regras e pare de reorganizar toda semana. Reorganizações frequentes tornam a próxima mudança mais lenta, mesmo que o código pareça melhor.

Se você está construindo com Koder.ai (koder.ai), vale a pena salvar essas convenções como prompt inicial para que cada nova geração caia na mesma estrutura. A ferramenta pode ser rápida, mas são os limites sem firulas que mantêm o código fácil de mudar.

Perguntas frequentes

O que significa “arquitetura simples” para código gerado?

Use um mapa de pastas simples, nomes literais e um único caminho da interface para a API e os dados. O objetivo é que alguém consiga posicionar uma mudança corretamente sem antes aprender um padrão personalizado.

Por que a manutenção é mais difícil com código gerado por IA?

O código gerado pode adicionar arquivos rapidamente, então pequenas inconsistências se espalham depressa. Uma estrutura previsível evita que a segunda e a terceira mudança se transformem em uma busca por lógica duplicada e pastas desconhecidas.

Quais pastas um aplicativo web gerado deve usar?

Um ponto de partida prático é usar app/ para telas e roteamento, components/ para interface reutilizável, features/ para código específico de funcionalidades, api/ para requisições do cliente e server/ para lógica de backend. Mantenha o mesmo limite para cada funcionalidade.

Como devo nomear arquivos e código gerados?

Use um estilo de forma consistente: kebab-case para arquivos e pastas, PascalCase para componentes React, camelCase para funções e SCREAMING_SNAKE_CASE para constantes. Nomeie os arquivos pela função que desempenham, como user-repository.ts ou money-format.ts.

Como separo o código de interface, API e banco de dados?

Mantenha o código da interface focado na renderização e nas entradas, deixe os manipuladores de API validarem as requisições e retornarem respostas estáveis, coloque as regras de negócio em serviços e mantenha chamadas SQL ou ORM em repositórios. Cada camada deve chamar a próxima, em vez de fazer o trabalho dela.

Devo editar arquivos gerados diretamente?

Coloque a saída gerada em uma pasta dedicada, generated/ ou gen/, e evite editá-la diretamente. Escreva wrappers ou adaptadores personalizados fora dessa pasta para poder regenerar o código sem perder suas alterações.

Quais padrões “espertos” devo evitar?

Evite globais ocultos, conexão automática, carregamento dinâmico e abstrações que introduzem um novo conceito apenas para economizar algumas linhas. Prefira importações diretas, configuração centralizada e configuração de módulos visível.

Como configuro um projeto gerado que seja fácil de manter?

Comece com uma funcionalidade pequena de ponta a ponta, como criar um contato. Gere-a, mova tudo o que estiver pouco claro para as pastas planejadas, remova duplicatas e crie uma segunda funcionalidade semelhante para verificar se a estrutura ainda parece natural.

Como mantenho a saída do Koder.ai consistente?

Dê ao gerador regras explícitas antes de cada mudança: o mapa de pastas, o estilo de nomenclatura, os limites entre camadas e o padrão de tratamento de erros. Com o Koder.ai, você pode salvar essas regras em um prompt de planejamento para que o novo trabalho siga a mesma estrutura.

Como verifico se meu projeto é fácil de modificar?

Peça a alguém que adicione uma pequena mudança, como um campo de cadastro, sem orientação. Se essa pessoa encontrar rapidamente a interface, o manipulador de API, o serviço e o local dos dados, o projeto é fácil de trabalhar. Caso contrário, simplifique o mapa antes que ele cresça mais.

Related posts