Поиск текста против понимания кода

Почему текстовый поиск — это не то же самое, что понимание кода

Разработчик впервые открывает большой устаревший код. Ему нужно понять, что происходит с записью о клиенте при закрытии учетной записи: какие программы обновляют ее, какие пакетные задания считывают ее впоследствии, какие поля изменяются в процессе и зависит ли какая-либо нижестоящая система от конечного состояния. Естественным первым шагом является поиск. Он использует grep для поиска имени поля, просматривает результаты, открывает несколько файлов и начинает читать. В течение часа он находит ссылки в двенадцати программах, трех SQL-скриптах и ​​потоке заданий JCL. Он также находит то же имя поля в семнадцати блоках комментариев, четырех строках форматирования логов, двух тестовых фикстурах и переменной в совершенно несвязанной подсистеме, которая случайно имеет то же имя. По результатам поиска он не может определить, какие из этих операций являются фактическим чтением данных, какие — записью, какие — преобразованием, а какие — случайным совпадением имен. Он знает, как называется поле. Он еще не понимает, что код с ним делает.

Понимание кода начинается здесь.

SMART TS XL Создает структурную модель всей вашей кодовой базы, отображая зависимости между каждым языком и платформой.

Кликните сюда

Разрыв между поиском строки и пониманием кода — это не тот разрыв, который можно устранить с помощью улучшенного поиска. Это разрыв между двумя принципиально разными типами запросов: один спрашивает: «Где встречается этот текст?», а другой — «Что делает этот код?». Текстовый поиск — отличный ответ на первый вопрос. Но он совершенно не подходит для ответа на второй, и смешение этих двух подходов является одним из наиболее распространенных источников напрасных усилий, упущенных зависимостей и неверных оценок влияния в разработке программного обеспечения. Это различие имеет большее значение в крупных, гетерогенных корпоративных системах, чем в небольших современных кодовых базах, поскольку эти системы содержат десятилетия накопленной структуры, межъязыковые зависимости и неявные связи, которые существуют только в поведении кода, а не в какой-либо строке, встречающейся в его исходных файлах. Как показано в анализе Метрики качества кода и их влияниеСложность кодовой базы существенно влияет на удобство сопровождения, и ни один показатель, основанный только на текстовых шаблонах, не отражает структурных взаимосвязей, определяющих фактическое поведение кода.

Содержание

Что на самом деле делает текстовый поиск

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

Это чрезвычайно полезно для определенной категории задач: поиск места появления известной строки, подтверждение использования или отсутствия конкретного термина, быстрая проверка правильности соглашений об именовании, поиск файла, содержащего конкретное сообщение об ошибке. Для этих задач текстовый поиск — подходящий инструмент, поскольку эти задачи действительно связаны с поиском строк. Скорость, портативность и отсутствие необходимости в настройке grep и его аналогов — это функции, которые идеально подходят, когда вопрос звучит так: «Существует ли эта строка в этих файлах, и если да, то где?»

Проблема возникает при использовании текстового поиска для вопросов, не связанных со строками. «Что вызывает эту функцию?» — это не вопрос о том, где встречается имя функции. Это вопрос о графе вызовов, который является структурным свойством кода и требует для построения синтаксического анализа и семантики. «Где написано это поле?» — это не вопрос о том, где встречается имя поля. Это вопрос о потоке данных, для ответа на который необходимо понимать семантику присваивания в конкретном языке. «Что сломается, если я изменю этот интерфейс?» — это не вопрос о том, где встречается имя интерфейса. Это вопрос о зависимостях, для правильного ответа на который необходимо разрешить импорты, наследование и межмодульную связь.

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

Проблема шума: слишком много результатов, которые ничего не значат.

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

Короткий идентификатор, например status, id, type или date Идентификаторы могут встречаться тысячи раз в большом коде. Даже более длинные идентификаторы конфликтуют в разных языках и пространствах имен: calculate_tax В качестве имени функции в модуле Python, имени абзаца COBOL, хранимой процедуры базы данных, вспомогательной функции JavaScript и строки в конфигурации логирования — все эти варианты приводят к результатам текстового поиска. Разработчик, получивший эти результаты, должен отфильтровать их вручную, используя собственное понимание кода, чтобы определить, какие из них являются релевантными. Эта ручная фильтрация сама по себе является задачей понимания кода, а это значит, что разработчик выполняет работу, для которой предназначен инструмент, без какой-либо помощи с его стороны.

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

Рассмотрим конкретный пример. Разработчик на COBOL ищет имя абзаца перед его удалением:

кобол

SEARCH-RESULTS FOR "CALC-INTEREST":

