Una petición comercial se vuelve peligrosa cuando deja de ser una decisión explícita de producto y pasa a materializarse como un if ($tenantId === ...). Al principio resuelve una urgencia. Con el tiempo, esa condición aparece en controladores, plantillas, procesos de cola, exportaciones y APIs. El resultado no es una configuración: son variantes implícitas del producto, difíciles de probar, explicar y retirar.
La configuración por cliente en SaaS PHP debe permitir diferencias deliberadas y gobernadas, no preservar cada excepción histórica. La pregunta útil no es «¿podemos hacerlo para este cliente?», sino «¿esta variación representa una dimensión estable del producto que otros clientes podrían necesitar, con reglas y soporte sostenibles?».
La señal de alerta: una excepción permanente de código

Hay una diferencia entre adaptar una experiencia y mantener una rama oculta del producto. Conviene intervenir antes de que una solicitud particular cree cualquiera de estas señales:
- El identificador del tenant, dominio o cliente aparece en lógica de negocio.
- Una misma regla se replica en interfaz, API y trabajador asíncrono.
- El equipo no puede responder qué clientes tienen una excepción ni quién la aprobó.
- Un cambio de plan altera comportamientos funcionales sin una definición central.
- Retirar una adaptación exige buscar condicionales en varios repositorios o servicios.
Una excepción puede ser legítima durante un descubrimiento o una migración, pero debe tener propietario, fecha de revisión y una salida: convertirla en capacidad de producto, aislarla como integración específica o rechazarla. Dejarla sin clasificación convierte la deuda técnica en una promesa comercial no documentada.
No confunda configuración, permisos, capacidades y desarrollo específico
Estos mecanismos responden a preguntas distintas. Mezclarlos produce diseños opacos y reglas contradictorias.
- Configuración: define cómo se comporta una función ya existente para un tenant. Por ejemplo, el formato de una numeración, el idioma predeterminado o si un flujo requiere una aprobación adicional.
- Permisos: determinan qué puede hacer una identidad dentro de un tenant. Un usuario puede tener permiso para aprobar pagos aunque la aprobación esté configurada como obligatoria.
- Capacidades: indican si el tenant tiene acceso a una función o a un límite operativo. Pueden depender de contrato, plan o activación controlada, pero no deberían contener toda la lógica del dominio.
- Desarrollo específico: cubre un comportamiento que no encaja en una dimensión reutilizable del producto, como una integración con un sistema propio del cliente o una transformación contractual singular.
Una regla práctica ayuda a decidir: si cambia quién ejecuta una acción, use permisos; si cambia si existe o está disponible una función, use capacidades; si cambia cómo opera una función disponible, use configuración. Si cambia el modelo de negocio de forma exclusiva, no lo disfrace de bandera.
Qué debe ser configurable y qué debe seguir en el núcleo
Una opción merece entrar en el catálogo de configuración cuando tiene semántica clara, un conjunto finito de valores, validaciones conocidas y una expectativa razonable de reutilización. También necesita una experiencia de soporte comprensible: alguien debe poder explicar el efecto de cambiarla sin inspeccionar código.
Suelen ser buenos candidatos los parámetros de presentación, políticas de notificación, umbrales, secuencias de aprobación, preferencias regionales y elecciones entre flujos ya soportados. En cambio, deben permanecer en el núcleo las invariantes de seguridad, integridad de datos, cálculo financiero base y reglas cuyo cambio exigiría reinterpretar entidades o contratos existentes.
No convierta datos arbitrarios en configuración sólo por flexibilidad. Un campo JSON sin esquema puede ocultar dependencias imposibles de descubrir. Cuando una opción modifica una regla crítica, defina tipos, valores permitidos, condiciones de uso y consecuencias sobre datos previos.
Construya un modelo de configuración gobernado
Una clave aislada no basta. Cada definición del catálogo debe incluir metadatos que permitan operar el producto con seguridad:
- Clave y descripción funcional: nombres estables, orientados al dominio y no a detalles de implementación.
- Propietario: equipo o responsable que decide su evolución y retirada.
- Alcance: global, tenant, unidad organizativa, proyecto o usuario. Evite permitir todos los alcances por defecto.
- Valor por defecto: comportamiento explícito cuando no existe una anulación.
- Tipo y validación: booleano, enumeración, número con rango o estructura validada mediante esquema.
- Dependencias: requisitos respecto a otras opciones, capacidades o estado de migración.
- Sensibilidad: clasificación de datos y reglas de acceso para lectura y modificación.
- Ciclo de vida: fecha de introducción, revisión, deprecación y retirada prevista cuando aplique.
En PHP, centralice la resolución en un servicio de dominio, por ejemplo TenantSettings, y entregue objetos tipados en lugar de arrays sin contrato. La aplicación puede combinar el valor global, el valor de tenant y un valor más específico mediante una precedencia documentada. La ausencia de valor debe resolverse siempre al valor por defecto, no a una interpretación distinta en cada consumidor.
$policy = $tenantSettings->approvalPolicy($tenantId);
if ($policy->requiresSecondApproval()) {
$workflow->requestSecondApproval($order);
}
El almacenamiento puede ser relacional o documental, pero el catálogo y la validación no deberían depender de la forma de persistencia. Mantenga además historial inmutable de cambios: valor anterior y nuevo, actor, momento, motivo y canal de modificación. El historial no sustituye un registro de auditoría de acciones de negocio, pero permite reconstruir qué configuración estaba vigente.
Evalúe la decisión en el límite adecuado
El problema de los condicionales dispersos no se resuelve moviéndolos todos a un controlador. Una configuración que afecta una regla de negocio debe evaluarse en el servicio o política de dominio que aplica esa regla. El controlador traduce la petición; la plantilla presenta el resultado; ninguno debería decidir por sí mismo una política de tenant.
Para comportamientos complejos, use estrategias o políticas registradas, en vez de cadenas de booleanos. Una política de facturación puede seleccionar una implementación entre modos soportados tras validar que el tenant tiene la capacidad necesaria. Así, la interfaz, la API y la cola invocan la misma decisión.
Las plantillas pueden recibir una vista ya preparada, incluidos indicadores de capacidad para mostrar u ocultar acciones. Ocultar un botón no es autorización. La API debe aplicar permisos, capacidad y configuración en el servidor, aunque la interfaz no exponga la operación.
Capacidades y límites sin rigidizar los planes
Un plan comercial puede conceder capacidades, pero no debe convertirse en una colección de if ($plan === '...'). Modele una capacidad estable, como advanced_approvals o api_access, y resuelva qué tenants la poseen mediante una fuente contractual o administrativa. Después, la lógica funcional consulta la capacidad, no el nombre del plan.
Los límites requieren una definición todavía más precisa: qué se cuenta, en qué ventana temporal, cuándo se aplica el bloqueo y cómo se comportan reintentos y procesos en cola. Un límite debe ser observable y consistente en todos los puntos de entrada. Si una integración crea recursos fuera de la interfaz principal, no puede eludir el mismo control.
Cambie ajustes de forma segura y reversible
Modificar una opción puede tener efectos inmediatos sobre trabajos en curso, registros existentes o integraciones. Antes de guardar, valide tipo, permisos administrativos, dependencias y compatibilidad con el estado actual. Cuando el impacto sea relevante, ofrezca una previsualización del cambio: qué flujo se activará, qué restricciones incumple y qué operaciones futuras afectará.
La activación gradual es distinta de divulgar una opción en toda la interfaz. Puede habilitar una capacidad para un conjunto controlado de tenants y observar su comportamiento antes de exponerla de forma general. Defina también una reversión: qué valor restaura el estado anterior, si hay migraciones de datos asociadas y qué ocurre con operaciones iniciadas bajo la configuración nueva.
Un cambio reversible en la interfaz puede no ser reversible en los datos. Trate ambas dimensiones por separado antes de activar una política nueva.
Mantenga consistencia en colas, APIs e integraciones
Los procesos asíncronos introducen una decisión adicional: resolver la configuración al ejecutar el trabajo o conservar una instantánea al crearlo. Para acciones que deben respetar la política vigente, resuelva al ejecutar e incluya el tenant en el contexto del trabajo. Para documentos, cálculos o comunicaciones que deben reproducir la decisión original, guarde una versión o instantánea explícita junto al comando.
No mezcle ambas opciones sin declararlo. Un reintento puede cambiar de resultado si consulta una configuración actualizada. Defina idempotencia, versión de configuración y comportamiento esperado en reintentos. Las integraciones externas necesitan contratos equivalentes: validación previa, tratamiento de errores, límites y trazabilidad por tenant, sin enviar secretos o datos personales al registro de diagnóstico.
Auditoría y soporte: explique el comportamiento observado

El soporte necesita responder por qué un cliente ve un flujo, no sólo qué valor tiene una clave. Registre una traza de decisión con identificador de tenant, versión de definición, fuente del valor efectivo —por defecto o anulación—, capacidades relevantes y resultado de la evaluación. Limite el acceso a esa información y enmascare valores sensibles.
Complete esta trazabilidad con métricas de uso por opción, errores de validación, cambios fallidos y opciones sin consumo. Una configuración sin uso puede estar obsoleta; una utilizada por un único tenant durante mucho tiempo merece revisión de producto. El objetivo no es eliminar toda diferencia, sino hacer que cada diferencia sea explícita, comprobable, observable y retirada cuando deje de aportar valor.



