IA y contexto

Ontology-Driven Design: ¿y si la ontología se convirtiera en la red de seguridad del código generado por IA?

Cuando un agente de IA escribe el código, los tests pasan, pero el negocio puede derivar sin que nadie lo note: dos nombres para un mismo concepto, una regla contradicha, una noción abandonada que reaparece. Una ontología extraída del código, contrastada con la intención antes de cada desarrollo y después con el código producido, hace visibles esas derivas y separa la evolución buscada del error.

Info

Escrito originalmente en francés. Traducido por IA — se ha preservado el sentido, no la prosa.

Portada del libro Le Contexte
En la línea del libro «Le Contexte»

Este artículo prolonga lo que escribí en mi libro Le Contexte. Para leerlo, descárgalo o cómpralo en Amazon. La edición de Amazon solo está disponible en francés; si deseas una versión en otro idioma, contáctame.

El TDD usa los tests para guiar el desarrollo. ¿Y si usáramos también una ontología para preservar la coherencia de negocio de un software? Es la vía que exploro con Claude Code: partir del código para construir una ontología y, poco a poco, invertir el recorrido para que sea esa ontología la que guíe, controle y acompañe las evoluciones del software.

El código funciona. Pero ¿sigue haciendo lo que el negocio necesita?

Con agentes de IA como Claude Code, hemos entrado en una etapa en la que producir código resulta cada vez menos difícil.

Describo una funcionalidad. El agente explora el código existente, modifica varias clases, añade funciones, adapta interfaces y escribe tests. Unos minutos después, me presenta su trabajo.

El código compila. Los tests pasan.

Pero queda una pregunta: ¿sigue respetando este código nuevo la lógica de negocio de mi aplicación?

Un agente puede producir una implementación técnicamente correcta e introducir una incoherencia en el modelo del negocio. Puede usar dos nombres para designar un mismo concepto, recuperar una noción abandonada o crear una regla que contradice las que ya existen.

El problema se vuelve especialmente interesante cuando la aplicación crece y los agentes de IA realizan una parte cada vez mayor de las modificaciones.

¿Cómo darles un marco de referencia de negocio común? ¿Cómo detectar una deriva? ¿Y cómo evitar que ese marco de referencia se convierta en otra documentación más, que acaba por no corresponder al software?

A partir de estas preguntas empecé a experimentar, en mis desarrollos personales con Claude Code, un enfoque que aquí llamo Ontology-Driven Design (ODD).

Para ilustrarlo, tomaré el ejemplo de un ERP ficticio.

Primer paso: extraer la ontología del código

Mi punto de partida era bastante sencillo.

Considero el código fuente como la fuente de verdad del comportamiento efectivamente implementado. Una especificación describe una intención; el código revela lo que la aplicación hace realmente.

Así que pedí a Claude Code que explorara el código de mi aplicación y extrajera una representación del dominio de negocio en un archivo Markdown: ontology.md.

Este archivo describe, entre otras cosas:

  • los conceptos de negocio: Cliente, Pedido, Presupuesto, Factura, Abono…;
  • sus propiedades y sus relaciones;
  • las reglas de negocio y los invariantes;
  • los estados y sus transiciones;
  • el vocabulario usado para designar esos conceptos.

En nuestro ERP ficticio, un pedido de cliente está asociado a un cliente. Tiene una o varias líneas. Puede proceder de un presupuesto. Puede estar validado, anulado o facturado.

Esta información no está necesariamente descrita en un mismo lugar del código. Algunas reglas son explícitas; otras están repartidas entre varias funciones.

Claude Code puede buscarlas e intentar reconstruirlas.

El resultado no es una especificación perfecta. Es una interpretación estructurada del negocio a partir de su implementación, con los límites que eso implica: una regla implícita puede escapársele, y un comportamiento heredado puede tomarse por error como una regla intencionada.

Pero obtengo algo interesante: una representación del dominio independiente de los detalles técnicos de la implementación.

El archivo ontology.md se convierte en un punto de referencia para entender el software.

Segundo paso: verificar el código nuevo frente a la ontología

Una vez disponible esa ontología, pedí a Claude Code que se apoyara en ella al desarrollar nuevas funcionalidades.

La idea era comparar las modificaciones realizadas con los conceptos y las reglas de ontology.md.

Imaginemos, por ejemplo, que la ontología indica que una factura validada ya no puede modificarse.

