main content
< Volver a blog sobre aplicaciones móviles

Versionado y control de cambios en tu web a medida

Cómo diseñar un sistema de versionado y control de cambios en tu aplicación web a medida

"Quien cambio esto y cuando?" Esa pregunta aparece en toda aplicacion empresarial que madura lo suficiente. A veces es un requisito legal: el RGPD exige trazabilidad sobre quien modifica datos personales. Otras veces es operativo: el departamento financiero necesita reconstruir el estado de una factura tal como estaba hace tres meses. Y muchas veces es funcional: los usuarios quieren deshacer cambios o comparar versiones.

He visto aplicaciones donde la respuesta a esa pregunta es un encoger de hombros. Un campo updated_at y poco mas. La realidad es que eso no sirve cuando un cliente te llama preguntando por que su contrato tiene un importe diferente al que firmo.

Implementar versionado de verdad no es trivial. Pero tampoco es ciencia espacial si eliges la estrategia adecuada. Vamos a recorrer las opciones, desde lo mas sencillo hasta event sourcing completo.

Tres niveles de trazabilidad: elige el tuyo

Antes de escribir codigo, necesitas decidir hasta donde quieres llegar. No es lo mismo "saber que algo cambio" que "poder rebobinar al estado exacto de hace seis meses".

Nivel 1: Audit trail basico

Registra quien hizo que y cuando. No almacena el estado anterior ni permite restaurarlo. Se implementa con una tabla de logs que guarda la accion (crear, editar, eliminar), el usuario, la fecha y la entidad afectada. Es el minimo para cumplir con el RGPD y la LOPDGDD.

Rapido de montar. Util para auditorias basicas. Pero limitado cuando alguien necesita saber que cambio exactamente.

Nivel 2: Versionado de entidades

Aqui almacenas el estado completo de cada entidad en cada momento de modificacion. Puedes responder "como estaba este registro el 15 de marzo" y comparar diferencias entre versiones. Es lo que necesitan la mayoria de aplicaciones empresariales: CRMs con historial de cambios en fichas de cliente, ERPs con trazabilidad de precios, plataformas de contenido con historial editorial.

Nivel 3: Event sourcing completo

No almacenas el estado actual. Almacenas la secuencia completa de eventos que lo produjeron. El estado se reconstruye reproduciendo los eventos desde el inicio (o desde un snapshot). Potente. Tambien complejo. Adecuado para sistemas financieros, plataformas de trading o cualquier dominio donde la integridad temporal del dato es critica.

Estrategias de implementacion

Tablas de historico

La estrategia mas directa. Para cada entidad que requiera versionado, creas una tabla espejo. Si tienes contratos, creas contratos_historico con las mismas columnas mas version_id, version_number, modified_by, modified_at y operation (INSERT, UPDATE, DELETE).

Cada vez que un registro cambia, un trigger o un hook del ORM copia el estado anterior a la tabla de historico antes de aplicar la modificacion. En PostgreSQL, un trigger BEFORE UPDATE lo resuelve limpiamente:

CREATE OR REPLACE FUNCTION contratos_audit()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO contratos_historico
    (contrato_id, version_number, titulo, importe, estado,
     modified_by, modified_at, operation)
  VALUES
    (OLD.id, OLD.version, OLD.titulo, OLD.importe, OLD.estado,
     current_setting('app.current_user'), now(), TG_OP);
  NEW.version := OLD.version + 1;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Funciona bien. Se consulta con SQL estandar. Compatible con cualquier ORM. El inconveniente: cada vez que tocas el esquema de la tabla principal, tienes que actualizar tambien la tabla de historico. Y el almacenamiento crece con cada cambio.

Tabla de auditoria generica con JSONB

En lugar de una tabla espejo por entidad, metes todos los cambios en una unica tabla generica. Los estados anterior y posterior se guardan como documentos JSON:

CREATE TABLE audit_log (
  id          BIGSERIAL PRIMARY KEY,
  entity_type VARCHAR(100) NOT NULL,
  entity_id   BIGINT NOT NULL,
  version     INT NOT NULL,
  old_data    JSONB,
  new_data    JSONB,
  diff        JSONB,
  user_id     BIGINT NOT NULL,
  ip_address  INET,
  created_at  TIMESTAMPTZ DEFAULT now()
);

El campo diff almacena solo los campos que cambiaron, calculado por la aplicacion. Esto te permite consultas como "todos los cambios que el usuario X hizo en contratos durante junio" con un solo query indexado.

Esta es la estrategia que implementan librerias populares: django-auditlog en Python, paper_trail en Ruby on Rails, laravel-auditing en PHP. En Java/Spring, Hibernate Envers ofrece una implementacion madura e integrada con JPA.

Ventaja clara: esquema unificado, funciona con cualquier entidad, y los indices GIN de PostgreSQL sobre JSONB hacen que las busquedas dentro del JSON sean rapidas. El trade-off: reconstruir el estado completo de una version antigua requiere recorrer la cadena de diffs o almacenar snapshots completos.

Event Sourcing

Event sourcing le da la vuelta al modelo mental. Los eventos son la fuente de verdad. El estado actual es una proyeccion derivada.

Para un sistema de contratos, los eventos serian algo asi:

  • ContratoCreado { id: 42, titulo: "Servicio anual", importe: 24000 }
  • ImporteModificado { id: 42, importe_anterior: 24000, importe_nuevo: 28000, motivo: "Ampliacion de alcance" }
  • ContratoFirmado { id: 42, firmante: "Maria Lopez", fecha_firma: "2026-03-15" }
  • ContratoRenovado { id: 42, nueva_fecha_fin: "2027-12-31" }

