Управление устаревшим кодом

Устаревший код: Предупреждение, которое вы будете игнорировать, в конечном итоге всё сломает.

В каждом программном проекте рано или поздно встречается устаревший код, компоненты, помеченные как устаревшие, нежелательные или запланированные к удалению. @deprecated аннотации в Java, DeprecationWarning В Python, например, зачеркивание в автозаполнении вашей IDE — все это сигналы из кода о том, что что-то, что вы используете или поддерживаете, устарело. Игнорирование этих сигналов незаметно накапливает риски до тех пор, пока не будет удалена зависимость, патч безопасности не пропустит устаревший API или обновление фреймворка не сломает все, что все еще зависело от того, что было объявлено устаревшим три основные версии назад.

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

Прекратите обнаруживать устаревшие функции в производственной среде.

SMART TS XL Проверяйте устаревшие компоненты, прежде чем они приведут к инцидентам.

Подробнее

Содержание

Что такое устаревший код?

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

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

Deprecated против Depreciated: орфографическая путаница

Эти два слова часто путают, и программы проверки орфографии не помогают, потому что это настоящие английские слова с разными значениями.

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

Амортизация (в бухгалтерском учете): уменьшение стоимости с течением времени. Например, «оборудование сервера амортизировалось в течение трех лет».

Если в техническом документе вы видите фразу «depreciated code» (устаревший код), это почти всегда означает «deprecated code» (устаревший код), то есть автор использовал бухгалтерский термин, имея в виду термин, относящийся к программному обеспечению. Эта ошибка достаточно распространена, чтобы отразиться в данных Search Console для этой статьи. В программном обеспечении всегда используйте термин «deprecated».

Устаревший код, устаревший код, код, относящийся к категории "мертвый код"

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

СрокЧто это значитОно удалено?Поддерживается ли он?Уровень риска
УстаревшееОфициально не рекомендуется, помечено для удаления в будущем.Нет, всё ещё присутствует.Нет, техническое обслуживание прекращено.Средний размер, увеличивающийся со временем
УстаревшийБольше не актуально и не применимо; устарело.ИногдаНетСредне-высокая
НаследиеСтарый код, который всё ещё работает и, возможно, всё ещё используется в производственной среде.Нет, всё ещё активен.РедкоПеременная, зависит от темпа изменений.
Мертвый кодВо время исполнения приговора с ними никто не связывался и не звонил.Нет, всё ещё в исходном коде.Недоступно, никогда не запускаетсяНизкий/средний уровень риска миграции/аудита
Устаревший кодКод, который долгое время не изменялся, но не был официально объявлен устаревшим.НетНеясноСредний уровень, может скрывать предположения

Устаревший против неактуального«Устарело» — это официальное обозначение, кто-то явно это отметил. @deprecated или выпустили уведомление об устаревании. «Устаревший» — более широкое определение: код может всё ещё работать, но его использование больше нецелесообразно с учётом современных альтернатив. Весь устаревший код в конечном итоге становится устаревшим, но не весь устаревший код был официально объявлен устаревшим.

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

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

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

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

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

Python: DeprecationWarning

питон

import warnings

# Marking a function as deprecated
def old_function():
    warnings.warn(
        "old_function is deprecated, use new_function instead",
        DeprecationWarning,
        stacklevel=2
    )
    # original implementation

def new_function():
    # improved implementation
    pass

В Python предупреждения об устаревании функций отображаются во время выполнения. Наиболее распространенное сообщение компилятора:

DeprecationWarning: old_function is deprecated, use new_function instead

Или для пакетов сторонних производителей:

DeprecationWarning: pkg_resources is deprecated as an API.
Use importlib.resources or importlib.metadata instead.

Java: @Устаревшая аннотация

Ява

public class LegacyProcessor {

    @Deprecated
    public void processData(String input) {
        // old implementation
    }

    // Replacement method
    public void processDataV2(String input, ProcessOptions options) {
        // new implementation
    }
}

Компилятор Java выдает следующий результат:

Note: SomeFile.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

JavaScript/TypeScript: JSDoc @deprecated

Javascript

/**
 * @deprecated Use fetchUserById() instead.
 * This function will be removed in version 4.0.
 */
function getUser(id) {
    // old implementation
}

// Modern replacement
async function fetchUserById(id) {
    // new implementation
}

машинопись

class ApiClient {
    /** @deprecated Use post() with typed options instead */
    sendRequest(url: string): Promise<any> {
        // deprecated implementation
    }
}

IDE отображают getUser с зачеркиванием везде, где это вызывается, и TypeScript @typescript-eslint/no-deprecated Это правило помечает его в CI.

C++: [[устаревший]] Атрибут

CPP

// C++14 and later
[[deprecated("Use processV2() instead")]]
void process(int value) {
    // old implementation
}

void processV2(int value, ProcessFlags flags = ProcessFlags::Default) {
    // new implementation
}

