Construir un grafo de dependencias COBOL me mostró que la modernización falla en la recuperación, no en la traducción, y por qué una ventana de contexto más grande nunca cierra la brecha.
COBOLLegacy SystemsSoftware Modernization

Un solo archivo COBOL le dijo a la IA todo excepto el único hecho que importaba, así que construí el mapa primero.

Ashutosh SinghalAshutosh Singhal30 de junio de 202615 min

La línea de COBOL que encendió todo esto tenía tres palabras, y las tres me mentían.

COMPUTE WS-RESULT = WS-WIRE-AMOUNT - TRN-LIMIT. Estaba construyendo un patrimonio de demostración para el subsistema de transferencias electrónicas de un banco de nivel medio, y esta era la línea fatídica dentro de un programa llamado WIRETXN. Parece aritmética que un estudiante de primer año podría portar. Resta un límite de un importe, escribe el resultado. Si le entregas ese único archivo a cualquier modelo moderno y pides Java, te dará Java limpio, que compila y pasa las pruebas unitarias en unos cuatro segundos. Tiparía TRN-LIMIT como un long. Y en la primera transferencia electrónica en vivo, escribiría bytes corruptos en una base de datos de producción.

Lo sé porque TRN-LIMIT no es un long. Es un campo decimal empaquetado COMP-3, definido a tres archivos de distancia, cuya interpretación en vivo la elige un indicador fijado en un programa completamente distinto, secuenciado por un trabajo por lotes que se ejecuta a las dos de la madrugada. Nada de eso es visible en WIRETXN. El archivo que contiene el peligroso COMPUTE no contiene ninguno de los hechos que lo hacen peligroso.

Esa brecha es toda la razón por la que construí CodeGraph, y este ensayo trata de lo que me equivoqué en el camino hasta llegar ahí. Empecé convencido de que el problema era la calidad de la traducción. Me equivocaba. El problema es que el modelo no puede ver lo que necesita ver, y pasé un tiempo demostrándome a mí mismo que ninguna cantidad de «dale más contexto» lo arregla.

El COMPUTE que parecía seguro y no lo era

Mapeé el cambio de transferencias electrónicas a mano primero, antes de confiar en que cualquier herramienta lo hiciera, y había exactamente nueve hechos que una migración correcta tenía que conocer.

Tres de ellos viven dentro de WIRETXN y son genuinamente visibles para un lector de un solo archivo. WIRETXN usa TRN-LIMIT en ese COMPUTE en la línea 33. Importa un copybook llamado CBACCT, solo por el nombre, en la línea 13. Dispara un UPDATE sobre la tabla DB2 ACCOUNTS en la línea 37. Una herramienta de ventana de texto ve los tres. Si esos fueran los únicos hechos, la portación ingenua estaría bien.

Los otros seis son los que duelen. TRN-LIMIT se declara PIC S9(9)V99 COMP-3 en CBACCT.cpy en la línea 11, lo que significa decimal empaquetado, lo que significa BigDecimal en Java y absolutamente no un long. Justo debajo, TRN-LIMIT-ALPHA REDEFINES TRN-LIMIT, superponiendo los mismos seis bytes sobre el campo como texto en bruto. Un tercer campo, LIMIT-TYPE-FLAG, decide en tiempo de ejecución cuál de esas dos interpretaciones es la viva. Ese indicador lo escribe un programa llamado LIMITSET, y de nuevo por un trabajo por lotes nocturno llamado BATCHUPD. Y el trabajo JCL NIGHTLY se ejecuta a las 02:00 como predecesor del trabajo de transferencias, que es el único lugar en todo el patrimonio donde el orden entre «fijar el indicador» y «ejecutar la transferencia» queda siquiera registrado.

