… но это нормально. Любое проектирование отстой. И всегда будет отстоем.

Если вы мне не верите, давайте объясню…

Ни один проект не переживает встречи с реализацией


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

Данные, которые вы ожидали как обязательные в ответе внешнего сервиса, могут отсутствовать (или быть невалидными). Ожидаемая уникальность может оказаться совсем не уникальной на практике (даже в sha1 когда-нибудь случаются коллизии). Процессы, которые предполагались надежными, будут падать гораздо чаще, чем вы ожидали.

Это нормально.

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

Недостающие данные могут быть сделаны опциональными или заменены умолчальными.

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

Ограничение уникальности можно отменить, или смержить данные, или выбрать одну из версий.

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

Считать ли нарушение ваших предположений ошибкой, добавлять ли промежуточный слой или исправить концепцию системы для большего соответствия реальности — решать надо в каждом случае отдельно. Это всегда решение о справедливом компромиссе: стоит ли обработка бардака во внешнем мире усложнения вашей системы?

Ни один проект не переживает встречи с настоящими пользователями


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

Люди будут искать по неожиданным параметрам («да, я знаю, что могу поискать по client_id, но гораздо проще посмотреть, с какого телефона мне звонят, и поискать по нему»).

У людей будут появляться суеверные привычки — ожидайте, что функции типа «погасить все и перестроить заново» будут использоваться гораздо чаще, чем предполагалось, потому что пользователи воспримут ее как «выключить и включить» и отнесутся соответственно.

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

Когда вы сталкиваетесь с вопросами производительности типа поиска данных, вы или ограничиваете поля, по которым можно искать (возможно, порождая шквал жалоб), или добавляете индексы, или реорганизуете данные, или реализуете внешнюю поисковую систему.

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

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

Еще раз: это все компромиссы; и становится заметно, что есть три основных варианта действий.

Три Типичных Ответа


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

Приспособиться, что покрывает «ослабить ограничения» и «добавить индексы» — то есть подправить дизайн, чтобы проблемный сценарий обрабатывался бы хорошо. Это лучший умолчальный вариант для сценариев, которые «почти подходят», то есть когда невелики расхождения между «что есть» и «что требуется».

Перенести вовне, что включает промежуточные слои между вашей системой и внешним миром или между системой и ожиданиями пользователей — то есть добавление чего-то вне ядра системы, но внутри системы в целом, как ее видят пользователи. Этот вариант — лучший для сценариев, которые скорее ортогональны базовым принципам вашей системы, чем противоположны им.

Ни один проект не переживает встречи с будущим


Даже учитывая все изложенное выше, только две вещи можно сказать наверняка: по крайней мере часть вашего проекта — отстой; и спустя полгода вы осознаете, что эта часть больше, чем вы думаете сейчас.

Мы учимся, пока разрабатываем наши системы; мы учимся, когда наблюдаем за пользователями наших систем; и мы определенно учимся, когда система падает и кто-то в панике звонит в три ночи.

И что со всем этим делать? Лучшая идея, которую мне до сих пор удалось найти, довольно простая: проектируйте в предположении, что все пойдет не так, и постарайтесь сделать, чтобы фиксить последствия этих «не так» было бы настолько легко, насколько возможно.

На самом деле, все базовые принципы хорошего программирования можно рассматривать как следствия этого подхода.

Защитное программирование подразумевает, что когда внешний мир выявляет расхождение между вашим проектом и реальностью, система сообщает об ошибке — что означает, что вы можете исправить ошибку и жить дальше, и не нужно перед выкладкой исправления чинить попорченные данные, которые образовались в результате недообработки ошибки (или: дотошные проверки гарантируют, что все, к чему система не приспособлена, будет хорошо заметно).

Построение простейшей конструкции, которая только может работать, означает не впадать в гипотетические предположения, что означает не только защиту от того, что будущее вы предсказываете тоже отстойно, но и то, что у вас оказывается просто меньше всего. Меньше кода означает меньше багов, меньше архитектурных элементов означает меньше концептуальных ошибок (или: YAGNI — это подготовка к приспособлению).

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

