Продолжение статьи «От монолита и чатов к FMS и FMS App». Читать первую часть не обязательно — нужные вводные дам по ходу, но с ней будет вкуснее.

Всем привет! Меня зовут Даня Сухарев, я директор по продукту внутренних сервисов в каршеринге Ситидрайв. Герой этой статьи — один из таких сервисов: FMS, Fleet Management System. Полтора года назад, ещё в роли продакта FMS, я написал первую статью — про то, как мы за три квартала с нуля построили систему управления автопарком и мобильное приложение FMS App, чтобы победить два монолита, чаты и онлайн-таблички.

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

Операционка до FMS: созвон по ролям и инструменты той эпохи — гугл-формы, телеграм-боты и чаты.
Операционка до FMS: созвон по ролям и инструменты той эпохи — гугл-формы, телеграм-боты и чаты.

Та статья заканчивалась фразой «FMS — это только начало пути». Перечитывая её сейчас, понимаю: тогда я описал процентов десять того, что мы в итоге построили.

Время честных признаний. На момент публикации первой части в FMS App жил ровно один боевой флоу — стационарная мойка. Один. Вылизанный, с кастомной камерой и динамическими опросниками, но один. Всё остальное — заправки, перегоны, шиномонтаж, поиск авто — продолжало крутиться в старой Диспетчерской, чатах и табличках. То есть в том самом хаосе, с которым мы так яростно боролись.

Сегодня в FMS — 52 типа задач в девяти доменах: от заправки до «Вытащить из сугроба» (да, это официальный тип задачи, и зимой без него никуда). Через систему ежедневно проходят тысячи задач, их выполняют сотни исполнителей.

Историй в этой статье будет две. Первая — про конструктор задач: инструмент, благодаря которому большинство типов задач теперь собирается вообще без разработки. Вторая — про три флоу, которые в конструктор не влезли и потребовали честной кастомной разработки: поиск авто, заправки и задачи техников. Именно связка «конструктор для типового + руки для сложного» позволила нам не растянуть этот путь до 2029 года.

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

Поехали! 🚀

FMS App — приложение, в котором живут все 52 типа задач.
FMS App — приложение, в котором живут все 52 типа задач.

Часть 1. Похмелье после MVP

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

Вот только вспомним, что значило «перенести тип задачи» в архитектуре образца первой статьи:

  • написать микросервис-стратегию — так мы называем сервис с бизнес-проверками задачи (можно ли создать, можно ли взять, корректно ли выполнена);

  • собрать флоу этапов;

  • нарисовать и настроить экраны в приложении;

  • составить опросники осмотров;

  • всё это протестировать и выкатить.

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

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

Разработка стала бутылочным горлышком собственного продукта. Обидно: систему строили ради скорости, а самым медленным звеном оказались мы сами.

Часть 2. Инсайт: любая задача — это кубики. Почти любая

В какой-то момент мы сели и разложили все типы задач старой Диспетчерской на этапы. Все сто с лишним.

Выяснилось то, что задним числом кажется очевидным: 90% любой задачи состоит из одних и тех же блоков. Доехать до авто. Осмотреть и сфотографировать. Сделать целевое действие. Подтвердить результат. Вернуть авто на линию. Разница между «мойкой» и «перегоном» — не в коде. Она в наборе и порядке этапов, в вопросах осмотра и в проверках.

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

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

Отсюда решение, которое перевернуло весь роадмап. Точнее, две его половины:

  1. Типовые задачи перестаём переносить руками — строим машину, которая переносит задачи. Так появился конструктор.

  2. Оставшиеся флоу — те самые «не 90%» — делаем по-честному, кастомом. Освободившимися руками.

Дальше по порядку про обе половины.

Часть 3. Конструктор задач

Конструктор — это no-code интерфейс внутри FMS, в котором новый тип задачи собирается как из «Лего»: этапы, экраны, опросники, условия, связи с другими задачами и даже деньги. Без разработчиков, без релизов, без «у нас бэклог, приходите в Q5».

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

Что в нём собирается:

