Pull to refresh

Как вырастить программу из прототипа

Reading time 8 min
Views 23K
Каждую неделю на профильных блогах мы читаем как нужно использовать методологию X и фреймворк Y, чтобы написать хорошо спроектированный и легко поддерживаемый софт. Нам постоянно говорят, что, мол, говнокод — это плохо, рефакторинг — наше все, дают те или иные очень важные сферические советы в вакууме. В большинстве этих статей можно встретить абстрактные философские нравоучения, например, вот это я распечатаю и повешу при входе в офис:


А что, если я скажу, что не все проекты одинаковые, и некоторые из них не то что можно, а даже нужно тщательно выращивать из прототипа? Об этом я рассказывал на конференции Unite'12, а сейчас расскажу вам.

Вступление


Идея написать этот текст у меня пылилась в голове уже очень давно. Но реальным катализатором вдруг стала вчерашняя статья под заголовком «Почему нельзя превращать прототип в итоговую программу». Оказывается, мне вдруг нельзя делать то, что я успешно делаю уже два года и горжусь этим.

Пока недовольный читатель не начал быстро скролить вниз, чтобы первым написать комментарий и поведать мне как же я неправ, хочу указать на один факт, который (почему-то) многие игнорируют — все проекты разные! Если вы делаете enterprise приложения, это не значит, что никто не пишет embedded системы, аппы для смартфонов или одноразовые презентации.

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

Проекты


Я работаю в компании Interactive Lab. В основном, мы делаем оффлайн интерактивные инсталляции, создание софта для которых сильно отличается от обычного software development:

  • Нет четкого ТЗ (вот выставка, сделайте круто!),
  • Все проекты небольшие, но очень разные (совсем разные, а не «в том проекте мы юзали mysql, а в этом будем юзать mongodb — придется ЗАГУГЛИТЬ КОД!»),
  • Очень короткие дедлайны, которые нельзя перенести («извините, мы что-то не успеваем, перенесите пожалуйста Атом Экспо на месяцок-другой»),
  • Часто работают всего 1 день во время выставки, а потом выкидываются (update: но, при этом код должен быть понятен, чтобы его поправить, если что вдруг сломается за час до дедлайна),
  • Комбинация софта и специального железа,
  • Полный контроль над железом (тормозит? оптимизировать? да вы что, пошли купим gtx 690).

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

Процесс


Принцип итеративной разработки уже совсем не нов, и ему посвящены сотни статей, в том числе и на Хабре. Как там, евангелисты Agile говорят? Берем, значит, фичелист, смотрим, что сможем сделать за итерацию, скажем, в две недели. Делаем. Смотрим опять…

Так, стоп! Фичелист? У нас нет ни ТЗ, ни четкого представления что это вообще должно быть. Итерация в две недели? У нас времени всего 15 рабочих дней, какие две недели, вы что!?

Вот так мы пришли к следующему алгоритму:

  1. Брейншторм идеи.
  2. Максимально простой и быстрый прототип с тестовыми ассетами за день-два.
  3. Тест на целевом железе.
  4. Полнейший отстой? GOTO 1.
  5. Вроде неплохо, но нужно изменить/добавить кое-что.
  6. Апдейтим прототип за денёк.
  7. Если еще не дедлайн — GOTO 3.
  8. Отсыпаемся.

Как Вы видите, итерациями по 1-2 дня прототип растет в финальное приложение. И это отлично работает.

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

  • Концепт создается итеративно с обратной связью от прототипа. Нет необходимости писать исчерпывающее ТЗ перед началом работы, которое еще и никак не гарантирует успешность первоначальной идеи.
  • Насколько концепт хороший видно в первые же два дня. Нет ничего неприятнее, чем понять за день перед дедлайном, что вся идея полнейший отстой.
  • Ближе к дедлайну всегда есть готовое приложение. В нем не все фичи, оно не достаточно красивое, но его уже можно ставить на выставку. Такого, что у нас просто ничего не готово, не может быть в принципе.
  • Все нюансы связки софт+железо можно выяснить сразу же на этапе первых итераций. Если вы думаете, что на 9ти экранах и 5ти видеокартах с общим разрешением 4098х2304 ваше приложение заработает так же, как и на офисном FullHD мониторе, вы сильно ошибаетесь.
  • Поигравшись с прототипом, нет необходимости все переписать *более правильным кодом* в финальное приложение. Сам прототип будет приложением.


Прототип


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



Мне иногда задают укоризненный вопрос: «А Вам не стыдно за код своего приложения, который получился постепенным апгрейдом прототипа?». Мне, честно говоря, стыдно за весь свой код, который я писал больше месяца назад. Почему? Потому что за прошедший месяц, в том числе и пока писался этот код, лично я вырос как разработчик. И сегодняшний Я написал бы все уже совсем по-другому. Ну а завтрашнему Я очень бы хотелось все это переписать к чертям! Поэтому, я каждый раз грязно улыбаюсь, когда слышу предложения взять и переписать все заново.

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

Вообще, сходимость написанного кода к говнокоду при количестве строк n > N сильно зависит от конкретного человека. Если ваш программист лепит к дереву-прототипу щупальца, не особо заботясь о его будущем, почему вы думаете, что заново все переписав, он в конце асимптотически не сойдется опять к подобному результату?



Делать, просто, нужно все правильно. Хорошо себе представлять, что это не прототип на выброс, а живой организм, который от ваших действий на этапе ростка сильно изменится, выросши в финальную свою форму. И форма эта может СИЛЬНО отличаться от того, что вы закладывали в его генотип при старте.

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

