Yagni

Yagni

@firstgambit

По поводу YAGNI. В начале своей карьеры я сильно усложнил(в том числе для меня самого) программу абстракциями, которые управляли абстракциями. Тимлид тогда вполне законно попросил меня это переписать и посоветовал очень хорошую книгу — «Рефакторинг. Улучшение существующего кода» Фаулера.

Эта книга действительно очень много мне дала. Писать более качественные приложения стало заметно проще, и за счёт этого я быстро вырос как разработчик, а следом и по карьерной лестнице.

Но принцип YAGNI используется также для оправдания херовой архитектуры.

Приведу абсурдный пример. Человек делает относительно простенький интернет-магазин.

На ум приходят два способа:

  • Я возьму Laravel.
  • Я сделаю всё на голом PHP, потому что Laravel обладает «ненужными» компонентами — YAGNI.

По времени это плюс-минус одно и то же. Более того, первый вариант скорее всего займёт меньше. А если потратить время и вместо тупого MVC хотя бы вынести логику из моделей куда-нибудь, это сильно окупится. (А с ИИ так вообще всё стало проще, такой процесс можно поставить на поток.)

Но есть люди, которые сознательно делают по второму варианту, и их решение перестаёт обладать запасом прочности.

Кстати, пример почти реальный. На фрилансе мне было проще поддерживать даже кривой проект на Laravel, чем относительно качественный «голый PHP»: читать его было проще, и структура была понятнее. Под «голым PHP» я здесь имею в виду код без всякой архитектуры. Есть много хороших кастомных решений без фреймворка, которые тоже отлично поддерживаются, речь не о них.

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

Я решил обратиться к гуру: https://martinfowler.com/bliki/Yagni.html

Тут есть очень много любопытных моментов.

1. Автор приводит пример реализации пока не существующего функционала. То есть случай, когда разработчик по факту приступает к разработке другой фичи.

2. Автор говорит:

Often people don't think through the comparative cost of building now to building later. One approach I use when mentoring developers in this situation is to ask them to imagine the refactoring they would have to do later to introduce the capability when it's needed. Often that thought experiment is enough to convince them that it won't be significantly more expensive to add it later. Another result from such an imagining is to add something that's easy to do now, adds minimal complexity, yet significantly reduces the later cost. Using lookup tables for error messages rather than inline literals are an example that are simple yet make later translations easier to support.

Это очень частый аргумент в подобных случаях. И это действительно крутой способ разработки. Когда я работал в финтехе, это так и работало (упомянул в самом начале). Сначала пишешь простенький функционал, потом видишь херню, рефачишь по определённым правилам, и хорошая архитектура появляется как следствие. Но есть важный нюанс: там время на рефакторинг давали, и даже давали сверх нормы.

3. И самое важное. Фаулер прямо пишет:

Yagni is only a viable strategy if the code is easy to change.

Дальше он поясняет: YAGNI относится только к функционалу «на будущее» и не относится к усилиям, которые делают код проще для изменений. Рефакторинг, тесты, нормальная архитектура — это не нарушение YAGNI, а его обязательное условие. Без них, по его же словам, принцип превращается из полезной практики в проклятие. То есть люди, которые отказываются от архитектуры «по YAGNI», нарушают сам YAGNI.

YAGNI — практика экстремального программирования. Но экстремальное программирование подразумевает вместе с ним набор других практик, тот же постоянный рефакторинг, который игнорируется.

Чаще всего не в финтехе я сталкивался с тем, что времени на рефакторинг просто не существует. Разговоры о том, что «потом, если что, перепишем», как правило, означают «забей хер, и так сойдёт». И уже начали забивать не просто на какой-то плюс-минус ожидаемый запас прочности, который делается минут за пять, а на архитектуру текущего функционала.

То есть мы одновременно пишем приложение, которое планируется поддерживать в долгосрок, при этом быстро, при этом без чётких требований. Дальше происходит следующее: заказчик с удивлённым лицом котёнка приходит и говорит, что теперь нужно не так, а по-другому. Да, на правах тимлида можно сказать «это невозможно», но на самом деле это не всегда так. И чаще приложение покрывается заплатками и всё больше вязнет, что сильно усложняет разработку новых фич. ИИ без хорошего контроля (не обязательно ручного) тут усугубляет ситуацию, так как пишет не всегда интуитивный код.

