# Deprecation And Migration

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

- Skill: `aleksandr-litvinenko/deprecation-and-migration` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aleksandr-litvinenko/deprecation-and-migration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aleksandr-litvinenko/deprecation-and-migration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: Aleksandr-Litvinenko (https://skillmd.com/u/aleksandr-litvinenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aleksandr-litvinenko/deprecation-and-migration

---


# Вывод из эксплуатации и миграция

## Обзор

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

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

## Когда применять

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

## Базовые принципы

### Код — это обуза

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

### Закон Хайрама делает удаление трудным

При достаточном числе пользователей от любого наблюдаемого поведения кто-то начинает зависеть — включая баги, причуды таймингов и незадокументированные побочные эффекты. Именно поэтому вывод из эксплуатации требует активной миграции, а не только объявления. Пользователи не могут «просто переключиться», когда они зависят от поведения, которое замена не воспроизводит.

### Планирование вывода из эксплуатации начинается при проектировании

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

## Решение о выводе из эксплуатации

Прежде чем что-либо выводить, ответь на эти вопросы:

```
1. Даёт ли эта система до сих пор уникальную ценность?
   → Если да, поддерживай. Если нет, продолжай.

2. Сколько пользователей/потребителей от неё зависят?
   → Оцени объём миграции в числах.

3. Существует ли замена?
   → Если нет, сначала построй замену. Не выводи из эксплуатации без альтернативы.

4. Какова стоимость миграции для каждого потребителя?
   → Если тривиально автоматизируется — делай. Если вручную и трудоёмко — взвесь против стоимости сопровождения.

5. Какова постоянная стоимость сопровождения, если НЕ выводить?
   → Риск безопасности, время инженеров, упущенные возможности из-за сложности.
```

## Рекомендательный и принудительный вывод из эксплуатации

| Тип | Когда применять | Механизм |
|------|-------------|-----------|
| **Рекомендательный** | Миграция необязательна, старая система стабильна | Предупреждения, документация, подталкивания. Пользователи мигрируют в своём темпе. |
| **Принудительный** | У старой системы есть проблемы с безопасностью, она блокирует развитие или стоимость её сопровождения непосильна | Жёсткий срок. Старая система будет удалена к дате X. Предоставь инструменты миграции. |

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

## Процесс миграции

### Шаг 1: построй замену

Не выводи из эксплуатации без работающей альтернативы. Замена должна:

- Покрывать все критичные сценарии старой системы
- Иметь документацию и руководства по миграции
- Быть проверенной в продакшне (а не «теоретически лучшей»)

### Шаг 2: объяви и задокументируй

```markdown
## Уведомление о выводе из эксплуатации: OldService

**Статус:** выведен из эксплуатации с 2025-03-01
**Замена:** NewService (см. руководство по миграции ниже)
**Дата удаления:** рекомендательно — жёсткого срока пока нет
**Причина:** OldService требует ручного масштабирования и лишён наблюдаемости.
             NewService решает и то, и другое автоматически.

### Руководство по миграции
1. Замените `import { client } from 'old-service'` на `import { client } from 'new-service'`
2. Обновите конфигурацию (примеры ниже)
3. Запустите скрипт проверки миграции: `npx migrate-check`
```

### Шаг 3: мигрируй инкрементально

Переводи потребителей по одному, а не всех сразу. Для каждого потребителя:

```
1. Найди все точки соприкосновения с выводимой системой
2. Переведи на замену
3. Убедись, что поведение совпадает (тесты, интеграционные проверки)
4. Убери ссылки на старую систему
5. Подтверди отсутствие регрессий
```

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

### Шаг 4: удали старую систему

Только после того, как мигрировали все потребители:

```
1. Убедись, что использования нет (метрики, логи, анализ зависимостей)
2. Удали код
3. Удали связанные тесты, документацию и конфигурацию
4. Удали уведомления о выводе из эксплуатации
5. Порадуйся — удаление кода это достижение
```

## Паттерны миграции

### Паттерн «душитель»

Держи старую и новую системы параллельно. Постепенно переводи трафик со старой на новую. Когда старая система обслуживает 0% трафика, удали её.

```
Фаза 1: новая система обслуживает 0%, старая — 100%
Фаза 2: новая система обслуживает 10% (канареечный запуск)
Фаза 3: новая система обслуживает 50%
Фаза 4: новая система обслуживает 100%, старая простаивает
Фаза 5: удалить старую систему
```

### Паттерн «адаптер»

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

```typescript
// Адаптер: старый интерфейс, новая реализация
class LegacyTaskService implements OldTaskAPI {
  constructor(private newService: NewTaskService) {}

  // Старая сигнатура метода, делегирует новой реализации
  getTask(id: number): OldTask {
    const task = this.newService.findById(String(id));
    return this.toOldFormat(task);
  }
}
```

### Миграция через фича-флаг

Используй фича-флаги, чтобы переводить потребителей со старой системы на новую по одному:

```typescript
function getTaskService(userId: string): TaskService {
  if (featureFlags.isEnabled('new-task-service', { userId })) {
    return new NewTaskService();
  }
  return new LegacyTaskService();
}
```

### Миграции схемы БД (расширение/сжатие)

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

```
РАСШИРЕНИЕ ──────────→ МИГРАЦИЯ ──────────────→ СЖАТИЕ
добавить новую колонку, заполнить существующие   когда старую колонку
nullable, рядом со       строки, писать в обе     никто не читает, удалить
старой                   из приложения            её отдельным деплоем позже
```

**Разобранный пример — переименование `name` в `full_name`:**

1. **Расширение.** Добавь `full_name` как nullable. Выкати. (Старый код её игнорирует, ничего не ломается.)
2. **Двойная запись.** Приложение пишет и `name`, и `full_name` при каждой вставке/обновлении. Выкати.
3. **Заполнение.** Скопируй `name → full_name` для существующих строк порциями, чтобы не блокировать таблицу.
4. **Переключение чтения.** Направь приложение на `full_name`, продолжая писать в обе. Выкати и дай отстояться.
5. **Сжатие.** Прекрати писать в `name`, а затем — *отдельным, более поздним* деплоем — удали колонку.

Каждый шаг выкатывается и откатывается независимо: если шаг 4 повёл себя плохо, откати код, и `full_name` продолжит заполняться. Относись к каждой фазе как к тонкому вертикальному срезу — см. скилл `incremental-implementation`.

**Правила:**
- **Сначала аддитивное, разрушительное — последним и в одиночку.** Добавления (новая nullable-колонка, новая таблица, новый индекс) безопасны в любом деплое; удаления и переименования получают свой деплой *после* того, как код перестал ссылаться на старую форму.
- **У каждой миграции есть проверенный путь отката.** Миграция, которую нельзя обратить, — это деплой, который нельзя откатить. Напиши и прогони `down` до вливания.
- **Заполняй порциями, вне горячего пути.** Один `UPDATE` по миллионам строк блокирует таблицу; дроби и притормаживай.
- **Строй большие индексы без блокировки записи** (например, `CREATE INDEX CONCURRENTLY` в Postgres).
- **Разделяй с кодом через фича-флаг**, когда переключение рискованно, ровно как в паттерне миграции через фича-флаг выше.

## Зомби-код

Зомби-код — это код, которым никто не владеет, но от которого все зависят. Он не сопровождается активно, у него нет ясного владельца, и он накапливает уязвимости и проблемы совместимости. Признаки:

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

**Реакция:** либо назначить владельца и нормально сопровождать, либо вывести из эксплуатации с конкретным планом миграции. Зомби-код не может оставаться в подвешенном состоянии: он получает либо вложения, либо удаление.

## Типовые самооправдания

| Самооправдание | Как на самом деле |
|---|---|
| «Оно ещё работает, зачем удалять?» | Работающий код, который никто не сопровождает, накапливает долг по безопасности и сложность. Стоимость сопровождения растёт незаметно. |
| «Может, кому-то ещё понадобится» | Если понадобится позже — можно построить заново. Держать неиспользуемый код «на всякий случай» дороже, чем построить его снова. |
| «Миграция слишком дорогая» | Сравни стоимость миграции со стоимостью сопровождения за 2–3 года. В долгую миграция обычно дешевле. |
| «Выведем из эксплуатации, когда доделаем новую систему» | Планирование вывода начинается при проектировании. К моменту готовности новой системы у вас будут новые приоритеты. Планируй сейчас. |
| «Пользователи мигрируют сами» | Не мигрируют. Дай инструменты, документацию и стимулы — или сделай миграцию сам (правило оттока). |
| «Мы можем поддерживать обе системы сколько угодно» | Две системы, делающие одно и то же, — это двойные затраты на сопровождение, тестирование, документацию и ввод новых людей. |
| «Просто переименуй колонку, это одна строка» | Во время выкатки старый и новый код работают вместе — один из них обратится к несуществующей колонке. Расширение/сжатие, никогда не переименование на месте. |
| «Добавлю колонку и удалю старую в той же миграции» | Это связывает безопасное добавление с разрушительным удалением. Удаления получают свой деплой, после того как код перестал ссылаться на старую форму. |
| «Напишем откат, если понадобится» | Миграция без пути отката — это деплой, который нельзя обратить. Напиши и прогони `down` до вливания. |

## Тревожные признаки

- Выводимые из эксплуатации системы, для которых нет замены
- Объявления о выводе из эксплуатации без инструментов миграции и документации
- «Мягкий» вывод, который годами остаётся рекомендательным без всякого прогресса
- Зомби-код без владельца, но с активными потребителями
- Новая функциональность, добавляемая в выводимую систему (вкладывайся в замену)
- Вывод из эксплуатации без измерения текущего использования
- Удаление кода без проверки, что активных потребителей нет
- Изменение схемы и зависящий от него код, выкаченные одним деплоем
- Колонка, переименованная или удалённая на месте, а не через расширение/сжатие
- Миграция, влитая без проверенного пути отката, или заполнение, блокирующее таблицу

## Проверка

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

- [ ] Замена проверена в продакшне и покрывает все критичные сценарии
- [ ] Есть руководство по миграции с конкретными шагами и примерами
- [ ] Все активные потребители мигрированы (подтверждено метриками/логами)
- [ ] Старый код, тесты, документация и конфигурация полностью удалены
- [ ] В кодовой базе не осталось ссылок на выведенную систему
- [ ] Уведомления о выводе из эксплуатации удалены (они выполнили своё назначение)

После миграции схемы БД:

- [ ] Изменение выкатывается аддитивными фазами (расширение → заполнение → сжатие), а не одной правкой на месте
- [ ] И старый, и новый код корректны относительно схемы на каждом шаге выкатки
- [ ] У каждой миграции есть проверенный путь отката; заполнение идёт притормаживаемыми порциями
- [ ] Разрушительные шаги (удаление/переименование) выкатываются отдельным деплоем после того, как код перестал ссылаться на старую форму

