Construí sozinho um SaaS B2B
com IA. Do banco ao billing.
Sou dev há sete anos, quase tudo em sistema empresarial: ERP, CRM, integração bancária. No último ano fiz o mibi inteiro sozinho. Abaixo tem um bug que me custou umas boas noites. Lê o código e decide se vale a conversa.
O search_path não sobrevive ao pooler
Cada cliente do mibi fica num schema Postgres separado. Trocar de schema por requisição parece trivial: chama set_config('search_path', ...) e segue. Só que o Neon roda PgBouncer em modo transação, e aí a conexão só fica presa a um backend fixo dentro de uma transação. Meu search_path valia pro primeiro statement e sumia no segundo, que caía em outro backend. Deu 42P01 relation "candidate" does not exist, intermitente, só em produção, só sob carga.
// is_local=true (SET LOCAL): preservado pelo pooler em modo transação.
await client.query("BEGIN");
await client.query("SELECT set_config('search_path', $1, true)", [safe + ", public"]);
// app.current_tenant tem que bater com o search_path, senão a policy
// RESTRICTIVE tenant_schema_guard bloqueia tudo, calada.
await client.query("SELECT set_config('app.current_tenant', $1, true)", [safe]);
O nome do schema passa por allow-list antes de chegar no banco. E se o reset do search_path falhar, eu destruo o client em vez de devolver pro pool. Um client sujo voltando pro pool atende a próxima requisição, de outra empresa, apontando pro schema errado.
Como eu trabalho
Hoje qualquer pessoa coloca um app no ar com Claude Code, e o meu também saiu assim. A diferença aparece nas decisões que quem só tem a ideia não sabe que precisa tomar. O que acontece quando o modelo responde errado. Quem apaga o dado quando o candidato pede. Como voltar o banco se a migration subir quebrada. Quanto custou a chamada de ontem. A lista abaixo é o que eu levo pra qualquer projeto, e costuma valer mais que o produto que eu construí com ela.
-
Convenção escrita, não combinada de boca
Dez skills e cinco arquivos de regra versionados no repo, cobrindo API, testes, UI, workflow e estilo de código. Quem entrar depois lê o padrão em vez de descobrir por code review.
-
Nove workflows de CI no GitHub Actions
Lint, tipos, unitário, integração, e2e e regressão visual em todo PR, mais uma revisão de código por IA. Release sai por tag. As actions são fixadas por SHA de commit, não por versão móvel, porque tag de terceiro pode ser reescrita.
-
Pentest agendado contra o próprio site
Toda segunda-feira roda Nuclei contra produção, com os templates da comunidade e mais um conjunto que eu escrevi pro mibi. SQLMap sob demanda, npm audit barrando high e critical, e o binário do scanner conferido por sha256 antes de executar.
-
Ataque adversarial contra os próprios agentes
Um gerador cria entradas maliciosas contra cada agente, tentando prompt injection e extração de contexto, e um modelo juiz avalia o que passou. Os casos viram dataset fixo, então a mesma tentativa é testada de novo a cada mudança de prompt. Antes de trocar prompt em produção eu rodo a versão nova contra a antiga e comparo a saída.
-
Banco com migration, nunca na mão
Toda mudança de schema é migration versionada e reversível. Tem script que audita índice faltando, e backup de banco, repositórios e secrets com restore testado. Backup que nunca foi restaurado não é backup.
-
API documentada a partir do código
O OpenAPI é gerado do próprio código e publicado. Documentação escrita à mão desatualiza na primeira semana.
Stack
- Linguagem
- TypeScript · Node · React 19 · Next.js 16
- Infra
- Vercel · GitHub Actions · Vercel Blob
- Banco
- Postgres no Neon · node-pg-migrate · RLS
- IA
- Anthropic Claude (SDK) · custo por chamada rastreado
- Pagamento
- Stripe
- Resend · domínio verificado, SPF e DKIM
- Observabilidade
- Sentry · PostHog
- Testes
- Jest · Playwright (e2e e visual) · SWC
- Segurança
- bcrypt · jose (JWT) · otpauth (2FA) · DOMPurify · validação de upload por file-type · geo-blocking
- Validação
- Zod em toda entrada de API
- Documentos
- unpdf, mammoth e csv-parse para ler CV em PDF, DOCX e planilha
- Antes
- GeneXus · Oracle · SQL Server · C#/.NET · JavaScript
O que tem dentro do mibi
- Produto
- mibi.app.br ↗
- Papel
- Fundador e único desenvolvedor
- Arquitetura
- Multi-tenant, um schema Postgres isolado por cliente
- IA
- 8 agentes Anthropic Claude, custo medido por chamada
- Conformidade
- LGPD: anonimização, consentimento versionado, Art. 20
- Billing
- Stripe, 4 planos + enterprise
- Estado
- No ar. Sem cliente pagante ainda.
Arquitetura do mibi
O que eu pego como freela
-
LLM dentro de produto que já existe
Prompt versionado, custo medido por chamada, fallback pra quando o modelo erra e log que permite explicar uma decisão seis meses depois. É a parte chata que faz a POC virar feature.
-
Integração e automação de backoffice
Quatro anos num gerenciador financeiro: remessa e retorno, geração de título, pagamento a fornecedor, conciliação. Já sei onde esse código costuma quebrar. TypeScript ou Python, o que já rodar aí.
-
Produto novo do zero, sozinho
Banco, API, front, deploy e monitoramento. O mibi saiu assim, e dá pra conferir o resultado.
Antes disso
Cinco dos sete anos foram GeneXus, ERP e sistema financeiro. Não é stack que impressiona startup, mas foi ali que eu aprendi a mexer em código que fecha o mês de uma empresa, onde migração errada não tem ctrl+z. Legado não me assusta, passei metade da carreira dentro de um.
- 2025–CynclyDev sênior · ERP de varejo · GeneXus, Oracle, C#/.NET
- 2025DatainfoDev sênior · sistemas para educação
- 2021–25eSalesGerenciador financeiro · integração bancária e cobrança
- 2020–21SiscobraMigração de sistemas legados
- 2018–19EvolutizeManutenção e evolução de ERP
Como eu fecho um projeto
- Disponibilidade
- Sou CLT em tempo integral. O freela sai à noite e no fim de semana, de 10 a 15 h por semana.
- Formato
- Escopo fechado, combinado antes de começar. Não pego alocação por hora nem plantão.
- Tamanho típico
- De 40 a 120 horas. Nessa carga dá 1 a 2 meses de calendário. Combino prazo por data, não por esforço.
Tem algo parado no backlog?
Me conta em duas linhas o que é. Se não for pra mim, eu digo na resposta e paro por aí.
contato@lucaskarsten.com.br