Panel de impacto de CodeGraph para TRN-LIMIT que muestra recuperación por grafo 9 de 9 hechos recuperados frente a 3 de 9 del archivo único ingenuo, con cada hecho recuperado llevando su procedencia de archivo y línea desde WIRETXN.cbl y CBACCT.cpy.
El cierre de TRN-LIMIT en el fixture sintético publicado. La recuperación por grafo recupera 9 de 9 hechos de verdad de referencia, la ventana ingenua de un solo archivo ve 3 de 9, y cada hecho lleva su propio archivo y línea. F4 a F9, los seis que rompen la portación, son los marcados como ocultos en un solo archivo.

Seis hechos. Todos verdaderos, todos de peso estructural, y todos estructuralmente invisibles desde el archivo que realmente hace el cálculo. Cuando los alineé así, lo que me inquietó no fue que la portación ingenua estuviera mal. Fue que la portación ingenua no tenía forma de saber que estaba mal. Leyó el único archivo que se le dio, y ese único archivo callaba sobre los seis hechos que importaban.

El archivo que contiene la línea peligrosa no contiene ninguno de los hechos que la hacen peligrosa. Eso no es un error de traducción. Es un fallo de recuperación disfrazado de error de traducción.

Por qué dejé de intentar agrandar la ventana de contexto

Mi primer impulso fue el mismo que tiene todo el mundo ahora mismo, y quiero ser honesto en que lo perseguí durante un tiempo: simplemente dale más al modelo.

El razonamiento parecía hermético. Si el fallo es que el modelo solo vio un archivo, entonces alimenta también el copybook. Alimenta los programas que tocan el indicador. Alimenta el JCL. Las ventanas de contexto son enormes ahora y crecen cada trimestre, así que seguro que la respuesta es dejar de ser tacaño y volcar todo el vecindario de código en el prompt. De verdad esperaba que esto funcionara, y para un ejemplo de juguete más o menos funciona, porque cuando ya sabes qué seis archivos pegar, ya has resuelto el problema real a mano.

Ahí estaba la grieta. Para alimentar al modelo el contexto correcto, primero tenía que saber qué contexto era el correcto. Y saber que el tipo de TRN-LIMIT lo decide un indicador escrito en BATCHUPD y ordenado por un trabajo JCL a las 02:00 no es algo que extraigas leyendo WIRETXN con más ahínco. Es algo que solo puedes obtener habiendo trazado ya el grafo de dependencias. La ventana de contexto no te dice qué poner en la ventana de contexto. Había estado intentando responder la pregunta con la respuesta.

Luego los números hicieron el punto permanente. Los patrimonios que estos bancos realmente ejecutan no son seis archivos. Son de uno a diez millones de líneas de COBOL, a veces más, y 220.000 millones de líneas siguen en producción activa en toda la industria (meta-análisis del sector, 2025). Un cambio real de transferencias electrónicas puede tener un cierre transitivo de cuarenta archivos o cuatrocientos. Eso nunca cabe en una ventana de contexto, ni hoy ni en la versión del modelo que llegue dentro de tres años, porque el patrimonio crece más rápido que la ventana y la ventana nunca fue la restricción de todos modos. La restricción es saber cuáles de los cuarenta archivos entre los diez millones toca este cambio, y demostrar que encontraste los cuarenta y no treinta y ocho.

Una ventana de contexto más grande es una mejor respuesta a una pregunta que dejé de hacer. La pregunta no es «¿puede el modelo contener más código?», es «¿qué código, y cómo demuestras que es todo?».

Ese replanteamiento es toda la razón por la que CodeGraph no es un traductor. Deliberadamente no pego COBOL y devuelvo Java. El mapa es el producto, y la traducción es un caso de uso posterior que cualquier herramienta puede hacer una vez que el mapa existe. Lo que construyo es la capa de comprensión debajo. En el fixture publicado el patrimonio se analiza en un grafo de conocimiento tipado de 47 nodos y 70 aristas, y el «impacto de un cambio» es un recorrido del grafo, el cierre transitivo de todo lo que el cambio toca, con cada arista llevando el file:line del que vino. Es deliberadamente trabajo de grafo aburrido en Python puro, sin modelo en el camino crítico, porque lo que necesito que sea no es ingenioso. Necesito que sea completo y reproducible. Mismo fixture de entrada, mismo cierre de salida, cada vez.