Итого


Ваше проектирование — отстой.

И всегда будет отстоем.

Если ваш проект сейчас не кажется отстоем — это потому, что вы что-то упускаете.

Если вы уверены, что ничего не упускаете, вы забыли, что не можете видеть будущее.

Проектируйте в предположении, что позже вы определенно, совершенно точно, обязательно будете перепроектировать.

И тем не менее помните, что новый вариант все равно будет отстойный — возможно, просто меньше, или по крайней мере по-другому.

А самое главное: не переживайте слишком из-за этого. Звук «рука-лицо» — это не признание некомпетентности, это — шум, издаваемый проблемой, превращающейся из неразрешимой в тривиальную.

Лично мне нравится осознавать, что я был идиотом, потому что это означает, что я собираюсь сделать лучше, чем было.

Так что это нормально, что ваше проектирование — отстой.

Продолжайте отстойничать. Продолжайте отстойничать чуть меньше. Продолжайте отстойничать по-другому. Продолжайте учиться.

И помните — выпустить что-то отстойное на 100% лучше, чем не выпустить вообще ничего.

Удачного хакинга.

Об авторе: Мэтт Траут — Perl-разработчик, сооснователь и техлид компании Shadowcat Systems Limited (консультирование в области разработки ПО), автор ORM DBIx::Class, один из ментейнеров веб-фреймворка Catalyst, автор многих модулей на CPAN.

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


  1. yTko
    16.01.2016 22:44
    +64

    image


  1. Kolyuchkin
    16.01.2016 22:51
    +21

    Был у нас один проект, когда требовалось реализовать систему управления безопасностью… После макетирования заказчик раскритиковал нашу систему, потому что она «слишком сложная», хотя имела большой потенциал к расширению и гибкость в использовании. После долгих и мучительных месяцев реализации «простой» системы, на испытаниях, заказчик поблагодарил нас не за выбранное решение, а за то, что кнопки были большие)) После этого к критике со стороны заказчиков я отношусь с определенной долей скептицизма))


  1. giffok
    17.01.2016 15:00

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


    1. terryP
      17.01.2016 15:28
      +5

      Нет, это плохая практика. Код всех функций превращается в бесконечные if'ы. На самом деле, во всем должны быть золотая середина — параноидальные проверки зло возможно ещё более худшее чем недостаток проверок. Важно точно определить все возможные диапазоны данных, все бизнес кейсы и места где проверки нужны и где они будут лишними.


      1. asm0dey
        17.01.2016 17:01
        +3

        Ну в моём Java-мире вешаются JSR-303 аннотации валидации, код выглядит очень красиво, пользователю приходят красивые сообщения. А всюду ниже сервисного слоя мы уже вешаем не ифы, а что-нибудь типа Objects.requireNonNull — чтобы приложение прямо валилось если до этого слоя каким-то образом дошли невалидные данные.


        1. terryP
          17.01.2016 18:31
          +1

          Я тоже живу в Java мире, но тут важен не мир, в избытке явно бессмысленных проверок все равно ничего хорошего нет. К тому же не всегда это хорошо когда из-за каждого чиха приложение валиться, были задачи когда требование были ровно противоположенными (например, была задача обработки логов посещений пользователей сайтов, когда клиент считал потерю/искажение 1% статистики допустимой, если приложение укладывалось по скорости обработки терабайт данных за указанное время и никогда чтобы не случилось не падало, там требовалось лишь сохранять варнинги при самом минимальном кол-ве проверок).


          1. asm0dey
            17.01.2016 22:35

            Ну соответственно те места, где падать не надо — ассертами не снабжаются. А вот как сделать нестрогую валидацию мне как-то непонятно. Как реализовали если не секрет? Всмысле как мерили что не более чем 1% данных невалиден?


            1. terryP
              19.01.2016 14:07

              Все просто, там была построчная обработка данных хитрой структуры, с одним try catch блоком на всю структуру, проверки были только на необязательные данные, падал любой exception (вроде numberFormatException), строка выкидывалась в отдельный error файл, информация об ошибке сохранялась в мапу <тип_ошибки, кол-во> и выполнение шло дальше. А error файл потом анализировался на то кто виноват: кривые данные или код.


          1. khim
            17.01.2016 23:11

            На самом деле это как раз зависит от «мира», в котором вы работаете. Есть очень хорошее наблюдение Хоара:

            Есть два метода создания программного обеспечения. Один из них — сделать программу настолько простой, чтобы стало видно, что в ней, очевидно, нет ошибок. И другой, сделать приложение таким сложным, чтобы стало видно, что в ней нет очевидных ошибок. Первый метод — гораздо сложнее.
            (В оригинале: «There are two ways of constructing a software design: one way is to make it so simple that there are obviously no deficiencies; the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult»)
            Java — исповедует первый подход, Форт — второй. Подход к ошибкам в Форте концептуально прост, но вот привыкнуть к нему непросто: GIGO стоит во главе всей идеологии. Если вызывающая процедура может сделать проверку сама — то, стало быть, она и должна делать проверку. Если вы вызвали функцию с неверными параметрами — ССЗБ, не ждите, что вам кто-то плохое слово скажет, всё будет отлично, просто результат вы получите даже и близко непохожий на ожидаемый. Перемножить два указателя на строки? Что может быть проще! Какие-то ошибки порождаются только функциями, общающимися с внешним миром (скажем функция чтения из файла, так как заранее сказать — будет на диске ошибка или нет нельзя).

            Удивительным образом подобное «программирование по бразильской системе» тоже вполне себе работает и приводит к написанию очень компактных, быстрых и не требующих много ресурсов программ — но при этом имеет проблемы с масштабированием… Примечание, увы, оказывается правдой: программы на Форте, как правило, имеют очень мало ошибок (а часто — и не имеют их вообще), но так как метод «тяп-ляп и в продакшн» недоступен, то нанять 100500 индусов, чтобы быстро что-то состряпать не получается.


            1. terryP
              19.01.2016 14:09
              +2

              Java — исповедует первый подход, Форт — второй

              А может наоборот? А то получается, что в Java очень простые программы.


              1. khim
                19.01.2016 14:16

                Да, наоборот, конечно. Спасиба за замечание. Программы в Java обычно просты локально (и все методы на 2-3 строки покрыты тестами, да), но как вся эта структура работает в целом — не знает никто. Отсюда — куча странных backtrace'ов в разных местах (в том числе и на публичных web-сайтах). Причё зачастую вся беда — в проверке, которую можно просто убрать…


      1. Idot
        18.01.2016 09:34
        +2

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


  1. Delphinum
    17.01.2016 15:28
    +1

    Работаю удаленно и периодически бывает сложно понять задачи начальства, потому мы нашли хорошее решение — я открываю Google Docs с установленным плагином Plant UML Gizmo и проектирую там решение задачи. По окончанию, начальство (привет Влад) оценивает решение и вносит свои правки. Так может повторяться несколько раз, но в результате мы получаем единое понимание задачи и устраивающее нас обоих решение, что ни раз спасало от переписывания кода.

    В статье забыли о двух важных составляющих проекта:

    1. Обратная связь — проект нужен для того, чтобы можно было общаться с заказчиком на одном языке, показывать ему понятные схемы, идеи, интерфейсы, а не для самолюбования
    2. Изменения — проект может и должен изменяться в процессе жизненного цикла ПО. Это не просто нормально, это необходимо. Проект для того и создается, чтобы изменять его, а не исходные коды. Ведь изменить проект гораздо проще и дешевле, чем изменить программу


  1. VolCh
    17.01.2016 17:39
    +5

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


  1. tumikosha
    17.01.2016 22:19
    +2

    Мэтт Траут — Perl-разработчик, жалуется что проекты отстойные. ;0)))


    1. alaska332
      17.01.2016 22:37

      Судя по вашему профилю — вы программист на nginx.
      Получается?


    1. cynovg
      17.01.2016 22:44
      +3

      Каталисту около 10 лет, DBIx::Class больше 10 лет. Человек с вами опытом делится, а вы ослоумничаете.


  1. igrishaev
    17.01.2016 23:12
    -3

    У чела случился кризис, он написал, что все гавно. Искренне желаю ему включиться в процесс со свежими силами.
    Есть такая штука ФФФ. Знал бы о ней автор оригинала, может, не писал бы таких статей.


    1. khim
      17.01.2016 23:36
      +3

      А чем ФФФ поможет? Если вы занимаетесь ремеслом, то да ФФФ — работает. И неважно — клепаете вы под заказ сайты или кухни.

      А вот если вы делаете что-то новое… тут ФФФ не работает. Посмотрите на рынок операционок к примеру. Есть там работающие по принципе ФФФ компании? Да — есть: Ubuntu, FirefoxOS, etc. Полнейшие аутсайдеры без каких-либо шансов куда-то продвинуться. Причём появились они как раз когда iOS и Android, сырые, косые и убогие пришли и уничтожили Symbian, Palm, BlackBerry…

      Хороший пример представляет из себя также Android и ChromeOS: команда Android'а — работает «с надрывом», часто на праздниках и выходных, при этом ChromeOS — «ФФФ как он есть». Ну и? На чём там у нас Pixel C? А почему — как вы думаете? Одна компания, сравнимые по качеству инженеры, etc.

      Такие дела. Нет, я не против ФФФ: кто-то же должен и кухни делать, не всем уникальные скульптуры ваять… но нужно чётко отдавать себе отчёт в том, что вы делаете, когда и почему…


      1. igrishaev
        18.01.2016 09:15

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


        1. cynovg
          18.01.2016 10:42
          +1

          Похоже, что это вам так кажется, что у автора проблемы с планированием. Что касается ФФФ или любого другого принципа, уже давно было сказано Ф. Бруксом, что «серебряной пули не существует».


          1. igrishaev
            18.01.2016 11:01

            Не существует, поэтому давайте писать, что все говно.


            1. cynovg
              18.01.2016 11:11
              +1

              О проблемах в ИТ, в том числе и проблемах проектирования, пишут на протяжении всей истории ИТ. Что касается «все говно» — это всего лишь ваше восприятие статьи.

              PS: Статья заканчивается замечательным разделом «итого», в которой автор более полно раскрывает свою мысль.


              1. igrishaev
                18.01.2016 14:26
                +1

                Значит, у нас разное восприятие. Осмысленных выводов в разделе «Итого» я не вижу.


          1. tumikosha
            20.01.2016 13:19

            Существует!
            Например один мой знакомый взялся внедрять ITIL в РФ
            Он говорит что это серебряная пуля для Российского рынка.
            ;0))


            1. VolCh
              20.01.2016 22:08
              +1

              Скоро выйдет закон по которому софт без ITIL будет запрещён?


    1. alaska332
      17.01.2016 23:37
      +1

      Да вы что!!! ФФФ!!! Мэтт Траут потратил жизнь зря, он не читал «ЖЖ Товеровского», жаль его, дурака.


  1. lair
    18.01.2016 14:18
    +3

    Проблема посыла «Ваше проектирование — отстой» в том, что он позволяет разработчику сказать «ну да, отстой; но все равно же будет отстой, что ни делай».


  1. overmes
    19.01.2016 11:03

    Я считаю вы путаете понятия. Выработанные требования практически всегда имеют изъяны. Это болезнь нашей индустрии.
    А мое проектирование по требованиям достаточно хорошо, вот только требования могут измениться в любой момент и к этому стоит быть готовым.

    И помните — выпустить что-то отстойное на 100% лучше, чем не выпустить вообще ничего.

    Это похоже на «Лучше сделать и жалеть, чем не сделать и жалеть». Оба выражения лишь утешения для людей, которые ошиблись. Не стоит обманывать себя и злоупотреблять ими.