Si Claude Code añade una funcionalidad que permite cambiar sus líneas después de la validación, debe señalar esa contradicción.

Del mismo modo, si la ontología distingue claramente un presupuesto de un pedido, el agente no debe introducir una función que mezcle las responsabilidades de ambos conceptos sin una razón explícita.

Es una primera red de seguridad.

Pero tiene un defecto.

La verificación llega después de que se haya producido el código.

El agente ya ha hecho su trabajo. Quizá ha creado varias clases, añadido métodos y construido tests en torno a una mala interpretación del negocio.

Podemos detectar el problema, pero luego hay que volver sobre el código producido.

Dicho de otro modo, había añadido un control a posteriori. Es útil, pero insuficiente.

Tercer paso: inspirarse en el Test-Driven Development

Ahí fue cuando pensé en el TDD.

El principio del Test-Driven Development es conocido: antes de implementar un comportamiento, se escribe un test que expresa lo que se espera. El test falla, se escribe el código necesario para que pase y luego se mejora la implementación conservando esa red de seguridad.

¿Por qué no aplicar una lógica parecida a la ontología?

Así que pedí a Claude Code que cambiara su proceso.

Antes de programar, debe contrastar primero la funcionalidad pedida con la ontología existente.

El orden de las operaciones cambia.

Tomemos una petición nueva:

"Quiero permitir que se cree un pedido de cliente sin pasar obligatoriamente por un presupuesto."

Si la ontología indica que un pedido debe proceder obligatoriamente de un presupuesto, el agente detecta una contradicción antes de tocar el código.

Pero esa contradicción no es necesariamente un error en la petición.

Puede corresponder a una evolución perfectamente legítima del negocio.

El agente debe, por tanto, reconocer que la intención del desarrollador modifica una regla existente, proponer su evolución y determinar las consecuencias sobre los conceptos relacionados.

Después podrá desarrollar la funcionalidad teniendo en cuenta el modelo del negocio revisado.

El proceso queda así:

  1. Entender la funcionalidad pedida.
  2. Identificar los conceptos y las reglas de la ontología afectados.
  3. Señalar las posibles contradicciones entre la petición y el modelo existente.
  4. Resolver esas contradicciones, distinguiendo entre evolución deliberada y error de diseño.
  5. Desarrollar la funcionalidad.
  6. Verificar que el código producido es coherente con el modelo del negocio esperado.

No es TDD en sentido estricto: una ontología no es una batería de tests automatizados, y un análisis hecho por un LLM no ofrece las mismas garantías que una aserción determinista.

Pero la filosofía se parece: definir las expectativas y las restricciones antes de implementar, en lugar de descubrir las incoherencias después.

En este punto, la ontología empieza a desempeñar un papel activo en el desarrollo.

Lo que plantea un problema nuevo.

Cuarto paso: una ontología que debe evolucionar con el código

Si la ontología se convierte en un marco de referencia para el desarrollo, ¿cómo se mantiene al día?

Lo último que quiero es crear un documento nuevo que haya que acordarse de modificar a mano en cada evolución de la aplicación.

Sería volver al problema de partida: una documentación que se desincroniza del código.

La idea siguiente consiste, por tanto, en introducir una parte mecánica en el proceso.

Un script analiza los cambios del código fuente, en particular a partir de Git, e identifica los elementos añadidos, modificados o eliminados:

  • funciones y métodos;
  • clases e interfaces;
  • módulos y paquetes;
  • firmas y contratos de API;
  • herramientas, recursos y contratos expuestos mediante MCP;
  • relaciones y comportamientos de negocio identificables en las modificaciones.

El objetivo no es reconstruir la ontología a partir de los detalles técnicos de la aplicación.

Al contrario, quiero mantenerme independiente de las decisiones de implementación. Una arquitectura hexagonal, el Domain-Driven Design o cualquier otra organización del código (incluso muy sucia) no deben imponer su estructura a la ontología.

La ontología representa el dominio de negocio, no la arquitectura técnica.

Una clase no es necesariamente un concepto de negocio. Un paquete nuevo puede no ser más que una reorganización técnica. Una función puede implementar una regla de negocio sin constituir por sí misma un concepto nuevo.

Hay que separar, por tanto, dos operaciones.

La primera es mecánica: determinar con precisión qué ha cambiado.

