Абстракции для реализаций мертвы. Да здравствуют абстракции для проектирования
Благодаря искусственному интеллекту код удешевился. Но ошибочные архитектурные решения остаются такими же дорогими, как и раньше.
Время от времени я спорю сам с собой о том, какие аспекты программной инженерии остались особенно важными в наше время, когда ИИ бегло пишет код. Вывод, к которому я прихожу, не понравится многим опытным разработчикам. Паттерны реализации, на усвоение которых мы потратили годы, стремительно обесцениваются. Но что насчёт умения структурировать системы в духе правильного проектирования? Это умение становится всё более, а не менее востребованным.
Давайте объясню это на конкретном примере. Система электронных продаж, написанная на Java — как раз такая штука, с которой большинство бэкендеров сталкивались хотя бы раз на протяжении карьеры.
Чем ИИ завтракает
Java-разработчики отдавали целые годы, а иногда и всю карьеру на освоение паттернов и фреймворков, существующих только для одной цели: облегчить когнитивную нагрузку, с которой приходится иметь дело при написании кода.
Рассмотрим паттерн «Стратегия» для движка, управляющего ценообразованием:
public interface PricingStrategy { BigDecimal calculate(CartItem item, Customer customer); } public class SeasonalPricingStrategy implements PricingStrategy { … } public class LoyaltyPricingStrategy implements PricingStrategy { … } public class BulkPricingStrategy implements PricingStrategy { … }
Разработчику приходится попотеть, решая: “Какой паттерн здесь следует использовать — Стратегию или Цепочку Обязанностей? А, может быть, Компоновщик?” Теперь вы сообщаете ИИ-агенту: “примени сезонные скидки, карту лояльности и оптовую скидку, именно в такой последовательности” — и он сгенерирует любой подходящий паттерн, либо вообще обойдётся без паттерна, напишет просто чистый процедурный код. Выбор паттерна никогда не был сложной частью задачи. Это был механизм, призванный упростить саму процедуру набора кода.
Задумаемся о том, почему существует паттерн Стратегия. На уровне бизнеса проблема «примени три скидки в таком порядке» — тривиальна. Далёкий от программирования человек мог бы объяснить её в одном предложении. Сложность как таковая проблемы не представляет. Она заложена в самом программировании на Java. У вас есть класс, отвечающий за ценообразование. Вы добавляете в него второе правило. Затем третье. Без паттернов такой код превращается в 200-строчный кошмарный if-else. Поэтому вы извлекаете интерфейс, создаёте реализации, связываете всё это. Теперь код стал «чистым».
Но чего именно вы добились? Бизнес-задачу вы не решили. Вы преодолели когнитивную проблему: помогли вашему мозгу, который не мог нормально удерживать в памяти 200 строк ветвящейся логики. Поэтому паттерн дробит этот код на мелкие порции и снабжает их этикетками.
Здесь всё равно можно поспорить о принципе открытости-закрытости и о других принципах, но не в этом суть (вы всё равно можете навязать свой стиль, соответствующим образом формулируя промпты и развивая агента).
Паттерны похожи на таблетки для усиления оперативной памяти, они упрощают поддержку кода с точки зрения программиста, но никак не упрощают пользователю процесс оформления заказа.
Представьте, что вы могли бы взглянуть на код длиной 10 000 строк — и мгновенно его понять. Затем вы могли бы модифицировать любую его часть, не привнося в этот код багов, а также одновременно удерживать в голове все предусмотренные в коде взаимодействия. Разве в таком случае вы утруждали бы себя созданием отдельных классов SeasonalPricingStrategy, LoyaltyPricingStrategy и BulkPricingStrategy? Либо написали бы всю логику напрямую, поскольку так она получится проще, и в ней ничем не придётся «управлять»?
Это принципиально отличается от таких решений из области проектирования как «информация о заказах и складских запасах должна поступать асинхронно». Здесь речь не о том, «как справиться». Это констатация того, как устроена бизнес-логика, как должен распространяться отказ, и где именно обнаруживаются узкие места при масштабировании.
Это будет истинно независимо от того, кто напишет код — человек, агент или тысяча обезьян. Абстракции реализации полезны тому, кто пишет код. Абстракции проектирования служат системе.
Вот в чём суть: итерации почти ничего не стоят. Вы просите ИИ написать движок для ценообразования. Он сгенерирует код. Уйдут секунды на то, чтобы его протестировать. Вы напишете: «Попробуй другой вариант». Цикл обратной связи занимает от нескольких секунд до нескольких минут. Стоимость ошибки нулевая. Вы просто заново сгенерируете код.
Ошибки, стоимость которых исчисляется месяцами
А теперь давайте рассмотрим такие решения, при которых ошибки не исправить, просто сгенерировав новый код. За такие ошибки приходится платить.
Границы предметной области
Когда клиент оформляет заказ, резервируете ли вы соответствующий товар на складе синхронно с этой операцией, внутри транзакции заказа? Или выдаёте событие OrderPlaced, в ответ на которое «склад» должен асинхронно отреагировать?
Синхронный путь
OrderService → InventoryService.reserve() → PaymentService.charge(). Всё в одной транзакции. Но блокировка склада превращается в узкое место, если приходится во время экспресс-распродажи одновременно обслуживать 10K конкурентных заказов.
Событийно-ориентированный подход
OrderService порождает OrderPlaced. На это реагирует склад. Реагирует система платежей. Получается слабосвязанный код, который хорошо масштабируется. Но для такого подхода требуется писать саги, компенсационную логику, а клиент может увидеть «Заказ сформирован» — а зарезервировать товар на складе не удастся, причём, эта операция откажет только через 200 мс.
Никакой ИИ не примет за вас таких решений. Они зависят от ваших SLA, закономерностей вашего трафика, от того, насколько ваш бизнес устойчив к отказам из области пограничных случаев, а также от того, есть ли у вас команда девопсов, которые могут обеспечить согласованность в конечном счёте.
Владение данными
Кто владеет «прайсом продукта»? Базовая цена содержится в сервисе Catalog. В сервисе Promotions содержатся действующие скидки. Сервис Pricing вычисляет итоговую стоимость. Сервис Orders делает фиксирует цену в виде мгновенного снимка, когда начинается оформление заказа. Сервису Analytics требуется знать, какова была цена в момент продажи. Это проблема абстрагирования: откуда брать информацию о цене как из источника истины, в какой именно момент, и как эта истина просачивается через систему?
Границы согласованности
Корзина → Заказ → Платёж → Склад → Доставка. Где провести границу ACID? Если Платёж и Склад реализованы в виде отдельных сервисов с отдельными базами данных, то не обойтись без обеспечения согласованности в конечном счёте. Что, если платёж пройдёт успешно, но зарезервировать товар на сладе не удастся? Вы автоматически вернёте средства покупателю? Поставите операцию в очередь для повторной попытки? Оповестите человека?
Эволюция схемы
Сегодня вы продаёте вещи, а завтра — цифровые загрузки. В следующем квартале — товары по подписке. Либо вы сможете аккуратно приспособить схему вашего продукта к этим изменениям, либо будете вынуждены пойти на миграцию, на время которой вся команда окажется в оффлайне.
Изоляция отказавшего домена
Если ваша страница с подробным описанием товара синхронно вызывает рекомендательный сервис, а этот сервис не отвечает — сорвётся ли загрузка страницы целиком? Либо отказ будет аккуратным? Здесь не идёт речь о реализации предохранителя. ИИ справится с этим тривиально — задействует Resilience4j. Здесь же наиболее решить, какие именно сервисы расположены на критическом пути выполнения, и как будет с точки зрения бизнес-логики выглядеть поведение каждого из них в процессе деградации.
ИИ может реализовать какой угодно фреймворк для работы с сагами, предохранитель или стратегию кэширования на ваш выбор, а вам на этот выбор могут потребоваться считанные минуты. Но гораздо сложнее определиться с тем, где пролегают границы согласованности, кто именно владеет данными, как именно должна деградировать система. Для этого нужно понимать бизнес-логику, трафик, а также знать реальные условия, в которых эксплуатируется приложение.
Контраргументы (и почему они отчасти справедливы)
Контраргумент #1
“Можно просто спрашивать ИИ, как это спроектировать — итерация за итерацией.”
ИИ феноменально хорош в качестве партнёра-мыслителя на этапе, когда требуется исследовать возможности проектирования. Но здесь есть критически важная асимметрия: итерации при реализации обходятся дёшево, итерации при проектировании обходятся запредельно дорого.
Вы спрашиваете ИИ: «как организовать коммуникацию между «заказами» и «складом» — синхронно или асинхронно?». Он отвечает: «Асинхронно». Вы собираете. Три месяца спустя наступают самые крупные продажи в году, вы обнаруживаете дыру в механизме согласованности в конечном счёте только после того, как система успеет продать 500 единиц товара (и принять за них оплату), тогда как на складе этих 500 единиц нет. Эту ситуацию не исправить, если «повторно сгенерировать код». Здесь требуется переосмысливать границы вашей предметной области, переписывать сагу, может быть, даже менять топологию базы данных.
В случае неудачного решения цикл обратной связи, в результате которого вы увидите последствия, займёт от недель до месяцев. К тому времени, как вы узнаете, что решение было неверным, вы успеете положить его в основу целой системы.
Контраргумент #2
“Я могу заранее изложить ИИ все сценарии отказа.”
Джун: “Напиши мне сервис оформления заказов.”
Сеньор: “Напиши мне сервис оформления заказов, но учти при этом: экспресс-распродажу с 10K одновременных конкурентных заказов, условия гонки при работе со складом, задержки при работе платёжного шлюза в период пиковых нагрузок, устаревание цен, взятых из кэша CDN, частичные отказы в тех случаях, когда платёж пройдёт успешно, но зарезервировать товар на складе не удастся, а также то, что в третьем квартале появится доля товаров, добавленных по подписке.”
В результате второго промпта получается радикально иная система, чем в результате первого. Сам промпт — это абстракция для проектирования. Это не ИИ сгенерировал этот список соображений, вы составили его, опираясь на свой опыт. Ценный навык заключается не в том, чтобы решить задачу, а в том, чтобы представить, какие сценарии вообще возможны.
Контраргумент #3
“Можно собрать базу знаний и скармливать её ИИ целиком.”
Действительно, тщательно подобранная база знаний из постмортемов, ранбуков, записей об архитектурных решениях, также включающая логи с описанием инцидентов и впоследствии заложенная в ИИ-систему — это огромный шаг к результату. Может быть, 90% пути.
Но последние 10% обходятся дороже всего. Вот почему они трудноуловимы:
В базах знаний содержатся сведения о прошлом, а решения при проектировании принимаются на будущее. Вы заходите на новый рынок с новым провайдером платежей, новыми регулирующими нормами, новыми паттернами трафика. Никто не написал постмортема об отказе, который пока не случился.
Базу знаний кому-то придётся поддерживать. Что в неё войдёт? Что уже неактуально? Чего ещё не хватает? Проектирование базы знаний как таковой – это задача на проектирование ещё одной большой абстракции.
Честная картина
Зазор между «что может сделать ИИ» и «в каком случае решение должен принимать человек» сейчас сужается.

