К содержимому
14 авг. 2026 г.·7 мин чтения

Как общий граф зависимостей находит скрытые вызовы

Граф зависимостей всей кодовой базы показывает межъязыковые вызовы, создаваемые имена, пакетные передачи и пропущенные контракты данных.

Как общий граф зависимостей находит скрытые вызовы

Граф зависимостей честен лишь в пределах границы, по которой его построили. Анализ по одному репозиторию может выглядеть точным и полным, но почти не приносить пользы: вызовы, от которых зависит сохранение поведения при переписывании, часто проходят через планировщик, базу данных, созданный файл, общий copybook или протокол, не принадлежащий ни одному репозиторию.

Совместное чтение всего дерева меняет единицу анализа. Программа COBOL и ее JCL больше не считаются отдельными ресурсами. Задание RPG, оболочка CL и таблица, куда пишет ночной импорт, превращаются в один путь выполнения. Граф связывает свидетельства из разных языков и отмечает места, где свидетельства заканчиваются. Это важно: надежный граф отличает доказанное ребро от правдоподобного, а не объявляет достоверным любое совпавшее имя.

Я видел, как анализ репозиториев выдавал успокаивающие схемы, хотя производственный путь шел через код, которого на схеме не было. Анализатор работал правильно. Слишком узким был вопрос.

Границы репозиториев административны

Граф отдельного репозитория описывает ссылки между исходными файлами внутри выбранного контейнера. Граф системы описывает движение поведения через все артефакты, способные повлиять на выполнение. Это разные результаты. Их смешение сразу создает неполный объем миграции.

Репозитории обычно отражают зоны ответственности, права доступа, поставки подрядчика или давнюю миграцию. Выполняемое поведение этим решениям не подчиняется. Веб-запрос может начаться в Classic ASP, вызвать компонент COM на VB6, обратиться к хранимой процедуре из репозитория базы и оставить работу запланированной программе COBOL. Каждый репозиторий может быть хорошо изучен, а вся бизнес-транзакция останется невидимой.

Термин «зависимость» тоже требует точности. Текстовая ссылка доказывает, что один артефакт называет другой. Вызываемая зависимость означает, что цель при определенном условии действительно может получить управление. Поведенческая зависимость означает, что изменение цели способно поменять результат, заметный пользователям или следующим системам. Инструменты анализа репозитория часто рисуют для всех трех одну линию. При переписывании это порождает ложную уверенность и лишнюю работу.

Практическому графу мало файлов и функций. Узлами должны стать задания, объекты базы, адресаты сообщений, созданные артефакты, внешние программы, схемы, экраны, отчеты и настройки, если они участвуют в поведении. Ребро должно хранить причину: прямой вызов, динамический выбор, поток данных, расписание, генерация или неразрешенное имя. Тогда инженер сможет проверить граф, а не просто рассматривать его.

Широкая модель не требует собрать все в одном репозитории. Репозитории можно сохранить, изменив границу анализа. Правильная граница охватывает все артефакты, способные влиять на переписываемое поведение, даже если владение и развертывание их разделяют.

Межъязыковые ребра скрываются в обычных механизмах

Важные вызовы редко объявляют себя межъязыковой архитектурой. Они выглядят как обычные механизмы платформы: строка команды, имя хранимой процедуры, шаг задания, экспортируемый символ, адрес очереди или имя файла для следующего процесса. Парсер языка видит синтаксис. Граф системы должен понять, к чему этот синтаксис приводит в другом месте.

Рассмотрим шаг JCL с PGM=BILLRUN. Ребро не заканчивается на токене BILLRUN. Анализ должен разрешить программу через нужные загрузочные библиотеки, связать ее с реестром исходников или двоичных файлов и сохранить неопределенность при нескольких кандидатах. Внутри программы COBOL-вызов CALL WS-PROGRAM нельзя считать обычным статическим вызовом, поскольку цель приходит из данных. Значение может поступить из copybook, файла параметров или предыдущего чтения базы.

Тот же рисунок встречается на других платформах. CL может отправить программу RPG в пакетную очередь. VB6 может создать класс COM по строке из реестра или настройки. ColdFusion может вызвать процедуру, реализованную в PL/SQL. Монолит PHP может записать управляющий файл, который демон Perl воспринимает как инструкцию. Экзотическая рефлексия здесь не нужна. Ребра исчезают потому, что каждый парсер останавливается на границе своего языка или репозитория.

