В общем и целом, я убежден в верности принципа 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, которое практически ничего нам не стоит (хотя… всякое случается).

Такой вот у меня список. Со временем он, вероятно, будет пополняться. Согласны? Что я пропустил?

Комментарии (7)


  1. fireSparrow
    21.10.2022 16:41
    +3

    Пункт с базами весьма спорный.
    Идея о том, что нереляционная база — это более простая и примитивная замена реляционной базы, не верна в корне. У разных видов баз просто разные ниши.


    1. Nipheris
      21.10.2022 19:59
      +3

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

      Например, многие привыкли, что БД должна поддерживать транзакционное изменение нескольких элементов данных сразу, и только потом узнают горькую правду о выбранной ими документно-ориентированной СУБД.


  1. Didimus
    21.10.2022 19:57
    +1

    Тем людям, кто против сохранения временных меток в записях, обычно невозможно объяснить, зачем это нужно. Они же экономят байты работодателя


    1. danilovmy
      22.10.2022 12:57
      +2

      Я против сохранения непосредственно в записях. Если необходимо фиксировать изменения стейта объекта - так и надо это делать. А вот стоит у тебя только дата последней правки. Что было исправлено? Эта дата перезаписала предыдущую дату правки. А если ошибка была в предыдущие разы? Не стоит полагаться на утверждение что дата спасет от ошибки или поможет ее исправить. Не поможет. И Стейт восстановить не получится.


      1. Kerman
        22.10.2022 16:46
        +1

        Дата последнего изменения это не дата создания. В статье именно речь про дату внесения записи в БД. Такая дата не перезаписывается. И не понимаю, почему Вы против даты изменения. Сохранение даты изменения во всех таблицах ничуть не отменяет истории стейтов или что там у вас... Но добавляет способ выбрать объекты изменённые в определённом промежутке времени и ещё много чего полезного.


        1. danilovmy
          22.10.2022 20:57

          Ээ. А история изменения стейта это разве не даёт? А дата первой записи изменений стейта это разве не дата создания объекта? Все же, Появление поля автодаты в модели стоит объяснять с точки зрения бизнес задач модели. Понимаю, когда поле появилось по причине денормализации таблиц из за проблем со скоростью получения записей. Понимаю, если действительно есть бизнес-кейс на это, например, структура модели должна совпадать со страной сторонней базой управлять которой мы не можем но данные оттуда хотим. Но например, Если есть uuid опять же непонятно зачем дублировать. А если - вляпаем дату и всем будет проще - нет не будет. Будет сложнее. Но, вероятно, не сейчас а сильно позже. У меня в старом проекте аж 4 поля автор, едитор, создано, исправлено. Спустя 9 лет они добавляют ещё одно полено в адский огонь под моим котлом. Не сильно, но добавляют.


  1. Moraiatw
    22.10.2022 22:20
    +2

     с плоскими файлами

    Кажется, plain files должно переводиться как то по другому.