El estado actual del contrato 42 se obtiene reproduciendo esos cuatro eventos. Para saber como estaba en cualquier fecha pasada, reproduces hasta esa fecha y paras.

En Espana, event sourcing encaja especialmente en tres sectores. El financiero, donde el Banco de Espana y MiFID II exigen trazabilidad completa. El sanitario, donde la Ley 41/2002 sobre el historial clinico pide inmutabilidad. Y el legal, donde la contratacion publica (Ley 9/2017) requiere un registro inmutable de cada paso.

Las tecnologias mas usadas: EventStoreDB (disenada para este patron), Apache Kafka (como log distribuido), y Axon Framework o Marten para Java/.NET.

La realidad es que event sourcing trae complejidad seria. Necesitas CQRS para consultas eficientes. La curva de aprendizaje es pronunciada. Y migrar esquemas de eventos una vez en produccion es doloroso. Pero cuando el dominio lo pide, no hay nada mejor.

Comparacion y diffing: mostrar que cambio

Un sistema de versionado que almacena todo pero no lo muestra bien es como un archivador donde nadie encuentra nada.

Diffing para datos JSON

El estandar de facto es JSON Patch (RFC 6902): operaciones atomicas (add, remove, replace, move, copy, test). Librerias como jsondiffpatch, dictdiffer o zjsonpatch generan estos diffs automaticamente.

Un diff entre dos versiones de un contrato:

[
  { "op": "replace", "path": "/importe", "value": 28000 },
  { "op": "add", "path": "/clausulas/5", "value": "Penalizacion por retraso: 0,5% diario" }
]

Diffing de texto enriquecido

Para campos de texto largo, los algoritmos linea a linea se quedan cortos. La libreria diff-match-patch de Google implementa el algoritmo de Myer con optimizaciones semanticas que producen diffs legibles a nivel de palabra. Si trabajas con editores como TinyMCE, CKEditor o ProseMirror, el versionado se implementa almacenando el HTML o JSON del editor y usando diffing a nivel de nodo DOM.

La interfaz de usuario marca la diferencia

He visto sistemas de versionado tecnicamente impecables que nadie usa porque la interfaz es impenetrable. Los tres componentes que hacen falta son claros.

Un timeline cronologico que muestre quien cambio que y cuando, filtrable por usuario, fechas o campo modificado. Una vista de comparacion lado a lado que resalte las diferencias entre dos versiones cualesquiera. Y una funcion de restauracion que cree una nueva version con los datos de la version seleccionada, sin borrar el historico.

Ojo con la restauracion: es un evento en si mismo que debe quedar registrado. Y no todos los usuarios deberian tener permisos para ejecutarla.

Rendimiento y almacenamiento: los numeros asustan

El volumen de datos de auditoria crece rapido. Una aplicacion con 10 000 usuarios activos modificando registros 20 veces al dia genera 200 000 entradas diarias. Unos 73 millones al ano. Como lo controlas?

Particionamiento temporal en PostgreSQL. Divides la tabla de auditoria por mes: las consultas recientes van contra particiones pequenas, el historico antiguo se queda en almacenamiento mas barato.

Politica de retencion sensata. Los ultimos 12 meses online. De 1 a 5 anos en almacenamiento frio (S3, Azure Blob Storage). Eliminar despues del periodo legal, conforme al principio de minimizacion del RGPD.

Snapshots periodicos en event sourcing. Cada 100 eventos, guardas un snapshot para evitar reconstrucciones lentas desde el evento cero.

Cumplimiento normativo en Espana

El marco regulatorio espanol tiene requisitos especificos. La LOPDGDD y el RGPD exigen registros de quien accede y modifica datos personales. La Ley 11/2007 pide trazabilidad en expedientes administrativos electronicos. Y el Reglamento VeriFactu de la AEAT obligara a mantener registros inalterables y secuenciales de facturas, con hash encadenado que impida manipulacion retroactiva.

Un sistema de versionado bien disenado convierte estos requisitos legales en funcionalidad que el usuario ni nota. Cumples porque la arquitectura cumple, no porque alguien se acuerde de rellenar un log a mano.

Errores que vemos una y otra vez

Los mas comunes: registrar solo el estado nuevo sin el anterior (imposible calcular diffs), no capturar el contexto del cambio (IP, sesion, motivo), y almacenar la auditoria en la misma transaccion que el cambio principal. Ese ultimo error acopla los dos procesos innecesariamente. Si la escritura de auditoria falla, falla tambien el cambio de negocio. La solucion: colas asincronas para la auditoria.

Cuando invertir en cada nivel

Para un MVP o una aplicacion con menos de 50 usuarios, un audit trail basico con tabla generica JSONB basta. Se monta en uno o dos dias. Para aplicaciones empresariales con cientos de usuarios y requisitos regulatorios, versionado de entidades con tablas de historico o JSONB generico cubre casi todo.

Event sourcing completo se justifica cuando los eventos de negocio son el modelo mental natural del dominio y cuando poder reconstruir el estado en cualquier punto temporal es un requisito de negocio, no solo de auditoria.

Si necesitas implementar versionado y control de cambios en tu aplicacion web y quieres saber que estrategia encaja mejor, contacta con nuestro equipo para una consulta tecnica sin compromiso.

Para cerrar

Un sistema de versionado no es un complemento que anades despues. Es una pieza estructural que afecta al modelo de datos, la arquitectura y la confianza que los usuarios tienen en el sistema. Disenarlo desde el principio sale mucho mas barato que injertarlo cuando ya hay datos en produccion. Audit trail, versionado de entidades o event sourcing: cada estrategia tiene su sitio. Lo que no cambia es la pregunta fundamental que tu sistema debe responder: que cambio, quien lo hizo, cuando y por que.

Contacta con nosotros
Fila 1