La segunda es semántica: entender si esos cambios introducen, modifican o contradicen conceptos de negocio.

Aquí es donde interviene el LLM.

El script produce un inventario de las modificaciones. El LLM contrasta ese inventario con la ontología actual y con las directrices que pusieron en marcha el desarrollo.

Pueden darse entonces varias situaciones.

Aparece un concepto de negocio nuevo. El agente puede proponer o preparar su incorporación a la ontología, con sus relaciones y las reglas asociadas.

Un concepto existente cambia de significado. Hay que determinar si se trata de una evolución buscada o de una deriva.

Dos conceptos dejan de ser coherentes entre sí. El agente señala la contradicción, con las referencias al código afectado.

El vocabulario diverge. Dos nombres designan la misma realidad de negocio, o un nombre existente se usa para otra noción.

El cambio es puramente técnico. No hace falta ninguna modificación en la ontología.

Hay un punto que sigue siendo esencial: el código no debe tener razón automáticamente.

Si un agente introduce un error, no quiero que un segundo agente modifique la ontología para que ese error resulte aceptable.

El triángulo: intención, código y ontología

Aquí es, creo, donde está la verdadera evolución del concepto.

Comparar el código y la ontología no basta.

Hay que contrastar tres elementos:

La intención: lo que el desarrollador pidió a Claude Code.

El código: lo que efectivamente se implementó.

La ontología: los conceptos y las reglas de negocio de referencia.

Es la comparación de estos tres elementos lo que permite entender la naturaleza de una desviación.

Volvamos a nuestro ERP ficticio.

La ontología indica que un pedido de cliente procede obligatoriamente de un presupuesto.

El desarrollador pide explícitamente que el presupuesto sea opcional.

Claude Code modifica las funciones afectadas e introduce la posibilidad de crear un pedido directamente.

La ontología y el código nuevo están ahora en contradicción. Pero el código respeta la intención.

El resultado correcto no es, por tanto, rechazar sistemáticamente la modificación. Es proponer la evolución correspondiente de la ontología y luego buscar los demás puntos del software que todavía suponen que el presupuesto es obligatorio.

En cambio, si Claude Code hubiera eliminado esa obligación sin que ninguna directriz lo pidiera, la desviación sería sospechosa.

La diferencia es fundamental.

Una incoherencia no siempre es un bug. Puede señalar una transformación deliberada del negocio.

El papel del sistema no es impedir el cambio, sino hacerlo explícito, coherente y trazable.

Hacia un verdadero ciclo de desarrollo guiado por la ontología

Ahora imagino un proceso que interviene en tres momentos.

Antes del desarrollo: entender el negocio

Al recibir una funcionalidad nueva, Claude Code identifica los conceptos de la ontología afectados.

No necesita cargar toda la ontología. Puede seleccionar los conceptos, relaciones y reglas útiles para la petición.

Después verifica que la funcionalidad es compatible con ese modelo, o señala las evoluciones necesarias.

Así, el desarrollo parte de una comprensión explícita del dominio.

Durante y después del desarrollo: detectar las derivas

Un analizador mecánico localiza las modificaciones del código fuente.

Herramientas como Tree-sitter permiten, en particular, analizar su estructura sintáctica e identificar los elementos modificados (es mi próximo frente de trabajo). Las evoluciones de los contratos de API y de las interfaces MCP informan también sobre las capacidades que expone la aplicación.

El LLM se apoya en estos elementos para buscar las incoherencias con la ontología.

Esta operación podría integrarse en el workflow de Claude Code mediante hooks, por ejemplo después de una modificación o al final de una tarea.

No se trata de pedir a un LLM que relea a ciegas toda la aplicación en cada cambio, sino de concentrar el análisis en los elementos afectados.

Antes de la integración: controlar la coherencia

Una última verificación podría tener lugar antes de fusionar el código.

Produciría un informe que indicara:

  • los conceptos nuevos o modificados;
  • las reglas de negocio potencialmente afectadas;
  • las incoherencias detectadas;
  • las modificaciones propuestas para la ontología;
  • las decisiones que requieren una validación humana.

El objetivo es obtener un control reproducible, lo bastante preciso para ser útil, sin convertir cada modificación del código en una revisión interminable del modelo del negocio.

Conservar el vínculo entre los conceptos y su implementación

