
ملف COBOL واحد أخبر الذكاء الاصطناعي بكل شيء إلا الحقيقة التي كانت تهمّ، فبنيت الخريطة أولًا.
سطر COBOL الذي بدأ كل هذا كان من ثلاث كلمات، وكل واحدة منها كذبت عليّ.
COMPUTE WS-RESULT = WS-WIRE-AMOUNT - TRN-LIMIT. كنت أبني منظومة تجريبية لبرنامج فرعي للتحويلات المصرفية في بنك متوسط، وكان هذا السطر المفصلي داخل برنامج اسمه WIRETXN. يبدو كحساب يستطيع طالب سنة أولى نقله. اطرح حدًا من مبلغ، واكتب النتيجة. لو سلّمت ذلك الملف الواحد لأي نموذج حديث وطلبت Java، لأعطاك Java نظيفًا يُصرَّف ويجتاز اختبارات الوحدة في نحو أربع ثوانٍ. سيكتب TRN-LIMIT كـ long. وفي أول تحويل حيّ، سيكتب بايتات تالفة في قاعدة بيانات الإنتاج.
أعلم ذلك لأن TRN-LIMIT ليس long. إنه عشري محزوم COMP-3 معرَّف على بُعد ثلاثة ملفات، وتفسيره الحيّ يختاره عَلَم مضبوط في برنامج مختلف تمامًا، مرتَّب بمهمة دفعية تعمل في الثانية صباحًا. لا شيء من ذلك ظاهر في WIRETXN. الملف الذي يحتوي على الخطير COMPUTE لا يحتوي أيًا من الحقائق التي تجعله خطيرًا.
تلك الفجوة هي السبب كله لبناء CodeGraph، وهذا المقال عمّا أخطأت فيه في الطريق إليه. بدأت واثقًا أن المشكلة هي جودة الترجمة. وكنت مخطئًا. المشكلة أن النموذج لا يرى ما يحتاج أن يراه، وقد أمضيت وقتًا أثبت لنفسي أن لا قدر من «أعطِه مزيدًا من السياق» يصلح ذلك.
الـ COMPUTE الذي بدا آمنًا ولم يكن كذلك
رسمتُ خريطة تغيير التحويل المصرفي يدويًا أولًا، قبل أن أثق بأي أداة لتقوم به، وكان هناك تسع حقائق بالضبط يجب أن تعرفها عملية ترحيل صحيحة.
ثلاث منها تعيش داخل WIRETXN وهي فعلًا مرئية لقارئ ملف واحد. WIRETXN يستخدم TRN-LIMIT في ذلك COMPUTE عند السطر 33. يستورد copybook اسمه CBACCT، بالاسم فقط، عند السطر 13. ويُطلق UPDATE على جدول DB2 ACCOUNTS عند السطر 37. أداة نافذة نصية ترى الثلاث كلها. لو كانت تلك الحقائق الوحيدة، لكان الترحيل الساذج كافيًا.
أما الستّ الأخرى فهي التي تؤلم. TRN-LIMIT مُعلَن PIC S9(9)V99 COMP-3 في CBACCT.cpy عند السطر 11، وهذا يعني عشريًا محزومًا، أي BigDecimal في Java وليس إطلاقًا long. مباشرة تحته، TRN-LIMIT-ALPHA REDEFINES TRN-LIMIT، فيضع البايتات الستة نفسها فوق الحقل كنص خام. وحقل ثالث، LIMIT-TYPE-FLAG، يقرر وقت التشغيل أي التفسيرين هو الحيّ. ذلك العَلَم يكتبه برنامج اسمه LIMITSET، وكذلك مهمة دفعية ليلية اسمها BATCHUPD. ومهمة JCL NIGHTLY تعمل في 02:00 كسلَف لمهمة التحويل، وهو المكان الوحيد في المنظومة كلها الذي يُسجَّل فيه الترتيب بين «اضبط العَلَم» و«شغّل التحويل».

