Web Apps: быстро, дёшево, круто


Каждый Web App (веб-апп) — это маленькое и весёлое фронтенд-приключение.
Однажды в iFunny мы устроили «веб-апп марафон», развлекли миллионы пользователей и попутно заработали много денег. А теперь готовы рассказать, как же это всё было.


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


WebAppView


WebView — это встроенный в мобильное приложение браузер.
А если есть браузер, значит, есть и обширные возможности HTML5, JS, CSS.
Это в теории. А как же на самом деле?


А на самом деле нас интересовали и другие вопросы:


  • как хорошо примет новый формат аудитория iFunny?
  • насколько проблемны в разработке такие приложения? Два года назад в компании отсутствовал опыт в этой части, и очень хотелось его наверстать;
  • сэкономит ли данная технология время и деньги? Цикл разработки таких приложений короче, следовательно, такая возможность есть.

Web App #1: Hellowinner


В те времена команда состояла из двух человек и над всеми веб-аппами работал я один. С момента моего присоединения к команде прошло два месяца, поэтому с корабля я прямиком попал на «веб-апп бал».


Первое приложение было подготовлено к 31 октября — дню празднования Хеллоуина. С помощью мини-игры с двумя кнопками планировалось пощекотать нервы пользователям и вызвать вот такую реакцию:


Web App #1: Hellowinner


Само же приложение выглядело вот так:


Web App #1: Hellowinner


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


Технологический стек был простым:


  • генератор статичных сайтов Harp;
  • шаблонизатор Jade (ныне Pug);
  • препроцессор LESS.

Генератор Harp предоставил полную инфраструктуру из коробки, так что я написал код, сгенерировал статичный сайт с CSS, HTML, JS, положил всё это дело на CDN, завернул в WebView и радовался. И было чему: первый опыт с веб-приложениями дал такой пик посещаемости, что позже каждый product-менеджер считал своим долгом придумать что-то интересное.


Web App #2: Putin vs Trump


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


В следующем веб-аппе было решено проводить опросы пользователей. Разумеется, в любимом формате — на скользкие и горячие темы. Первый опрос назывался President you want, и в нём нужно было выбрать Путина или Трампа.


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


В качестве базы данных я использовал MongoDB: API для мобильных клиентов iFunny крутился именно на данной системе, был готовый кластер, и выбор был очевиден. Express.js был выбран в качестве серверного фреймворка. Все тот же Jade (Pug) в качестве шаблонизатора, LESS в качестве препроцессора и сборка статики с помощью Gulp.


В первые часы после запуска нового приложения проголосовало свыше миллиона пользователей. И тут произошел курьёз: сумма процентов перевалила за 100%. Не 146% конечно, рядом.


Вместо того чтобы использовать стандартный инкремент из базы данных, я использовал банальное «текущее значение = текущее значение + 1». А про такое понятие, как конкурентность запросов, узнал вообще только в продакшене. Ну да, пустили слона в посудную лавку.


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


Тот самый опрос, исправленный:


Web App #2: Putin vs Trump


Web App #2: Putin vs Trump


Web App #3: Booby Bird


Web App #3: Booby Bird


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


Парень — молодец, постарался на славу:


  • собрал игру в WYSIWYG-редакторе;
  • написал серверную часть на PHP по мануалам со StackOverflow;
  • скрепил эти части с помощью известного только ему одному клея;
  • отдал результаты в виде обфусцированных файлов.

Мой оптимизм окончательно иссяк после того, как приложение наотрез отказалось запускаться на боевом домене. В итоге, я перенес код на уже проверенный Express.js и, насколько смог, внес заплатки в обфусцированный клиентский код. Игра ожила. Уже тогда было понятно, что конструкция хрупкая и держится на честном слове.


В конце игры был рейтинг лучших игроков и, разумеется, его сломали через 10 минут после релиза. Пользователи раскопали ссылку на сохранение результатов и массово принялись менять в ней значения. Я выпустил обновление, в котором персонализировал ссылку с помощью JWT-токена, и массовое окучивание топа закончилось.