Me lo repito a mí mismo como regla. Los agentes aconsejan, el código decide. La capa de lenguaje opcional en la demo, la parte que responderá preguntas sobre el cierre en inglés sencillo, está desactivada por defecto y protegida detrás de una clave. El valor no depende de ella. El valor es la recuperación y la prueba, y ninguna de las dos es una capacidad del modelo.

¿Qué elimina realmente la vista ingenua?

Construí un interruptor en la demo específicamente para poder ver desaparecer los seis hechos, porque no creía del todo el fallo hasta que lo vi ocurrir.

Activa «Vista de contexto de IA ingenua» y el grafo se colapsa al único archivo fuente más una ventana de líneas alrededor del cambio, que es exactamente lo que una herramienta de ventana de texto alimenta a un modelo. El panel que leía 9 de 9 baja a 3 de 9. Los tres hechos dentro del archivo siguen iluminados. Los otros seis se atenúan y se callan: el tipo COMP-3, el REDEFINES de solapamiento, el indicador de control, sus dos escritores entre módulos, y el predecesor JCL a las 02:00. Un banner rojo detalla la consecuencia con las propias palabras de la app: que con solo los tres hechos visibles un modelo emite long TRN_LIMIT y corrompe la base de datos.

Vista de contexto de IA ingenua de CodeGraph activada, atenuando seis de los nueve hechos a gris con un banner rojo que afirma que solo 3 de 9 hechos viven dentro de WIRETXN.cbl y el resto son invisibles para una herramienta de ventana de texto.
La vista ingenua de un solo archivo, que es una simulación de lo que una herramienta de ventana de texto realmente ve, no un conector en vivo. Seis hechos se atenúan a gris. El banner nombra cada cosa que desapareció: el tipo COMP-3, el solapamiento REDEFINES, el indicador de control, sus dos escritores y el predecesor a las 02:00.

Quiero ser cuidadoso aquí, porque este es exactamente el punto en el que un fundador se siente tentado a exagerar. El resultado 9 frente a 3 se mide sobre el fixture sintético de transferencias electrónicas publicado, un patrimonio que escribí a mano para esta demo, precisamente para que el conjunto verdadero de dependencias sea conocido y el número de recall sea una medición etiquetada real en lugar de una impresión vaga. No es una garantía sobre tu COBOL. La vista ingenua es una simulación, no un pipeline z/OS en vivo. El grafo está en memoria con SQLite debajo, no una plataforma de grafo de producción. Construí un banco sintético porque no podía mostrarte éticamente uno real, y porque una verdad de referencia conocida es la única forma honesta de decir «el grafo obtuvo los nueve y el archivo único obtuvo tres».

Pero la forma del fallo no es sintética, y esa es la parte que importa. El campo COMP-3 cuyo tipo se decide en otra parte, el indicador fijado por un trabajo por lotes, el orden que solo existe en JCL, esa es la textura ordinaria de un patrimonio bancario de cuarenta años, no casos límite exóticos. Cuando aproximadamente entre el 70 y el 80 por ciento de los proyectos de modernización de mainframe no logran cumplir sus objetivos (meta-análisis del sector, 2025), ya no creo que sea porque el paso de traducción sea malo. El paso de traducción está bien. Se le alimenta una imagen con los seis hechos más importantes recortados.

Prueba, o no cuenta

La función de la que más orgulloso estoy es la que admite lo que no puede hacer, y no lo aprecié hasta que una conversación de cumplimiento lo replanteó para mí.

Un ingeniero quiere una migración correcta. Un regulador quiere algo distinto y más difícil: evidencia. Bajo DORA, un banco debe un inventario de activos TIC. Bajo SOC-2, debe recibos de control de cambios. Ninguno de esos se satisface con un modelo que diga «confía en mí, encontré las dependencias». Necesitan una prueba de completitud, una declaración de cuánto de la base de código pudo resolver realmente la herramienta y, más importante, una marca honesta de lo que no pudo. Así que construí una puerta de completitud. Cada PERFORM, CALL, COPY y referencia DB2 en el fixture tiene que resolverse a un nodo real en el grafo o marcarse «necesita revisión». Nada puede desaparecer en silencio.

