Customização sem fork: como atender cada tenant sem multiplicar codebases
Tenants precisam de experiências diferentes. O desafio é representar essas diferenças como configuração governada no produto comum, sem transformar o SaaS em instalações independentes.
- Publicado
- Autoria
- Equipe RhizzaDocs
- Leitura
- 8 min
Diferente não precisa significar separado
SaaS B2B vive uma tensão legítima. A plataforma precisa ser operável como um produto comum, mas clientes têm marcas, processos, contratos, níveis de maturidade e requisitos distintos. Ignorar diferenças reduz aderência. Atender cada uma com um branch, um serviço ou uma instalação exclusiva reduz a vantagem operacional do modelo SaaS.
O AWS SaaS Lens formula essa tensão de maneira direta: mesmo quando uma única conta precisa de uma capacidade, a preferência é introduzi-la como customização da plataforma central, aplicável por configuração, para manter uma versão implantável e administrável do produto. A orientação não diz que toda infraestrutura deve ser compartilhada; diz que a experiência operacional precisa continuar unificada.[1]
Fork é mais do que uma branch permanente
O fork óbvio é uma codebase copiada. Existem versões menos visíveis: rota exclusiva por cliente, template com lógica de negócio própria, job implantado só num ambiente, tabela paralela, variável de ambiente que seleciona comportamento irreproduzível e um conjunto de patches aplicado manualmente depois de cada release. Todos criam um caminho que precisa ser lembrado, testado e operado separadamente.
Feature flags não garantem arquitetura saudável por si. Uma sequência de condicionais pelo id do tenant continua espalhando conhecimento e aumenta combinações. Flags são úteis quando representam capacidades nomeadas, têm dono, ciclo de vida, telemetria e comportamento padrão. A pergunta é se a configuração expressa um conceito do produto ou apenas esconde uma cópia dentro do mesmo repositório.
Comece com uma taxonomia da variação
| Tipo | Exemplo documental | Mecanismo |
|---|---|---|
| Identidade | Logo, cor, tipografia, cabeçalho | Tema versionado |
| Composição | Ordem, seção opcional, capa | Modelo e blocos permitidos |
| Dados | Campos, agrupamento, cálculo | Schema e DataView |
| Capacidade | Galeria, assinatura, tabela especial | Primitiva genérica e catálogo |
| Acesso | Quem edita, publica ou lê | Papéis, escopos e contexto validado |
| Isolamento | Carga ou requisito regulatório dedicado | Modelo pool, silo ou ponte, operado em comum |
A classificação evita usar a ferramenta errada. Trocar logo não deveria criar lógica; incluir um cálculo novo não deveria ser tratado como tema; isolar infraestrutura por compliance não precisa duplicar o produto. Cada eixo ganha contrato, validação e ciclo de publicação proporcionais ao risco.
Configuração declarativa precisa de uma linguagem fechada
“Tudo é configuração” pode virar apenas código escondido em JSON. Se o cadastro aceita JavaScript, expressões arbitrárias ou URLs irrestritas, a plataforma perde auditabilidade e abre outra superfície de segurança. Uma abordagem declarativa madura usa schema estrito, primitivas permitidas, expressões com raízes fechadas e limites de tamanho e profundidade.
O cadastro descreve intenção: texto, campo, tabela, grupo, galeria, condição conhecida. O runtime implementa comportamento genérico e testável. Nenhum tenant injeta um componente React próprio nem executa código na árvore. Quando uma capacidade falta, a plataforma avalia se uma nova primitiva serve a um conjunto de casos, versiona o contrato e a entrega pelo mesmo pipeline.
Isolamento não pode depender da honestidade do JSON
Customização multi-tenant só funciona quando a fronteira de autorização permanece fora do conteúdo. O tenant deve vir de identidade validada, nunca de um campo enviado pelo navegador. Queries tenant-scoped precisam de contexto obrigatório e defesa no banco. Assets, modelos e documentos precisam de regras de leitura independentes da aparência.
Workspace, plano ou feature flag podem influenciar experiência; isso não os transforma automaticamente em autorização. Nomear cada dimensão impede que uma configuração de visualização passe a liberar dados. Também permite observar uso por segmento sem incluir ids do host ou conteúdo sensível em logs.
Versione contrato e publicação separadamente
Um schema pode evoluir sem que todos os documentos mudem naquele instante. Um tema pode ganhar nova revisão enquanto relatórios já emitidos preservam o snapshot anterior. Uma definição pode estar em rascunho antes de ser publicada para um tenant. Separar versão do contrato, revisão do cadastro e versão visível torna rollout e rollback compreensíveis.
Compatibilidade também precisa de regra. O runtime anuncia primitivas e versões suportadas; a publicação valida dependências antes de tornar o relatório disponível. Assim, configuração nova não chega antes do código que a interpreta. O sistema falha no gate administrativo, não na sessão do cliente.
Sinais de que a configuração virou um fork disfarçado
- A chave se chama pelo nome do cliente, e ninguém consegue explicar a capacidade sem citar aquela conta.
- Ativar a opção exige editar banco ou variável manualmente, sem trilha administrativa.
- O mesmo conceito possui formatos diferentes em endpoints, frontend, worker e template.
- Não existe fixture que represente a combinação nem teste que garanta o comportamento.
- A equipe não sabe se a flag pode ser removida, quem a usa ou qual é o default.
- Uma atualização comum exige uma lista paralela de passos para contas especiais.
Esses sinais não pedem uma reescrita imediata. Comece nomeando a capacidade, centralizando leitura, adicionando validação e registrando uso. Depois mova a ativação para um plano administrativo e crie uma migração para os valores existentes. O objetivo é reduzir gradualmente caminhos implícitos sem interromper clientes.
Também defina um orçamento de complexidade. Uma opção binária parece barata, mas dez flags independentes produzem muitas combinações possíveis. Prefira presets, tiers e modelos que representem experiências testadas. Restrições explícitas são parte do produto: elas permitem prometer que cada configuração publicada continua suportada.
Faça rollout da capacidade, não do patch
Uma customização reutilizável pode nascer em quatro passos. Primeiro, descreva o contrato e teste a primitiva com dados extremos. Segundo, publique a capacidade sem ativá-la para tenants. Terceiro, habilite para a conta piloto por cadastro versionado e observe resultado, falhas e suporte. Quarto, transforme aprendizados em defaults e documentação antes de ampliar o acesso.
Esse processo muda a conversa com o cliente. A empresa não entrega “seu código especial”; entrega uma capacidade suportada, configurada para seu contexto. O piloto ainda influencia o produto, mas o compromisso de manutenção passa a ter fronteira clara. Vendas consegue explicar o que é configuração, o que exige nova capacidade e o que permanece serviço.
Observe tanto a experiência quanto a operação. O tenant concluiu o trabalho? O suporte consegue diagnosticar pela revisão ativa? Um rollback preserva documentos existentes? Outros tenants permanecem isolados? Customização sem fork é uma propriedade do ciclo inteiro, não apenas do formato do arquivo de configuração.
Exemplo: três clientes, uma capacidade comum
Três construtoras usam o mesmo SaaS. A primeira quer relatório fotográfico com marca própria; a segunda precisa agrupar fotos por etapa; a terceira exige capa e assinatura do engenheiro. Num desenho por fork, surgem três templates com lógicas divergentes. Num desenho por capacidades, a plataforma possui uma galeria genérica, agrupamento por DataView, tema por empresa, modelo de relatório e bloco de assinatura condicionado aos dados.
Cada tenant recebe uma combinação publicada. A correção de acessibilidade na galeria chega a todos. A evolução da marca afeta apenas o tema escolhido. A assinatura continua indisponível onde o papel ou o dado não permitem. A quarta construtora começa com os mesmos blocos sem que Engenharia copie a solução anterior.
O ganho não é somente menos código. Produto passa a enxergar o catálogo de capacidades, implementação monta experiências com peças conhecidas, QA testa primitivas e combinações, e suporte consegue identificar exatamente que revisão está ativa. A diferença do cliente permanece explícita sem virar outro produto.
Quando isolamento dedicado ainda faz sentido
Há razões legítimas para infraestrutura silo: compliance, residência de dados, perfil de carga, contrato de disponibilidade ou tecnologia legada. O próprio SaaS Lens reconhece modelos pool, silo e híbridos. O ponto é operar tenants por uma experiência unificada, mesmo quando alguns recursos físicos são dedicados, e não transformar cada ambiente em instalação artesanal.[2]
Documentos também podem exigir motores ou regiões diferentes. Trate backend de renderização como capacidade configurada e observável, com contrato comum de job, erros e resultado. Isolar o recurso não obriga duplicar editor, modelo de autorização e fluxo de publicação.
Checklist antes de aceitar a próxima customização
- Qual conceito do produto explica a diferença solicitada?
- Ela é conteúdo, tema, composição, dado, capacidade, acesso ou isolamento?
- Outro tenant poderia usar a mesma opção de forma segura?
- O contrato é fechado, versionado e validado antes da publicação?
- A autorização continua baseada em identidade validada, não em configuração visual?
- Deploy, observabilidade, suporte e rollback continuam unificados?
Veja a arquitetura do RhizzaDocs e monte o primeiro tipo pelo quickstart. Se a decisão agora é qual categoria de ferramenta adotar, siga para o comparativo de geradores, editores e SDKs documentais.