Агент в старом проекте: как подготовить legacy и внедрять постепенно
AI-First — это не «подключить чат к IDE». Это сделать проект понятным и проверяемым, чтобы агент мог в нём работать. Разберём паттерны для старых проектов и порядок внедрения.
После урока вы сможете
- оценить, насколько ваш проект готов к работе с агентами;
- назвать семь паттернов, которые делают legacy понятным для агента;
- составить план внедрения: какие задачи давать агенту первыми, а какие позже;
- заменить старый модуль по частям, не останавливая систему.
Склад переходит на роботов
Старый склад: коробки где попало, схема только в голове кладовщика. Если запустить туда робота, он будет ездить кругами и ронять полки. Сначала размечают полки, потом ставят датчики, потом пускают робота на простой участок.
Агент работает так же хорошо, как хорошо устроена среда вокруг него. В понятном проекте с быстрыми проверками он полезен. В хаосе — опасен.
От простого к сложному
Агенту нужны карта и обратная связь
Новый разработчик в команде первые недели спрашивает: где что лежит, как запустить, как проверить. У агента та же проблема, только спросить ему некого, а памяти между сессиями нет. Поэтому ответы должны лежать в проекте.
Две вещи решают почти всё:
- Карта. Где модули, какие правила, что нельзя трогать. Это файл правил для агента и короткие README.
- Обратная связь. Одна команда, которая за минуты говорит: «всё хорошо» или «сломано вот тут». Тесты, линтер, проверка типов.
Семь паттернов AI-First
- Карта проекта и правила.
AGENTS.md(илиCLAUDE.md) в корне: как запустить, как проверить, правила кода, запреты. Короткие README в ключевых модулях. - Быстрая обратная связь. Одна команда
make checkилиcomposer check: тесты, линтер, статический анализ. Чем быстрее, тем чаще агент её запускает. - Характеризационные тесты. Перед правкой старого кода фиксируем, как он работает сейчас, даже если странно. Агент правит — тест показывает, что поведение не уехало.
- Границы модулей и контракты. Чем меньше модуль связан с остальными, тем меньше контекста нужно агенту и тем меньше он сломает.
- Генераторы для шаблонного. CRUD, DTO, клиенты API делает генератор, а не агент (урок 6). Быстрее, дешевле, одинаково.
- Спецификации для изменений. Новая функциональность начинается со спеки, а не с «сделай фичу» (урок 5).
- Постепенное внедрение. Сначала безопасные задачи, потом средние, потом сложные. На каждом шаге — метрики.
Пример AGENTS.md для legacy-проекта — шаблон
Коротко и по делу. Агент читает этот файл в начале каждой сессии, поэтому каждая строка стоит токенов.
# Магазин — правила для агента ## Запуск и проверка - Поднять: `docker compose up -d` - Проверить всё: `make check` (тесты + PHPStan + php-cs-fixer, ~3 мин) - Один тест: `vendor/bin/phpunit --filter ИмяТеста` ## Карта - `src/Order/` — заказы и расчёт цены. Владелец: команда Checkout. - `src/Billing/` — платежи. Любая правка — только с тестом. - `legacy/` — старый код. Не рефакторить без характеризационных тестов. ## Правила - Деньги — только в копейках (int), никаких float. - Миграции БД не писать без отдельной задачи. - Не трогать `config/secrets/` и `.env`. ## Готово, если - `make check` зелёный; - на новое поведение есть тест; - в описании PR — что и зачем изменено.
Одна команда «проверь всё» — PHP, JS, Python
{
"scripts": {
"test": "phpunit",
"stan": "phpstan analyse --no-progress",
"cs": "php-cs-fixer fix --dry-run --diff",
"check": ["@cs", "@stan", "@test"]
}
}
{
"scripts": {
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"check": "npm run lint && npm run typecheck && npm test"
}
}
.PHONY: check check: ruff check . mypy src pytest -q
Сделайте вывод коротким: агенту нужны имена упавших тестов и строки ошибок, а не тысяча строк лога.
Как внедрять, не ломая систему и команду
Лестница задач. Сначала агент только читает и объясняет код. Потом пишет тесты и документацию. Потом чинит мелкие баги, где есть тест. Потом делает средние фичи по спеке. Сложные изменения с деньгами и миграциями — последними и с человеком на ключевых шагах.
Удушающее дерево (strangler fig). Старый модуль не переписывают целиком. Ставят перед ним фасад и по одной функции переносят в новый код. Новый код агент пишет по спеке и с тестами, старый тем временем продолжает работать.
Метрики внедрения. Сколько задач решено с агентом, время от задачи до PR, доля откатов и регрессий, стоимость на задачу. Если откатов стало больше — притормозите и усильте проверки.
Люди и процесс. За код агента отвечает человек, который открыл PR. Ревью остаётся. Команде нужно обучение: как ставить задачи, как читать сессию агента, когда остановить его.
Характеризационный тест — PHP, Python, JS
Мы не знаем, правильно ли старый код считает доставку. Но знаем, что сейчас клиенты платят именно так. Фиксируем текущие ответы на реальных входах. Потом агент рефакторит — тест следит, чтобы ничего не уехало.
<?php
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
final class LegacyShippingCharacterizationTest extends TestCase
{
// Значения сняты с текущего прода. Странное (бесплатно при 0 кг) — тоже фиксируем.
public static function cases(): iterable
{
yield 'лёгкая посылка' => [0.5, 'MINSK', 350];
yield 'тяжёлая посылка' => [12.0, 'MINSK', 1890];
yield 'регион' => [2.0, 'BREST', 790];
yield 'вес 0 — так сейчас' => [0.0, 'MINSK', 0];
}
#[DataProvider('cases')]
public function testKeepsCurrentBehaviour(float $kg, string $city, int $expectedKopecks): void
{
self::assertSame($expectedKopecks, legacy_calc_shipping($kg, $city));
}
}
import pytest
from legacy.shipping import calc_shipping
# Значения сняты с текущего поведения. Не «как должно быть», а «как есть».
@pytest.mark.parametrize("kg, city, expected", [
(0.5, "MINSK", 350),
(12.0, "MINSK", 1890),
(2.0, "BREST", 790),
(0.0, "MINSK", 0), # странно, но так сейчас работает прод
])
def test_keeps_current_behaviour(kg, city, expected):
assert calc_shipping(kg, city) == expected
import { test } from "node:test";
import assert from "node:assert/strict";
import { calcShipping } from "../legacy/shipping.js";
const cases = [
[0.5, "MINSK", 350],
[12.0, "MINSK", 1890],
[2.0, "BREST", 790],
[0.0, "MINSK", 0], // фиксируем как есть
];
for (const [kg, city, expected] of cases) {
test(`доставка ${kg} кг в ${city}`, () => {
assert.equal(calcShipping(kg, city), expected);
});
}
Оцените свой проект и спланируйте внедрение
Насколько проект готов к агентам
интерактив 1 из 4Ответы заполнены примером «типичный legacy-проект». Поменяйте их под свой проект и нажмите «Оценить».
Лестница внедрения: какие задачи и когда
интерактив 2 из 4Что дают вложения в проект
интерактив 3 из 4Двигайте ползунки и давайте агенту 50 задач. Модель — иллюстрация: коэффициенты подобраны, чтобы показать направление, а не точные цифры.
Strangler fig: заменяем модуль скидок по частям
интерактив 4 из 4скидки внутри OrderService, 2 000 строк
по спеке, с тестами
Песочницы с автопроверкой
Код выполняется в браузере. Ctrl+Enter — запуск. Ваш код сохраняется.
Песочница 1. Оценка готовности проекта
JSОтветы — числа 0 (нет), 1 (частично), 2 (да) по областям. Уже объявлены константы WEIGHTS (вес области) и PLAN_STEPS (первый шаг для области). Посчитайте оценку и план.
const WEIGHTS = { docs: 2, tests: 3, feedback: 2, modules: 1, safety: 3, env: 1 };
const PLAN_STEPS = { docs: '…', tests: '…', feedback: '…', modules: '…', safety: '…', env: '…' };
Песочница 2. Разложить бэклог на волны
JSРешите, какие задачи давать агенту сначала. Риск задачи: деньги +3, миграции +2, размер S/M/L — +0/+1/+2, нет тестов +2. Волна 1 — риск 0–1, волна 2 — риск 2–3, волна 3 — риск 4 и выше (там нужна спека и человек).
Проверьте себя
Команда решила внедрять агентов в старый проект. С чего начать?
Без карты агент блуждает, без проверок — ломает незаметно. Это фундамент, на котором работают все следующие шаги.
Что такое характеризационный тест?
Мы не всегда знаем, как «правильно». Но знаем, как работает сейчас. Тест не даст агенту случайно это поменять.
Как работает паттерн strangler fig?
Замена идёт маленькими шагами. Каждый шаг можно проверить и откатить. Когда старый код больше не получает трафик, его удаляют.
Какая метрика первой покажет, что внедрение агентов вредит?
Объём кода и активность ничего не говорят о качестве. Откаты и регрессии — прямой сигнал, что проверки слабее, чем скорость.
Задание на своём проекте — 1 час
- Пройдите самооценку выше честно. Запишите три самые слабые области.
- Напишите первую версию
AGENTS.mdпо шаблону из урока. Не больше 40 строк. - Сделайте команду
check. Засеките, сколько она идёт. - Возьмите 10 задач из бэклога и разложите на волны функцией из песочницы 2. Первую задачу из волны 1 отдайте агенту.
Отметьте, что усвоили
- Понимаю: AI-First — это понятный и проверяемый проект, а не чат в IDE.
- Знаю, что писать в AGENTS.md и почему он должен быть коротким.
- У меня есть или я знаю, как сделать одну команду «проверь всё».
- Могу объяснить, зачем характеризационные тесты перед правкой legacy.
- Знаю порядок внедрения: чтение, безопасные правки, фичи по спеке, сложное.
- Могу объяснить strangler fig на примере.
- Знаю анти-паттерны: прод-доступ агенту, «большой взрыв», внедрение без тестов.
Что почитать и где попробовать
Материалы на русском отмечены зелёной меткой. Все ссылки проверены 18 октября 2026 года.
Почитать
- Ты приходишь в проект, а там легаси…пошагово: smoke-тесты как страховка, удаление мёртвого кода, статический анализ. рус.
- Чему можно научиться у фикуса-душителя? Паттерн Stranglerstrangler fig на примере сервиса доставки, смена хранения в три этапа. рус.
- Как ИИ помогает разгрести легаси и ускорить модернизациюИИ как инструмент понять старую систему, а не только писать код. рус.
- Лучшие практики для Claude Codeофициально: как дать агенту способ проверить себя и как вести его по большому проекту. рус., офиц.
- AGENTS.mdоткрытый формат «README для агентов»: что положить в репозиторий, чтобы агент понимал проект. англ.
Песочницы по теме
- Rector Demoавтоматический детерминированный рефакторинг старого PHP-кода: вставить код и посмотреть дифф.
- PHPStan Playgroundподнимать уровень строгости шаг за шагом, как при постепенном внедрении на легаси.
- 3v4l.orgпрогнать один фрагмент на разных версиях PHP и зафиксировать текущее поведение перед правкой.
Запустить код из урока
- TypeScript PlaygroundJS и TS из примеров урока: вставить и запустить кнопкой Run.
- 3v4l.orgPHP-примеры урока на любой версии PHP.
- JupyterLitePython-примеры урока прямо в браузере, без установки.