Как запустить MVP и не превратить его в технический долг
Последние пять лет я работаю в аутсорсинге, поэтому часто занимаюсь запуском новых продуктов. Чаще всего жизненный цикл создаваемых нами приложения начинается с разработки так называемого MVP (minimum viable product).
При запуске MVP большинство компаний стремятся зафиксировать сроки и бюджет: еще нет уверенности, что бизнес-модель продукта жизнеспособна, поэтому требуется как можно быстрее проверять гипотезы на практике. Когда (и если) продукт выстрелил, приходят настоящие пользователи, которые хотят не только новые фичи (и как можно быстрее), но и стабильность продукта. Их перестает устраивать ПО, собранное на коленке для демонстраций и тестирования гипотез. Поэтому сроки остаются актуальными, но вместо бюджета на первый план выходит качество.
Проблема в том, что мы каждый раз оказываемся в том самом знаменитом треугольнике компромиссов, но два угла, которые мы должны зафиксировать — не совпадают. Довольно часто эту дилемму предлагают решать в формате «давайте сначала сделаем прототип, попробуем его, а потом выкинем и с нуля перепишем всё уже по-хорошему». К сожалению, на практике осуществить этот трюк удается довольно редко. Поэтому многим командам приходится годами жить с «архитектурными» решениями, заложенными на этапе тестирования гипотез.
Сегодня я расскажу, как мы вышли из этого треугольника (это расшифровка моего выступление на эту тему на конференции TeamLeadConf 2020).
Под MVP я буду понимать любой новый продукт — хоть отпочкование от существующего, хоть новая большая фича в существующем продукте, хоть разработка нового приложения. Ограничения на бюджет MVP обычно выражаются в рублях, в долларах или иной валюте. Сроки варьируются от «сделайте как можно быстрее» до «все горит, ничего не помогает, надо было вчера». Таким образом два параметра уже зафиксировано, поэтому качество получается таким, каким получится. На что мы можем повлиять в этом треугольнике?
В разных интерпретациях «качество» может быть как одним из углов треугольника, так и находиться внутри него. Во втором случае на вершине будет находиться «объем». Но в рамках статьи эта разница не важна, и ниже мы увидим, что «объем» (функциональная полнота) в некотором смысле может трактоваться как один из аспектов качества продукта.
Что, если мы потребуем больше бюджета и сроков, чтобы обеспечить должное качество ПО? Возможно, получится хорошо, даже очень хорошо. Но будут ли люди готовы платить за мишленовский ресторан, если им нужен бургер в Макдональдсе — большой вопрос.
Другой вариант: попробуем все сделать быстро, оставив качество на следующий этап. Тогда первой нашей проблемой в готовом продукте станет несоответствие ожиданий и реальности. Люди бизнеса обладают магическим свойством сразу же после релиза забывать о накопленном техническом долге и о том, что соглашались на рефакторинг, как только проект взлетит. В итоге поддерживать прежний темп разработки станет все сложнее и сложнее — постоянно будет отваливаться что-то, то тут, то там. Масла в огонь добавят реальные пользователи, потому что их надо поддерживать: исправлять ошибки и дорабатывать систему в соответствии с их запросами. Из-за роста напряжения рефакторинг будет переноситься на потом, а вскоре мы о нем и вовсе забудем.
В итоге перед нами встает непростой выбор: похоронить продукт еще на стадии MVP или подписаться на ад поддержки. Обе перспективы — так себе. Поэтому мы задумались:, а что, если делать достаточно качественно, потому что на качество в полном объеме нет ни сроков, ни бюджета? Скажем, пусть разработка первой версии займет на 10% — 20% больше времени, но зато ее можно будет поддерживать и развивать, а значит — не придется выкидывать? Идея показалась нам разумной. Осталось ответить на вопрос: «Что значит — достаточно»?
Качество ПО — штука весьма эфемерная. Однако существуют некоторые успехи по описанию этого чуда-юда. В рамках статьи я буду опираться на ISO/IEC 9126. Этот стандарт уже устарел и его заменили на новый, но мне он кажется гораздо более понятным нового, а суть у обоих стандартов принципиально не поменялась.
Основная его идея в том, что «качество» состоит из многих компонентов, каждый из которых может находиться в конфликте с другими. Качество — это удобство использования и сопровождения, производительность, переносимость, надежность и многое другое. Например, функциональность, когда ваша программа делает то, что было заявлено — это тоже про качество.
Такое представление «качества» позволяет выбрать только необходимые компоненты, а на другие «забить». Состав компонентов и будет определять нужное нам «достаточное» качество. Так мы сопоставили треугольник компромиссов с аспектами качества. При этом рассмотрели как стадию разработки MVP, так и стадию дальнейшего развития продукта.
Сроки
Для MVP всё надо, как обычно, вчера. На стадии роста пользователи уже готовы немного подождать, потому что продукт им приносит какую-то пользу. Поэтому сроки здесь — сегодня-завтра. Вывод: независимо от стадии писать код нужно максимально быстро.
Функциональность
MVP — это, в том числе, и про тестирование гипотез. Вполне возможно, что начнете вы с одного, а закончите другим, потому что при тестировании гипотеза провалилась. Поэтому здесь мы должны еще уметь быстро менять.
Но быстро менять придется и на стадии роста, когда функциональность продукта уже понятна, а пользователь активно запрашивает новые фичи. Может, конечно, показаться, что это — тоже про «писать быстро». Так оно и было бы, если бы мы не ломали что-то старое, создавая новое. Поэтому «писать быстро» не то же самое, что «менять быстро».
UI/UX
MVP должен не только занимать правильную рыночную нишу, но и быть правильно оформлен. Поэтому UI/UX тоже важны. Продукт должен нравиться пользователям. Есть множество примеров, когда очень классные продукты не взлетали, потому что их UI был недостаточно классным.
А на стадии роста важно, чтобы программистам было удобно это сопровождать. Если переформулировать, то нам должно нравиться то, что мы поддерживаем. На рынке труда достаточно предложений для квалифицированных инженеров, поэтому «современные технологии» — это не просто текст в вакансии.
К сожалению, выбрать что-то одно невозможно. Нам потребуется и UI, и UX, как разные аспекты качества.
В результате мы получили вот такую простую схему:
Но если всё так просто, почему все так не делают? Simple is not easy — дьявол кроется в деталях. И я их сейчас покажу.
Что значит — писать быстро?
В нашей индустрии существует принцип «garbage in — garbage out». Что значит: пока наши требования фиговые (а на этапе стартапа это довольно часто), то сложно сделать хороший продукт. Поэтому чтобы писать быстро, первое, на что мы должны обратить внимание, это на требования.
Фичакат и Specification by Example
Мы используем фичакат (to cut features), чтобы отрезать все лишнее и сосредоточиться на важном. На мой взгляд, лучшая техника для определения что важно, а что нет — это Impact mapping от Гойко Аджича. Я рекомендую прочитать книгу всем, кто работает в разработке, независимо от занимаемой сейчас позиции, благо книга короткая и с картинками.
Это скриншот из книги Гойко АджичайнеЕсли кратко, то суть техники — ответить на вопросы: Зачем? Что? Как? и Кто? Например, на этой карте в ответ на вопрос: «Что мы хотим сделать?» поставлена цель — нарастить присутствие в секторе мобильной рекламы. Задав вопрос «А кто это?», мы можем обнаружить, что это супер-фанаты, организаторы концертов, агенты артистов и промоутеры. Ок, какое воздействие они смогут получить? Фаны могут оставаться дольше, приходить почаще, смотреть больше рекламы. Этот impact мы дальше уже и разбираем, чтобы добавить какую-то функциональность, которая разбивается на определенные истории. В результате мы понимаем, зачем мы что-то делаем, кто на это повлияет и как. То есть получаем дорожную карту — трассировку бизнес — цели на наши фичи.
Если же вдруг в бэклоге появляется фича, что надо добавить котиков, то мы, конечно, можем их добавить — котики милые, всем нравятся, почему нет? Но если сделать трассировку по целям, то мы можем увидеть, сколько денег потратится на котиков. И тут возникает вопрос: готовы ли мы платить столько за котиков, которые никак не приближают наш стратап к цели? Так у нас появляется больше мотивации ориентироваться на метрики, а не на удовлетворение желаний руководителя проекта или других заинтересованных лиц.
Фичакрип
У фичаката, как у любой хорошей вещи, есть злой брат — фичакрип. Он работает ровно наоборот — мы начинаем навешивать новую функциональность, которая в общем-то нам и не нужна. Что такое фичакрип, в 2018 году отлично продемонстрировала японская компания Nissin Cup Noodles. Она твитнула, во что мог бы превратиться дизайн рекламы их лапши быстрого приготовления, если бы дизайнер выполнял каждый запрос заказчика. Начиналось все с простой рекламы: девушка на фоне города и детей рекламирует продукт. А дальше начались правки: девушку лучше сделать блондинкой, глаза у нее должны смотреть на лапшу и как-то повеселее, должно быть модно, стильно, молодежно, и вообще давайте эко-тему добавим. Смешнее, чем эти правки оказался только их результат:
Сравните продукт слева и справа. И то, и другое — реклама, но нет никаких нормальных критериев сказать, что продукт справа лучше, чем продукт слева. А вот времени — которого у нас нет — на второй затрачено гораздо больше.
Specification by example — предельное упрощение всего
В мире проектной деятельности заказчики часто приходят уверенные, что знают, чего хотят. То есть они думают, что они знают, что они хотят. Specification by example хорошо подходит для прояснения этих деталей через иллюстрации, не тратя наше драгоценное время.
Прочтение книги Specification by Example Гойко Аджича побудило семь меня семь лет назад написать первую статью на Хабре. Кто знает, как бы развивались события, не прочитай я ее:)
Этот паттерн, помогающий писать быстро, я покажу на примере самолета F16 из этой книги. Военные запросили у инженеров самолет, который будет развивать скорость, в 3 раза превосходящую скорость звука. Инженеры задумались, как же это сделать, потому что и тогда, и даже сейчас изготовить такой самолет — крайне дорого.
И тогда один из инженеров спросил: «Ребята, а зачем вам такая скорость?» На что военные ответили: «Знаете, у Советов есть МИГи с крейсерской скоростью 2,8, а вообще они могут развивать 3,2. Но мы не хотим с ними воевать. Нам нужно просто прилететь и выполнить боевое задание, а если на радарах появятся МИГи, то быстренько смотаться». Тогда инженер сказал: «А если самолет не будет развивать скорость, но будет таким маневренным, что в него не попадут и не собьют?» — «О, конечно, это нас устраивает!».
Как итог, по состоянию на 2014 год, F16 стоит на вооружении 25 стран мира и является самым распространенным боевым самолетом в мире. Всего одно маленькое уточнение требований помогло достичь такого успеха. Используйте Specification by example вместо требований в виде ТЗ — то есть приведите примеры и разберите их с заказчиком, чтобы понять, что же он все-таки хочет. Так вы дойдете до истинных требований вместо его представлений о том, как получить то, что он хочет.
Технологии
После того как мы точно узнали, что мы строим, можно подумать о технологиях. И здесь первый момент — стоит ли влезать в каждом стартапе в свою in-house-разработку? Есть ли у вас сроки и бюджет на то, чтобы тратить примерно в 6 раз больше времени на собственную разработку (данные из книги Роберта Гласса «Факты и заблуждения профессионального программирования»)? Если это делает другая команда в рамках вашей компании, и бюджет на это уже выделен — да, может быть. Но если ваши ресурсы ограничены (а так бывает часто), то я бы не рекомендовал.
Также не стоит выбирать экзотические и новейшие разработки. У них просто нет комьюнити, инфраструктуры и всего остального. Потому что новые технологические стеки просто еще не успели всё это создать, а старыми экзотическими никто не пользуется.
Самая лучшая технология — та, с которой умеет работать команда. Но у технологии должна быть инфраструктура и широкое community. Потому что это даст вам уже готовые Code Style, UI-kit, наработки, сэкономит вам время и к тому же позволит быстро менять.
Мне сейчас очень нравятся C# и Kotlin в качестве языков программирования для новых проектов. CLR и JVM — два состоявшихся рантайма, для обоих существует взрослая экосистема и набор библиотек и фреймворков на любой вкус. C# очень правильно съехал с иглы Windows на CoreCLR, а Kotlin очень правильно сразу запускался на JVM. Конечно, это не универсальная рекомендация. Для разработки под IOS лучше подойдет Swift, для ML — Python, а если в наличии только команда со знанием JavaScript, то nodejs может быть неплохим выбором.
Что значит — менять быстро?
Быстрый фидбэк
По данным все того же Роберта Гласса разработка ПО чаще всего выходит из-под контроля всего по двум причинам. Первая — это плохая оценка (а в случае запуска стартапа оценка гарантированно будет плохой). А вторая — это изменчивые требования. Но изменчивые требования — это не то, что может произойти. Это случается всегда.
Поэтому нам важно иметь максимально короткий цикл обратной связи, оптимально — раз в день. Тогда вы вовремя поймете, что клиенты хотят не девятиэтажный дом, а коттеджный поселок, объединенный сетью подземных тоннелей. Главное, не перепутайте тестирование гипотез с фичакрипом. Но с этим может помочь impact mapping (техника трассировки по целям).
Удаляйте ненужное
Чтобы быстро менять, мы должны не бояться выбрасывать. Да, прям удалять, ведь есть же git. Если что-то потребуется, мы вернемся на релизе и возьмем этот код. Поэтому удаляйте его, выпиливайте, не бойтесь выбрасывать. Да, это очень больно для программистов. Но, тем не менее, есть определенные техники, которые помогают эту боль немножко ослабить.
Во-первых, конструкторы позволяют писать код быстрее. А сгенерированный код вместо самолично написанного (и выстраданного) выбрасывать не так обидно. Также поддержка наборов тестовых данных помогает тестовые данные, чтобы в случае чего вообще жахнуть базу данных, если мы ошиблись в проектировании. А потом накатить ее заново, внеся существующие тестовые данные скриптом, чтобы не заставлять наших бедных аналитиков и тестировщиков повторять их из бизнес-процесса с самого начала.
Нам помогают также фреймворки. Главное, чтобы это были не супер-монструозные фреймворки, которые выглядят больше как CMS — просто потому что CMS недостаточно гибкий. Опять же цифры Роберта Гласса: если изменения требований составляет больше 30%, то, скорее всего, трудозатраты на изменение существующего продукта превысят 100%. Иными словами, в этом случае выгоднее написать фреймворк с нуля, чем пытаться допилить напильником существующую CMS.
В целом в начале не имеет смысла запариваться слишком сильно над качеством. Излишний фокус на переносимость, или (преждевременную) оптимизацию или на перфоманс на этом этапе — корень всех бед, потому, что на это нет ни бюджета, ни сроков. Зато благодаря этому у нас будет время для проработки и UI/UX, и для удобства его сопровождения.
UI важен
Потому что очень здорово иметь UI-киты и прототипы как на старте, так и в работе с продуктом. Мы используем их и для десктопа, и для мобильного приложения. Но что использовать сначала — Mobile или Desktop — вопрос дискуссионный. Посмотрите, что у вас за продукт, что за пользователи.
Когда прототипы уже нарисованы, ни в коем случае не давайте дизайнерам воплощать все самые смелые их фантазии о визуальном стиле. Возможно, ваш дизайнер гениален, а ваш UI — фича вашего продукта. Но такое бывает редко. Более надежный и консервативный подход — взять прототипы и готовый UI-kit, например, Material. Потому что у разработчика Material есть PHD по UI/UX, а есть ли PHD у вашего дизайнера?
Некоторые дизайнеры могут даже возводить требования в абсолют, и у этого безобразия есть имя — Pixel-perfect. Есть много дизайнеров, уверенных, что если у вас проект сверстан не по Pixel-perfect, это вообще ужас и ваши разработчики криворукие. Хотя разные браузеры по-разному рендерят шрифты, и вообще дизайн — это про наложение элементов и удобство пользования вашим продуктом.
И здесь еще есть другой важный момент. Корректная обработка ошибок важна, так как обрабатывать ошибки надо правильно. Это важный аспект качества. Мы используем так называемое «Железнодорожно-ориентированное программирование» от Скотта Влашина (перевод-расшифровка доступна на Хабре). Доклад очень классный и визионерский — он в принципе о том, как сильно наша индустрия забивает на обработку ошибок.
Удобство сопровождения
Модульная архитектура — это здорово
Все знают, что все проблемы в разработке из-за страшных, ужасных монолитов, которые не масштабируются и через пару месяцев превращаются в big ball of mud. Единственное лекарство от монолита — это конечно же микросервисы. Никто, правда, не вспоминает, что эти самые прекрасные микросервисы очаровательно сложны с точки зрения инфраструктуры.
Вообще идея микросервисов довольно банальная. Если у вас есть слоёное приложение и в нём есть кусок бизнес-функциональности — почему бы этот кусок не положить в отдельный сервис? Абсолютный Unix-way, который существовал в Unix задолго до изобретения этих концепций. А дальше мы можем разделить всю нашу бизнес-функциональность на отдельные модули. И совершенно не обязательно, чтобы получить преимущество от разделения на отдельные вертикальные бизнес-срезы, делать ваше приложение распределенным и добавлять отдельную сложность по удаленному взаимодействию ваших сервисов.
Да, это нужно Netflix, ему надо публиковать отдельные инстансы приложений в разное время и делать это независимо. Подумайте — вы Netflix или нет? Если нет, то модульный монолит может оказаться гораздо проще, и при этом обладать очень многими плюсами микросервисного подхода.
By feature, а не by layer
Альтернатива микросервисам — модульный монолит. Организация по фичам в виде модульной архитектуры (а не по слоям и даже не по микросервисам), может стать тем, что вам понравится использовать. С ней удобно менять маленькие кусочки программы без дополнительных расходов на инфраструктуру — как если бы вы начинали сразу с микросервисов.
Подробнее о микросервисах в статье «Остановитесь!!! Вам не нужны микросервисы» и в докладе Джимми Богарда «Avoiding Microservice Megadisasters». А о том, как проектировать модульные монолиты — в моем докладе «Быстрорастворимое проектирование» (расшифровка доступна на Хабре).
И если код организован модульно, то можно реализовать Feature toggle, который поможет с быстрыми релизами, особенно когда в последний момент надо что-то менять.
Feature toggle
Для большинства наших проектов эта фича оказалась очень удобной. Я узнал об этом достаточно давно из доклада одного из инженеров мобильной команды Facebook. Он рассказывал примерно так: «Иногда так бывает, что мы приготовили релиз, а дальше прибегает Марк и говорит»:
— Так, ребята, те три фичи, которые вы пилили, пока не надо релизить. У меня есть другая, супер-классная, запилите, пожалуйста, ее.
— Марк, но у нас же уже все смержено, в релизный бренч все протестировано. Что же нам делать?
— Мне пофиг, заливайте мою фичу.
И тогда они добавили переключатели, чтобы выбирать, какой код может пройти в релиз. Особенно это актуально для мобильных приложений, потому что надо ждать ответ от магазина, когда же он проверит приложение. А так можно какую-то фичу выключить, заменить на другую, включить нужную — и собирать проект из таких блоков. Это просто очень удобно.
Нетехнический аспект
Бывает так, что команда усердно трудится, делает ошибки, поклоняется Cargo-культу, но есть один человек, который знает, как правильно. И он рассказывает, как правильно, указывая на все ошибки, но ничего не делает для того, чтобы их исправить. И если у вас есть такой товарищ и есть возможность, чтобы он не участвовал, то скорее всего, без него будет лучше, каким бы классным специалистом он ни был. Потому что так всем работать будет проще и комфортнее.
Заключение
В итоге у нас получилась вот такая схема:
Если раньше для каждого нового клиента мы подбирали подход, теперь нам достаточно одного. Мы смогли повысить интенсивность разработки и теперь успеваем и новые запросы обработать, и существующую функциональность развить /допилить, и технический долг уменьшить. Не могу сказать, что проблем вообще не осталось, но жить стало определенно легче.
TechLead Conf 2021 — конференция, полностью посвященная инженерным процессам и практикам — случится совсем скоро. 30 июня и 1 июля она пройдет в Radisson Slavyanskaya (Москва). Расписание уже готово. Билеты еще есть. И открыт доступ к докладам TechLead Conf 2020.
До встречи в оффлайне!