KISS, DRY, OOP, проектирование и другие экстремумы


Если бы мне давали 10 рублей за каждую статью, в которой упоминаются KISS или DRY, я бы давно купил BMW 3, о которой мечтаю. Эти баззворды уже заткнули за пояс страшное OOP, на котором сидят The Gang of Four и хлестают его паттернами проектирования (та еще картина, конечно).

Все, что имеет значение — может ли человек вырастить из прототипа здоровое дерево.

Конкретно в наших реалиях всё, к тому же, нужно сделать ещё и очень быстро. Итерации-то день-два, не больше. Вот и начинаешь бороться с самовольными интерпретациями KISS, DRY и всякими OOP головного мозга. Что, честно признаюсь, не всегда получается.

Это как, простите, с РПЦ. Опытные священники, теологи и историки религии, поди, знают почему вот этот конкретный обряд именно такой, хотя со стороны выглядит весьма странно. Но спроси любого из 97% остальных верующих — получишь ответ, мол, делай как сказали и будет тебе спасение. И хорошо, если не побьют.

И KISS, и DRY, OOP… что там еще? Проектирование всякое. Все это хорошие концепции, но дрожь берет, когда видишь, как они в жизни применяются. И понимаешь, что такое применение с выращиванием прототипа совместимо чуть менее, чем никак. Отсюда и страшные хромые деревья с цветками-людоедами.

Особенно, когда человека бросает на экстремальные ситуации. Если OOP, то обязательно 100500 абстрактных классов и по интерфейсу на каждого, да еще, чтобы что-то конкретное создать нужно делать это через специальную фабрику фабрик билдеров. Зато полнейший DRY. Все мегаабстрактно и никакой код не повторяется. И никого не смущает, что, чтобы всего этого добиться, потребовалось написать в 20 раз больше кода. А ведь еще и IoC с DI надо вфигачить, чтоб совсем никто не мог понять где эта программа начинается вообще.

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

А KISS, в основном, сваливается в противоположный экстремум. Где в середине разработки понимаешь, что все настолько Simple, что ветка, вообще говоря, у дерева может быть только одна, и ничего с этим уже не поделаешь. Выкидываем прототип, говорим, что прототипирование отстой и начинаем заново.

Так а что делать-то?


Как это обычно в фильмах говорят. «Забудь все, что ты знаешь!», «Чтобы поймать кошку, тебе нужно стать кошкой!» и тд. Но это не про нас. Не нужно ничего забывать. Наоборот, хорошо было бы помнить примеры экстремумов применения всяких методик, чтобы в минутку мимолетной рефлексии вовремя осознать, что сносит вас куда-то не туда.

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

Итак, что нужно делать, чтобы дерево-прототип не завяло? Несколько на первый взгляд простых принципов:

Очень короткие итерации и фидбэк
От специфики проекта нужно выбрать минимальное время на итерацию и обязательно запускать приложение в конце каждой итерации на целевом устройстве / у целевой аудитории. Если вы делаете интерфейс для большого мультитач-стола, не поленитесь, подойдите к нему и потыкайте в нарисованные кнопочки. До них легко дотянуться? По ним легко толстым пальцем попасть? Конечно, мышой-то все могут на мелком экране. Садись переделывай!

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

Не важно на какой стадии разработки сейчас модуль. А если (как обычно бывает) подошло время интеграции, а код в таком состоянии, что ничего из прошлого УЖЕ не работает, а из нового ЕЩЕ не работает, возможно, вас это шокирует, но неработающие места МОЖНО ЗАКОММЕНТИРОВАТЬ!

Решение текущих проблем
Допустим, есть простая задача на час-два. Но, программисты же люди такие, как у шахматиста, в голове сразу же разворачивается партия на 64 хода вперед. Становится понятно, что потом придется сделать одно, а чтобы была расширяемость, второе. К тому же, ходу на 32м уже сейчас видно, что для пущей фотореалистичности балансировки нагрузки просчета физики нужно начинать делать третье прямо сейчас. Да и вообще, напишем-ка фреймворк!

ЛИНЕЙКОЙ ПО РУКАМ!!! А потом ногами-ногами! Сейчас решаем только текущие проблемы. Самым быстрым способом. Как показывает практика, у таких размашистых фичей есть большая вероятность сильно измениться или вообще отпасть полностью.

Аккуратно следить за экстремумами
Хорошо, все делаем быстро и тупо… НЕТ! Ну что ж вы опять! Очень старайтесь не впадать в крайности и делать все прямо совсем тупо. Вы же видите куда вас все это дело приведет — оставьте себе моменты для маневра: аккуратное OOP без излишеств, впилите в компоненты state machines, сделайте чуть больше настроек, чем нужно сейчас, отрефакторите немного разросшийся класс. Делать все просто и только то, что нужно, не значит обязательно плодить говнокод. Просто, у некоторых по-другому не получается.

Максимальная приближенность ассетов к финальным
Все ассеты с самого начала по основным параметрам нужно подгонять максимально близко к ожидаемым. Пусть в первом прототипе вместо картинок будут lolcats из личного архива разработчика, а вместо планируемых FullHD видео скачанныйкупленный Iron Man 2 в BluRay качестве.

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

***


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

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

Updates


  • Добавил в описание «но, при этом код должен быть понятен, чтобы его поправить, если что вдруг сломается за час до дедлайна». Потому что многие обратили внимание на время жизни инсталляции. НО! Это абсолютно не значит, что на качество кода всем положить.
Tags:
Hubs:
+59
Comments 49
Comments Comments 49

Articles