Полезный промежуточный артефакт делает соединение видимым и проверяемым. Файл ребер с табуляцией может содержать минимальные свидетельства:

source	target	mechanism	evidence
JCL:AR_CLOSE:STEP20	COBOL:BILLRUN	program-load	PGM=BILLRUN
COBOL:BILLRUN:PARA140	DB2:SP_POST_LEDGER	dynamic-sql	value-set:POST_LEDGER
DB2:SP_POST_LEDGER	TABLE:GL_ENTRY	write	INSERT INTO GL_ENTRY
CL:ENDDAY:CMD7	RPG:RECONCILE	submit-job	CALL PGM(RECONCILE)

Это еще не окончательный граф, а контракт между извлечением и проверкой. Каждая строка называет источник, цель, механизм и свидетельство. Если инструмент не может заполнить последнюю колонку, он не должен молча рисовать твердое ребро.

Имена разрешаются при объединении свидетельств

Динамические вызовы не обязательно непознаваемы. Многие удается разрешить, когда анализ объединяет присваивания, настройки, метаданные сборки и наблюдения выполнения со всего дерева. Ошибка состоит в том, чтобы объявить нелитеральный вызов тупиком до поиска значений, способных до него дойти.

Допустим, абзац COBOL вызывает WS-NEXT-PGM. В одном репозитории есть вызов, но нет присваивания. JCL в другом репозитории передает символический параметр. Общий copybook задает ширину поля. Экспорт управляющей таблицы содержит развернутые значения. По отдельности вызов не разрешается. Вместе эти материалы позволяют вывести набор кандидатов, отбросить значения неподходящей длины и связать остальные с реестром программ.

Разрешение должно опираться на свидетельства. При проверке я использую четыре состояния ребра: подтверждено синтаксисом, выведено из ограниченного набора значений, замечено в записанном трафике и не разрешено. Названия менее важны, чем разделение состояний. Выведенный кандидат полезен для объема работ, но не доказывает прохождение пути в производстве. Наблюдавшийся вызов доказывает случай в выборке, но не отсутствие других целей.

Именно здесь ломается простое сопоставление имен. POST, UPDATE или CLOSE могут встречаться в десятках пространств имен. Совпадение заслуживает доверия только после применения правил платформы, ограничений поля, соглашения о вызове, контекста развертывания и достижимых присваиваний. Общий анализ дает больше свидетельств, но требует более строгого различения. Дополнительные входные данные без правил лишь уплотняют неверный граф.

Созданный исходный код требует такого же подхода. Генератор, шаблоны, входные данные и результат образуют цепочку. Если анализировать только результат, граф принимает следствие за источник истины. Если читать лишь шаблоны, исчезают конкретные имена развертывания. Сохраните обе стороны и отметьте ребро генерации, чтобы изменение можно было проследить до элемента, который позже заново создаст файл.

Передача данных часто заменяет вызов

Одного графа вызовов недостаточно для многих старых систем, потому что передачу выполняют данные. Один процесс пишет строку, файл или элемент spool, другой позже его толкует. Прямого вызова между функциями нет, но первая программа управляет действиями второй. Для поведенческого соответствия это зависимость.

Ночная обработка хорошо это показывает. Онлайн-транзакция пишет код состояния в таблицу. При закрытии планировщик запускает пакетное задание. Оно выбирает строки с этим кодом, создает файл фиксированной ширины, а отдельная программа импортирует файл в главную книгу. Локальные графы показывают четыре острова. Граф системы показывает транзакцию с условием, расписанием, форматом записи и правилом имени файла.

Если считать зависимостью каждую общую таблицу, возникнет шум. Граф должен учитывать операцию и поля. Запись в customer.last_seen может никак не влиять на читателя, который отбирает по customer.credit_hold. Запись в проверяемое поле состояния влияет. Две программы с одним файлом тоже не всегда связаны, если используют разные типы записей.