ستّ حقائق. كل واحدة منها صحيحة، وكل واحدة أساسية لا غنى عنها، وكل واحدة غير مرئية هيكليًا من الملف الذي ينفّذ الحساب فعلًا. حين رتّبتها هكذا، ما أقلقني لم يكن أن الترحيل الساذج كان خاطئًا. بل أن الترحيل الساذج لم يكن لديه أي وسيلة لمعرفة أنه خاطئ. قرأ الملف الوحيد الذي أُعطي له، وكان ذلك الملف صامتًا عن الستّ حقائق التي كانت تهمّ.
الملف الذي يحتوي السطر الخطير لا يحتوي أيًا من الحقائق التي تجعله خطيرًا. هذه ليست علة ترجمة. إنها فشل استرجاع يرتدي ثياب علة ترجمة.
لماذا توقّفت عن محاولة تكبير نافذة السياق
كان حدسي الأول هو الحدس نفسه الذي لدى الجميع الآن، وأريد أن أكون صادقًا أنني طاردته لفترة: فقط أعطِ النموذج المزيد.
بدا المنطق محكمًا. إذا كان الفشل أن النموذج رأى ملفًا واحدًا فقط، فغذِّه بالـ copybook أيضًا. غذِّه بالبرامج التي تلمس العَلَم. غذِّه بـ JCL. نوافذ السياق هائلة الآن وتكبر كل ربع سنة، فلا بد أن الإجابة هي التوقف عن البخل وصبّ حيّ الكود كله في الموجّه. توقّعت حقًا أن ينجح هذا، ومع مثال لعبة ينجح نوعًا ما، لأنك حين تعرف أصلًا أي ستة ملفات تلصقها، تكون قد حللت المشكلة الحقيقية يدويًا.
تلك كانت الثغرة فيه. لأغذّي النموذج بالسياق الصحيح، كان عليّ أولًا أن أعرف أي سياق هو الصحيح. ومعرفة أن TRN-LIMIT نوعُه يقرره عَلَم مكتوب في BATCHUPD ومرتَّب بمهمة JCL في 02:00 ليس شيئًا تستخرجه بقراءة WIRETXN بصرامة أكبر. إنه شيء لا تحصل عليه إلا بعد أن تكون قد تتبّعت الرسم البياني للتبعيات. نافذة السياق لا تخبرك ماذا تضع في نافذة السياق. كنت أحاول الإجابة عن السؤال بالإجابة.
ثم جعلت الأرقام النقطة دائمة. المنظومات التي تشغّلها هذه البنوك فعلًا ليست ستة ملفات. إنها من مليون إلى عشرة ملايين سطر من COBOL، وأحيانًا أكثر، ولا يزال 220 مليار سطر منها في الإنتاج الفعّال عبر الصناعة (تحليل تلوي صناعي، 2025). قد يكون لتغيير تحويل مصرفي حقيقي إغلاق متعدٍّ من أربعين ملفًا أو أربعمائة. ذلك لا يدخل أبدًا في نافذة سياق، لا اليوم ولا في نسخة النموذج التي تُشحن بعد ثلاث سنوات، لأن المنظومة تنمو أسرع من النافذة والنافذة لم تكن القيد أصلًا. القيد هو معرفة أي أربعين ملفًا من العشرة ملايين يلمسها هذا التغيير، وإثبات أنك وجدت الأربعين كلها لا ثمانية وثلاثين.
نافذة سياق أكبر هي إجابة أفضل على سؤال توقّفت عن طرحه. السؤال ليس «هل يستطيع النموذج حمل مزيد من الكود»، بل «أي كود، وكيف تثبت أنه كله».
إعادة التأطير هذه هي السبب كله لأن CodeGraph ليس مترجِمًا. أنا عمدًا لا ألصق COBOL وأعيد Java. الخريطة هي المنتج، والترجمة حالة استخدام لاحقة تستطيع أي أداة القيام بها بمجرد وجود الخريطة. ما أبنيه هو طبقة الفهم من تحت. على التركيبة المُشحونة تُحلَّل المنظومة إلى رسم بياني معرفي منمَّط الأنواع من 47 عقدة و70 حافة، و«أثر التغيير» هو اجتياز للرسم البياني، الإغلاق المتعدّي لكل ما يلمسه التغيير، وكل حافة تحمل file:line الذي جاءت منه. إنه عمل رسم بياني بـ Python العادي عمدًا مملّ، بلا نموذج في المسار الساخن، لأن ما أحتاجه أن يكون عليه ليس ذكاءً. أحتاجه أن يكون كاملًا وقابلًا للتكرار. التركيبة نفسها داخلة، الإغلاق نفسه خارجًا، في كل مرة.
أكررها لنفسي كقاعدة. الوكلاء ينصحون، والكود يقرّر. طبقة اللغة الاختيارية في العرض التوضيحي، الجزء الذي يجيب عن أسئلة حول الإغلاق بلغة إنجليزية عادية، معطَّلة افتراضيًا ومحمية بمفتاح. القيمة لا تعتمد عليها. القيمة هي الاسترجاع والإثبات، ولا واحد منهما قدرة نموذج.
ما الذي تحذفه الرؤية الساذجة فعلًا؟
بنيتُ مفتاح تبديل في العرض التوضيحي خصيصًا لأراقب اختفاء الستّ حقائق، لأنني لم أصدّق الفشل تمامًا حتى رأيته يحدث.
فعّل «Naive AI context view» فينهار الرسم البياني إلى الملف المصدري الواحد مع نافذة أسطر حول التغيير، وهو بالضبط ما تغذّي به أداة نافذة نصية نموذجًا. اللوحة التي قرأت 9 من 9 تهبط إلى 3 من 9. الحقائق الثلاث داخل الملف تبقى مضاءة. أما الستّ الأخرى فتصبح رمادية وتصمت: نوع COMP-3، وREDEFINES المتراكب، والعَلَم المسيطر، وكاتباه عبر الوحدات، وسلَف JCL في 02:00. لافتة حمراء تصرّح بالعاقبة بكلمات التطبيق نفسه: بأن نموذجًا أُعطي الحقائق الثلاث المرئية فقط يُصدر long TRN_LIMIT ويُفسد قاعدة البيانات.

