Похоже, что в мире стартапов существует общее мнение, что вы должны создавать MVP (минимально жизнеспособный продукт), не слишком заботясь о технической масштабируемости. Я много раз слышал, что единственный приоритет - просто выпустить продукт. Пока ваша бизнес-модель будет работать в масштабе, у вас все хорошо. Не стоит тратить время и деньги на создание технически масштабируемого продукта. Все, о чем вы беспокоитесь, - это проверять свои предположения, проверять рынок и набирать обороты. О масштабируемости мы поговорим позже. К сожалению, это несколько слепое убеждение привело к ужасным неудачам. И Pokémon GO напомнил нам об этом.

Один человек, который больше не совершит эту ошибку, - это Джонатан Зарра, создатель GoChat для Pokémon GO. Парень, который за 5 дней достиг 1 миллиона пользователей, создав приложение для чата для фанатов Pokémon GO. На прошлой неделе, как вы можете прочитать в статье, он разговаривал с венчурными инвесторами, чтобы узнать, как он может развивать и монетизировать свое приложение. Сразу после этого GoChat отключился. Много пользователей потеряно и много денег потрачено. Настоящий позор за гениальный ход.

В статье говорится, что Зарра с трудом оплачивал серверы, необходимые для размещения 1 миллиона активных пользователей. Он никогда не думал, что у него будет столько пользователей. Он создал это приложение как MVP, позже позаботившись о масштабируемости. Он построил его на провал. Зарра нанял подрядчика для Upwork, чтобы исправить множество проблем с производительностью. Подрядчик заявил, что стоимость сервера составляет около 4000 долларов. Поскольку в моем календаре 2016 год, я предполагаю, что он говорит не о 4000 долларов на оборудование, а о 4000 долларов ежемесячных или годовых виртуальных серверов и затрат на трафик.

На протяжении большей части своей карьеры я проектировал и создавал веб-платформы для сотен миллионов активных пользователей. Я могу сказать, что 4000 долларов - это совершенно ненужная сумма для 1 миллиона пользователей в приложении для чата. Даже для MVP. Это означает, что серверная технология приложения была плохо спроектирована. Нелегко построить экономичную масштабируемую систему для миллионов пользователей в месяц. Но также не так уж сложно иметь какую-то настройку, которая могла бы обслуживать, по крайней мере, приличное количество пользователей на некоторых дешевых серверах в облаке. Вам просто нужно принять это во внимание при создании MVP, сделав правильный выбор.

GoSnaps: 500 000 пользователей за 5 дней на сервере за 100 долларов в месяц

Подобно GoChat, на прошлой неделе я также запустил фан-приложение Pokémon GO под названием GoSnaps. GoSnaps - это приложение для публикации снимков экрана и изображений Pokémon GO на карте. Instagram / Snapchat для Pokémon GO. GoSnaps вырос до 60 тыс. Пользователей в первый день, 160 тыс. Пользователей во второй день и 500 тыс. Уникальных пользователей через 5 дней (что сейчас). Сейчас на него загружено 150–200 тыс. Снимков. Он имеет около 1000 одновременных пользователей в любой момент времени. Я создал программное обеспечение для распознавания изображений, чтобы автоматически проверять, связано ли загруженное изображение с Pokémon GO, и инструменты для изменения размера загруженных изображений. Мы запускаем всю эту установку на одном среднем сервере Google Cloud стоимостью 100 долларов в месяц плюс (дешевое) облачное хранилище Google для хранения изображений. Да, 100 долларов. И работает хорошо.

Техническое сравнение GoChat и GoSnaps

Давайте сравним GoChat и GoSnaps. Оба приложения, вероятно, запускают много запросов в секунду для получения чатов / изображений в определенной области карты. Это геопространственный поиск в базе данных (или поисковой системе) либо по многоугольнику местоположений широты / долготы, либо по определенной точке. Мы используем многоугольник и запускаем этот запрос каждый раз, когда кто-то перемещает карту. Эти типы запросов представляют собой тяжелые операции с базой данных, особенно в сочетании с сортировкой или фильтрацией. Мы получаем этот тип поискового запроса сотни раз в секунду. GoChat, вероятно, тоже.

