Reconstruir sem recomeçar: extrair a fundação para um app novo, mais simples e rápido

Um roteiro honesto para quando você quer clonar as boas ideias de um projeto que cresceu demais — sem herdar a bagagem. Separar regras, design system e features; deixar a performance vir da arquitetura (não das telas bonitas); e reimplementar por fatias verticais. Escrito a partir da experiência com este próprio site (Astro + Cloudflare + Supabase, rotas estáticas e dinâmicas).

Todo projeto que dá certo passa por um momento estranho: ele cresce tanto que você olha para a base de código e pensa “eu faria isso melhor hoje”. Não porque estava errado — porque você aprendeu no caminho, e porque features foram sendo pregadas umas nas outras até virar um organismo que ninguém projetou de propósito.

Foi daí que veio a vontade de começar um app novo, parecido com este (mesmo domínio, com partes estáticas e dinâmicas, praticamente as mesmas rotas), mas mais simples, mais rápido e mais organizado. Este texto é o roteiro que eu daria a mim mesmo antes de abrir o git init. Guardo aqui para refletir com calma depois.

O reframe: não é “melhorar”, é “extrair”

A primeira armadilha é pensar “vou melhorar a UI deste app e depois o resto”. Não. Um app que cresceu organicamente carrega complexidade que você não quer levar junto: sistemas com estado over-engineered, abstrações criadas para um caso que nunca veio, features que você nem vai reusar.

E apagar coisa de uma base grande é pior do que parece — sempre sobram fantasmas: um import solto, um token órfão, um if que ninguém entende mais. Copiar deliberadamente para um repo novo e limpo é mais honesto do que “forkar e deletar”. Você começa com uma folha em branco e traz, uma a uma, só as peças que provaram valor.

Três camadas, três durabilidades

O erro clássico é tratar o código como uma coisa só. Ele tem, na verdade, três camadas com “vida útil” bem diferente:

CamadaReutilizaçãoQuando mexer
Regras / convenções (a constituição)~100% portávelPrimeiro. É barato agora e caríssimo depois.
Design system + primitivos (tokens, Button, Input, Card, layout)Muito portávelLogo em seguida, como base limpa.
Features de produto (loja, checkout, IA, orçamentos…)ReimplementarPor último, uma fatia vertical de cada vez.

A intuição de “começar pela UI” está certa — desde que “UI” signifique a fundação de design + os primitivos + as regras, e não “deixar as telas bonitas”. Telas bonitas em cima de uma arquitetura errada, você refaz. A fundação (tokens, contrato de componente, tema claro/escuro, a regra de “nenhuma cor cravada”) é o que realmente se paga com o tempo.

A armadilha da performance

Aqui vai o aviso mais importante: performance não vem de UI mais bonita — vem de arquitetura. Dá para passar semanas polindo componentes e o site continuar pesado, porque o problema real é quanto JavaScript você embarca e o que renderiza no servidor.

Antes de investir na UI, decida a estratégia de renderização. Para uma stack como esta (Astro + Cloudflare + Supabase), com rotas mistas, as alavancas de “simples e rápido” são:

  1. Menos JS no cliente. Ilhas do Astro: hidrate só o que precisa de interação. A regra nova é HTML/CSS por padrão, JavaScript só numa ilha justificada. Muita coisa que hoje é componente interativo poderia ser HTML estático.
  2. SSR só onde faz sentido. Renderizar tudo no servidor é confortável, mas caro e lento. Catálogo e páginas de conteúdo podem ser estáticos/ISR; deixe o SSR para o que é genuinamente por-usuário (carrinho, conta, checkout). O mapa de rotas praticamente já te diz o que é estático e o que é dinâmico — use isso como planta.
  3. Menos camadas de abstração. Comece mínimo e só abstraia quando doer duas vezes (YAGNI). Uma abstração agnóstica de provedores, por exemplo, só se justifica quando existe um segundo provedor de verdade — antes disso é peso morto.
  4. Token system enxuto. Mantenha as CSS Custom Properties como fonte única da verdade, mas audite e corte o que não é usado. Um núcleo pequeno e bem definido (cor, espaço, tipografia, raio, sombra) vale mais que 16 paletas que ninguém lembra por quê.
  5. Camada de dados fina. Um cliente, contratos tipados, sem N invólucros em volta.

Um caminho em fases

  • Fase 0 — Regras (um doc curto). Um CONVENTIONS.md enxuto: estrutura de pastas, naming, contrato de componente (props tipadas, BEM, tokens), quando adicionar JS, acessibilidade, tema, e a disciplina de um SPEC.md por pasta. Essa disciplina é ouro — mas com uma regra de higiene que este projeto aprendeu na marra: SPEC desatualizado mente. Ou o SPEC acompanha o código, ou ele vira armadilha.
  • Fase 1 — Fundação de design. Tokens + reset + primitivos limpos (Button, Input, Card, Stack/Grid) + tema claro/escuro. Uma página /_ui que mostra tudo de uma vez (um mini-Storybook caseiro) para ver o sistema respirar.
  • Fase 2 — Shell de layout. Navegação e estruturas de página — mais simples que o necessário hoje. Resista à tentação de recriar a máquina de estados complexa; comece pelo suficiente.
  • Fase 3 — Features por fatia vertical. Uma jornada completa de cada vez (ex.: loja → catálogo → produto → carrinho → checkout), trazendo do projeto antigo só o que aquela fatia precisa.

O que herdar e o que largar

Herdar (as boas raízes): a disciplina de SPEC.md por pasta · a estrutura ui/categoria/Componente/Componente.astro · os tokens --c-* e a regra “sem hex cravado” · o hábito de um CLAUDE.md/doc de regras do projeto · o padrão de abstração agnóstica — quando houver mais de uma implementação real.

Largar ou simplificar: SSR em tudo · sistemas com estado over-engineered · abstrações multi-provedor antes de existir o segundo provedor · o acúmulo de features que o app novo não vai usar · SPECs que descrevem um passado que o código já abandonou.

O fio da meada

Se eu tivesse que resumir numa frase: defina as regras enquanto elas são baratas, construa a fundação antes das telas, deixe a performance nascer da arquitetura, e traga as features por último — uma fatia de cada vez, olhando nos olhos cada linha que você copia.

O objetivo não é um app perfeito. É um app do qual, daqui a um ano, eu não sinta vontade de fugir. E isso se decide agora, na folha em branco — não depois, no meio de 200 lugares com #1c1c18 cravado no CSS.