Практическое ребро данных хранит объект, операцию, значимые поля или формат и условие, включающее читателя. Для пакетных файлов укажите производителя, потребителя, правило имени, кодировку, разделитель или позиции и проверяемые контрольные суммы. Для хранимых процедур отличайте вызов процедуры от чтения изменяемых ею таблиц. От этих деталей зависит безопасное введение новой схемы или границы сервиса.

Возникает неудобный вопрос: не станет ли граф слишком широким? Станет, если каждое совпадение данных превратить в твердое ребро. Он остается полезным с фильтрованными представлениями передачи управления, влияния данных, расписания, генерации и внешней неопределенности. Единая модель должна сохранять типы ребер, а не превращать их в массу стрелок.

Даже у полного дерева есть внешняя сторона

Постройте полный путь выполнения
CodeHero читает все языки вместе до выбора границ новой системы.

Чтение каждого предоставленного файла не дает полного знания о работающей системе. Оно дает полное знание об этом наборе свидетельств. Внешние планировщики, триггеры базы, команды оператора, записи реестра, маршрутизация middleware, двоичные файлы поставщиков и производственные настройки могут добавлять поведение, которое нельзя доказать по исходникам.

На этом различии многие отчеты о модернизации становятся нечестными. Граф называют полным, хотя завершился лишь сканер. Завершение сканирования ничего не говорит о полноте входных данных относительно среды выполнения. Хороший результат содержит явную границу: узлы и ребра, ведущие за пределы свидетельств, с причиной каждого открытого вопроса.

Сделайте границу конкретной. Выгрузите неразрешенные имена программ, внешние объекты базы, адреса очередей, пути и ключи настроек в таблицу проверки. Назначьте каждому владельца и статус: будет предоставлено, подтверждено как внешнее, выведено из эксплуатации или неизвестно. Не удаляйте узел только потому, что его никто не узнает. В старых системах бывает спящий код, но узнавание не доказывает достижимость.

Наблюдения выполнения помогают, если их не объявляют исчерпывающими. Записанный производственный трафик подтверждает прохождение пути и дает значения динамических целей. Он не доказывает, что редкая годовая ветвь, процедура восстановления или команда только для оператора никогда не запускается. Статические свидетельства дают возможную структуру, а данные выполнения показывают увиденное поведение. Их пересечение убедительно, расхождения требуют изучения.

Поэтому граф должен отдельно отвечать на два вопроса: что способны вызвать предоставленные артефакты и что действительно вызвала записанная нагрузка? Объем только по первой картине может навсегда сохранить мертвые пути. Объем только по второй способен удалить допустимое исключение. Сохраните оба представления и сделайте решение видимым.

Одно пропущенное ребро портит чистую новую систему

Пропущенная зависимость обычно дает сбой далеко от исключенного кода. Поэтому команды недооценивают границы репозиториев при планировании, а при переключении обвиняют тесты, хотя проявилась настоящая система.

Возьмем закрытие месяца из четырех репозиториев. Программа RPG отмечает подходящие счета и вызывает оболочку CL. Оболочка отправляет задание с именем из области данных. JCL на связанном узле запускает выбранную программу COBOL. Та пишет файл исключений фиксированной ширины, а настольный инструмент VB6 позволяет оператору утвердить записи до проводки процедурой PL/SQL. В каждом репозитории есть тесты, и каждая команда понимает свой участок.

Локальный анализ находит прямой вызов RPG к CL и записи PL/SQL. Он не видит отправленную программу, поскольку имя приходит из данных, не видит JCL в другом месте и не видит настольное утверждение, поскольку интерфейсом служит файл. Новая система заменяет первый участок сервисом и повторяет изменения базы. Автоматические тесты на обычных счетах проходят.

При закрытии исключения остаются без проводки. Новый сервис не выпускает файл, поскольку этот выход никто не включил в контракт. Инструменту нечего показать, оператор ничего не утверждает, процедура не получает исключения. Дефект похож на проблему интерфейса или базы, но пропуск возник, когда анализ очертил сервис одним репозиторием.

Общий граф связал бы путь разными ребрами: вызов, отправленное задание, запланированная программа, выпуск файла, действие оператора и вызов хранимой процедуры. Он также показал бы область данных и правило имени как управляющие входы. Тесты могли бы записать характерный трафик обычного пути и исключения и сравнить наблюдаемые выходы на каждой границе.