Но обратите внимание: пусть зелёная полоска и становится короче, концентрация ценности в оставшейся зелёной части возрастает. В этих последних 10% заключена разница между системой, работающей в демках, и системой, которая выдержит контакт с крупномасштабным продакшеном. В зелёной части — решения о разграничении предметных областей, моделях согласования, владении данными, изоляции отказов, эволюции схемы. Именно эти решения дороже всего откатывать назад.
Что это означает для инженеров
Если вы потратили 10 лет, осваивая внутреннее устройство Spring, паттерны GoF и эквилибристику с аннотациями JPA — учтите, что эти знания постепенно устаревают.
Сейчас ценится умение взглянуть на проект из области электронной коммерции и сказать: “Вот здесь нужно проложить асинхронную границу между системами резервирования товаров на складе и оформления заказов. Истинное значение цены будет находиться в мгновенном снимке заказа, а не в запросе к каталогу”
Это не знания о реализации. Это суждения о проектировании, которым можно научиться на опыте, в том числе, опыте работы с отказами, подкреплённые глубоким пониманием того, как именно системы работают в реальных условиях.
Самые эффективные разработчики, которых я знаю, уже сместили акценты в своей практике именно таким образом. Они меньше времени тратят на написание кода и больше — на подготовку спецификаций, проведение границ, критику допущений и на нагрузочное тестирование с применением ИИ ещё до того, как будет сдана хотя бы одна строка кода.
Ссылки:
https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html
https://code.claude.com/docs/en/skills
https://aws.amazon.com/devops-agent/
Источник: habr.com
Похожие записи
Оцените материал:
Похожие записи
Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