أريد أن أكون حذرًا هنا، لأن هذا بالضبط الموضع الذي يُغري المؤسس بالمبالغة. نتيجة 9 مقابل 3 مُقاسة على تركيبة التحويل المصرفي التجريبية المُشحونة، منظومة ألّفتها يدويًا لهذا العرض، تحديدًا حتى تُعرف مجموعة التبعيات الحقيقية ويكون رقم الاستدعاء قياسًا معلَّمًا حقيقيًا لا انطباعًا. ليست ضمانًا عن COBOL لديك. الرؤية الساذجة محاكاة، وليست خط أنابيب z/OS حيًّا. الرسم البياني في الذاكرة مع SQLite من تحت، وليس منصة رسم بياني إنتاجية. بنيت بنكًا تركيبيًا لأنني لا أستطيع أخلاقيًا أن أريك بنكًا حقيقيًا، ولأن حقيقة مرجعية معروفة هي الطريقة الوحيدة الصادقة لقول «الرسم البياني حصل على التسع كلها والملف الواحد حصل على ثلاث».
لكن شكل الفشل ليس تركيبيًا، وهذا هو الجزء الذي يهمّ. الـ COMP-3 الذي يُقرَّر نوعه في مكان آخر، والعَلَم المضبوط بمهمة دفعية، والترتيب الذي لا يوجد إلا في JCL، هذه هي نسيج منظومة مصرفية عمرها أربعون سنة العادي، وليست حالات حدّية غريبة. حين تفشل نحو 70 إلى 80 بالمئة من مشاريع تحديث الحاسوب المركزي في تحقيق أهدافها (تحليل تلوي صناعي، 2025)، لم أعد أظن أن السبب أن خطوة الترجمة سيئة. خطوة الترجمة جيدة. إنها تُغذّى بصورة قُصّت منها أهم ستّ حقائق.
إثبات، وإلا فلا يُحسب
الميزة التي أفتخر بها أكثر هي التي تعترف بما لا تستطيع فعله، ولم أقدّر ذلك حتى أعادت محادثة امتثال تأطيره لي.
المهندس يريد ترحيلًا صحيحًا. والمنظّم يريد شيئًا مختلفًا وأصعب: أدلة. تحت DORA، يدين البنك بجرد أصول تكنولوجيا المعلومات والاتصالات. وتحت SOC-2، يدين بإيصالات ضبط التغيير. ولا واحد منهما يُرضى بنموذج يقول «ثق بي، وجدت التبعيات». يحتاجون إثبات اكتمال، بيانًا بكم من قاعدة الكود استطاعت الأداة حلّه فعلًا، والأهم، عَلَمًا صادقًا عمّا لم تستطعه. فبنيت بوابة اكتمال. كل PERFORM، وCALL، وCOPY، ومرجع DB2 في التركيبة يجب أن يُحلّ إلى عقدة حقيقية في الرسم البياني أو يُعلَّم «يحتاج مراجعة». لا شيء يُسمح له بالاختفاء بصمت.
على التركيبة، تحلّ تلك البوابة 33 من 34 مرجعًا، أي تغطية 97.1 بالمئة. الذي لا تستطيع حلّه برنامج اسمه DISPATCH، ينفّذ ديناميكيًا CALL WS-PROGNAME، هدفًا محسوبًا وقت التشغيل لا يستطيع أي محلّل ساكن تتبّعه لأن الوجهة غير معروفة حتى يعمل البرنامج. والسلوك الصحيح هناك ليس التخمين. بل رفع عَلَم يقول «يحتاج إنسان أن ينظر إلى هذا»، وتركه في التقرير.