Типы задач и этапы. Флоу складывается из готовых блоков: прибытие, осмотр, целевое действие, приёмка, возврат. Настраиваются экраны, тексты, порядок — всё, что исполнитель увидит в приложении.

Создание типа задачи: слева — конфигурация, справа — предпросмотр того, что увидит исполнитель в FMS App.
Создание типа задачи: слева — конфигурация, справа — предпросмотр того, что увидит исполнитель в FMS App.

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

Условная логика: выбрал «Есть заметные повреждения» — появится блок «Видео».
Условная логика: выбрал «Есть заметные повреждения» — появится блок «Видео».
Опросник целиком: экраны осмотра глазами исполнителя.
Опросник целиком: экраны осмотра глазами исполнителя.
Тот же осмотр в FMS App: единый чек-лист, пункты — в любом порядке.
Тот же осмотр в FMS App: единый чек-лист, пункты — в любом порядке.
Цепочка задач: дефект → перегон → мойка. Только что активирована.
Цепочка задач: дефект → перегон → мойка. Только что активирована.

Цепочки задач. Завершение одной задачи автоматически рождает следующую. Диагностика нашла проблему — создаётся мелкий ремонт. Мойка выполнена — создаётся задача на валидацию мойки. Раньше такие связки жили в головах координаторов и чатах, теперь это просто настройка.

Тарифы настраиваются без разработки.
Тарифы настраиваются без разработки.

Тарифы. Сколько исполнитель получит за задачу — настраивается там же, в админке тарифов.

Технически под капотом ничего мистического: конструктор — это редактор всё тех же JSON-схем флоу и опросников. На выходе — конфигурация, которую сервер отдаёт приложению. Бэкенд и мобилка при этом не меняются — именно поэтому 52 типа задач не потребовали 52 релизов мобилки. Специфические проверки для отдельных задач по-прежнему живут в микросервисах-стратегиях, но большинству задач хватает стандартных кубиков.

Эффект проще всего показать на времени. Раньше новый тип задачи — это недели разработки и место в очереди бэклога. Теперь — десять минут. И главное, кто эту работу делает: не разработчик и даже не продакт, а операционная команда. Люди, которые лучше всех знают процесс «на земле», впервые могут менять его сами.

Часть 4. Три флоу, которые в конструктор не влезли

Теперь вторая история. Конструктор прекрасен, пока задача складывается из стандартных кубиков. Но есть флоу, где сама механика работы другая — и никакими опросниками её не соберёшь. Таких у нас оказалось три, и каждый потребовал полноценной разработки с кастомными экранами.

Поиск авто

Иногда машину нужно найти в плотной застройке: во дворах, на закрытых парковках, среди десятков похожих авто. Такой поиск — отдельная дисциплина со своими профессионалами.

Флоу выглядит так: исполнитель видит на карте координаты машины и её фотографии, едет в район — и дальше начинается детектив. Обходишь дворы, заглядываешь за углы. И тут главный инструмент — сканер. Видишь похожую машину — открываешь сканер в FMS App, наводишь камеру на номер, приложение распознаёт ГРЗ и сразу отвечает: наша или нет, та машина или соседняя. Нашёл — отмечаешь фактическое местоположение. Кроме сканера, есть поиск по фото: в карточке машины лежат её свежие фотографии с последних осмотров — опознать по ним авто во дворе сильно проще.

Сканер в деле: навёл камеру на номер — приложение распознало ГРЗ и нашло машину.
Сканер в деле: навёл камеру на номер — приложение распознало ГРЗ и нашло машину.

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

Заправки

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

Кастомной разработки здесь тоже хватило, и почти вся она о том, чтобы избавить исполнителя от лишней ручной работы. Флоу такой: взял задачу → нажал «Выбрать АЗС» — приложение показывает заправки на карте → приехал, выбрал колонку в приложении → нажал «Старт заправки» → приложил отчёт. Расчёты за топливо проходят на стороне системы. Эффект почувствовали быстро: заправок делаем заметно больше меньшими силами, а машины меньше простаивают в деактивации.

Выбор АЗС на карте.
Выбор АЗС на карте.
Выбор колонки в приложении.
Выбор колонки в приложении.

