SaaS Starter

AI-ready

Como este boilerplate está otimizado para que um agente de IA escreva código que funcione.

Última atualização 3 de mai. de 2026

Filosofia

LLMs não leem sua mente — leem seu código. Um boilerplate AI-friendly minimiza ambiguidade: tipos exatos, padrões repetidos, ADRs explícitos.

Tipos como contrato

@app/contracts exporta schemas Zod e tipos inferidos. Um agente que olha o contract sabe exatamente qual payload enviar e o que esperar de volta. Sem documentação que desatualiza.

import { CreateUserInput } from '@app/contracts';
// O tipo é a doc.

Branded IDs

UserId ≠ OrganizationId ≠ string. Se um agente confunde um com o outro, o TS server pega antes do commit. Menos bugs sutis.

Result no lugar de throws

LLMs propagam erros melhor quando são valores. Result<T, E> mapeia naturalmente para JSON, para testes, para docs.

ADRs versionados

docs/adr/ documenta decisões arquiteturais (por que BetterAuth, por que billing agnóstico ao provider). Um agente lê o ADR antes de propor uma mudança que o contradiga.

Generators

bun run gen:domain <name> gera o scaffold de um aggregate completo (entity, VOs, repo, use cases, controller, testes). O agente segue o padrão existente sem inventar um novo.

MCP-friendly

Schemas Prisma + OpenAPI gerado em apps/server/openapi.json. Qualquer agente com tools OpenAPI explora a API sem adivinhar.

Padrões repetidos

Cada aggregate tem a mesma estrutura (domain/, application/, infrastructure/, interface/). Um LLM aprende o padrão em um domínio e replica no próximo sem fricção.

Guardrails do endpoint de chat

POST /api/v1/ai/chat custa dinheiro de verdade, então é blindado:

  • O cliente só pode enviar mensagens user e assistant. O papel system é rejeitado pelo schema de @app/contracts.
  • model deve ser AI_MODEL ou estar em AI_ALLOWED_MODELS; qualquer outro dá 422 antes de chamar o upstream.
  • A saída é limitada por AI_MAX_OUTPUT_TOKENS.
  • Se o cliente desconecta, o stream upstream é abortado para que uma aba fechada pare de gastar tokens.

[!NOTE] No Bun 1.3.x um POST cujo body já foi consumido não emite evento de desconexão, então o abort por desconexão pode não disparar. Dois limites rígidos cobrem isso: AI_MAX_OUTPUT_TOKENS e AI_STREAM_TIMEOUT_MS (padrão 120000), um teto de tempo que aborta a chamada upstream. O stream também é abortado se uma escrita na resposta falha ou a resposta é destruída.

Nesta página