Вывод не требует сохранять каждый старый артефакт. Сначала нужно понять, потом удалять. При видимом пути команда может заменить файл и настольное утверждение клиентом TypeScript и API сервиса. Это изменение архитектуры с известным обязательством, а не случайная потеря поведения.

Транслитерация графа сохраняет неверные границы

Оставьте регулируемый код внутри
Предоставленные модели могут работать изолированно на оборудовании внутри периметра клиента.

Новый модуль для каждой старой программы кажется безопасным, потому что такое соответствие легко проверить. Так переписанная система наследует десятилетия случайных решений развертывания. Граф должен сохранять обязательства и показывать лучшие границы, а не диктовать перевод файл в файл.

Старые границы часто возникли из-за памяти, пакетных окон, ограничений языка или истории команд. Шаг JCL мог лишь переносить данные между форматами, потому что соседние программы не поддерживали оба. Хранимая процедура могла содержать бизнес-правила из-за редких выпусков исходного клиента. Общий copybook мог связывать независимые программы, поскольку оставался единственным способом распространения. Повторение каждого артефакта в новом языке сохраняет ограничения без прежней причины.

Используйте граф для поиска цельного поведения. Узлы, которые меняются вместе, разделяют транзакционные правила и участвуют в одном наблюдаемом результате, могут стать современным компонентом. Ребра через зоны доверия, независимые циклы выпуска или разные нагрузки могут стать явными интерфейсами. Ребра данных показывают, где схема Postgres должна защищать инварианты. Числовые ядра со строгими требованиями могут перейти на Rust, оркестрация на Go, а работа оператора на TypeScript. Цель следует поведению и ограничениям, а не расширениям файлов.

Я не советую начинать с конвертации репозиторий за репозиторием, даже если закупкам и распределению людей так удобнее. Она быстро дает измеримый прогресс, но откладывает сквозное поведение до интеграции, когда изменение границ дороже. Сначала постройте граф системы и выберите новые границы, затем распределяйте работу.

Трассируемость остается обязательной. Каждый новый компонент должен указывать на исходное поведение и ребра, которые заменяет. Такая карта позволяет спросить, сохранили ли старое ребро, осознанно переделали или вывели с доказательством. Без нее модернизация превращается в спор о сходстве кода, а это неверная мера.

Проверка соответствия должна следовать путям

Тесты репозиториев полезны, но редко доказывают соответствие системы, поскольку их проверки заканчиваются на локальных границах. Стенд соответствия должен воспроизводить полные транзакции и сравнивать наблюдаемые эффекты вдоль найденного графом пути.

Выбирайте пути с разным поведением, а не только частые входы. Включите обычную обработку, динамическую цель, пакетную передачу, исключение с участием оператора и отказ или повтор, если они есть. Записывайте входы и выходы на устойчивых границах: запросы, изменения базы, созданные записи, состояния, отчеты и видимые ошибки. Внутренние вызовы могут поменяться с архитектурой. Наблюдаемые обязательства не должны меняться случайно.

Компактный манифест делает объем теста проверяемым:

path: close-exception-approval
entry: account-status-change
observations:
  - eligible-account-row
  - exception-record
  - approval-state
  - ledger-entry
dynamic-targets:
  - reconciliation-program
external-frontier:
  - scheduler-calendar

Для каждого записанного производственного случая запустите исходную и новую системы в контролируемом состоянии, нормализуйте допустимо меняющиеся значения и сравните наблюдения. Другая временная метка может быть приемлемой, пропавшая запись исключения нет. При расхождении идите по пути до первой отличающейся границы вместо сравнения миллионов строк или только конечного состояния базы.

Образцы трафика нужно целенаправленно дополнять. Они отражают период записи, поэтому добавьте календарные границы, права, восстановление, редкие управляющие значения и найденные статическим анализом действия оператора. Если статический анализ видит достижимую ветвь, которой нет в выборке, не удаляйте ее молча. Установите, неактивна она, недоступна или просто редка.

