Когда твой проект живёт уже не первый год, кодовая база разрослась, а каждое изменение напоминает поход по минному полю — это сигнал: пришло время что-то менять.
Но полная переписка — почти всегда провал. Время, риски, несовместимость, десятки регрессий, а потом ещё и «почему это не работает, ведь раньше работало»…
Паттерн Strangler Fig предлагает совсем другой путь: медленно, безопасно и осознанно обновлять систему, сохраняя рабочее состояние на каждом этапе.
🧬 Что такое «Удушающая смоковница» и откуда это название
Термин предложил Мартин Фаулер (тот самый, из ThoughtWorks и книги по рефакторингу).
Он заметил аналогию с деревом — смоковницей, которая растёт рядом с другим деревом, оплетает его корнями, и постепенно замещает, пока старое не погибает.
Но при этом процесс идёт плавно и естественно — лес продолжает жить, экосистема не рушится.
То же самое мы можем сделать с нашей кодовой базой.
Не нужно уничтожать старое приложение — просто постепенно «обрастите» его новым, пока старое само не станет ненужным.
🧩 Проблема: почему просто «переписать» — почти всегда плохая идея
Представь старое приложение, которое:
- работает в продакшене уже 5+ лет;
- обслуживает тысячи пользователей;
- хранит тонны данных;
- интегрировано с десятками внешних сервисов;
- и написано на PHP 7.3, с кучей устаревших зависимостей.
Ты хочешь перейти на Laravel 11, добавить микросервисы, Docker, Horizon и всё остальное «по красоте».
Но вот что реально произойдёт при попытке «всё переписать с нуля»:
- ⚠️ Потеря фич (новая версия не покрывает всё, что было);
- ⚠️ Миграция данных превращается в боль;
- ⚠️ Тестирование — сплошная импровизация;
- ⚠️ Продакшен стоит на паузе, пока всё не готово;
- ⚠️ Команда делится на “старых” и “новых” — конфликт поколений в коде.
Итог — через 6 месяцев проект всё ещё «почти готов», а бизнес ждёт.
Strangler Fig решает всё это простым принципом:
“Не трогай то, что работает. Замени постепенно”.
🧠 Суть паттерна Strangler Fig
Идея проста:
- Создаём прослойку между пользователем и старой системой (например, через API Gateway или Reverse Proxy).
- Для новых частей функционала — направляем запросы в новую систему.
- Для старых — всё работает как раньше.
- Когда модуль полностью перенесён — переключаем маршрут.
В итоге новая система растёт рядом со старой, забирая на себя всё больше ответственности.
⚙️ Как это выглядит архитектурно
Пользователь
│
▼
[ Gateway / Proxy Layer ]
├── /api/v1/... → Legacy Laravel
└── /api/v2/... → New Laravel 11 / Microservices
Ты можешь использовать:
- Nginx / Traefik — если это HTTP-приложение;
- API Gateway (например, Kong, Laravel Octane Gateway, Laravel Reverb + Routes Proxy);
- или даже встроенный router middleware в Laravel.
🧩 Пример на Laravel и Nginx
1️⃣ Добавляем слой маршрутизации
server {
listen 80;
server_name api.example.com;
location /api/v2/ {
proxy_pass http://new-api:8000; # Новый Laravel 11
}
location /api/ {
proxy_pass http://legacy-app:8080; # Старый Laravel 7
}
}
Теперь:
/api/v2/— обслуживается новым приложением,/api/— идёт на старую систему.
Можно разворачивать новую архитектуру по кускам, не ломая продакшен.
2️⃣ Создаём новые маршруты
В новом Laravel 11:
// routes/api.php
Route::prefix('v2')->group(function () {
Route::get('/users', [App\Http\Controllers\V2\UserController::class, 'index']);
});
А старое приложение по-прежнему обслуживает /api/users.
Постепенно фронтенд начинает использовать /api/v2/users, и старый эндпоинт можно выключить.
3️⃣ Переносим бизнес-логику по кускам
Например, сервис работы с пользователями:
Было:
// LegacyUserService.php
public function getAll() {
return DB::table('users')->get();
}
Стало:
// NewUserService.php
use App\Models\User;
public function getAll() {
return User::query()
->select('id', 'name', 'email')
->where('active', true)
->get();
}
Постепенно переносим не только код, но и структуру данных — например, добавляем миграции в новую БД.
4️⃣ Используем адаптеры для совместимости
Если фронтенд ожидает старый формат ответа — сделаем адаптер:
// UserTransformer.php
public static function legacyFormat(User $user): array
{
return [
'userId' => $user->id,
'userName' => $user->name,
'email' => $user->email,
];
}
Так новый код может подменить старый, не ломая интерфейсы.
🧭 Этапы внедрения паттерна
| Этап | Описание | Цель |
|---|---|---|
| 1 | Определяем границы старого приложения | Понять, что можно изолировать |
| 2 | Добавляем gateway / прокси | Возможность направлять трафик по маршрутам |
| 3 | Переписываем первый модуль | Проверка подхода на практике |
| 4 | Накатываем метрики и фичефлаги | Контроль за переключениями |
| 5 | Переходим модуль за модулем | Безболезненная миграция |
| 6 | Удаляем legacy | Чистая архитектура |
🧩 Варианты реализации
🔹 HTTP (API)
Самый частый вариант — как в примере выше.
Трафик маршрутизируется по URL.
🔹 Модульно внутри монолита
Можно делать это даже внутри одного Laravel-приложения:
- Старый код — в
app/Legacy/... - Новый — в
app/New/... - В роутинге — решаем, куда направить запросы.
🔹 Через Events / Messaging
В микросервисной архитектуре можно перехватывать события (например, RabbitMQ, Kafka) и обрабатывать их уже новой системой.
🧰 Практические советы
🧩 1. Выбирайте правильные границы
Не нужно сразу трогать сложные части (например, биллинг или авторизацию).
Начните с модулей, где меньше зависимостей: отчёты, публичное API, внутренние сервисы.
⚙️ 2. Используйте фичефлаги
Laravel Pennant идеально подходит для переключения старого и нового кода:
if (Feature::active('new_profile')) {
return $this->newProfileHandler();
}
return $this->legacyProfileHandler();
Можно включать новую реализацию только для тестовых пользователей.
🔄 3. Shadow Traffic
Очень крутая техника:
Пусть Gateway отправляет копию реальных запросов в новый сервис (без влияния на пользователя).
Так можно протестировать, что ответы совпадают — до того, как переключишь трафик.
📊 4. Метрики и логирование
Собирайте:
- количество запросов к каждому маршруту;
- время ответа;
- ошибки / исключения.
Инструменты:
- Horizon, Telescope, Sentry, Prometheus.
🔐 5. Храните данные централизованно
На первых порах лучше, чтобы новый и старый код использовали одну БД.
Когда всё стабилизируется — можно разделить на микросервисы и базы.
🚀 Результаты, которых можно достичь
- Никакого даунтайма;
- Всегда рабочее приложение;
- Постепенная миграция (без шока для команды);
- Возможность отката;
- Параллельная работа legacy и нового кода.
Это реальный путь эволюции, а не революции в коде.
🔮 Бонус: связка с другими паттернами
Паттерн Strangler Fig часто работает в паре с другими:
- Adapter — для сохранения интерфейсов при миграции;
- Proxy / Gateway — для маршрутизации трафика;
- Façade — для унификации старого и нового API;
- Event Sourcing — для плавного перехода на новую модель данных;
- Feature Flags — для динамического включения нового кода.
Всё это вместе даёт невероятно мощную стратегию безопасной трансформации архитектуры.
🧭 Резюме
| Что делает Strangler Fig | Почему это важно |
|---|---|
| Позволяет переписывать частями | Без остановки системы |
| Сохраняет старую логику | Нет потери бизнеса |
| Даёт контроль и метрики | Можно замерять эффект |
| Готовит систему к микросервисам | Эволюционный переход |
| Подходит даже для малого проекта | Простая реализация |
🌱 Заключение
Паттерн «Удушающая смоковница» — это про зрелость.
Он учит не рушить, а заменять, не спешить, а выстраивать.
Вы не переписываете проект — вы растите новый поверх старого, пока он не станет самостоятельным.
Самый надёжный способ переписать систему — никогда не делать это «в один день».