Escolhendo Rails para um Produto de Compliance
Um desenvolvedor. Dezoito semanas. Um produto de compliance B2B. A decisão de stack errada pode dobrar o esforço sem você perceber até a semana doze.
Rails venceu — não porque é "melhor", mas porque o custo de contexto para um único dev é menor que o de qualquer alternativa que avaliei. A matriz de decisão mostrou isso antes de eu escrever uma linha.
As restrições reais
Essa é a parte que a maioria dos debates de stack pula. A decisão só faz sentido contra a situação concreta:
- Um desenvolvedor, cerca de 290 horas estimadas para o primeiro milestone.
- PostgreSQL com Row-Level Security já decidido, e irreversível.
- Um produto CRUD-heavy: dashboard de compliance, upload de fornecedores, alertas.
- O gargalo é I/O, não CPU — o servidor chama APIs externas de busca e de LLM e espera.
Esse último ponto importa mais do que parece, e eu volto nele.
As alternativas, e por que perderam
FastAPI pontuou 3.10. A vantagem real do Python aparece quando existe workload de ML rodando no seu servidor. Aqui o servidor chama APIs externas — então essa vantagem é exatamente zero. O scaffolding é mais lento, o SQLAlchemy integra de forma menos limpa com RLS do PostgreSQL, e não há equivalente ao Hotwire para construir um dashboard sem adotar React.
NestJS pontuou 2.45. Exige quatro contextos distintos para um dev: NestJS, BullMQ, Prisma, React. Pior, o Prisma tem suporte limitado ao set_config do PostgreSQL, o que significa raw queries para multi-tenancy com RLS — o núcleo do produto. Esse overhead cognitivo era incompatível com o prazo.
Rails 8 pontuou 4.95, com ActiveRecord, Hotwire e Sidekiq.
O ponto de ancoragem
A decisão dependeu mesmo de uma integração: multi-tenancy. Rails tem uma resposta nativa.
class Current < ActiveSupport::CurrentAttributes
attribute :tenant
attribute :user
attribute :role
end
CurrentAttributes é thread-local por design e limpo ao fim de cada request ou job. Não há risco de contexto de tenant vazar entre requests — o que, num produto de compliance, é o modo de falha que encerra a empresa.
O Current.tenant é setado no controller base após a autenticação e propagado para jobs em background via middleware do Sidekiq. Sem essa propagação, um job assíncrono poderia rodar sem contexto de tenant, derrotando o RLS silenciosamente. Silenciosamente é a palavra perigosa: a query continua funcionando, só que retornando as linhas do tenant errado.
O que eu abri mão
Ruby tem um ecossistema menor para IA/ML. Irrelevante para este produto, já que a inteligência mora atrás de uma API, mas estreita contratações futuras.
Hotwire é a escolha errada se o produto um dia precisar ser mobile-first ou offline-first. Hoje ele não é.
E Rails é convention-heavy, o que fica restritivo para lógica de domínio complexa e não-CRUD. Este produto é CRUD por todos os lados, então as convenções são alavanca pura — mas isso é uma propriedade deste produto, não uma verdade geral.
A lição
A pergunta de stack quase nunca é "qual é melhor". É "qual tem o menor custo total de contexto para quem vai construir e operar isto, sob estas restrições".
Mude qualquer uma dessas restrições — três devs em vez de um, um workload de ML real no servidor, um produto mobile-first — e a matriz produz outro vencedor. Esse é o motivo de escrever a matriz em vez de discutir por preferência.
O score não é o resultado. A ponderação explícita é.