1. CALC-INTEREST.PGM        line   5  : IDENTIFICATION DIVISION.
2. CALC-INTEREST.PGM        line  42  : CALC-INTEREST.
3. FINPROCESS.CBL            line 178  : PERFORM CALC-INTEREST
4. RPTMONTH.CBL              line  91  : * Old routine: CALC-INTEREST replaced by CALC-INT-V2
5. CUSTBATCH.CBL             line 234  : PERFORM CALC-INTEREST THRU CALC-INTEREST-EXIT
6. DATADICT.txt              line  12  : CALC-INTEREST - computes monthly interest for savings accts
7. TESTHARNESS.CBL           line  67  : PERFORM CALC-INTEREST
8. ARCHIVEJOB.CBL            line 156  : * PERFORM CALC-INTEREST (disabled 2019-03-14)

Из этих восьми результатов ровно два являются активными вызывающими объектами, которые перестанут работать, если удалить абзац: строки 3 и 5. Строка 2 — это определение. Строки 4 и 8 — комментарии. Строка 6 — запись в словаре данных. Строка 7 — тестовый стенд. Чтобы определить, какие два из этих восьми результатов представляют собой места активных вызовов, необходимо прочитать каждый файл в контексте, понять синтаксис COBOL и оценить, что на самом деле означает «отключено» в комментарии в строке 8 для выполнения. Текстовый поиск предоставил исходные данные. Понимание кода дало ответ.

Проблема молчания: важные результаты, которые так и не были получены.

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

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

Межъязыковые ссылки — это систематически отсутствующая категория. Java-сервис, который вызывает программу COBOL по имени через промежуточный слой, содержит имя программы COBOL в виде строкового литерала, который может быть найден с помощью текстового поиска. Но тот же Java-сервис, который динамически формирует имя программы, считывает его из файла конфигурации или отправляет данные через слой абстракции, вообще не содержит это имя. Это вызывающие функции, которые текстовый поиск не может найти, независимо от того, насколько тщательно он применяется. Как рассмотрено в контексте статический анализ обфусцированного и динамически генерируемого кодаКогда пути выполнения выражаются косвенно через конфигурацию, шаблоны или механизмы диспетчеризации во время выполнения, структурные взаимосвязи, которые они представляют, невозможно восстановить только из текста исходных файлов.

Псевдонимы полей и преобразования создают еще одну категорию скрытых ошибок. Поле COBOL с именем WS-ACCT-BAL запись в столбец базы данных с именем ACCT_BALANCE, впоследствии считываемый Java-сервисом как accountBalanceи в конечном итоге был опубликован в виде сериала. account_balance В JSON-ответе для одного и того же элемента данных отображаются четыре разные текстовые строки. Поиск любой из этих строк пропускает остальные три. Чтобы понять, что все четыре строки относятся к одной и той же базовой бизнес-концепции, необходимо понимать цепочку преобразований, а не искать все вхождения какого-либо одного имени.

Что на самом деле требуется для понимания кода

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

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

Анализ текста: от текста к структуре.

Первый шаг после текстового поиска — это синтаксический анализ: чтение исходного кода в соответствии с грамматикой языка и создание структурированного представления, как правило, абстрактного синтаксического дерева, кодирующего синтаксические связи между элементами кода. Разобранное представление PERFORM CALC-INTEREST THRU CALC-INTEREST-EXIT Это не строка; это структурированный объект, который идентифицирует это как оператор PERFORM с целевым диапазоном, где обе конечные точки являются именами абзацев в текущей программе, разрешаемыми в структуру PROCEDURE DIVISION программы.

Синтаксический анализ зависит от языка. Парсер COBOL понимает грамматику COBOL. Парсер Java понимает грамматику Java. Парсер JCL понимает синтаксис JCL. В многоязычной корпоративной системе для понимания кода требуется парсер для каждого языка, присутствующего в среде, создающий структурные представления, которые можно анализировать согласованным образом на разных языках. Как обсуждалось в подробном рассмотрении Статический анализ TypeScript в масштабах предприятияСтруктурный и семантический анализ, позволяющий понять, как организован код, как взаимодействуют модули и как происходит поток управления и данных в приложении, является основой для перехода от проверки синтаксиса к подлинному интеллекту кода.

Разрешение символов: от имен к сущностям

После анализа имена в исходном коде должны быть сопоставлены с сущностями, на которые они ссылаются. Идентификатор CALC-INTEREST В операторе PERFORM необходимо указать конкретное определение абзаца в конкретной программе или копибуке. Имя метода должно соответствовать этому определению. calculateLegacyFee В вызове Java имя столбца должно быть разрешено в соответствии с конкретным определением метода в конкретном классе, с учетом наследования и перегрузки. ACCT_BALANCE В SQL-запросе необходимо указать конкретный столбец в конкретной таблице в схеме базы данных.

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

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

Анализ потока управления: понимание путей выполнения

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