Задачи техников

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

Смена техника — отдельный флоу: принял техничку, пополнил со склада, поехал по задачам. Каждая деталь и жидкость на борту — учтённая ТМЦ (товарно-материальная ценность) с QR-кодом: установил на машину — отсканировал — списалось. Раньше весь этот учёт жил в xls-файлах. Теперь в приложении живёт аналог складского ТСД — только склад ездит по городу. Под капотом — интеграции со складами, списания прямо в задаче и автоматические перемещения ТМЦ между авто.

Приёмка технички: проверка ТМЦ на борту.
Приёмка технички: проверка ТМЦ на борту.

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

Задача «Замена дворника», созданная диагностикой: списание щётки по QR-коду прямо в задаче.
Задача «Замена дворника», созданная диагностикой: списание щётки по QR-коду прямо в задаче.

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

Часть 5. Что под капотом

Теперь немного для тех, кто пришёл за технической частью.

С момента первой статьи платформа выросла с 11 микросервисов до нескольких десятков — плюс шлюзы, фронт админки и вспомогательные сервисы. Всё по-прежнему на Go, крутится в Kubernetes, у каждого сервиса своя PostgreSQL. Снаружи общаемся по REST API, между сервисами — gRPC, асинхронные события гоняем через Kafka.

Сервисы нарезаны по доменам, а не по техническим слоям — узкие и предметные, по одному ограниченному контексту на сервис: машины, задачи, тарифы.

Типичный поток такой. Синхронно — gRPC: скажем, один сервис запрашивает у другого данные по автомобилю. Асинхронно — Kafka: задача сменила статус, событие улетело в топик — и соседний сервис завёл валидационную задачу. Это та самая цепочка «мойка → валидация» из части 3 — вид со стороны бэкенда.

Контур FMS сегодня: админка, платформа микросервисов, приложение и интеграции.
Контур FMS сегодня: админка, платформа микросервисов, приложение и интеграции.

Каждый сервис собран по одному шаблону, feature-first: внутри фичи лежат бизнес-логика, доступ к данным, транспортный слой и модели.

Ключевое правило — зависимости через интерфейсы: бизнес-логика принимает абстракции чтения и записи, а не конкретные типы, поэтому юнит-тесты живут без базы. CI у всех сервисов общий: линт, покрытие, проверка миграций.

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

По периметру — интеграции со смежными системами компании. И пунктиром на схеме — диспатч, про который в самом конце.

Часть 6. 52 типа задач: полная карта

Сложим всё вместе. Девять доменов, которые сегодня живут в FMS:

  • Перегоны и релокации — самый большой домен: вернуть авто в зону, перегнать на ТО, доставить новые машины в город, релоцировать парк между городами.

  • Заправки и незамерзайка — про них вы уже всё знаете.

  • Сервис и диагностика — диагностика, мелкий ремонт, замена АКБ, оклейка.

  • Логистика исполнителей и техничек — смены, пополнения, довоз подрядчиков.

  • Мойки — с них всё началось: стационарная, приоритетная, срочная и отдельная для «электричек».

  • Шиномонтаж — включая сезонный. Представьте пиковую неделю, когда «переобуть» нужно весь парк.

  • Поиск авто — детектив из части 4.

  • Клиентские и полевые — выезд к клиенту и легендарное «Вытащить из сугроба».

  • Служебные — статусы смены и компенсации.

Задачи одного дня на карте города.
Задачи одного дня на карте города.
Карта задач глазами исполнителя: увидел на карте — открыл карточку — взял.
Карта задач глазами исполнителя: увидел на карте — открыл карточку — взял.
«Моя смена»: машины исполнителя с командами — посигналить, закрыть двери, открыть капот.
«Моя смена»: машины исполнителя с командами — посигналить, закрыть двери, открыть капот.

Исполнитель может вести до трёх задач одновременно — на скрине «Моей смены» их как раз три. Так машины обслуживаются быстрее.

