суббота, 21 апреля 2012 г.

Как программисты пишут часы. Опасности общих моделей


Именно так выглядят часы,
дизайн которых придумал программист

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

В рамках нашего нового продукта ребята разрабатывали интерактивные виджеты для HTML5. И, в какой-то момент, дело дошло до анимации. Естественно, анимация представляла собой красивый и плавный переход между состояниями. Действительно, программисты довольно быстро и аккуратно всё реализовали, и я, вспомнив  вебдевную молодость, выпросил недоделанный компонент "поиграться".


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

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

А может быть - вот так
Но тут возникла непредвиденная заминка. Часы отлично шли до тех пор, пока секундная стрелка не доходила до отметки 59. А потом... Да, вы правы. Она красиво и плавно обращалась вспять и, пройдя весь циферблат в обратном порядке, возвращалась к отметке 0.

И ведь с точки зрения программистов всё выглядело логично! Действительно, чтобы анимировать переход от значения 59 к значению 0, надо, чтобы стрелка прошла в обратном порядке все числа от 59 до 0. Вот только в реальности никто никогда не видел часы, в которых стрелка возвращалась бы назад...

Итак, что же полезного мы можем вынести из этой ситуации?
  • Во-первых: чтобы описать любой объект или явление, программистам приходится строить модель. По определению модели, часть факторов из реальной жизни остаётся "за бортом". И в этот момент особенно важно следить, чтобы модель, которую построили программисты, соответствовала модели, которую строит для себя пользователь. Действительно, когда я отключил анимацию, часы стали вести себя вполне ожидаемо (хотя и не так красиво).
  • Во-вторых: тестирование должно обязательно проводиться на реальных данных в условиях, максимально приближенных к тем, который возникают у пользователя. Сейчас это кажется очевидным. Однако иногда, желая досконально протестировать работу продукта в граничных случаях и под нагрузкой, мы можем упустить проверку самых очевидных кейсов, которые, в силу их тривиальности, все просто игнорируют (и забывают).
  • В-третьих: обычно программисты пишут программы, а не продукты. Они увлекаются описанием объектной модели, построением архитектуры и алгоритмов, забывая о том, что продукт должен собой представлять в реальности. То есть надо возвращать их с неба на землю. Здесь сильно помогает итеративный подход и частая демонстрация промежуточных результатов владельцу продукта (честно говоря, затрудняюсь более точно перевести скрамовское сочетание product owner).
  • В-четвёртых: если вы пишите ТЗ на разработку внешней команде, по возможности описывайте исходный объект или процесс из реальной жизни, который лежит в основе создаваемого ПО. Это не только позволит программистам увидеть задачу шире, чем программный код, но и вы сможете более чётко сформулировать ключевые требования. 

    С другой стороны я с огромным трудом представляю себе ТЗ, которое содержит фрагмент типа "при переходе от 59 к 0 стрелки должны двигаться по часовой стрелке". Такая детализация возможна разве что в системе жизнеобеспечения космического корабля, но мне такие задания, к сожалению, пока читать не доводилось. Поэтому описание реального объекта поможет команде более точно понять задачу.

  • В-пятых: идеальный программист всегда немного аналитик. Он мыслит не только интерфейсами, классами и методами, но и объектами предметной области, понимает не только алгоритмы, но и бизнес-логику системы. И это одна из основных черт, которая отличает опытного разработчика от начинающего кодера (каким бы классным он ни был).

В заключение хочу сказать, что никоим образом не пытаюсь посмеяться над "глупыми программистами" или как-то ещё задеть их в профессиональном плане. У меня отличные ребята и классная команда. Просто есть вещи, которые кажутся настолько очевидными, что перестаёшь обращать на них внимание и видеть исключения из общих правил. И поэтому очень важно смотреть на систему с разных сторон: как менеджер по продукту, как аналитик, как программист, как пользователь системы. Попробуйте, и ваши продукты будут великолепны! Как у нас :).