Una mejora complementaria consistiría en mantener una trazabilidad entre la ontología y el código.

Un concepto como Invoice podría asociarse a varias clases, funciones, eventos u operaciones de API que participan en su implementación.

Estas asociaciones no definen el concepto. Indican dónde está implementado.

Esa distinción permitiría responder a preguntas interesantes.

Si modifico el concepto de factura, ¿qué partes del código corren el riesgo de verse afectadas?

Si desaparece una función de negocio, ¿qué regla de la ontología podría dejar de estar implementada?

Si aparece una operación de API nueva, ¿expone un concepto existente o revela una noción nueva?

Y si una clase cambia de nombre, ¿se trata de un simple refactoring o de una modificación real del dominio?

Este mapa podría mantenerse de forma progresiva, a medida que evoluciona el código.

Aun así, debería conservar las pruebas de cada asociación: archivos, símbolos y cambios observados. Una relación deducida por el LLM no debe confundirse con una correspondencia demostrada.

De los invariantes de la ontología a los tests automatizados

Otra vía consiste en hacer ejecutable una parte de la ontología.

Algunas reglas de negocio pueden expresarse en forma de invariantes.

Por ejemplo:

  • una factura validada ya no puede modificarse;
  • una factura debe tener al menos una línea;
  • un abono hace referencia a la factura que corrige;
  • un pedido anulado no puede enviarse.

Algunas de estas reglas podrían servir para generar tests automatizados, o para verificar que los tests existentes las cubren.

Obtendríamos entonces dos niveles complementarios.

El primero sería semántico: el LLM examina la coherencia entre las intenciones, los conceptos y el código.

El segundo sería determinista: tests y restricciones ejecutables verifican los comportamientos que es posible formalizar.

La ontología no sustituiría, por tanto, ni a los tests unitarios ni a los tests de integración.

Podría ayudar a explicar por qué ciertos comportamientos deben probarse, y a localizar reglas de negocio que todavía carecen de verificación.

Una ontología viva, pero no oportunista

Este dispositivo entraña un riesgo importante.

Si la ontología cambia sistemáticamente para seguir al código, deja de ser una red de seguridad. Se convierte en una simple documentación regenerada sin cesar.

A la inversa, si prohíbe cualquier evolución que contradiga sus reglas actuales, impide que el producto cambie.

Hay que gestionar, por tanto, las evoluciones de la ontología de forma explícita.

Distingo tres situaciones.

La evolución es intencionada y clara. El sistema puede preparar una modificación versionada de la ontología, o incluso aplicarla en una rama de trabajo antes de su validación.

La modificación parece incoherente con la intención. El sistema propone una corrección del código en lugar de una actualización de la ontología.

La intención es ambigua. El sistema pide una decisión en lugar de inventarse una regla de negocio nueva.

La ontología se convierte así en un contrato evolutivo entre la intención y la implementación.

Esto supone también conservar un historial, por ejemplo con un pequeño registro de decisiones inspirado en los ADR. Un concepto renombrado no debe convertirse en dos conceptos independientes. Una regla eliminada debe poder vincularse a la decisión que justificó su desaparición.

La evolución de la ontología debe ser tan trazable como la del código.

Medir la coherencia en lugar de contar líneas de código

Este enfoque podría producir también algunos indicadores interesantes.

La cobertura de la ontología mediría la proporción de conceptos de negocio identificados en el código que tienen una representación en la ontología.

La trazabilidad mediría cuántos conceptos disponen de referencias verificables a su implementación.

El número de incoherencias confirmadas y no resueltas daría una idea de la deriva entre el código y el modelo.

Por último, la proporción de invariantes asociados a tests ejecutables podría ayudar a medir la solidez de la red de seguridad.

Estas cifras no serían indicadores de calidad absolutos. Su interés depende por completo de la capacidad del sistema para distinguir los verdaderos conceptos de negocio del ruido técnico.

Pero podrían hacer observable algo que hoy sigue siendo en gran medida implícito: la coherencia conceptual de un software a lo largo de sus evoluciones.

¿Y si el proceso funcionara en ambos sentidos?

Al principio, mi forma de trabajar era unidireccional.

El código producía la ontología.

Después añadí un control inverso: la ontología servía para verificar el código nuevo.

Inspirándome en el TDD, introduje un paso previo: contrastar las intenciones con la ontología antes de empezar a desarrollar.

