[Перевод] Исключения из принципа YAGNI
В общем и целом, я убежден в верности принципа YAGNI (You Aren’t Gonna Need It — Это вам не понадобится), согласно которому нужно внедрять в ПО функциональность — это касается также универсальности и абстракции, — только когда станет ясно, что она действительно вам нужна, и не раньше.
Однако существует ряд вещей, которые в действительности проще осуществить на ранних этапах, чем на поздних, хотя инстинкт и жесткая приверженность принципу YAGNI часто заставляет нас ими пренебрегать. На текущий момент я собрал небольшую коллекцию подобных вещей — вы найдете ее под катом.
1. Всё, что относится к правилу «ноль, один, бесконечность». Если в требованиях пункт «нам нужно обеспечить возможность сохранять адрес для каждого пользователя» превращается в «нам нужно обеспечить возможность сохранять два адреса для каждого пользователя», в девяти случаях из десяти нужно переходить сразу на этап «нам нужно обеспечить возможность сохранять много адресов для каждого пользователя», а гибкое ограничение в две штуки вводить только на уровне интерфейса, потому что с очень большой долей вероятности двух адресов вам не хватит. Делая такое предположение, вы практически точно останетесь в выигрыше, а если даже окажетесь в проигрыше, то потеряете очень немного.
2. Управление версиями. Это применимо к протоколам, API, форматам файлов и так далее. Не помешает заранее задуматься о том, как, допустим, система клиент — сервер будет распознавать разные версии и реагировать на них (пусть даже на текущий момент версия всего одна), особенно если у вас нет доступа к обеим сторонам или возможности вносить в них изменения синхронно. Будет поздно об этом беспокоиться, когда вы обнаружите, что версия номер два вам всё-таки нужна. На самом деле, вы таким образом просто следуете правилу Embrace Change (Примите изменения), которое лежит в основе принципа YAGNI.
3. Логирование. Особенно в тех случаях, когда баги устраняются постфактум или в недетерминированных, с трудом поддающихся воспроизведению ситуациях — зачастую, когда становится ясно, что возникла проблема, вводить его уже поздно.
4. Временные метки. Например, время создания, о котором Саймон Уиллисон в своем твитте высказался так:
Вот урок, который я по новой усваиваю на каждом проекте: на всех таблицах до единой в базе данных должен быть столбец created_at, заполняющийся автоматически. Стоит только подумать: «Да зачем мне это здесь?» — и спустя несколько недель эти данные гарантированно понадобятся для устранения какого-нибудь бага.
Если говорить в общем, гораздо полезнее флага истины типа completed будет временная метка с null-допустимостью, указывающая, когда произошел переход в состояние — completed_at.
Если обобщить сказанное о логировании и временных метках: в том, чтобы собирать больше данных, чем необходимо здесь и сейчас, обычно нет ничего страшного (если это не личные данные или конфиденциальная информация другого рода), от них ведь всегда можно избавиться. А вот если их не собрать, они пропадут с концами. Я оставался в дамках в тех случаях, когда заранее учитывал, что будет необходимо при анализе кода, даже если в требованиях это конкретно не прописывалось, и попадал впросак, когда уходил в минимализм, в результате теряя ключевую информацию и связывая себе руки в дальнейшей работе с данными.
5. Реляционные базы данных. Тут я имею в виду, что если вам в принципе нужна база данных, то лучше сразу делать реляционную и принимать реляционную модель как вариант по умолчанию, даже в случае когда для обслуживания первого набора требований достаточно документоориентированной СУБД или какой-нибудь простой системы с плоскими файлами. Большая часть данных реляционна по своей природе, поэтому нереляционная база — плохой выбор по умолчанию практически для любого приложения.
Если вы отдадите предпочтение реляционной базе данных вроде PostgreSQL, а потом выяснится, что большая часть данных у вас документообразна, вы сможете извлечь пользу из ее отличной поддержки JSON.
Если же вы выберете нереляционную базу данных типа MongoDB, пусть даже вам кажется, что она идеально подходит под текущие потребности модели, скорее всего, какое-нибудь новое требование создаст вам множество проблем и потребует переписывания в Postgres (по ссылке об этом говорится в эпилоге и разделе «Как MongoDB хранит данные»).
Недавно мне попался комментарий на Lobsters, показавшийся очень метким:
Интересно, не объясняется ли ценность совета «ничего не планируйте, не абстрагируйте, не программируйте с расчетом на будущее» тем, что большинство программистов доводят до ума платформы, которые уже и так от души абстрагированы и насыщены функциональностью и в дальнейшем асбтрагировании не нуждаются?
Мы можем себе позволить придерживаться принципа YAGNI, когда система уже развитая и податливая. Реляционные базы данных — невероятно гибкие системы, которые подстраховывают нас на случай будущих изменений в требованиях. Например, совет, который я давал в предыдущем параграфе, опирается на невысказанное предположение, что удаление данных сводится к простому DROP COLUMN, которое практически ничего нам не стоит (хотя… всякое случается).
Такой вот у меня список. Со временем он, вероятно, будет пополняться. Согласны? Что я пропустил?