En el fixture, esa puerta resuelve 33 de 34 referencias, que es un 97,1 por ciento de cobertura. La que no puede resolver es un programa llamado DISPATCH, que hace un CALL WS-PROGNAME dinámico, un destino calculado en tiempo de ejecución que ningún analizador estático puede seguir porque el destino no se conoce hasta que el programa se ejecuta. Y el comportamiento correcto ahí no es adivinar. Es levantar una bandera que diga «un humano tiene que mirar este», y dejarlo en el informe.

Pestaña de auditoría de CodeGraph que muestra el 97,1 por ciento de referencias resueltas con 1 marcada para revisión, el elemento marcado siendo el CALL WS-PROGNAME dinámico de DISPATCH en DISPATCH.cbl línea 15, listado como marcado y no descartado en silencio.
La puerta de completitud en el fixture. 33 de 34 referencias se resuelven, 97,1 por ciento, y la única no resuelta, el CALL dinámico de DISPATCH a un destino calculado en tiempo de ejecución, se marca para revisión en lugar de descartarse. La honestidad sobre la que no puede seguir es el punto, no una nota al pie.

Esa llamada DISPATCH marcada es mi cosa favorita de toda la construcción, y lo digo en serio. Una herramienta que resuelve el 97 por ciento y te dice exactamente qué 3 por ciento no pudo vale más que una herramienta que afirma 100 y oculta la brecha, porque la brecha oculta es donde vive la transferencia electrónica corrupta. La puerta de completitud produce un «Informe de topología y completitud de la base de código» exportable, un JSON y un HTML imprimible con el resumen de nodos y aristas, los cierres por módulo con procedencia file:line, el resultado de recall y los elementos marcados con una marca de tiempo. Ese artefacto es el punto. Es lo que puedes entregar a un regulador, volver a ejecutar el próximo trimestre y obtener la respuesta idéntica porque es determinista.

Prefiero publicar un número que admite su propio agujero que uno más redondo que lo oculta. El CALL dinámico marcado no es una debilidad de la demo. Es la demo.

Esta es también la parte que no caduca. Un modelo perfecto, uno que nunca alucine una sola línea de Java, aún no puede demostrar a un regulador qué dependencias recuperó. Aún no puede seguir un CALL calculado en tiempo de ejecución de forma estática, porque eso es una propiedad del código y no del lector. La procedencia y la completitud son propiedades del sistema que construyes alrededor del modelo, no capacidades que desbloqueas escalándolo.

El orden en que tocas las cosas

Lo último que el grafo me dio fue algo que ni siquiera me propuse construir: un orden seguro para hacer el trabajo.

Una vez que tienes la topología completa de dependencias, puedes puntuar cada programa por cuán enredado está. Uso una fórmula sencilla, acoplamiento ponderado frente a trampas COMP-3, criticidad JCL y llamadas no resueltas, y ordena los catorce programas del fixture en un orden de extracción de higo estrangulador. El programa de menor riesgo se extrae primero, el programa dios al final. En el fixture, AUDITLOG sale en el puesto 1 con una puntuación de riesgo de cero, porque no tiene acoplamiento y nada depende de que esté bien. Es el lugar seguro para empezar. El programa WIRETXN por el que hemos estado preocupándonos está en el puesto 11, llevando su única trampa COMP-3 y su criticidad JCL. DISPATCH, con su llamada dinámica no resuelta, está en el puesto 12. Y ACCTMGR, el programa dios del que todo depende, se extrae al final absoluto en el puesto 14 con una puntuación de riesgo de 15.