CodeHero одновременно читает всю кодовую базу на всех ее языках и проверяет переписанную систему по записанному производственному трафику на стенде соответствия, завершая проект менее чем за 30 дней. Для любого подхода действует один практический стандарт: за каждой замененной границей нужны свидетельства графа и проходящие через нее свидетельства поведения.

Граф должен оставаться проверяемым артефактом

Принесите все старые языки
COBOL, JCL, RPG, CL, VB6, PL/SQL и веб-монолиты входят в единый объем.

Общий граф вызывает доверие, когда инженеры могут изучить свидетельства, воспроизвести извлечение и записать решения по неопределенным ребрам. Красивый рисунок без происхождения остается презентацией, а не техническим артефактом.

Храните исходный реестр ребер в системе версий со стабильными идентификаторами артефактов и мест. Записывайте версию извлекателя и ревизию входных данных. Для выведенного ребра храните ограниченные значения и правила. Когда трафик подтверждает ребро, прикрепляйте идентификатор образца, а не чувствительную нагрузку. Тогда изменения графа объяснимы по мере развития дерева и проекта миграции.

Проверка должна сосредоточиться на границах и неопределенности. Спросите, какие видимые результаты пересекают репозитории, какие динамические цели остаются открытыми, какие данные управляют последующим поведением и какие внешние системы добавляют работу. Огромная центральная схема неудобна. Фильтруйте тот же граф до одной транзакции, открытой границы, влияния записи на чтение или карты будущих компонентов.

После обнаружения назначьте ответственных. Передайте открытые ребра людям, способным получить выгрузки планировщика, определения базы, производственные настройки или инструкции операторов. Записывайте ответ в граф, а не протокол встречи. Если артефакт признан мертвым, сохраните доказательства, например недостижимые значения и отсутствие в достаточно представительной выборке. Молчание не доказывает вывод из эксплуатации.

Первый полезный результат не равен числу файлов или впечатляющему количеству узлов. Это небольшой набор сквозных путей, где каждый переход подтвержден, а неизвестные места открыты. По этим путям архитекторы рисуют границы, тестировщики выбирают случаи, операторы замечают пропущенные шаги. Расширяйте набор, пока поведение в объеме не получит такую проработку.

Правила сборки выбирают настоящее ребро

Имена в исходниках не говорят, какая реализация выполняется. Сценарии сборки, карты линковщика, порядок поиска библиотек, манифесты, описания развертывания и настройки среды выбирают получателя управления. Игнорируя их, общий граф может связать правильное имя с неправильным кодом.

Повторяющиеся имена нормальны в долгоживущих системах. Тестовая версия может лежать рядом с производственной. Две загрузочные библиотеки могут содержать одно имя программы. Проект VB6 может ссылаться на совместимый интерфейс COM, а развертывание выберет зарегистрированную реализацию. Синонимы базы могут направить одинаковый SQL в разные схемы. Графу нужен контекст развертывания, а не универсальный ответ со всеми кандидатами сразу.

Моделируйте разрешение как последовательность свидетельств. Сначала соберите имя и соглашение о вызове. Затем примените порядок поиска выбранной среды, проверьте экспорт ожидаемой точки входа и совместимую форму. В конце приложите запись настройки, которая выбрала реализацию. Если входных данных не хватает, сохраните кандидатов и покажите отсутствующее решение вместо выбора первого проиндексированного файла.

Выходы компилятора и линковщика часто отвечают на вопросы, неразрешимые по исходникам. Карта линковки показывает символ в двоичном файле. Журналы сборки показывают версии созданных исходников и copybook. Реестры поставки связывают артефакт с узлом, где его достиг записанный трафик. Это не второстепенные бумаги, а свидетельства об исполняемой системе. Им нужны стабильные узлы или вложения.

Сохраняйте различия сред. Разработка, тест, восстановление и производство могут по-разному разрешать одно логическое имя. Их слияние скрывает чисто производственную зависимость или убеждает тестировщиков, что они запустили код, который среда не загружала. Помечайте ребра контекстом и сравнивайте контексты. Различие может быть намеренным, но входит в объем, если новая система должна поддержать среду.

Такая работа находит и устаревшие объявления. Файл сборки может называть библиотеку, которая больше не поставляется, а двоичный файл разрешает символы в другом месте. И наоборот, исходник может казаться неиспользуемым, хотя оператор компилирует его отдельной процедурой перед годовым запуском. Не выбирайте удобную версию. Запишите конфликтующие свидетельства и получите недостающую инструкцию.