Уникальность GoChat в том, что ему приходилось получать и публиковать множество сообщений чата каждую секунду. В статье о GoChat говорится о 600 запросах в секунду для всего приложения. Эти 600 запросов представляют собой комбинацию запросов карты и сообщений чата. Эти сообщения чата небольшие, и их можно / нужно отправлять через простое соединение через сокет, но они случаются часто и должны быть переданы другим участникам чата. Этим можно управлять при правильной настройке, но катастрофически при плохой настройке, подобной MVP.

GoSnaps, с другой стороны, имеет множество изображений, которые загружаются и "лайкаются" каждую секунду. Снимки накапливаются на сервере, поскольку старые снимки остаются актуальными. Старых чатов нет. Поскольку фактические файлы изображений хранятся в облачном хранилище Google, количество запрошенных файлов изображений не является проблемой для меня как разработчика. Google Cloud справляется с этим, и я доверяю Google. Но меня беспокоят запрошенные снимки на карте. GoSnaps имеет программное обеспечение для распознавания изображений, которое ищет шаблоны во всех загрузках, чтобы определить, связано ли изображение с покемонами или нет. Он также изменяет размер изображений и отправляет их в облачное хранилище. Все это тяжелые операции с точки зрения ЦП и пропускной способности. Намного труднее, чем рассылать небольшие сообщения в чате, но реже.

Я пришел к выводу, что оба приложения очень похожи с точки зрения масштабируемости. GoChat обрабатывает больше небольших сообщений, в то время как GoSnaps обрабатывает большие изображения и более тяжелые серверные операции. Разработка архитектуры для этих двух приложений требует немного разного подхода, но одинаково сложна.

Как я создал масштабируемый MVP за 24 часа

GoSnaps создан как MVP, а не как профессиональный бизнес-продукт. Он был построен полностью за 24 часа. Я взял шаблонный проект NodeJS для хакатонов и использовал базу данных MongoDB без какого-либо кеширования. Ни Redis, ни Varnish, ни модных настроек Nginx, ничего. Фактическое приложение для iOS было построено на собственном коде Objective-C с использованием некоторого кода, связанного с Apple Maps, из Unboxd, нашего основного приложения. Итак, как мне сделать его масштабируемым? Не быть ленивым.

Скажем, я бы рассматривал MVP только как гонку на время, направленную на создание функционального приложения как можно быстрее, независимо от технического качества серверной части. Куда бы я поместил свои изображения? В базе: MongoDB. Это не потребует настройки и почти никакого кода. Легкий. MVP. Как бы я запросил снимки в определенной области, получившие наибольшее количество лайков? Просто запустив простой запрос MongoDB ко всей куче загруженных снимков. Всего один запрос к базе данных в одной коллекции. MVP. Все это разрушило бы мое приложение и его функцию.

Посмотрите на запрос, который мне пришлось бы выполнить, чтобы получить эти снимки: «найти все привязки в многоугольнике местоположения [A, B, C, D], за исключением снимков, отмеченных как злоупотребление, исключая снимки, которые все еще обрабатываются, упорядоченные по количеству лайки, сначала заказанные действующими снимками Pokémon GO, а затем сначала заказанные новейшими ». Это отлично работает с небольшим набором данных, отлично, MVP. Но это было бы катастрофой при любой серьезной нагрузке. Даже если бы я упростил приведенный выше запрос, включив только три условия / операции сортировки, это было бы катастрофой. Почему? Потому что это не то, как предполагается использовать базу данных. База данных должна запрашивать только один индекс за раз, что невозможно с этими геопространственными запросами. Вам это сойдет с рук, если у вас мало пользователей, но вы проиграете, как только добьетесь успеха. Нравится GoChat.

