Парное веб-приложение — это браузерная сторона продукта, который живёт прежде всего в мобильном приложении: место, где клиент регистрируется до установки, где расшаренная ссылка открывается у того, у кого приложения нет, где работа, начатая на телефоне, продолжается на десктопе. Это тот же продукт, на том же API, оформленный под то, что браузер действительно умеет хорошо, — а не порт приложения и не брошюра о нём.

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

Какие продукты мы делаем

  • Клиентские веб-приложения рядом с мобильными — полный продукт в браузере там, где это оправдано, или осознанное подмножество там, где нет
  • Публичные, расшариваемые страницы — страницы, в которые разворачивается пересланная ссылка, отрендеренные и индексируемые, с передачей в приложение, когда оно установлено
  • Веб-клиенты реального времени — живые данные в браузере из той же ленты, которую потребляют приложения
  • Бэк-офисы того же продукта — операторская сторона — отдельная дисциплина, разобранная на странице админ-панелей и дашбордов
  • Веб-сторона на Nuxt — когда парная веб-часть должна ранжироваться, машинерия для этого — наша практика разработки на Nuxt

Кто будет делать ваше парное веб-приложение

Паттерн видно на двух продуктах в продакшене: у каждого — приложение и веб на одном общем бэкенде.

Formtastic — одна платформа, три клиента

Formtastic работает как веб-приложение на Nuxt плюс Flutter-приложения для iOS, iPad и Android — все против одного бэкенда на Django и Go. Показательнее всего то, насколько по-разному одна и та же функция устроена на каждой стороне: голосовой ввод в вебе записывает аудио в браузере и отправляет его на эндпоинт транскрипции, потому что бэкенд, который можно расширить, уже был, — а мобильные приложения несут Whisper прямо на устройстве, потому что на объектах нет связи. Одна функция, две реализации, и каждая честно считается со своей средой. Именно это суждение, больше чем владение фреймворком, и есть то, чего продукт с двумя клиентами требует на самом деле.

ExtraETF — одна лента реального времени, все клиенты

Для ExtraETF мы построили на Go сервис на базе , который транслирует живые рыночные данные и веб-, и мобильным клиентам: тысячи одновременных подключений, обновления сведены к той частоте, которая реально нужна человеку, следящему за ценой. Мы сделали мобильные приложения и слой реального времени; веб-клиент, который его потребляет, принадлежит команде самого клиента — и в этом весь смысл. Хорошо спроектированный сервис кормит и те интерфейсы, которые вы никогда не будете строить сами.

Чего на самом деле требует продукт с двумя клиентами

Четыре дисциплины удерживают приложение и веб от превращения в два разных продукта.

Один API, честно обслуживающий обоих

В момент, когда вебу нужен «всего один особый эндпоинт», у вас уже два бэкенда под одним именем. Мы проектируем API как единый контракт продукта — одна аутентификация, одни модели, одно версионирование для каждого клиента, — а там, где сторонам действительно нужны разные формы данных, это различие прописывается в контракте, а не залатывается в клиенте.

Решить, чем является веб-версия

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

Передача между вебом и приложением

Пересланная ссылка должна открывать приложение, когда оно установлено, веб-страницу — когда нет, и никогда — сломанную промежуточную страницу. Universal links на iOS и app links на Android, фолбэк-страницы за ними и сценарий регистрации, который начинается в браузере и продолжается в приложении, — на этом шве продукты с двумя клиентами теряют пользователей заметнее всего, поэтому мы прорабатываем его намеренно, не рассчитывая, что он заработает сам.

Рендеринг под задачу страницы

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

Как мы работаем

Проект с фиксированным объёмом. Мы ведём веб-сторону от определения набора функций до деплоя — против вашего существующего API или того, который построим рядом с ним.

Staff augmentation. Наши инженеры входят в команду, которая владеет продуктом, — см. расширение команды. Если мобильную сторону тоже нужно строить, это наша практика Flutter.

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

