Навигация по коду хорошо работает, когда разработчик остается в рамках одного языка и одной кодовой базы. Нажимаешь F12 — переходишь к определению. Щелкаешь правой кнопкой мыши по методу — находишь все ссылки. Эти взаимодействия кажутся мгновенными, потому что IDE имеет полную, согласованную модель кода: она знает каждый символ, каждый тип, каждую цепочку импорта. Однако в тот момент, когда эта граница расширяется, чтобы включить второй язык, эта модель разрушается. IDE знает свой язык, но не другой. Разработчик видит вызов, отслеживает его до края текущего файла, а затем натыкается на стену: вызываемая функция находится на другом языке, возможно, в другом репозитории, подчиняясь другим соглашениям, которые инструмент не понимает. С этого момента навигация становится ручной. Разработчик переключается между инструментами, ищет по тексту и надеется, что результат будет тем, что он искал.
Навигация по коду между языками
SMART TS XL Обеспечивает унифицированное разрешение символов и навигацию по коду для всех языков в вашей среде.
Кликните сюдаДля команд, работающих в действительно полиглотных средах, это не просто случайное неудобство. Это стандартное состояние каждой важной задачи. Корпоративные системы обычно используют COBOL и Java, JCL и SQL, Python и C++, или любое другое сочетание, отражающее десятилетия технологических решений, наложенных друг на друга. Каждая языковая граница в этом стеке — это место, где автоматическая навигация прекращается и начинается ручная реконструкция. Трение накапливается для каждого разработчика, каждой задачи и каждой команды, пока не становится структурным: замедление адаптации, более рискованные изменения, более длительные расследования инцидентов и растущая зависимость от немногих людей, обладающих знаниями о разных языках. Как показано в контексте решений для статического анализа COBOL , проблема рассуждений через языковые границы — это не просто проблема инструментов. Это фундаментальное препятствие для безопасной работы с большими, гетерогенными системами.
Понимание причин возникновения этой проблемы и ее практической стоимости — первый шаг к ее решению. В этой статье рассматривается проблема от ее технических корней до операционных последствий, анализируются причины сбоев в работе широко используемых инструментов на языковых уровнях и объясняется, что необходимо для эффективной межъязыковой навигации в масштабах предприятия.
Что на самом деле требуется для работы навигации по коду?
Навигация по коду — это не операция поиска, а операция разрешения. Когда разработчик спрашивает: «Где определена эта функция?», IDE не сканирует файлы на наличие соответствующего текста. Она сопоставляет идентификатор со структурированной моделью кодовой базы: разобранным представлением каждого класса, метода, переменной и типа, существующих в области видимости, а также связей между ними. Эта модель строится во время индексирования, постоянно поддерживается по мере изменения файлов и мгновенно запрашивается при запуске действия навигации. Точность и полнота модели определяют точность и полноту каждого результата навигации, получаемого разработчиком.
Различие между поиском и разрешением имеет значение, поскольку оно определяет требования к межъязыковой навигации. Текстовый поиск может просматривать любые файлы независимо от языка, поскольку он не считывает код как код. Инструмент навигации не может функционировать за пределами языковых границ, если он не создал модель, охватывающую оба языка, а не просто модель одного языка, которая также может находить строки в файлах, принадлежащих другому языку. Создание такой унифицированной модели технически сложно, в отличие от навигации по одному языку, и сложность возрастает с количеством задействованных различных языков. Как было подробно рассмотрено в разделе анализа потока данных и управления , код, корректно работающий на разных путях выполнения, требует структурного понимания всего пути, а не только тех сегментов, которые попадают в область действия какого-либо отдельного инструмента.
Три специфические возможности, необходимые для навигации по коду и имеющие свои недостатки на границах языков программирования, — это разрешение символов, построение графа вызовов и трассировка зависимостей. Каждая из них заслуживает отдельного рассмотрения, прежде чем анализировать их взаимодействие на практике.
Разрешение символов и причины его сбоев на языковых границах
Разрешение символов — это процесс сопоставления идентификатора в исходном коде с его определением. В одноязычной среде этот процесс хорошо изучен: компилятор или интерпретатор уже выполняет его, а IDE воспроизводят эту логику разрешения, используя те же правила грамматики и системы типов. Разрешение является точным, поскольку правила однозначны в рамках одного языка.
На границе языков разрешение требует модели моста, которая может представлять символы обоих языков в единой структуре и отслеживать связь от идентификатора в языке А к соответствующему определению в языке В. Такой мост отсутствует в стандартных IDE или языковых серверах, поскольку протокол языковых серверов был разработан исходя из предположения, что каждый языковой сервер обрабатывает только один язык. Когда метод Java вызывает программу COBOL через определенный интерфейс, языковой сервер Java понимает вызов метода, но не может разрешить целевой объект COBOL. Разработчик видит вызов, знает, что он куда-то ведет, и не может отследить его, не покинув инструмент полностью.
Рассмотрим показательный пример. Java-сервис вызывает программу COBOL по имени через промежуточный слой:
Ява
// Java service calling a COBOL program via a legacy middleware adapter
LegacyAdapter.invoke("CUSTINQ", customerRequest);
Среда разработки Java решает проблему. LegacyAdapter.invoke Без труда. Он знает сигнатуру метода и может перейти к его реализации. Но "CUSTINQ" Это строковый литерал на уровне Java. IDE не имеет представления об именах программ COBOL и не понимает, что... CUSTINQ Это относится к конкретному скомпилированному программному блоку со своими собственными определениями данных и структурой абзаца. Навигация останавливается на строке. Разработчику необходимо вручную найти исходный код COBOL, открыть его в другом редакторе и начать чтение без какого-либо структурного контекста о том, как программа связана с вызывающим кодом Java.
Построение графа вызовов в разнородных кодовых базах
Граф вызовов — это структура данных, которая показывает, какие функции вызывают другие функции в кодовой базе. IDE используют графы вызовов для реализации таких функций, как «найти все вызывающие функции» и «иерархия вызовов», которые показывают разработчику каждый путь, ведущий к данной функции, и каждую функцию, которую эта функция вызывает. В одноязыковой среде построение графа вызовов является естественным побочным продуктом индексирования кодовой базы.
В многоязычной среде граф вызовов должен охватывать языковые границы, чтобы быть полным. Граф вызовов, который обрывается в каждой точке, где выполнение переходит на другой язык, не является графом вызовов системы; это набор частичных графов, по одному на каждый язык, с несвязанными ребрами на каждой языковой границе. Для разработчика, отслеживающего путь выполнения в системе, использующей разные языки, это означает, что трассировка обрывается каждый раз, когда путь пересекает языковую границу, и требуется ручной шаг для продолжения трассировки на следующем языке.
Проблема особенно остро стоит в средах мэйнфреймов, где одна бизнес-транзакция может включать в себя JCL, управляющий последовательностью выполнения, программы на COBOL, выполняющие основную бизнес-логику, и SQL-запросы для чтения и записи данных. Как подробно описано в анализе сопоставления JCL и COBOL , эти три уровня глубоко переплетены: JCL определяет, что выполняется и в каком порядке, COBOL определяет, что делают программы, а SQL определяет, к каким данным они обращаются. Граф вызовов, охватывающий только COBOL, или только JCL, или только SQL, описывает фрагмент системы, а не саму систему. Для отслеживания чего-либо значимого необходимо, чтобы все три уровня были связаны в единой модели.
Отслеживание зависимостей при совместном использовании данных языками программирования
Зависимости между компонентами в многоязычной системе часто опосредуются общими данными: таблицей базы данных, которую COBOL записывает, а Java читает, файлом, который создает пакетное задание и который обрабатывает API, или очередью сообщений, которую Python-разработчик записывает, а Go-пользователь читает. Эти опосредованные данными зависимости реальны и имеют существенные последствия. Изменение схемы таблицы, формата файла или структуры сообщения влияет как на разработчика, так и на потребителя, но они не представлены в модели зависимостей ни одного отдельного языка.
Отслеживание зависимостей в многоязычной среде, следовательно, требует понимания не только вызовов кода, но и взаимосвязей между данными и кодом: какие программы читают или записывают определенный столбец таблицы, какие сервисы зависят от определенного формата файла, какие потребители затрагиваются изменением схемы сообщений. Такое отслеживание полностью выходит за рамки стандартной навигации IDE и требует инструмента, который моделирует всю систему, включая уровень данных, а не рассматривает код каждого языка изолированно.
Конкретные причины сбоев навигации в распространенных многоязычных системах
Проблемы, возникающие при навигации по коду между языками программирования, не являются абстрактными. Они проявляются в конкретных, предсказуемых ситуациях, которые регулярно возникают в корпоративных средах разработки. Их детальное изучение наглядно демонстрирует, почему универсальные инструменты поиска не могут заменить настоящую кросс-языковую навигацию.
COBOL и Java: наиболее распространенная граница между предприятиями.
Граница между COBOL и Java является наиболее распространенной языковой границей в крупных корпоративных системах, особенно в сфере финансовых услуг, страхования и государственного управления. Десятилетия инвестиций в COBOL сосуществуют с усилиями по модернизации Java в гибридной архитектуре, где COBOL обрабатывает пакетную обработку, а Java — транзакционную обработку и API. Два языка взаимодействуют через определенные интерфейсы: транзакции CICS, очереди сообщений, общие базы данных и файловые интерфейсы.
На практике преодоление этой границы выявляет глубину проблемы. Java-разработчику, исследующему неожиданное поведение в транзакции, необходимо проследить путь выполнения пакетной программы COBOL, которая обрабатывала базовые данные. Java IDE показывает, где вызывается интерфейс. Она не может показать, что программа COBOL делает с входными данными, какие данные она считывает, какие вычисления выполняет или что записывает обратно. Разработчику необходимы знания COBOL и инструменты COBOL для продолжения работы, ни то, ни другое может быть недоступно в команде, ориентированной на Java. В результате получается либо медленное ручное расследование, либо эскалация проблемы специалисту с необходимыми знаниями, и то, и другое представляет собой ошибки навигации, которые отнимают время и увеличивают продолжительность инцидента.
В COBOL аналогичная ошибка возникает, когда разработчику COBOL необходимо понять, какие Java-сервисы потребляют данные, генерируемые программой COBOL. Стандартные инструменты COBOL не имеют модели кода Java. Разработчик может видеть вывод программы COBOL, включая запись в базу данных или обновление файла, но не может проследить этот вывод дальше, чтобы определить, какие Java-сервисы его считывают. Любое изменение формата вывода требует ручной координации с командами разработчиков Java, поскольку нет инструмента, который мог бы автоматически перечислить потребителей. Модернизация COBOL критически зависит от решения именно этой проблемы: пока полная цепочка зависимостей не будет видна в обоих языках, безопасная модернизация невозможна.
JCL и COBOL: оркестровка без видимости
JCL — это уровень оркестровки для пакетной обработки данных на мэйнфреймах. Он управляет тем, какие программы запускаются, в какой последовательности, с какими параметрами и с какими файлами и наборами данных. Взаимосвязь между JCL и вызываемыми им программами COBOL представляет собой фундаментальную структурную зависимость: изменение JCL приводит к изменению поведения программ COBOL. Изменение ожидаемого формата входных данных для программы COBOL может потребовать изменения и наборов данных JCL, которые она использует.
Стандартные инструменты анализа COBOL не анализируют JCL. Стандартные инструменты анализа JCL не анализируют COBOL. Связь между шагом JCL, который вызывает PGM=CUSTINQ и программа COBOL под названием CUSTINQ Она существует в работающей системе, но не в модели какого-либо отдельного инструмента. Разработчик, использующий любой из этих инструментов изолированно, не может увидеть полную картину. Он знает, что вызывает шаг JCL по имени, но не знает, что делает программа. Или он знает, что делает программа COBOL, но не знает, как она вызывается, с какими параметрами или в какой последовательности потоков заданий.
Этот пробел создает специфические риски для производственных систем. Разработчик, изменяющий определения рабочей памяти программы COBOL, может непреднамеренно изменить способ обработки данных, передаваемых из определенного шага JCL, без предупреждения со стороны какого-либо инструмента о том, что это изменение влияет на контекст выполнения, определенный в JCL. Разработчик, реструктурирующий процедуру JCL, может изменить последовательность выполнения программ, без какого-либо инструмента, показывающего, какие программы COBOL зависят от этой последовательности для корректной работы. Как подробно описано в разделе, посвященном решениям для статического анализа JCL , для обеспечения прозрачности межпрограммных зависимостей и использования наборов данных в средах JCL требуется специальный анализ, который стандартные инструменты просто не предоставляют.
Вот как выглядит одна и та же зависимость с точки зрения каждого языка при использовании стандартных инструментов, и как это показала бы единая модель:
| Что видит разработчик | Просмотр только с использованием JCL | Просмотр только на языке COBOL | Единый межъязыковой интерфейс |
|---|---|---|---|
| Вызов программы | PGM=CUSTINQ (Только имя) | Невидимый | CUSTINQ вызывается тремя процедурами JCL с определенными значениями PARM. |
| Входные наборы данных | Список имен ДД | Невидимый | Считывает файл CUSTFILE (определенный в шаге 2 файла CUSMAST.JCL). |
| Выходные наборы данных | Список имен ДД | Невидимый | Записывает CUSTRPT (используется заданием RPTPRT) |
| Бизнес логика | Невидимый | Отдел процедур (видимый) | Полный цикл от вызова JCL через логику COBOL до вывода результата. |
| Влияние изменений | Неизвестный | Неизвестный | 4 процедуры JCL, 2 последующие программы COBOL, 1 таблица базы данных |
Современные языковые стеки: Python, Go и C# в различных сервисах
В распределенных системах, построенных на современных языках программирования, проблема навигации принимает иную форму. Вместо языкового разрыва COBOL-Java, задача состоит в преодолении разрыва между сервисами в сочетании с многоязычным стеком. Сервис обработки данных на Python передает данные в API на Go, который, в свою очередь, передает данные во фронтенд на C#. Каждый сервис построен со своими собственными инструментами, собственной конфигурацией IDE и собственной моделью зависимостей. Связи между сервисами существуют на уровне API, но стандартные инструменты навигации не имеют модели взаимоотношений между сервисами и API.
Разработчику, изменяющему структуру ответа в Python-сервисе, необходимо знать, от каких полей зависит Go API и какие поля в конечном итоге отображает C# интерфейс. Без межъязыковой и межсервисной навигации ему приходится вручную проверять код каждого нижестоящего сервиса, искать ссылки на соответствующие имена полей и надеяться, что соглашения об именовании достаточно согласованы, чтобы поиск был надежным. Как обсуждалось в контексте инструментов статического анализа Go , даже в рамках одного Go-сервиса понимание иерархии вызовов и отслеживание зависимостей между модулями — нетривиальная задача. Расширить эту задачу на границы сервисов и языков одновременно — задача на порядок сложнее.
Та же закономерность применима к системам на C# , которые вызывают общие сервисы, написанные на Java, или к конвейерам Python , которые записывают данные в базы данных, используемые приложениями .NET. В каждом случае стандартные инструменты для соответствующего языка обеспечивают точную навигацию внутри этого языка и не создают ничего полезного на границе, где выполнение переходит в другой язык или сервис.
SQL и код приложений: невидимый слой данных
SQL присутствует практически в каждой корпоративной системе, и тем не менее это наиболее часто игнорируемый компонент кросс-языковой навигации. Код приложения пишет SQL-запросы, которые ссылаются на имена таблиц, имена столбцов, условия объединения и хранимые процедуры. Схема базы данных определяет эти таблицы и столбцы. Связь между кодом приложения и схемой базы данных представляет собой зависимость, нарушение которой при изменении схемы приводит к сбоям во время выполнения. Но стандартные IDE рассматривают SQL-строки как строки, а не как код с навигационной структурой.
Разработчику, изменяющему имя столбца в схеме, необходимо найти все ссылки на этот столбец в каждом приложении, на каждом языке программирования и в каждом запросе. Текстовый поиск по имени столбца ненадежен: короткие имена столбцов конфликтуют с именами переменных, сообщениями логов и комментариями. Для поиска с учетом символов требуется инструмент, который моделирует как схему SQL, так и код приложения, ссылающийся на нее, и понимает, что "customer_id" В строке запроса Java это ссылка на столбец базы данных. customer_idи может перечислять все подобные ссылки на разных языках. Без этой модели внесение изменений в схему требует больших ручных усилий и является статистически неполным.
Почему расширения IDE и языковые серверы не могут решить эту проблему?
Расширения IDE и языковые серверы предназначены для предоставления специфичных для каждого языка функций. Они анализируют код в соответствии с определенной грамматикой, создают специфический для языка индекс символов и обрабатывают запросы через протокол языковых серверов (Language Server Protocol), который определяет стандартный интерфейс для языковых функций, включая переход к определению, поиск ссылок и всплывающую документацию. Протокол является языково-независимым на транспортном уровне, но специфичен по своему содержанию: каждый языковой сервер выдает результаты только для своего языка.
Подключение двух языковых серверов в рамках одной IDE не решает проблему межъязыковой навигации. У каждого сервера свой индекс. Когда разработчик запрашивает «найти все ссылки» для символа, запрос отправляется на языковой сервер для языка текущего файла. Этот сервер возвращает известные ему ссылки, которые ограничены файлами, которые он проиндексировал. Он не запрашивает данные у другого языкового сервера, и даже если бы запрашивал, не было бы общей модели символов, с помощью которой можно было бы выразить межъязыковые связи.
Это структурное ограничение архитектуры LSP, а не проблема конфигурации. Его можно частично обойти в конкретных, узких случаях, например, в случае языкового сервера, который также анализирует встроенный SQL в f-строках Python, но его нельзя обобщить на произвольные межъязыковые зависимости без создания именно той унифицированной многоязычной модели, которая выходит за рамки того, для чего был разработан любой языковой сервер. Проблемы, с которыми сталкивается статический анализ при метапрограммировании в рамках одного языка, иллюстрируют глубину проблемы: если рассуждения о динамически генерируемом коде в одном языке требуют специализированных методов, то рассуждения в нескольких языках с различными грамматиками и моделями времени выполнения требуют совершенно иного архитектурного подхода.
Что хорошо предоставляют языковые серверы (и где их возможности заканчиваются)
Языковые серверы превосходно справляются с задачами, для которых они были разработаны: диагностика в реальном времени, интеллектуальное автодополнение, разрешение символов в рамках одного языка и рефакторинг в редакторе в ограниченном объеме. Эти возможности ценны и не должны игнорироваться. Проблема не в том, что языковые серверы являются неадекватными инструментами; проблема в том, что это одноязычные инструменты, применяемые к многоязычным задачам, и это несоответствие приводит к предсказуемым и дорогостоящим сбоям именно в тех местах, где точность имеет наибольшее значение.
В таблице ниже приведено сопоставление конкретных задач навигации с тем, что предоставляют языковые серверы, и показано, где начинается разрыв:
| Задача навигации | LSP в рамках одного языка | LSP преодолевает языковые барьеры |
|---|---|---|
| Перейти к определению | Точно, мгновенно | Сбой: остановка на месте вызова |
| Найти все ссылки | Завершено в индексированных файлах | Неполный текст: отсутствуют ссылки на другие языки. |
| Иерархия вызовов | Точно для звонков на одном языке. | Сокращено: участники, устанавливающие границы, отсутствуют. |
| Переименование символа | Безопасно в рамках одного языка | Опасно: переименование не учитывает межъязыковое использование. |
| Анализ воздействия | Ограничено текущим языком. | Невосприимчивость к потребителям на других языках. |
Поиск с помощью Grep и текстовый поиск: почему они не являются приемлемой заменой.
Когда языковые серверы не справляются с определением границ, разработчики прибегают к текстовому поиску. grep, поиск на уровне IDE и поиск по платформе, например, GitHub Code Search, находят строки в файлах независимо от языка. У них нет понятия «символ» или «ссылка», только вхождения строк. Для коротких, распространенных идентификаторов это означает огромные наборы результатов, требующие ручной фильтрации. Для идентификаторов, существующих на нескольких языках с разными значениями, результаты объединяют различные элементы кода, которые случайно имеют одно и то же имя.
Более опасным, чем шум, является неполнота. Текстовый поиск пропускает ссылки там, где соглашения об именовании различаются в разных языках, где идентификатор формируется динамически, где связь осуществляется через конфигурацию или реестр имен, или где отношение выражается через данные, а не через прямую ссылку на код. Эти пробелы не видны в результатах поиска: разработчик видит то, что нашел поиск, не имеет возможности узнать, что он пропустил, и принимает решения, основываясь на неполной картине, которая кажется полной. Как рассматривается в более широком контексте статического анализа кода для обеспечения удобства сопровождения , неспособность точно рассуждать о том, что делает код и с чем он связан, — это не просто небольшое неудобство, а основная причина накопления технического долга, дефектов, возникающих в процессе сопровождения, и растущих затрат на безопасное внесение изменений.
Операционные расходы, накапливающиеся на языковых барьерах
Описанные выше сбои навигации не являются единичными проблемами. Они накапливаются в каждой задаче, у каждого разработчика и каждой команды, работающей в многоязычной среде. Чтобы понять последствия, необходимо проанализировать повторяющиеся ситуации, в которых навигация дает сбой, и рассчитать совокупный эффект.
Процесс адаптации новых сотрудников в полиглотических командах занимает значительно больше времени.
Разработчик, присоединившийся к команде, работающей с одним языком и одной кодовой базой, может относительно быстро стать продуктивным. IDE обеспечивает навигацию, код самодокументируется благодаря своей структуре, а создаваемая разработчиком ментальная модель отражает реальную систему. Разработчик, присоединившийся к команде, работающей с несколькими языками, сталкивается с принципиально иной ситуацией. Инструменты не позволяют преодолевать эти границы, поэтому ментальную модель приходится создавать вручную посредством документирования, парного программирования и проб и ошибок.
Создание такой модели вручную занимает недели, а не дни. Разработчик должен изучить не только код на своем основном языке, но и достаточно хорошо разбираться в смежных языках, чтобы понимать, что они вызывают, что вызывает их и какие данные передаются между ними. В крупных организациях с высокой текучестью кадров или частой ротацией команд это длительное время адаптации является постоянными затратами, а не разовым вложением. Каждый человек, присоединяющийся к полиглотической команде, оплачивает полную стоимость перестройки межъязыковой модели с нуля, поскольку инструменты ничего не предоставляют для ее дальнейшего развития.
Производственные инциденты имеют более длительный хронический характер, если их следы пересекают языковые границы.
Когда при возникновении производственного инцидента требуется отследить путь выполнения, пересекающий языковые границы, каждое такое пересечение является ручным шагом. Дежурный разработчик, и без того работающий в условиях нехватки времени, должен переключиться на другой инструмент, выполнить поиск по тексту в кодовой базе другого языка и вручную связать результаты с трассировкой, которую он создавал. В системе с тремя или четырьмя языковыми уровнями полное расследование первопричины может потребовать четырех или пяти таких пересечений, каждое из которых добавляет минуты к расследованию, измеряемому временем его влияния на пользователей.
В масштабах всей организации, использующей множество многоязычных сервисов, совокупный эффект заключается в систематическом увеличении среднего времени разрешения любых инцидентов, затрагивающих разные языки. Это не вина отдельных разработчиков; это структурное следствие инструментов, которые не моделируют реальные связи системы. Организации, инвестировавшие в обеспечение прозрачности межъязыковых взаимодействий, неизменно отмечают более быстрое разрешение инцидентов как одно из наиболее прямых и измеримых преимуществ, именно потому, что эти инвестиции устраняют ручные этапы пересечения границ, которые увеличивают время расследования.
Рискованные изменения становятся еще более рискованными, если не обеспечена прозрачность влияния на межъязыковые условия.
Каждое изменение общего кода в многоязычной системе несёт в себе неопределённый риск до тех пор, пока не будет известен полный набор потребителей на всех языках. Без межъязыковой навигации этот риск не определяется до внесения изменений. Он обнаруживается позже, когда в процессе тестирования или, что ещё хуже, в производственной среде выявляются неисправные потребители. Это не редкий случай отказа. Это стандартный результат поддержания общих структур данных, интерфейсов или утилит в системе, где конечные потребители говорят на разных языках.
Консервативный подход к этой неопределенности заключается в чрезмерной осторожности: более масштабные тестовые работы, более длительные циклы проверки, больше координационных совещаний и более частые заморозки изменений в критические периоды. Все это реальные затраты, которые накапливаются на каждом цикле изменений в многоязычной системе. Они представляют собой время и усилия, затраченные на компенсацию отсутствия межъязыковой навигации, а не на создание ценности. Ландшафт модернизации устаревших систем в значительной степени формируется этими накопленными затратами: организации стремятся к модернизации, потому что поддержание существующих систем стало непомерно дорогим, а сбои в межъязыковой навигации являются основной причиной этих затрат на обслуживание.
Что на самом деле требуется для кроссъязыковой навигации?
Для решения задачи навигации по коду на разных языках необходимо создать единую модель, которую языковые серверы по отдельности предоставить не могут. Эта модель должна удовлетворять ряду требований, которые являются необходимыми условиями для полезной межъязыковой навигации, а не дополнительными улучшениями.
Единый общий индекс символов, охватывающий все языки. Каждый именованный элемент в каждом языке, включая функции, классы, поля, процедуры, таблицы и определения данных, должен быть представлен в одном индексе с общей моделью идентификации. Идентификатор символа не может быть специфичным для конкретного языка, если необходимо разрешать межъязыковые ссылки относительно него.
Для каждого языка в системе необходимы языкознательные парсеры. Каждый язык должен анализироваться с использованием собственной грамматики, а не путем аппроксимации с помощью универсального парсера или сопоставления с образцом. Структурный результат каждого парсера должен соответствовать общей модели идентичности, чтобы межъязыковые отношения могли быть выражены как связи между правильно идентифицированными символами.
Явное моделирование межъязыковых интерфейсов. Механизмы взаимодействия различных языков, включая вызовы программ по имени, таблицы баз данных, форматы файлов, схемы сообщений и контракты API, должны быть представлены в модели как первоклассные типы соединений, а не рассматриваться как непрозрачные строки или вовсе исключаться из модели.
Отслеживание зависимостей, включающее взаимосвязи на уровне данных. Модель должна представлять не только вызовы кода, но и зависимости, опосредованные данными, поскольку в многоязычных системах данные часто являются основным средством, через которое выходные данные одного языка становятся входными данными другого языка.
Производительность запросов, поддерживающая интерактивную навигацию. Индекс должен обеспечивать время ответа на запросы менее секунды для распространенных операций навигации. Модель, требующая пакетного анализа, а не интерактивных запросов, полезна для анализа влияния на работу в автономном режиме, но не может заменить навигацию в реальном времени во время активной разработки.
Эти требования описывают платформу интеллектуального управления кодом предприятия, а не расширение IDE или языковой сервер. Создание и поддержка такой платформы являются технической основой для практической реализации навигации по многоязычному коду. Альтернативный вариант, предполагающий смирение с ошибками навигации и постоянную оплату их последствий, становится менее приемлемым по мере роста и усложнения многоязычной системы.
Как SMART TS XL Обеспечивает многоязычную навигацию.
SMART TS XL В основе системы лежит предположение, что корпоративные системы нельзя понять через призму какого-либо одного языка или какого-либо одного репозитория. Ее платформа Software Intelligence обрабатывает исходный код со всех языков и платформ в среде, анализирует каждый из них с помощью языково-специфического анализа и создает единый перекрестный индекс, который представляет взаимосвязи между элементами независимо от того, к какому языку они принадлежат. Навигационные запросы к этому индексу возвращают результаты, охватывающие языковые границы, поскольку индекс моделирует всю систему целиком, а не ее языковой фрагмент.
Платформа явно моделирует межъязыковые интерфейсы, которые игнорируются стандартными инструментами. Шаг JCL, вызывающий программу COBOL по имени, представлен как зависимость в графе перекрестных ссылок, связывающая шаг JCL с модулем программы COBOL. Метод Java, записывающий данные в таблицу базы данных, представлен как зависимость данных, связывающая код Java с определением таблицы, а оттуда — с любым другим языком, считывающим ту же таблицу. Копибук COBOL, на который ссылаются несколько программ, представлен как общее определение, так что любое изменение структуры копибука немедленно отображает все программы, затронутые этим изменением, независимо от языка. Именно это явное моделирование межъязыковых зависимостей отличает настоящую кроссъязыковую навигационную платформу от набора языковых инструментов, работающих параллельно.
SMART TS XLВозможности анализа влияния, предоставляемые платформой, демонстрируют практическую ценность этой унифицированной модели. Когда разработчику необходимо понять последствия изменения общего компонента, такого как определение данных COBOL, элемент схемы базы данных, интерфейс Java или процедура JCL, платформа отслеживает граф зависимостей от этого компонента через все языковые границы и возвращает полную картину того, что будет затронуто. Результат представляется в виде навигационного отчета, организованного по языку, по компоненту и по конкретному местоположению ссылки, предоставляя разработчикам полную информацию, необходимую до внесения изменений, а не после того, как они обнаружат последствия. Эта возможность напрямую решает проблему накопления рисков, описанную в предыдущем разделе, преобразуя неопределенный межъязыковой риск в количественно измеримое, перечислимое влияние.
Межъязыковая навигация как свойство всей системы.
Главная идея этой статьи заключается в том, что навигация по коду в многоязычных средах — это свойство всей системы, а не какого-либо отдельного языкового инструмента. IDE, которая идеально работает с COBOL, и отдельная IDE, которая идеально работает с Java, не создают вместе систему, которая бы идеально работала на границе COBOL и Java. Они создают две независимые системы навигации с разрывом между ними, и именно в этом разрыве находятся наиболее важные взаимосвязи в системе.
Для преодоления этого разрыва необходим инструмент иного типа: тот, который моделирует систему в целом, представляет отношения между языками как первоклассные сущности и обеспечивает навигацию, которая следует за этими отношениями, куда бы они ни вели. Для организаций, управляющих сложными многоязычными системами корпоративного масштаба, такая возможность не является роскошью. Каждый день разработки без нее — это день, в течение которого накапливаются издержки, связанные с ошибками в межъязыковой навигации: в виде замедления процесса адаптации, увеличения времени реагирования на инциденты, более рискованных изменений и постепенной концентрации незаменимых знаний в руках людей, которые вручную создали межъязыковые ментальные модели, которые инструменты не могут обеспечить.