13 комментариев:

  1. Время - это бесконечная величина. Представлять бесконечную величину конечными величинами должен тот, кто поставляет данные на вход виджету. Вопрос тут вовсе не в постановке задачи, а в неправильной подаче данных виджету. Направление стрелки - это не то что должно заботить виджет. Ему вообще без разницы направление, у него может быть циферблат наоборот. В общем виноват не пулемет, из которого стреляли по воробьям.

    ОтветитьУдалить
    Ответы
    1. Вот могу поспорить, что писал программист ;). Правильно? Просто очень характерное замечание, которое я практически в тех же словах слышал от своих ребят.

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

      Вот представьте, я - пользователь. У меня есть виджет, который принимает значения секунд от 0 до 59. О чём мне заботиться? Я могу получить это значение вызовом getSeconds(). Я что, должен написать свою обёртку в сто строк, чтобы удовлетворить изысканные требования вашего виджета к входным данным? Да я сам за это время такой же напишу!

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

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

      Да, кстати, решение-то очень простое. Если циферблат круглый, надо просто добавить возможность анимации "через 0".

      Удалить
  2. Неужели такая небольшая ошибка в недоделанном компоненте послужила толчком к написанию этой статьи ?

    ОтветитьУдалить
    Ответы
    1. Да :). Она просто очень показательна. Потому что с точки зрения разработчика всё работает отлично, а с точки зрения пользователя ничего не работает.

      И при этому ни одной ошибки в реализации не допущено и всё сделано именно так, как планировалось.

      Удалить
  3. Cразу вопрос. А у них в рамках этого проекта не было бага с тем, что когда часы показывают 16:30 часовая стрелка находится точно между 4 и 5, а то запросто может оказаться на 4 или на 5 заранее ;).

    P.S.
    Часы конечно да. Но вот если бы заданием было написать Минигру с вращением из какой нить казуальной игры(ввод кода на сейфе, ввод времени на часах, мазайка из нескольких вращающихся дисков, и прочие диски с шестеренками), то там такие перлы программистические бывают, что картина Сальвадора становится как раз в тему.

    ОтветитьУдалить
    Ответы
    1. Вот как раз промежуточным положением стрелок можно озаботиться пользователю...

      А по поводу шестерёнок - это да. Мы, кстати, как раз и делаем движок для всяких "вращающихся дисков с шестерёнками". То, что называется Gauges. Часы - это просто один из вариантов, собранный на той же базе "запчастей".

      Удалить
  4. Так чему же удивляться если компонент еще "недоделанный". ;-)
    Играться надо уже с готовым продуктом, а то что вы пытались работать с недоделаным компонентом, я бы назвал это тестированием.

    ОтветитьУдалить
    Ответы
    1. Зря вы так. Тестирование неготового продукта - это отличный способ сэкономить кучу времени и отловить ошибки на первых стадиях. Вообще, "есть слона по частям" - фундамент, например, любого agile. Кстати, фича с анимацией на тот момент уже была закрыта.

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

      Я говорю, что у программистов и пользователей разные модели поведения в голове, которые в результате ведут к непониманию:

      Пользователь: У меня ваши часы не работают
      Программист: У меня всё отлично работает, проблема на вашей стороне

      И, в общем-то, каждый из них прав со своей точки зрения.

      Удалить
    2. Я ничего не имею против тестирования, без него никуда. Если только сам заказчик от него не отказывается.

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

      Удалить
    3. Безусловно! Об этом и пытаюсь сказать.

      Удалить
  5. Извините, но ваши программисты - или дети, или сферические программисты в вакууме. Написать часы, которые не идут - это надо уметь. И не надо говорить про ТЗ, в котором этого нет. Любой несферический программист должен нормально относиться к спецификациям, в которых написано не все, а некоторые вещи просто подразумеваются.

    "Он мыслит не только интерфейсами, классами и методами, но и объектами предметной области" - это не про идеального программиста, а про нормального. Может я и не прав, но я, как программист, пишу не код, а продукт для пользователя. Код - всего лишь средство создания продукта, а хороший структурированный код с интерфейсами и всем прочим - всего лишь средство уменьшить затраты на написание и поддержку продукта.

    ОтветитьУдалить
    Ответы
    1. Хотя нет - не верю в программистов-детей. Видимо у вас все слишком формализованно, и задача программиста сводится не к тому самому написанию продукта, а к реализации классов и интерфейсов, которые ему нарисовал кто-то старший.

      Удалить
    2. Я бы извинил, если бы вы сказали, что я не умею ставить задачу или неправильно использовал компонент или ещё чего-нибудь в том же духе, извинил бы. За программистов не извиню, даже не надейтесь.

      С другой стороны - я сам виноват, что весь контекст не передал. Мы пишем платформу для создания абсолютно произвольного виджета путём сборки из составных частей. Т.е. всё основано на более-менее абстрактных спидометрах-тахометрах и прочих переключателях. И, соответственно, писался некий абстрактный движок для визуализации таких объектов. Пока всё было абстрактно, всё было великолепно, а когда дошли до конкретики, как раз и обнаружились интересные особенности.

      Удалить