Анализ потока управления позволяет ответить на такие вопросы, как «при каких условиях выполняется этот участок кода?» и «доступен ли этот код из любой точки входа?». Текстовый поиск не может ответить на эти вопросы, поскольку они касаются путей выполнения, а не того, где появляются строки. Оператор, который появляется в исходном коде, может быть выполнен или не выполнен в зависимости от условий, определяющих ветвь, в которой он находится. Функция, определенная в модуле, может быть вызвана или не вызвана в зависимости от того, достигает ли какой-либо участок выполнения места вызова. Только анализ потока управления может определить эти свойства. Как было рассмотрено в исследовании... Приоритизация проблем статического кода в процессе модернизацииПонимание того, какие участки кода действительно выполняются, как часто они запускаются и при каких условиях активируются, отличает практически применимый анализ от результатов, которые кажутся значительными, но не отражают реальную операционную ситуацию.

Анализ потока данных: отслеживание значений в коде

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

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

В корпоративных системах анализ потоков данных должен преодолевать языковые барьеры, чтобы быть полезным. Значение, которое возникает в программе на COBOL, проходит через операцию записи в базу данных и впоследствии считывается службой Java, представляет собой поток данных, пересекающий границы двух языков. Отслеживание этого потока требует анализа потоков данных, который понимает семантику присваивания в COBOL, перемещение данных в SQL и присваивание переменных в Java как часть одного и того же унифицированного анализа, а не как три отдельных анализа, результаты которых необходимо связывать вручную. Как подробно описано в анализе... Передача знаний от экспертов по COBOL современным командам разработчиков.Способность сделать сложные системы COBOL понятными для современных разработчиков без необходимости освоения языка зависит от наличия структурного анализа, способного представить поведение системы в форме, выходящей за рамки исходного текста.

Задачи, где различия имеют наибольшее значение.

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

Анализ воздействия перед внесением изменений

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

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

Рассмотрим разницу в результатах, которые возвращают эти два подхода при изменении схемы широко используемого столбца базы данных:

Что должен знать разработчикРезультаты текстового поискаРезультат понимания кода
Программы, которые читают эту колонкуВсе файлы, содержащие название столбца, включая комментарии.Только программы, содержащие операторы SQL SELECT, которые ссылаются на этот столбец.
Программы, которые пишут эту колонку.Тот же неотфильтрованный списокТолько программы, использующие операторы SQL INSERT или UPDATE для записи в этот столбец, могут записывать данные.
Услуги, зависящие от этой колонки.Отсутствует межъязыковая видимостьСервисы на Java, Python и .NET, которые сопоставляют столбец с полем объекта.
Ссылки на мертвый кодВключено в результаты, не отмеченоИсключены или отмечены отдельно.
Транзитивные зависимыеНевидимыйПеречислено до любой глубины
Уверенность в полнотеНеизвестныйПроверяется по индексированной области.

Ввод в курс дела и навигация по коду

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

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

Выявление мертвого кода и неиспользуемых элементов

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

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

Аудит безопасности и соответствия нормативным требованиям

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

Поиск по тексту, содержащий имя конфиденциального поля, находит файлы, которые его содержат. Он не может определить, осуществляется ли в этих файлах авторизованный доступ, неавторизованный доступ или вообще никакого доступа. Он не может определить, существует ли проверка контроля доступа в пути выполнения, ведущем к доступу к полю. Он не может отследить, записывается ли значение поля впоследствии в журнал или возвращается в ответе API, который не должен его содержать. Анализ «загрязнения» (taint analysis), который отслеживает поток конфиденциальных значений в системе и определяет, где они могут попасть в ненадежные выходные данные, является возможностью анализа потока данных. Это то, что предоставляют инструменты анализа кода, ориентированные на безопасность, и то, чего не может воспроизвести текстовый поиск.

Как SMART TS XL Обеспечивает понимание кода на уровне всего предприятия.

SMART TS XL В основе системы лежит предположение, что корпоративные системы требуют структурного понимания, а не поиска текста. Ее платформа Software Intelligence анализирует исходный код каждого языка и платформы в среде, создает для каждого языка абстрактные синтаксические деревья и преобразует эти деревья в единый межъязыковой граф, представляющий структурные связи всей системы. Программы COBOL, потоки заданий JCL, службы Java, приложения .NET, скрипты Python, схемы SQL, модули TypeScript и артефакты конфигурации представлены в этом графе в виде узлов и ребер, а связи выражены в виде типизированных соединений: вызовы, потоки данных, включения копибуков, ссылки на схемы и межъязыковые эквивалентности.

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

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

Разница между тем, что SMART TS XL Поиск текста позволяет получить ответ на вопрос и начать новое исследование. Поиск текста начинает расследование, а понимание кода завершает его.

Постоянные издержки замены понимания поиском

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

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

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