И маленькое признание. Все эти флоу существуют ради автоматизации и экономии — но мы следим и за тем, чтобы приложение было просто красивым, и периодически делаем редизайн. FMS App — инструмент, который исполнитель открывает десятки раз за смену: работать целый день в аккуратном приложении приятнее, а системе, которая выглядит собранно, больше доверяют. Свежий редизайн обновил карточки задач и навигацию — их вы и видите на скринах.

А что Диспетчерская? Оставшиеся её задачи переносятся в FMS.

Часть 7. Вид из офиса: задача как на ладони

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

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

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

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

Что это даёт на практике: спорные ситуации с исполнителями разбираются по факту, а не по переписке; контроль качества опирается на фото и трек, а не на честное слово; а метрики показывают не «задача заняла четыре часа», а из чего эти четыре часа состояли.

Часть 8. Вокруг задач выросла экосистема

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

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

Связки «мойка — исполнитель»: кто, куда и в какие часы может везти авто.
Связки «мойка — исполнитель»: кто, куда и в какие часы может везти авто.

Интеграция с 1С. При закрытии задачи автоматически списываются ТМЦ: кто, когда, что и в какое авто установил или залил. Звучит скучно, но это тот случай, когда бухгалтерия впервые сказала нам спасибо.

Модерация. Выполненные задачи проходят контроль качества.

Окно модератора: контроль качества выполненных задач.
Окно модератора: контроль качества выполненных задач.

Карточка авто и модели. Карточка из первой статьи выросла в полный профиль машины, а рядом появился модуль моделей и комплектаций: характеристики правятся массово на весь парк, а не по одной машине.

Часть 9. Планы: Ремонты и Диспатч

Расскажу про два больших фронта, которые в работе прямо сейчас.

Ремонты. Последний крупный контур, который ещё живёт в старой админке, экселях и чатах. Переносим его в FMS целиком: канбан ремонта по этапам СТО (новый → входной контроль → ждёт ремонта → в ремонте → готов к проверке), карточка ремонта со сроками, связанными задачами и лентой событий, учёт пропускной способности сервисов.

Опросник целиком: экраны осмотра глазами исполнителя.
Макеты ремонтов: канбан по этапам СТО — от входного контроля до готовности к проверке.
Карточка ремонта: сроки план/факт, связанные задачи и недостатки из осмотров — с фото и промерами протектора.
Карточка ремонта: сроки план/факт, связанные задачи и недостатки из осмотров — с фото и промерами протектора.

Диспатч. Теперь — обещанный ответ, почему его до сих пор нет. Всё просто: полтора года назад диспатчить было нечего. Чтобы система могла сама раздавать задачи, ей нужно знать все задачи (а не один флоу мойки), всех исполнителей с их навыками и цену каждой операции. Ровно это мы и собирали полтора года — и теперь ингредиенты на месте. Сегодня исполнитель сам выбирает задачу с карты — это уже неплохо, но система знает больше, чем человек: где какие задачи, кто с какими навыками свободен, сколько стоит каждая операция. Хотим перевернуть модель: не исполнитель ищет задачу, а задача находит исполнителя — по гео, навыкам, загрузке и бюджету операции. Под капотом это уже не CRUD, а честная алгоритмика (привет, задача маршрутизации) плюс отдельный квест: сделать так, чтобы людям, привыкшим выбирать самим, автоназначение было выгодно. Если долетим — это тема для третьей статьи.

Вместо заключения

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

Но есть и второй урок, не менее важный: платформа не отменяет рук. Три самых сложных флоу — поиск, заправки, задачи техников — мы всё равно писали кастомом, и это нормально. Смысл конструктора не в том, чтобы разработчики стали не нужны, а в том, чтобы они не тратили себя на типовое.

Спасибо команде, которая всё это построила: Руслан Ибрагимов, Вероника Репина, Георгий Гулуа, Кристина Ширяева, Никита Заикин, Владислав Иванов, вся команда Proil, Константин Свиридов, Олеся Багаева, Данил Севрюков, Кристина Ким, Александр Молявин, Александр Зильбер. И отдельное спасибо тем, кто каждый день выполняет все эти задачи в полях.

Вопросы, скепсис и «а как у вас с…» — жду в комментариях. Про диспатч и ремонты расскажу в следующей серии 🚀