ذلك المُعلَّم DISPATCH استدعاؤه هو الشيء المفضّل لديّ في البناء كله، وأعني ذلك. أداة تحلّ 97 بالمئة وتخبرك بالضبط أي 3 بالمئة لم تستطع خيرٌ من أداة تدّعي 100 وتخفي الفجوة، لأن الفجوة المخفية هي حيث يعيش التحويل المصرفي التالف. بوابة الاكتمال تنتج «تقرير طوبولوجيا قاعدة الكود واكتمالها» قابلًا للتصدير، JSON وHTML قابلًا للطباعة مع ملخص العقد والحواف، والإغلاقات لكل وحدة مع مصدر file:line، ونتيجة الاستدعاء، والعناصر المُعلَّمة بطابع زمني. ذلك المُخرَج هو النقطة. إنه ما تستطيع تسليمه لمنظّم، وإعادة تشغيله الربع القادم، والحصول على الإجابة ذاتها لأنه حتمي.
أفضل أن أشحن رقمًا يعترف بثقبِه على رقم أكثر استدارة يخفيه. الـ CALL الديناميكي المُعلَّم ليس ضعفًا في العرض التوضيحي. إنه العرض التوضيحي.
وهذا أيضًا الجزء الذي لا يشيخ. نموذج مثالي، لا يهلوس سطر Java واحدًا، ما زال لا يستطيع أن يثبت لمنظّم أي تبعيات استرجعها. ما زال لا يستطيع تتبّع محسوب وقت التشغيل CALL سكونيًا، لأن ذلك خاصية للكود لا للقارئ. المصدرية والاكتمال خاصيتان للنظام الذي تبنيه حول النموذج، لا قدرات تفتحها بتوسيعه.
ترتيب لمس الأشياء
آخر ما أعطانيه الرسم البياني كان شيئًا لم أقصد بناءه أصلًا: ترتيبًا آمنًا لإنجاز العمل.
بمجرد أن تملك طوبولوجيا التبعيات الكاملة، تستطيع أن تقيّم كل برنامج بمدى تشابكه. أستخدم صيغة بسيطة، الاقتران موزونًا مقابل فخاخ COMP-3، وحرجية JCL، والاستدعاءات غير المحلولة، فترتّب البرامج الأربعة عشر في التركيبة بترتيب استخراج شجرة التين الخانقة (strangler-fig). البرنامج الأقل خطرًا يُستخرج أولًا، والبرنامج الإله آخرًا. على التركيبة، AUDITLOG يخرج في الرتبة 1 بدرجة خطر صفر، لأنه بلا اقتران ولا شيء يعتمد على صحته. إنه المكان الآمن للبدء. برنامج WIRETXN الذي كنّا نقلق بشأنه يجلس في الرتبة 11، حاملًا فخه الواحد COMP-3 وحرجية JCL الخاصة به. DISPATCH، باستدعائه الديناميكي غير المحلول، يجلس في الرتبة 12. وACCTMGR، البرنامج الإله الذي يستند إليه كل شيء، يُستخرج أخيرًا في الرتبة 14 بدرجة خطر 15.