В некоторых случаях это следствие того, что некоторые разработчики разрабатывают текущую версию как «чётко задокументированную» и «последнюю», ещё и ссылаясь на YAGNI (мол, если так не разрабатывать, это плохо). Во многих случаях, где я наблюдал подобное отношение, оно приводило к ухудшению поддерживаемости.

Основная суть того, что я хочу сказать: формулировка принципа YAGNI не помогает писать код лучше. Помогает его расшифровка. То же самое относится к KISS. То же самое относится к DRY. Тут тоже очень интересно: есть механическое дублирование, где код одинаковый, но use case'ы сильно разные. Иначе говоря, изменение одного участка не затронет второй. А бывает ровно наоборот. Почему-то про SOLID спрашивают достаточно часто и подробно, а про подобные принципы я редко слышал, чтобы спрашивали (если вообще слышал).

То, о чём я говорю, не является какой-то догмой о том, как правильно писать код. Я просто к тому, что данная модель не подходит нашей разработке.

У нас есть модель, где:

  1. Приходит заказчик и говорит: «Нам захерачить вот это». А ещё лучше, когда он что-то уже навайбкодил (здесь одновременно иронично и неиронично: ТЗ в виде навайбкоженного прототипа мне очень понравилось, и я правда кайфанул, когда по нему разрабатывал), но либо не собрал это в MVP, либо собрал, но нужно причесать.
  2. Вы причёсываете и действительно лишних фич не добавляете, ибо сроки поджимают.
  3. Потом проект запускается, приходят пользователи, вы фиксите первичные баги, чтобы это как-то ехало дальше. Всё это, естественно, в облачной инфраструктуре и с подобными фокусами, как в сказке про трёх поросят: домик строится быстрее, чем его ломает волк.
  4. Глобальные правки решаются бесконечными подстройками и надстройками. Делается всё возможное, чтобы не согласовывать рефакторинг и не трогать это чудовище. Но трогать надо, потому что без этого не будет новых фич, а без новых фич никто не будет пользоваться продуктом. Не дай бог ещё какой-нибудь AI изобретут, и его тоже придётся впендюривать.

Это нормально. Лично знаю некоторые крупные конторы, которые стали строгими в разработке исключительно потому, что жизнь заставила. Слишком уж дорого стали обходиться баги, появлявшиеся из-за отсутствия рефакторинга и проектирования и помноженные на допущения архитектуры. Скейлиться дальше было либо нельзя, либо дороже и рискованнее.

Да что уж говорить, это проявляется даже в мелких конторах. Например, когда от чистого вайбкодинга переходят к более «осознанному вайбкодингу». Или когда при разработке не выбирают заведомо провальные решения вроде хранения крупного проекта в одном PHP-файле (здесь условно). Просто строгость тоже роляет.

Раньше я делал упор на то, что в команде необходимы хорошие технические решения, и старался продавливать их даже тогда, когда это было неуместно. Сейчас я стараюсь точнее понимать, что нужно конторе. Если со мной не согласны, я добиваюсь ответа, почему, чтобы лучше понимать условия, принимать компромиссы и предлагать решения, которые встраиваются в существующую парадигму. Проблема в том, что эта парадигма, без иронии, не всегда была мне понятна.

Как я уже упоминал выше, я борюсь за сохранение или улучшение поддерживаемости. В меру своих знаний и опыта я стараюсь не принимать заведомо провальных решений, а если какое-то решение занимает пять минут и не усложняет ни код, ни архитектуру, я его делаю. Иначе говоря, я ищу баланс между «дубовым решением» и оверинжинирингом.

При этом такой подход нужен далеко не всем. Проблемы, которые копятся долгое время, можно лечить и по-другому, а проект может вообще никогда не столкнуться с этими трудностями. Именно в таких условиях я и стараюсь найти решение, которое устроит всех, в том числе и меня.

И главное: называть вещи своими именами, прямо их прописывать или проговаривать и не прикрывать неэффективные решения.

Report Page