Команда работает по Scrum: есть sprint, есть backlog, каждые две недели — демо. Процесс выстроен, но релизы всё равно страшные. Каждое обновление рискует что-то сломать, тесты гоняют вручную перед выкаткой, а после мержа большой ветки полдня уходит на разбор конфликтов. Проблема не в процессе — процесс как раз в порядке. Проблема в том, как написан код: он не готов к тому, чтобы меняться часто и безопасно.
Extreme Programming (XP) — методология, которая закрывает именно этот пробел. Scrum и Kanban отвечают на вопрос «как организовать поток работы»: кто что берёт, когда планируем, как показываем результат. XP отвечает на другой вопрос — «как писать и менять код, чтобы быстрые итерации не превращались в накопление проблем». Это набор инженерных практик, а не церемоний. Разберём, из чего он состоит и почему части этого набора давно стали стандартом индустрии.
Круг слева и его следы справа. Проверку пишут первой — она красная, потому что кода ещё нет. Минимальный код делает её зелёной. Потом код чистят: строк меньше, проверка та же и остаётся зелёной — это и есть доказательство, что поведение не поехало. Дальше круг начинается заново для следующего поведения, а сеть проверок растёт и страхует всё, что чистят потом.
Идея: процесс и инженерия — это разные оси
Легко перепутать XP со Scrum, потому что оба относятся к Agile. Но они про разное и хорошо сочетаются.
Scrum и Kanban управляют потоком задач. Они говорят, как разбить работу, как её приоритизировать, как синхронизировать команду. Они не диктуют, как устроен ваш код. Можно идеально вести доску и при этом иметь код, который невозможно менять без риска.
XP смотрит внутрь кода. Его центральная гипотеза: стоимость изменения не обязана расти со временем. В классической модели чем позже вносишь правку, тем она дороже — потому что код запутан, тестов нет, никто уже не помнит, как это работает. XP утверждает, что при правильных практиках кривая стоимости изменения остаётся пологой, и тогда частые маленькие релизы становятся не риском, а нормой.
Правило получается простое: если что-то полезно делать — делай это постоянно и доводи до предела. Тестирование полезно — значит, пишем тесты на всё и всегда. Интеграция полезна — значит, интегрируемся по несколько раз в день, а не раз в спринт. Отсюда и слово «extreme».
Под практиками у XP лежат ценности — то, ради чего всё это затевалось. В первом издании книги их четыре: общение, простота, обратная связь и смелость; во втором Бек добавил пятую — уважение. Без них практики читаются как произвольный список, а с ними виден общий мотив: разговаривать чаще, делать проще, узнавать раньше и не бояться трогать работающий код.
Практики: как XP делает изменения дешёвыми
XP — это не одна идея, а связка практик, которые усиливают друг друга. Ни одна из них не работает в одиночку так же хорошо, как все вместе.
Test-Driven Development (TDD). Тест пишется раньше кода. Сначала — маленький тест, который описывает ожидаемое поведение и пока падает. Потом — минимальный код, чтобы тест прошёл. Потом — чистка. Цикл называют «red — green — refactor». Смысл не только в покрытии: тест, написанный первым, заставляет продумать интерфейс до реализации и оставляет за собой сеть проверок, которая ловит поломки при каждом изменении.
Вот тот же круг на одном правиле — «бонусами оплачивается не больше половины заказа». В настоящем цикле случаи проводят по одному: тест — код — чистка, потом следующий тест. Здесь три случая свёрнуты в один прогон ради краткости: проверки написаны первыми и дальше не меняются, меняется только расчёт, который в них подставляют:
живой пример
def run(rule):
cases = [((1000, 800), 500), ((1000, 300), 300), ((0, 300), 0)]
for (total, balance), expected in cases:
got = rule(total, balance)
if got != expected:
return f"красный: заказ {total}, баланс {balance} — ждали {expected}, вышло {got}"
return f"зелёный: {len(cases)} из {len(cases)}"
print(run(lambda total, balance: balance))
print(run(lambda total, balance: balance if balance < total // 2 else total // 2))
print(run(lambda total, balance: min(balance, total // 2)))
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →
Первая строка вывода красная: расчёта половины ещё нет, списывается весь баланс. Вторая — минимальный код, третья — он же после чистки: та же логика одним min, а вывод тот же. В этом и смысл тестов вперёд — они не только ловят ошибку, но и разрешают трогать работающий код.
Парное программирование (pair programming). Двое работают за одной задачей: один пишет код, второй думает на шаг вперёд — про краевые случаи, имена, дизайн. Роли меняются. Это непрерывное ревью в реальном времени: ошибки ловятся в момент написания, а не через день в pull request. Плюс знание о коде не запирается в одной голове.
Непрерывная интеграция (Continuous Integration, CI). Каждый вливает свои изменения в общую ветку часто — несколько раз в день, а не копит их неделями. На каждое вливание автоматически собирается проект и прогоняются тесты. Маленькие частые слияния почти не дают конфликтов; редкие большие — дают болезненные.
Рефакторинг. Улучшение структуры кода без изменения его поведения. Переименовать, разбить длинный метод, убрать дублирование. В XP это не отдельная «фаза уборки раз в квартал», а постоянная гигиена: увидел, что стало запутанно — почистил сразу. Именно тесты делают рефакторинг безопасным: если после перестановки код зелёный, поведение не изменилось.
Коллективное владение кодом. Любой участник команды имеет право править любую часть кода. Нет «это модуль Пети, к нему только Петя». Так исчезают точки, где всё встаёт из-за отсутствия одного человека, и любой может провести нужное улучшение там, где его заметил.
Простой дизайн и YAGNI. Принцип «You Aren't Gonna Need It» — не строй абстракцию под гипотетическое будущее требование. Делай самое простое решение, которое работает сейчас и покрыто тестами. Когда требование реально придёт — рефакторинг и тесты позволят безопасно расширить дизайн. Заранее заложенная «гибкость на всякий случай» чаще становится грузом, чем помогает.
Частые маленькие релизы. Выкатывать небольшими порциями и часто. Маленький релиз легче протестировать, легче откатить и быстрее даёт обратную связь от реальных пользователей. Большой редкий релиз копит риск и откладывает момент, когда узнаёшь, что что-то пошло не так.
Это семь практик из двенадцати, описанных в книге. Остальные пять — игра в планирование, метафора системы, единый стандарт кодирования, заказчик рядом с командой и устойчивый темп, то есть сорокачасовая неделя без героических авралов. Последние две не декоративные: если до заказчика не дотянуться за минуту, тесты вперёд кода и частые релизы упираются в недельное ожидание ответа, а без устойчивого темпа вся связка рассыпается первой же авральной неделей — чистку и работу в паре бросают первыми.
Простой дизайн: четыре правила вместо лозунга
«Делай самое простое решение» — это не критерий: два человека спорят о простоте до вечера и оба уверены, что правы. В XP у простого дизайна есть проверяемые правила, и их четыре, в порядке приоритета:
- Проходит все тесты. Дизайн, который не работает, не может быть простым. Это первое правило, а не последнее: простота не оправдывает неполноту.
- Не содержит дублирования. Одно знание живёт в одном месте. Дублируется не только код — дублируется правило: та же проверка в двух местах, та же константа в трёх.
- Выражает намерение автора. Читающий понимает, зачем этот код, из имён и структуры, а не из истории репозитория. Метод
process()намерение не выражает,holdPaymentUntilConfirmed()— выражает. - Минимально по числу элементов. Меньше классов, методов, полей — при выполнении первых трёх правил. Именно здесь живёт YAGNI: лишний уровень абстракции нарушает четвёртое правило, не добавляя к первым трём.
Порядок важен, и он решает споры. Убрать дублирование, сломав тесты, нельзя: правило 1 выше правила 2. Сократить число классов, размазав намерение по всей кодовой базе, нельзя: правило 3 выше правила 4. А вот выбор между «ввести абстракцию» и «повторить третий раз» решается правилом 2 против правила 4 — и обычно побеждает 2, потому что дублирование дороже одного лишнего класса.
Практическая польза правил в том, что спор «это просто или сложно» превращается в четыре вопроса с ответами да/нет. Ревью с таким списком идёт быстрее, а вывод получается один, а не два.
Непрерывная интеграция: два условия, без которых её нет
Конвейер стоит почти везде, и почти везде он сломан в одном из двух мест. Без этих условий команда формально «делает CI» и не получает от него ничего.
Условие первое: сборка быстрая. Ориентир из XP — десять минут на сборку с тестами. Дело не в красивой цифре, а в поведении людей: пока сборка идёт десять минут, разработчик ждёт результат и чинит поломку сразу. Когда она идёт сорок минут, он уходит делать следующую задачу, поломка находится через час, и к этому моменту в ветку влилось ещё три изменения — теперь непонятно, чьё. Двухчасовая сборка означает, что результат приходит после того, как человек уже переключился, и красная сборка живёт до утра. Лечится это разделением: быстрый набор проверок на каждое слияние, медленные интеграционные — отдельным прогоном.
Условие второе: красная сборка чинится немедленно и вперёд всего остального. Правило формулируется жёстко: пока сборка красная, никто не вливает новое. Причина простая: на красной сборке проверка не работает, и все последующие слияния идут без сети. Команда, которая живёт с красной сборкой третий день, не имеет непрерывной интеграции — у неё есть конвейер, который мигает. Практически это означает, что автор ломающего изменения либо чинит его в течение минут, либо откатывает и разбирается отдельно.
Отсюда же понятно, зачем вливаться каждый день, а не раз в неделю. Прикинем на арифметике: команда из шести человек за день трогает десятка два файлов из примерно двухсот, которые сейчас активно меняются. Ветка, прожившая день, пересекается с чужими правками редко — счёт идёт на единицы файлов, и слияние проходит само.
| Ветка живёт | Чужих правок накопилось | Что происходит при слиянии |
|---|---|---|
| несколько часов | единицы файлов | конфликтов обычно нет |
| один день | десятки файлов | один-два конфликта, решаются за минуты |
| одна неделя | больше сотни файлов | конфликты в нескольких местах, полдня работы |
| две недели | почти вся активная часть | конфликты плюс поломки на стыке, которых не видел ни один тест |
Числа здесь иллюстративные, но соотношение устойчивое: стоимость слияния растёт быстрее, чем время жизни ветки, потому что конфликты складываются не только между собой, но и с изменениями смысла. Именно поэтому в XP вливаются несколько раз в день, а не «когда задача готова».
Обратная связь на всех уровнях
Если посмотреть на практики XP вместе, видно общий мотив: сократить время между «сделал что-то» и «узнал, к чему это привело». Чем короче петля обратной связи, тем дешевле ошибка — её ловят, пока она свежая и маленькая.
| Петля обратной связи | Что даёт | Скорость |
|---|---|---|
| Тест (TDD) | сломал поведение — узнал сразу | секунды |
| Пара | плохое имя или краевой случай — заметили при написании | минуты |
| CI | конфликт или красная сборка — узнали при вливании | минуты |
| Маленький релиз | не то поведение для пользователя — увидели быстро | часы–дни |
| Спринт-демо (Scrum) | не ту функцию делаем | недели |
Каждый уровень ловит свой класс проблем и на своём масштабе времени. Тест не заметит, что вы делаете не ту функцию, — это увидит демо. Демо не заметит опечатку в условии — это поймает тест. XP старается, чтобы на каждом масштабе была своя быстрая проверка, а не одна медленная в самом конце.
Что прижилось повсеместно, а что реже
XP сложился на проекте C3 в Chrysler, куда Кент Бек пришёл в 1996-м: практики оформлялись прямо по ходу работы. Книга Бека «Extreme Programming Explained» вышла в 1999-м и звучала тогда радикально. Сам проект в 2000-м закрыли — методология его пережила. С тех пор индустрия «переварила» его практики неравномерно: часть стала стандартом де-факто, часть встречается реже.
Прочно вошли в обиход:
- TDD — как минимум в форме сильной культуры автотестов; писать код без тестов сегодня считается плохим тоном в большинстве команд.
- Непрерывная интеграция — сегодня это база: CI-конвейер, который на каждый коммит собирает проект и гоняет тесты, есть почти везде. Из него выросли CI/CD-практики частой автоматической доставки.
- Рефакторинг — стал обычной частью ежедневной работы, поддержан инструментами в любой IDE.
- Простой дизайн и YAGNI — вошли в общий словарь как здравый принцип против переусложнения.
Применяется реже и выборочно:
- Парное программирование на весь рабочий день — практикуют не все. Оно дорого по времени и утомительно, поэтому чаще используют его точечно: для сложных задач, введения в проект новых людей или разбора запутанного участка. Частичную замену дают асинхронные ревью в pull request.
- Полное коллективное владение — в больших организациях смягчается: часто есть «владельцы» подсистем, отвечающие за архитектурные решения, при этом мелкие правки разрешены всем.
Даже команда, которая не называет себя «XP-командой», почти наверняка живёт на его практиках: TDD, CI, рефакторинг и простой дизайн стали общим фоном инженерной культуры — и именно они делают частые релизы, которые обещает Agile, действительно безопасными.
Чем платят за практики
Разделы выше объясняют, что практики дают. Без второй половины — во что они обходятся — текст читается как проповедь, и первая же встреча с реальностью его опровергает.
TDD удлиняет первый заход. Написать поведение с тестами вперёд дольше, чем написать только код: ориентир — заметно дольше на той же задаче, и разработчик это чувствует сразу, а выигрыш получает через месяцы, при первом изменении. Это неудобный обмен, и его нужно называть вслух, иначе TDD бросают на второй неделе.
Тесты, привязанные к реализации, наказывают за изменения. Главная цена TDD не в написании, а в поддержке: тест, который проверяет, что метод вызвал такой-то метод с такими-то аргументами, ломается при любом рефакторинге, хотя поведение не изменилось. После двух-трёх таких случаев команда начинает бояться трогать код — то есть получает ровно то, от чего тесты должны были защитить. Лечится проверкой поведения через границу модуля, а не внутренних вызовов, но это отдельное умение.
Постоянный рефакторинг требует договорённости с бизнесом. Часть времени уходит не на новые функции, и это видно в сроках. Формулировать это честно: «из недели день уходит на приведение кода в порядок» — рабочая договорённость; «мы почистим, когда будет время» — не рабочая, времени не будет никогда.
Парное программирование стоит двух человек на одну задачу. Оно окупается на сложных задачах и на вводе нового человека, но команда должна понимать, что за это платит: два инженера производят одну задачу, и на простых задачах это чистый убыток.
Коллективное владение требует единого стандарта. Когда любой правит любой код, разнобой в стиле и подходах становится дорогим. Поэтому в списке двенадцати практик стоит единый стандарт кодирования — без него коллективное владение превращается в то, что каждый переписывает чужое под себя.
Связка не разбирается на части безболезненно. Практики опираются друг на друга: рефакторинг без тестов опасен, коллективное владение без стандарта даёт кашу, частые релизы без непрерывной интеграции невозможны. Взять две практики из семи можно, но эффект будет меньше, чем ожидается по описанию каждой, — и это тоже цена.
Где XP не складывается
Предыдущий раздел объясняет, что применяют реже, — через удобство: «дорого и утомительно». Это неполный ответ. Есть условия, при которых практики не работают в принципе, и это важнее привычек.
Команда распределена по часовым поясам. Парное программирование живёт на общем времени. Разница в три часа его ещё терпит, разница в восемь убивает: общего окна почти нет, а пара по видеосвязи в конце чужого дня — не пара. Замена — асинхронное ревью с жёстким временем ответа и совместные разборы сложных участков в редком общем окне.
Доступ к коду разделён требованиями регулятора. Коллективное владение упирается в разделение прав: где действует требование разделения обязанностей, один человек не может и написать, и провести в прод; где данные ограничены по доступу, к части кода допущены не все. Это не лечится уговорами — это внешнее условие, и владение становится частичным по дизайну.
Заказчика рядом нет и не будет. «Заказчик в команде» невозможен, когда заказчик — это тендер, регламент или орган, с которым общаются письмами. Быстрая петля обратной связи от заказчика тогда заменяется на прокси: аналитик с правом решать, зафиксированные критерии приёмки, макеты, согласованные заранее. Петля становится медленнее, и это надо учитывать в плане, а не делать вид, что она есть.
Цена релиза высокая по внешним причинам. Прошивка устройства, мобильное приложение с проверкой в магазине, система, требующая сертификации при изменении: «частые маленькие релизы» упираются не в код, а в процедуру. Тогда часто выкатывают внутрь (стенды, бета-канал, ограниченная группа), а наружу — по расписанию, и разделение выкладки и выпуска становится не удобством, а обязательным условием.
Код изначально без тестов и очень большой. XP предполагает, что тесты есть. На унаследованном коде без тестов правило «увидел запутанное — почисти» опасно: чистить нечем проверить. Порядок другой: сначала тесты на границах того куска, который собираешься менять, потом изменение. Это медленнее, и планировать надо именно так.
Команда меньше трёх или больше двенадцати. Двое не образуют коллективного владения в значимом смысле, а практики пары и стандарта становятся вырожденными. Больше десяти-двенадцати — рассыпается общее понимание кода, и владение приходится ограничивать подсистемами, то есть возвращаться к владельцам участков.
Общий признак: там, где ломается не удобство, а условие — общее время, общий доступ, доступный заказчик, дешёвый выпуск, наличие тестов, — практику не «не осилили», а честно заменили на ближайшую работающую. Разница важна, потому что первая формулировка порождает вину, а вторая — решение.
Глубже: чем процесс подпирается: транковая разработка, флаги и разделение выкладки и выпускарасширенное
«Маленькие частые релизы» звучат в трёх статьях фазы, и без трёх инженерных практик это лозунг: процесс может требовать выкат каждый день, а код не позволит.
Транковая разработка. Все работают в одной ветке, изменения сливаются в неё не реже раза в день, ветки живут часы. Длинные ветки это отложенная интеграция: две недели работы порознь, потом день слияния конфликтов и неделя ловли того, что сломалось на стыке. Условие транковой разработки это тесты, которые идут на каждое слияние, и защита ветки с обязательными проверками, о чём статья про ветвление в разделе CI/CD.
Флаги функций. Незавершённое попадает в главную ветку и в прод выключенным: код есть, поведение включается флагом для внутренних пользователей, потом для доли, потом для всех. Так функция на месяц работы едет в прод по частям, а «релиз» перестаёт быть событием и становится переключением. Флаги требуют дисциплины удаления, иначе через год код состоит из ветвлений.
Выкладка отдельно от выпуска. Выкладка это когда код оказался на сервере; выпуск это когда пользователь увидел поведение. Разделив их флагами и постепенным включением, команда выкладывает каждый день без риска, а выпускает, когда решил бизнес; откат выпуска это выключение флага, откат выкладки редок. Стратегии выкладки (по одной реплике, на процент трафика, две площадки) разбирает статья про стратегии релиза.
Связь с процессом: спринт с целью «показать готовое» держится на том, что готовое уже в проде под флагом, а не «почти готово в ветке»; Kanban с непрерывным потоком невозможен без непрерывной интеграции. XP это понял первым, и практики из этой статьи (непрерывная интеграция, тесты первыми, маленькие релизы) это и есть инженерная опора любого гибкого процесса.
Коротко
- XP отвечает не на вопрос «как организовать работу» (это Scrum и Kanban), а на вопрос «как писать код, чтобы частые изменения были дешёвыми и безопасными». Центральная идея — стоимость изменения не обязана расти со временем; при правильных практиках кривая остаётся пологой.
- Ключевые практики: TDD (тест раньше кода), парное программирование, непрерывная интеграция, рефакторинг, коллективное владение, простой дизайн (YAGNI), частые маленькие релизы.
- Практики усиливают друг друга: тесты делают рефакторинг безопасным, CI ловит конфликты рано, пара даёт ревью в реальном времени. Общий принцип — короткие петли обратной связи: тест реагирует за секунды, пара и CI за минуты, релиз за часы, демо за недели.
- TDD, CI, рефакторинг и YAGNI стали стандартом де-факто; парное программирование на весь день и полное коллективное владение применяют реже — точечно или в смягчённой форме.
- Частые релизы держатся на трёх практиках: транковая разработка с ветками на часы, флаги функций для незавершённого, выкладка отдельно от выпуска; без них гибкий процесс упирается в код.
- Простой дизайн это четыре правила по приоритету: проходит тесты, нет дублирования, выражает намерение, минимально по числу элементов — и порядок решает споры.
- Непрерывная интеграция держится на двух условиях: сборка около десяти минут и красная сборка чинится немедленно, вперёд всего остального; иначе конвейер просто мигает.
- Стоимость слияния растёт быстрее времени жизни ветки: ветка на день даёт один-два конфликта, ветка на неделю — конфликты в нескольких местах и поломки на стыке.
- За практики платят: TDD удлиняет первый заход, тесты по внутренним вызовам наказывают за рефакторинг, чистка требует договорённости с бизнесом, пара это двое на одну задачу.
- XP не складывается там, где ломается условие, а не привычка: разные часовые пояса, разделение доступа по требованию регулятора, недоступный заказчик, дорогой выпуск, код без тестов, команда меньше трёх или больше двенадцати.
Что почитать дальше
- Пирамида тестов — как устроить автотесты так, чтобы TDD давал быструю обратную связь.
- Принципы CI/CD — во что выросла непрерывная интеграция: автоматическая сборка, тесты и доставка на каждый коммит.
- Clean Code — принципы, на которые опирается рефакторинг и простой дизайн.
- Scrum — процессная методология, с которой XP хорошо сочетается: разные оси одной задачи.