Pestaña de extracción de CodeGraph que muestra los catorce programas del fixture ordenados en orden de higo estrangulador, AUDITLOG en el puesto 1 con puntuación de riesgo 0 y ACCTMGR en el puesto 14 con puntuación de riesgo 15, columnas para acoplamiento, trampas COMP-3 y criticidad JCL.
El orden de extracción de higo estrangulador en el fixture. AUDITLOG se extrae primero con riesgo 0, el programa dios ACCTMGR se extrae al final con riesgo 15, y WIRETXN y DISPATCH están altos en la lista por su trampa COMP-3 y su llamada dinámica no resuelta. La secuencia es una propiedad del grafo, no un juicio.

No esperaba preocuparme tanto por la secuenciación como lo hago ahora. Pero es la misma lección por tercera vez. Dónde puedes empezar con seguridad es un hecho sobre la topología, no una opinión que se discute en una reunión de planificación. Un equipo mirando un millón de líneas no discrepa realmente sobre cómo traducir un párrafo. Discrepa, sin fin y a gran coste, sobre dónde empezar y qué se rompe si tocan lo equivocado primero. Esa es una pregunta de grafo, y el grafo la responde igual en cada ejecución.

El orden de extracción, la puerta de completitud, el cierre de impacto, son todos el mismo objeto visto desde tres ángulos. Recupera el trozo verdadero, demuestra que es el trozo completo y ordena los trozos por riesgo. Ninguno de esos tres es un problema de traducción, y ninguno se resuelve con un modelo más inteligente.

La pregunta a la que sigo volviendo

He empezado a hacer una pregunta a cada propuesta de modernización con IA que veo, incluida la mía, y en silencio se ha convertido en la única en la que confío.

No «¿puede escribir buen Java?», porque la respuesta casi siempre es sí y casi nunca importa. La pregunta más dura es la que la línea TRN-LIMIT me enseñó: ¿puede demostrar, ahora mismo, qué dependencias recuperó, y sobreviviría esa prueba a un regulador que quisiera que fallara? Si la herramienta no puede mostrarme el cierre con procedencia file:line y no puede decirme con honestidad lo que no pudo resolver, entonces no importa lo fluido que se vea el resultado. Está adivinando con buena gramática, y he visto exactamente esa adivinación tipar long sobre un campo decimal empaquetado y meterse en la base de datos.

La industria ha pasado una década mejorando el paso de traducción mientras entre el 70 y el 80 por ciento de los proyectos seguían sin cumplir sus objetivos (meta-análisis del sector, 2025), y creo que es porque el paso de traducción nunca fue donde vivía el riesgo. El riesgo vive en la topología, en los seis hechos invisibles, en el indicador fijado a las dos de la madrugada. Si quieres ver un grafo recuperar esos seis hechos y luego marcar el que honestamente no puede, la demo está aquí: veriprajna.com/es/demos/modernizacion-cobol-con-un-grafo-de-conocimiento.

Y si prefieres verlo a leerme describirlo, aquí está todo el asunto funcionando de punta a punta.

Ya no creo que la próxima versión del modelo sea lo que desbloquea estas migraciones. Una ventana más grande contiene más código; no sabe qué código, y no puede demostrar que encontró todo. Eso era cierto cuando escribí la primera línea del analizador, y creo que seguirá siendo cierto mucho después de que el modelo que usé para construir esto se haya retirado. El mapa siempre fue la parte difícil. Solo seguíamos mirando la traducción porque esa era la parte que sabíamos cómo calificar.

Investigación relacionada

También publicado en

Construya su IA con confianza.

Colabore con un equipo que cuenta con amplia experiencia en la creación de la próxima generación de IA empresarial. Permítanos ayudarle a diseñar, construir e implementar una estrategia de IA en la que pueda confiar.

Veriprajna consultora de Deep Tech está especializada en la creación de sistemas de IA críticos para la seguridad en los sectores de salud, finanzas y ámbitos regulatorios. Nuestras arquitecturas se validan conforme a protocolos establecidos, con documentación de cumplimiento integral.