Voltar ao Blog
Disponível em:
20 de mai. de 20263 min read

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 é.

ArquiteturaRubyRailsDecisões