Вот только я не мог защитить код, который целиком и полностью попадал к пользователю: обфускация разбиралась, а запросы по сети просматривались в соответствующих утилитах. Отдельные юзеры, которые умели пользоваться снифферами, снова разобрали ссылку с результатами и много разной ерунды туда послали. За что потом сами были отправлены в бан, благо идущий в комплекте JWT-токен оказался им не по силам.


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


Web App #4: Holiday Giveaway


Web App #4: Holiday Giveaway


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


«Под капотом» были все те же Express.js, Jade (Pug), LESS. Ожидался большой пик посещаемости, и в качестве базы данных я использовал отдельный кластер MongoDB. И даже провел нагрузочное тестирование, чтобы вот в этот раз все точно получилось как надо. Ну, вы понимаете.


Веб-апп показал отличные результаты: около 3 миллионов участников уже в первом туре. Вот только вместе с успехом пришли и трудности.


Сначала проблема возникла в связке NGINX и PM2, который использовался для управления инстансами приложения. Мне искренне было страшно, я вообще не понимал, что делать: HTML5, CSS, JS, а тут какие-то таймвейты в сети, озадаченные DevOps-инженеры и молчаливый требовательный взгляд технического директора. Спустя некоторое время проблема была устранена, а до следующего розыгрыша призов был запас времени. Я наивно начал полагать, что теперь всё хорошо, отмучился.


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


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


Такие жертвы были оправданы: спустя 24 часа у веб-аппа было более 12 миллионов просмотров, тысячи комментариев и большой шум по социальным сетям. Пользователи сильно взбудоражились: им определённо понравилось приложение. Да и мне понравилось, что скрывать.


Минусы и плюсы веб-аппов


Минусы:


  • много слез было пролито с «Самсунгами», потому что WebView — это дефолтный браузер, а «Самсунги» очень любят все кастомизировать. И если десктопные браузеры, мобильные Chrome и Safari отображали веб-апп корректно, то у «Самсунгов» обязательно что-нибудь разносило. Эта марка очень популярна в США, приходилось чинить;
  • после того как были устранены все проблемы отображения могло оказаться, что приложение некорректно работало в боевых условиях. И причина была банальной: если при сборке мобильного приложения забыли поставить пару галочек — и раз! — скролл в WebView уже не работал как надо. А это, скорее всего, новый релиз и минимум несколько дней отложенного запуска.

Плюсы:


  • быстро. Среднее время разработки — две недели. Первая неделя — активная разработка, вторая — тесты, фиксы и доработка;
  • дёшево. Экономия стоимости разработки огромная. Веб-апп может создать один фронтенд-разработчик для всех платформ. Дорогие мобильные девелоперы здесь не привлекаются. И даже если привлекаются, то редко и мало.
  • круто. Формат действительно можно сделать интересным для пользователей. Судя по многим показателям (количеству комментариев, регистраций, времени сессий в играх), аудитории нравится такая интерактивность. Им нравится подмечать ошибки и смаковать их, нравится ломать и улетать в баны, нравится всё это дело обсуждать. А что может быть лучше подогретого и вовлечённого сообщества для UGC-продукта?

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


Сейчас разработка веб-аппов уже поставлена на поток: за прошедший 2017 их было 94, а по количеству сессий что-то около 101 миллиона. Кстати, большую часть можно посмотреть по ссылке.


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


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