Что я сделал вместо этого? После применения дорогостоящего процессора распознавания изображений и изменения размера изображения с измененным размером загружаются в Google Cloud Storage. Таким образом, сервер и база данных не получат обращения при запросе изображений. База данных должна беспокоиться о данных, а не об изображениях. Само по себе это экономит много серверов. Что касается базы данных, я разделяю снимки на несколько разных коллекций: все снимки, самые популярные снимки, новейшие снимки, новейшие действительные снимки и так далее. Всякий раз, когда снап добавляется, нравится или помечается как злоупотребление, код проверяет, принадлежит ли он (все еще) к одной из этих коллекций, и действует соответствующим образом. Таким образом, код может запрашивать из подготовленных коллекций вместо выполнения сложных запросов к одной огромной куче беспорядка. Это просто логическое разделение данных на несколько простых сегментов. Ничего сложного. Но это позволяет мне запрашивать исключительно геопространственные координаты с помощью одной операции сортировки, а не сложного запроса, как описано выше. Проще говоря: это упрощает выбор данных.

Сколько дополнительного времени я потратил на все это? Может быть, 2-3 часа. Почему я вообще это сделал? Потому что именно так я все устроил. Я предполагаю, что мои приложения будут успешными. Нет смысла создавать приложение, предполагая, что оно не будет успешным. Я не смогу заснуть, если мое приложение наберет обороты, а затем умрет из-за плохих технологий. Я внедряю в свое приложение минимальные жизнеспособные принципы масштабируемости. В этом разница между счастьем и полной паникой. Я считаю, что это должно быть частью MVP приложения.

Выберите подходящие инструменты для своего MVP

Если бы я создал GoSnaps с более медленным языком программирования или с большим фреймворком, мне потребовалось бы больше серверов. Если бы я использовал что-то вроде PHP с Symfony, Python с Django или Ruby on Rails, я бы тратил свои дни на исправление медленных частей приложения или добавление серверов. Поверьте, я делал это много раз раньше. Эти языки и фреймворки отлично подходят для многих сценариев, но не для MVP с небольшим серверным бюджетом. Это в первую очередь связано с множеством уровней кода, которые обычно используются для сопоставления записей базы данных с логикой и ненужным кодом инфраструктуры. Он просто слишком сильно ударяет по процессору. Позвольте мне привести пример того, насколько это действительно важно.

Как уже говорилось, GoSnaps использует NodeJS в качестве внутреннего языка / платформы, что, как правило, работает быстро и эффективно. Я использую Mongoose в качестве ORM, чтобы MongoDB работала просто как программист. Я ни в коем случае не эксперт по Mongoose, и я знаю, что библиотека сама по себе имеет огромную кодовую базу. Следовательно, Мангуст был красным флагом. Но да, MVP. В какой-то момент на прошлых выходных 4 процесса нашего сервера NodeJS работали с 90% ЦП каждый, что для меня неприемлемо для 800–1000 одновременных пользователей. Я понял, что это должен быть Mongoose, который что-то делает с моими полученными данными. По-видимому, мне просто пришлось включить функцию Mongoose «Lean ()», чтобы получать простые объекты JSON вместо волшебных объектов Mongoose. После этого изменения загрузка ЦП процессами NodeJS упала до 5–10%. Очень важна простая логика знания того, что на самом деле делает ваш код. Это снизило нагрузку на 90%. Представьте, что у вас действительно тяжелая библиотека, такая как Symfony с Doctrine. Потребовалась бы пара серверов с множеством ядер ЦП, чтобы просто выполнять только код, даже если узким местом должна быть база данных, а не код.