La próxima evolución consiste en automatizar la detección y la calificación de los cambios, para sostener esa coherencia en el tiempo.

Pero ahora veo una posibilidad más.

El código podría hacer evolucionar la ontología, y la ontología podría guiar las evoluciones del código.

Si de una modificación del código surge un concepto de negocio nuevo, el sistema propone incorporarlo a la ontología.

Si una petición implica la evolución de un concepto existente, la ontología permite identificar las reglas y las implementaciones potencialmente afectadas antes incluso de empezar.

El marco de referencia ya no es solo un producto del software. Participa en su transformación.

Es este bucle lo que me interesa.

Una idea antigua, un uso por reinventar

Sería falso presentar el Ontology-Driven Design como una idea completamente nueva.

El uso de ontologías para diseñar aplicaciones existe desde hace mucho. Pavel Hruby ya describía en 2005 un enfoque de Ontology-Based Domain-Driven Design.

Más recientemente, trabajos como ODDToolkit han explorado la generación de artefactos de software a partir de ontologías formales.

Mi exploración es algo distinta.

No busco necesariamente generar el software a partir de una ontología. Tampoco quiero imponer OWL, RDF ni ningún otro formalismo para empezar.

Lo que busco es usar una ontología de negocio como marco de referencia vivo de coherencia para agentes de IA que modifican el código fuente.

El matiz importa.

El modelo del negocio no sustituye a la implementación. No aspira a describir toda la complejidad de un software. No garantiza, por sí solo, la ausencia de bugs.

Aporta un marco de referencia explícito para evitar que cada agente, en cada tarea nueva, reconstruya su propia interpretación del negocio.

Lo que queda por demostrar

Sigo en una fase de experimentación personal.

Empecé generando una ontología desde el código y pidiendo después a Claude Code que controlara sus evoluciones a partir de ella. Más tarde modifiqué las directrices para adelantar esa verificación y situarla antes del desarrollo.

El bucle mecánico de sincronización, la trazabilidad sistemática y los controles automatizados son las próximas líneas de trabajo.

Quedan muchas preguntas abiertas.

¿Podrá el analizador detectar una modificación de negocio real cuando no cambia ningún nombre de clase? ¿Cuántos falsos positivos producirá el LLM? ¿Podrá JEV responder a esta necesidad? Ese es otro frente, que habrá que abrir algún día. ¿Cómo evitar sobrecargar la ontología de detalles técnicos? ¿Con qué frecuencia hay que revisar las correspondencias entre conceptos y código? Y, sobre todo, ¿justificará la ganancia en coherencia el coste de ejecución y de mantenimiento del dispositivo?

Habrá que poner a prueba este enfoque con evoluciones de software reales, con cambios de negocio deliberados, incoherencias introducidas de forma artificial y refactorings puramente técnicos.

No afirmo, por tanto, haber definido un método de desarrollo nuevo y validado.

Describo una dirección que estoy experimentando.

El negocio como red de seguridad

Las prácticas de desarrollo han ido multiplicando las protecciones.

Los tests unitarios protegen comportamientos. Los tests de integración protegen interacciones. La comprobación de tipos y las herramientas de análisis estático detectan ciertas categorías de errores. Las reglas de arquitectura ayudan a preservar la estructura del software.

Pero cuando los agentes de IA producen una parte creciente del código, otra pregunta cobra importancia:

¿Quién protege la coherencia del modelo del negocio que manipulan esos agentes?

El DDD aporta desde hace tiempo una parte de la respuesta, con el lenguaje ubicuo, los modelos de dominio y las fronteras de contexto. La arquitectura hexagonal ayuda a aislar el negocio de las preocupaciones técnicas.

El Ontology-Driven Design que exploro buscaría añadir a estos principios un mecanismo operativo: hacer explícito el modelo del negocio, contrastarlo con las intenciones de desarrollo y controlar después de forma continua su coherencia con el código.

En el fondo, mi objetivo no es producir más documentación.

Es permitir que Claude Code desarrolle más rápido sin reinventar en silencio el negocio con cada funcionalidad.

El código sigue siendo la verdad del comportamiento implementado. La ontología se convierte en la referencia del sentido de negocio. La intención explica por qué ese sentido puede cambiar.

Y de su confrontación podría nacer una nueva red de seguridad para el desarrollo asistido por IA.