К слову, те «ближайшие 2 года» мне всё-таки предстояло «пилить сайт», только в качестве новоиспеченного лида формировавшейся с нуля фронтенд-команды. Этот опыт повеселей веб-аппов будет. О нём расскажу в следующей статье.

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


  1. trigun117
    07.02.2018 15:14

    Я думаю что если приложению не нужен офлайн режим, то это идеальное решение с точки зрения цена/качество.


  1. DimNS
    08.02.2018 09:26

    «И причина была банальной: если при сборке мобильного приложения забыли поставить пару галочек»

    Поделитесь пожалуйста этими галочками. Как раз намедни воевал с Самсунгами и тут раз и статья в тему.


    1. veretennikov Автор
      08.02.2018 15:37

      У меня были две проблемы.
      Во-первых, мобильный клиент работал с жестами. И как следствие, частично перехватывал управление. В итоге ребята из мобильной команды внедрили в клиент механизм, который для игр вообще отключал жесты. А для всего остального проверял эту необходимость по canScrollVertically.
      Во-вторых, в самой верстке пришлось отказаться от overflow:hidden на корневых элементах. Это свойство препятствовало детектированию скролла в WebView.


      1. DimNS
        08.02.2018 15:47

        Забавно, при этом весь код локально на десктопе всегда работал без запинки да? ))


        1. veretennikov Автор
          08.02.2018 16:22

          И на десктопе. И в мобильных браузерах на реальных устройствах. И в эмуляторах. Все самое интересное начиналось при открытии в WebView. Мы даже тестовые веб-аппы делали только для проверки возможностей в реальных мобильных клиентах.


          1. DimNS
            08.02.2018 16:40

            Сейчас для решения своих проблем копаюсь в интернете, и тут я вспомнил, а ведь есть такая штука как Crosswalk WebView Cordova Plugin (правда она для вас не подходит, а для меня в самый раз). Плагин тянет себе в apk свою версию хрома, увеличивая размер apk на 17 Мб, а размер установленного приложения на 50 Мб. Да крутой такой оверхед, но если задумываться о цене поиска и исправления всех будущих багов и глюков, то возможно можно и пожертвовать.

            Хотя это такое себе решение, если все дружно так будут делать то общий оверхед зашкалит.

            Возглас в пустоту: Ну почему тюнинговщики Андроида такие зас***цы и что-то там химичат со встроенным браузером (( Думаешь ну вот оно наступило счастье можно быстро и дешево делать приложения для мобильных устройств и тут такая подлянка.


  1. alekciy
    08.02.2018 10:24

    10к RPS? А какого рода запросы (статика? динамика)? Бэк — нода? Это пик запросов или среднее значение (медиана?)? Если это относительно постоянная нагрузка, то как долго держится и на каком железе?


    1. veretennikov Автор
      08.02.2018 16:24

      10К — это пиковое значение. Все запросы — динамические. Северная часть была написана на Node.js с использованием фреймворка Express.js. В качестве железа выступила пара инстансов Amazon EC2. Наибольшую нагрузку приложение получило сразу после публикации в основной ленте работ. Это вообще особенность всех наших веб-аппов — ощутимые нагрузки сразу же после публикации. И спокойная размеренная жизнь спустя пару часов.


  1. cmohammedmedkeveo
    08.02.2018 15:12

    А использовали cordova, ionic или что-то такое?


    1. DimNS
      08.02.2018 15:34

      Вот как раз с cordova сейчас воюю.

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

      Непонятные глюки были на самсунгах (Android 6), приложение подгружало первый скрипт с сервера но не выполняла его, хотя стояло событие на выполнение кода после полной загрузки скрипта. Порешал поставив таймаут в 2 секунды перед запуском кода загруженного скрипта, проблема исчезла.

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

      При этом весь код без проблем работает на десктопе в браузере Хром, вообще никаких глюков.

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

      P.S. В качестве фронта использую Framework7


    1. veretennikov Автор
      08.02.2018 15:46

      Нет. Для наших задач эти решения были избыточны.


    1. DimNS
      08.02.2018 15:50

      А чем вы тогда упаковывали приложения в WebView, своими силами через нативный код стандартными средствами разработки платформы Android, iOS?


      1. veretennikov Автор
        08.02.2018 16:16

        iFunny — нативный клиент. Поэтому все делалось стандартными средствами.