Выбор экономичного и быстрого языка важен для масштабируемости, если у вас нет больших денег на серверы. Выбор языка с множеством доступных полезных библиотек еще более важен, поскольку вы хотите быстро создать свой MVP. NodeJS, Scala и Go - хорошие языки, отвечающие обоим этим требованиям. Они предоставляют множество хороших инструментов с хорошей производительностью. Такие языки, как PHP или Java, сами по себе не обязательно медленные, но обычно используются вместе с большими фреймворками и кодовыми базами, которые усложняют приложение. Эти языки отлично подходят для чистой объектно-ориентированной разработки и хорошо протестированного кода, но не для быстрой и дешевой масштабируемости. Я не хочу начинать споры о языке программирования, поэтому позвольте мне заявить, что это субъективно и неполно. Я лично люблю Erlang и никогда не стал бы использовать его в качестве MVP, поэтому все ваши аргументы недействительны.

Мои предыдущие стартапы Cloud Games

Несколько лет назад я стал соучредителем Cloud Games, издателя игр HTML5. Когда мы начинали, мы были игровым сайтом B2C, ориентированным на регион MENA. Мы потратили много усилий на привлечение пользователей и через несколько месяцев достигли 1 миллиона активных пользователей в месяц (MAU). В то время я использовал PHP, Symfony2, Doctrine и MongoDB в довольно простой и компактной настройке. Раньше я работал в Spil Games с 200 миллионами MAU, которые в то время использовали PHP, а затем перешли на Erlang. После того, как Cloud Games достигла примерно 100 000 MAU, мы начали видеть настоящую проблему с сервером с Doctrine и MongoDB из-за огромных накладных расходов этих библиотек PHP. Я правильно настроил MongoDB, индексы и запросы, но серверам было трудно обрабатывать весь код. И да, я использовал кеш APC PHP и так далее.

Поскольку cloudgames.com все еще оставался очень статичным, я смог перенести MVP на NodeJS с помощью Redis за несколько дней. Аналогичная настройка, другой язык. Это привело к немедленному снижению нагрузки примерно на 95%. Конечно, это было больше связано с отказом от библиотек PHP, чем с реальным языком. Но минималистичная установка NodeJS имеет больше смысла, чем минималистичная установка PHP. Тем более, что MongoDB и код внешнего интерфейса также на 100% состоят из JavaScript, как и NodeJS. PHP без своих фреймворков и библиотек - это просто еще один язык.

Нам была нужна эта дешевая установка, поскольку мы были самофинансируемым стартапом на ранней стадии. Cloud Games сейчас преуспевает и по-прежнему основывается на экономичной архитектуре NodeJS. Возможно, нам не удалось бы добиться успеха с более дорогостоящей технологической установкой, учитывая тот факт, что мы пережили действительно тяжелые времена в качестве стартапа. Создание недорогой масштабируемой архитектуры было важным условием успеха.

MVP и масштабируемость могут сосуществовать

Если у вашего приложения есть возможность экспоненциального роста из-за шумихи или возможного освещения в СМИ, обязательно учитывайте масштабируемость как часть своего MVP. Принципы минимальной жизнеспособности продуктов и масштабируемых технологий могут сосуществовать. Нет ничего печальнее, чем создать успешное приложение и увидеть, как оно терпит неудачу из-за технических проблем. У самого Pokémon GO было много проблем, но он настолько уникален и разрекламирован, что это не имело значения. У небольших стартапов нет такой роскоши. Время решает все. Вероятно, со мной согласны миллион пользователей GoChat и полмиллиона пользователей GoSnaps.

Редактировать 21 июля 2016 г.

Я немного отредактировал статью, потому что GoChat все еще жив в магазине Google Play. На странице Google Play говорится, что он «вернулся на 100%» с «более чем 2 миллионами пользователей». Версия для iOS должна скоро появиться снова.

Пожалуйста, ставьте лайки и подписывайтесь!

Если вам понравилась эта статья, поставьте лайк здесь, на Medium. Это много для меня значило. Не стесняйтесь комментировать советы по масштабируемости. В Unboxd мы всегда рады росту других приложений! И жители Нью-Йорка: ознакомьтесь с нашим приложением Кухня! Спасибо!