Сколько стоит парное веб-приложение

Парная веб-часть оценивается по тем же опубликованным тарифам, что и страница веб-разработки: веб-приложение с авторизацией на вашем существующем API попадает в тариф веб-приложения, 1 350 000–3 600 000 ₽, а полноценная вторая сторона продукта с реальным временем, платежами или мультитенантной структурой оценивается как работа уровня SaaS — от 4 500 000 ₽.

Это те же ключи, из которых рисуется таблица цен пилларной страницы, поэтому разойтись они не могут. Что двигает цифру: какая часть API уже существует и насколько чисто он спроектирован под больше чем одного клиента, какую часть продукта покрывает веб-версия и едут ли функции реального времени по существующей ленте — или её нужно строить.

Частые вопросы

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

Не всегда — и честный ответ зависит от того, где утекает ваш рост. Веб-сторона окупает бюджет, когда регистрации начинаются с поиска или пересланных ссылок, когда пользователи просят работать на большом экране или когда каждый расшаренный кусок контента сейчас упирается в тупик для людей без приложения. Если ничего из этого не про вас, мы так и скажем: хорошо сделанной карточки в сторе и диплинков может быть достаточно.
Обычно нет, и решить, что оставить за бортом, — большая часть работы по определению объёма. Браузер выигрывает в достижимости — поиск, пересланные ссылки, мгновенный доступ без установки — и в работе с клавиатурой на большом экране; приложение выигрывает камерой, офлайном, уведомлениями и тем, что оно в одном касании. Голосовой ввод Formtastic — образцовый пример: веб записывает в браузере и транскрибирует на сервере, приложения транскрибируют на устройстве, потому что на объектах нет связи. Одна функция, разные честные реализации — а часть функций справедливо остаётся только в приложении.
Мы регулярно строим поверх существующих API: веб-фронтенд Formtastic работает на том бэкенде на Django, который у продукта уже был, расширенном там, где это понадобилось вебу. Первым делом мы оценим, действительно ли API готов к нескольким клиентам: аутентификация, которой браузер может пользоваться безопасно, модели, не предполагающие сценариев только для приложения, и версионирование, позволяющее двум клиентам развиваться. Где он не дотягивает, мы точно скажем, что менять, — и можем сделать обе стороны сами, если так быстрее.
Да — это вопрос проектирования ленты, а не фреймворка. Для ExtraETF мы построили на Go сервис на базе WebSocket, который транслирует живые рыночные данные и веб-, и мобильным клиентам: тысячи одновременных подключений, обновления сведены примерно к одному на инструмент в секунду — частоте, которая реально нужна человеку. Спроектируйте слой реального времени один раз и правильно — и каждый нынешний и будущий клиент будет потреблять один и тот же поток.
Через universal links на iOS и app links на Android: один и тот же URL открывает приложение, когда оно установлено, и веб-страницу, когда нет, — причём веб-страница несёт полный контент, а не уговоры установить приложение. Сделанная правильно, ссылка переживает и установку: человек, попавший на веб, установивший приложение и открывший его, оказывается у того контента, который ему обещали. На этой передаче продукты с двумя клиентами теряют больше всего пользователей, поэтому мы относимся к ней как к функции с собственным инженерным бюджетом.
Веб-приложение с авторизацией на существующем API обычно стоит от 1 200 000 до 3 200 000 рублей и занимает один-три месяца — опубликованный тариф веб-приложения на нашей странице веб-разработки. Полноценная вторая сторона продукта с данными в реальном времени, платежами или мультитенантной структурой оценивается как работа уровня SaaS — свыше 4 000 000. Главный рычаг — ваш API: спроектированный под несколько клиентов, он держит веб-разработку компактной, а рассчитанный только на приложение добавляет бэкенд-работу, которую мы явно включим в оценку.

Посмотрите обе стороны в кейсе Formtastic, почитайте, как лента реального времени ExtraETF раздаётся всем клиентам, или начните с пилларной страницы веб-разработки.