Проверка ребра проста: другой инженер должен пройти по свидетельствам и получить тот же набор кандидатов при тех же предположениях о среде. Воспроизводимость важнее принудительного единственного ответа. Две обоснованные цели и отсутствующий файл настройки полезнее четкой стрелки, основанной на близости каталогов.

После первого построения важна и точная версия. Зависимость, привязанная к пути исходника, может устареть, если ветка выпуска, копия библиотеки или созданный элемент изменились, а путь остался прежним. Храните у узла отпечаток содержимого или идентификатор сборки и связывайте развернутый файл именно с этой ревизией. Иначе проверяющий пройдет по достоверным свидетельствам к неверной версии и одобрит замену поведения, которое никогда не выполнялось. Риск особенно велик для архивов, собранных командами с нескольких машин вместо чистой рабочей копии. Анализ должен сообщать о повторяющихся путях, противоречивых отметках времени и артефактах без доказанной связи с исходником. Такие предупреждения не мешают исследованию, но не дают графу притворяться точнее исходного реестра. Когда поздняя выгрузка приносит двоичный файл или журнал сборки, стабильный идентификатор позволяет обновить затронутые ребра.

Анализ по репозиториям по-прежнему помогает понять локальный код. Он не устанавливает поведенческую границу системы, собранной из языков, заданий, хранилищ, созданных артефактов и действий людей. Если важные вызовы пересекают контейнеры анализа, эти контейнеры заранее решили, что граф пропустит.

Вопросы

Что такое граф зависимостей всей кодовой базы?

Он строится по исходникам, настройкам, заданиям, схемам, созданным артефактам и наблюдаемым путям в заданном объеме. Ребра сохраняют механизм и свидетельство, поэтому прямой вызов не смешивается с передачей данных или выведенной динамической целью.

Почему анализ репозитория пропускает вызовы?

Репозитории отражают владение и поставку, а выполнение пересекает их через планировщики, базы, файлы, очереди и динамические имена. Не видя обе стороны, анализатор оставляет открытый токен или теряет ребро целиком.

Может ли статический анализ разрешать динамические вызовы?

Многие вызовы удается свести к ограниченному набору кандидатов через присваивания, параметры, настройки, поля и правила платформы. Результат нужно отметить как выведенное свидетельство, а не подтвержденный буквальный вызов.

Нужно ли включать чтение и запись базы в граф?

Да, если они переносят поведение между компонентами. Записывайте операцию, значимые поля и условие читателя, поскольку работа с одной таблицей сама по себе не означает влияния.

Находит ли чтение всего дерева каждую зависимость?

Нет. Внешние планировщики, настройки, инструкции операторов, триггеры и двоичные файлы могут остаться снаружи, поэтому графу нужна явная открытая граница. Наблюдения и выгрузки среды закрывают часть пробелов.

Как трассы выполнения улучшают граф?

Они подтверждают появление конкретных путей и динамических значений в записанном трафике. Они не доказывают смерть ненаблюдавшихся путей, поэтому нужны статическая достижимость и целевые редкие случаи.

Как показывать созданный код?

Храните генератор, шаблоны, входы и выходы отдельными узлами с ребрами генерации. Тогда инженеры не правят файл, который позже перезапишется, и сохраняют конкретные имена развертывания.

Может ли граф определить новые границы сервисов?

Он дает свидетельства о цельном поведении, транзакционных правилах, влиянии данных и настоящих внешних стыках. Не превращайте каждую старую программу или репозиторий в сервис только ради удобного соответствия.

Что должен сравнивать стенд соответствия?

Сравнивайте наблюдаемые эффекты полных путей: ответы, изменения базы, созданные записи, состояния, отчеты и ошибки. Нормализуйте только допустимые различия, затем ищите первую разошедшуюся границу.

Как проверять граф для миллионов строк?

Проверяйте фильтрованные пути и виды неопределенности, а не огромную схему. Стабильные ребра с механизмом, местом и свидетельством позволяют разбирать одну транзакцию, набор целей или границу за раз.