لم أتوقع أن أهتم بالتسلسل بقدر ما أهتم الآن. لكنه الدرس نفسه للمرة الثالثة. أين تستطيع البدء بأمان هو حقيقة عن الطوبولوجيا، لا رأيًا تتجادلون حوله في اجتماع تخطيط. فريق يحدّق في مليون سطر لا يختلف فعلًا حول كيفية ترجمة فقرة. يختلف، بلا نهاية وبغلاء، حول أين يبدأ وماذا ينكسر إن لمسوا الشيء الخطأ أولًا. ذلك سؤال رسم بياني، والرسم البياني يجيب عنه بالطريقة نفسها في كل تشغيل.
ترتيب الاستخراج، وبوابة الاكتمال، وإغلاق الأثر، كلها الكائن نفسه منظورًا من ثلاث زوايا. استرجع الشريحة الحقيقية، أثبت أنها الشريحة كلها، ورتّب الشرائح حسب الخطر. ولا واحد من الثلاثة مشكلة ترجمة، ولا واحد منها يُحلّ بنموذج أذكى.
السؤال الذي أعود إليه دائمًا
بدأت أطرح سؤالًا واحدًا على كل عرض تحديث بالذكاء الاصطناعي أراه، بما فيه عرضي، وقد صار بهدوء السؤال الوحيد الذي أثق به.
ليس «هل يستطيع كتابة Java جيد»، لأن الجواب شبه دائمًا نعم ويكاد لا يهمّ أبدًا. السؤال الأصعب هو الذي علّمني إياه سطر TRN-LIMIT: هل يستطيع أن يثبت، الآن، أي تبعيات استرجعها، وهل يصمد ذلك الإثبات أمام منظّم أراد له أن يفشل. إذا لم تستطع الأداة أن تُريني الإغلاق مع مصدر file:line ولا أن تخبرني بصدق ما لم تستطع حلّه، فلا يهمّ كم يبدو المخرج طليقًا. إنه تخمين بنحو جيد، وقد رأيت ذلك التخمين بالضبط يكتب long فوق حقل عشري محزوم ويمدّ يده إلى قاعدة البيانات.
قضت الصناعة عقدًا تجعل خطوة الترجمة أفضل بينما ظلّت 70 إلى 80 بالمئة من المشاريع تخطئ أهدافها (تحليل تلوي صناعي، 2025)، وأظن أن السبب أن خطوة الترجمة لم تكن قطّ حيث يعيش الخطر. الخطر يعيش في الطوبولوجيا، في الستّ حقائق الخفية، في العَلَم المضبوط في الثانية صباحًا. إن أردت أن تشاهد رسمًا بيانيًا يستعيد تلك الستّ ثم يُعلِّم ما لا يستطيع بصدق، فالعرض التوضيحي هنا: veriprajna.com/ar/demos/legacy-cobol-modernization.
وإن فضّلت مشاهدته على قراءتي وأصفه، فها هو الشيء كله يعمل من الطرف إلى الطرف.
لم أعد أعتقد أن إصدار النموذج التالي هو ما يفتح هذه الترحيلات. نافذة أكبر تحمل مزيدًا من الكود؛ لكنها لا تعرف أي كود، ولا تستطيع إثبات أنها وجدته كله. كان ذلك صحيحًا حين كتبت أول سطر من المحلّل، وأظن أنه سيظل صحيحًا طويلًا بعد تقاعد النموذج الذي استخدمته لبناء هذا. الخريطة كانت دائمًا الجزء الصعب. نحن فقط ظلّلنا ننظر إلى الترجمة لأنها الجزء الذي عرفنا كيف نقيّمه.