Компиляторы создают:

warning: 'process' is deprecated: Use processV2() instead [-Wdeprecated-declarations]

Swift: @available with deprecated

стриж

@available(*, deprecated, renamed: "fetchUser(withID:)")
func getUser(id: String) -> User {
    // old implementation
}

func fetchUser(withID id: String) -> User {
    // replacement
}

Kotlin/Java: @Deprecated with ReplaceWith

Котлин

@Deprecated(
    message = "Use processItems() instead",
    replaceWith = ReplaceWith("processItems(items)"),
    level = DeprecationLevel.WARNING
)
fun handleItems(items: List<Item>) {
    // deprecated
}

fun processItems(items: List<Item>) {
    // replacement
}

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

Устаревший код — это не просто вопрос поддержания порядка. Он создает конкретные, накапливающиеся риски по четырем направлениям:

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

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

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

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

Как обрабатывать устаревшие зависимости в программном проекте

Шаг 1: Проведение инвентаризации всех амортизационных отчислений.

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

колотить

# Python: find all DeprecationWarning instances
python -W error::DeprecationWarning -m pytest

# JavaScript/Node.js: run with deprecation tracing
node --trace-deprecation app.js

# Java: compile with full deprecation details
javac -Xlint:deprecation *.java

# npm: find deprecated packages
npm outdated
npm audit

Шаг 2: Классификация по степени риска

Не все случаи устаревания требуют немедленных действий. Классифицируйте каждый из них:

приоритетКритерииЭкшн
критическийУстаревшая, критически важная для безопасности библиотека; известная уязвимость CVE; будет удалена в следующей основной версии.Немедленно мигрируйте
ВысокийУстарело в текущей основной версии; в системе непрерывной интеграции отображаются предупреждения.Составьте расписание на текущий или следующий спринт
СреднийУстарело, но по-прежнему поддерживается для 2+ основных версий; не представляет угрозы безопасности.Добавить в бэклог с указанием времени.
НизкийУстаревшая аннотация во внутреннем коде с низкой частотой изменений.Отслеживание и решение проблем в процессе рефакторинга.

Шаг 3: Найдите все варианты использования перед миграцией.

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

питон

# Using grep for basic search
grep -r "old_function" src/

# Using ast-grep for code-aware search (TypeScript/JS)
ast-grep --pattern 'getUser($ID)' --lang ts

# Using ripgrep with file type filtering
rg "deprecated_method" --type java

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

Шаг 4: Систематическая миграция

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

питон

# Before: deprecated
import imp
module = imp.load_source('mymodule', '/path/to/mymodule.py')

# After: replacement
import importlib.util
spec = importlib.util.spec_from_file_location('mymodule', '/path/to/mymodule.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

Javascript

// Before: deprecated event property
document.addEventListener('keydown', (event) => {
    const key = event.keyCode;  // deprecated
});

// After: modern replacement
document.addEventListener('keydown', (event) => {
    const key = event.key;   // current standard
});

Шаг 5: Добавьте механизмы предотвращения устаревания в CI/CD.

Предотвратить попадание новых устаревших вариантов использования в код после очистки:

YAML

# .github/workflows/deprecation-check.yml
- name: Check for deprecated API usage (Java)
  run: javac -Xlint:deprecation -Werror src/**/*.java

- name: Check for deprecated packages (Node)
  run: npm audit --audit-level=moderate

- name: ESLint no-deprecated rule (TypeScript)
  run: npx eslint --rule '{"@typescript-eslint/no-deprecated": "error"}' src/

Разработка политики амортизации

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

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

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

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

Что подразумевается под «удалением» ? Код удален? Перемещен в отдельный необязательный пакет? Скрыт за флагом функции? Четко определите конечный результат.

Как документируется процесс миграции. Каждое примечание об устаревании должно содержать ссылку на замену. @deprecated Use fetchUserById() instead полезнее, чем @deprecated.

Работает ли по-прежнему устаревший код?

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

Ответ на вопрос «безопасно ли использовать устаревший код?» зависит от того, насколько близка дата его удаления и каково состояние безопасности устаревшего компонента. Функция, объявленная устаревшей в минорном релизе активно поддерживаемой библиотеки без известных уязвимостей CVE, представляет низкий непосредственный риск. Устаревшая библиотека аутентификации с неустраненной уязвимостью и объявленной датой окончания поддержки представляет высокий непосредственный риск.

Как SMART TS XL Выявляет устаревший код в корпоративных системах.

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

SMART TS XLАвтора статический анализ кода Сканирует все языки в среде и одновременно выявляет устаревшие аннотации, неактуальные способы использования API и шаблоны мертвого кода по всей кодовой базе. Когда COBOL-копибук помечен как устаревший, SMART TS XL Определяет каждую программу, которая его использует. Когда метод Java API объявляется устаревшим, он отслеживает каждое место вызова во всех сервисах портфеля.

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

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