惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
V
V2EX
C
Check Point Blog
GbyAI
GbyAI
D
Docker
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
T
Troy Hunt's Blog
博客园 - Franky
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Security Blog
Microsoft Security Blog
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
The Cloudflare Blog
S
SegmentFault 最新的问题
Latest news
Latest news
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
I
InfoQ
博客园 - 【当耐特】
NISL@THU
NISL@THU
A
About on SuperTechFans
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Scott Helme
Scott Helme
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
Security Latest
Security Latest
V
Vulnerabilities – Threatpost
Security Archives - TechRepublic
Security Archives - TechRepublic
A
Arctic Wolf
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
IT之家
IT之家
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
SecWiki News
SecWiki News
大猫的无限游戏
大猫的无限游戏
S
Security Affairs
The Register - Security
The Register - Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
T
Tor Project blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Про BDUI грабли на примере простого опросника, который не так прост, как кажется
zubbkovv (Ал · 2026-05-20 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение14 мин

Охват и читатели20

Ретроспектива

Однажды у нас появилась задача, которая (на первый взгляд) выглядела очень простой: сделать опросник в приложении. На макетах всего лишь пара экранов, несколько вопросов, кнопка «Далее». Всё красиво, не сухо, с картинками у вариантов ответа и нормальной подачей, а не в формате «Заполните обязательные поля»

Судя по макету всё просто: сверстать флоу и отправить все ответы одним POST в конце. Самый короткий путь — зашить вопросы, переходы и тексты на клиенте. Делов на пару дней — сделал и забыл. 

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

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

По некоторым косвенным признакам мы понимали, что с этой анкетой всё будет именно так, поэтому в качестве альтернативы мы выбрали путь backend-driven UI, когда клиент показывает поддерживаемые типы экранов, а backend управляет сценарием: текстами, изображениями, порядком шагов, переходами и состоянием прохождения.

Ниже расскажу почему мы пошли в backend-driven подход, где он действительно помог, а где показал явные ограничения и мог выбить нас из сроков.

Дисклеймер. Все названия систем, endpoint-ы, поля, бизнес-условия и архитектурные детали в статье изменены. Примеры ниже показывают принцип проектирования, а не конкретную внутреннюю реализацию.

Простая форма или будущий фреймворк

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

Но у нас были признаки, что история может быстро вырасти. В дизайне был не просто текст вопроса и радиокнопки. Были:

  • welcome-анимация,

  • заголовок и пояснение у каждого вопроса,

  • картинки у вариантов ответа,

  • кнопки «Назад» и «Далее»,

  • progress bar,

  • финальный экран,

  • вариант с раскрывающимся блоком прямо внутри вопроса.

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

В этот момент опросник перестаёт быть похожим на Google-форму. Он становится полноценным сценарием.

Почему мы не хотели зашивать это на три клиента

Логика на клиенте? Значит изменение начинает ехать через разные релизные циклы web, Android и iOS. Даже если команды синхронизируются, пользователи всё равно обновляются не одновременно.

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

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

Поэтому мы решили усложнить себе жизнь на старте: сделать не набор экранов на клиенте, а backend-driven механизм. Фронт умеет показывать определенные типы экранов, а backend управляет сценарием, порядком вопросов, переходами и состоянием прохождения.

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

А теперь предлагаю детальнее разобрать «грабли», которые мы обнаружили при проработке такой реализации.

Грабли № 1. Клиент легко становится владельцем маршрута

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

{
  "steps": [
    { "code": "question-1" },
    { "code": "question-2" },
    { "code": "question-3" }
  ]
}

Для линейной анкеты это удобно. Клиент получил массив, ходит по нему вперёд-назад и в конце отправляет результат.

Но как только появляются ветвления это превращается в огромный JSON, который используется клиентом только на половину: клиенту нужно знать, какой вопрос показать после конкретного ответа, какие шаги пропустить, что делать при возврате назад и какие ответы считать актуальными.

Мы не хотели, чтобы приложение знало весь маршрут. Маршрут должен жить там, где есть версия сценария, состояние прохождения и правила переходов, то есть на backend.

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

Как разделили ответственность?

В итоге у нас получилось три слоя

№1. Клиент

Клиент — это рендерер. Он умеет показывать заранее поддержанные типы экранов:

  • приветственный экран,

  • вопрос с одним вариантом ответа,

  • вопрос с несколькими вариантами ответа,

  • экран с раскрывающимся блоком,

  • финальный экран,

  • fallback, если сценарий недоступен или что‑то пошло не так.

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

№2. BFF

Промежуточный слой адаптирует данные под конкретный канал. Например, может привести структуру к формату, который удобнее — web, iOS или Android, и добрать специфичный для канала контент

Здесь важно не дать gateway превратиться во второй движок сценария. Если часть маршрута начинает жить в этом слое, логика снова расползается.

№3. Scenario-engine

Этот слой управляет сценарием:

  • проверяет, доступен ли опросник,

  • выбирает текущий шаг,

  • сохраняет ответ,

  • рассчитывает следующий шаг,

  • обрабатывает возврат назад, и решает возможен ли такой переход,

  • завершает сценарий,

  • хранит состояние прохождения.

Для системного аналитика сложность здесь не в том, чтобы придумать поля JSON, а в том, чтобы провести границу: что решает клиент, что адаптирует bff, а что остаётся в движке сценария.

Грабли № 2. POST может не только сохранять, но и возвращать следующий экран

Мы прочертили границы ответственности каждого компонента. Как настроить их взаимодействие? На первый взгляд достаточно такого порядка:

GET start
POST answer
GET next
POST answer
GET next

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

GET start → intro + currentStep
POST answer → save answer + next currentStep
POST answer → save answer + next currentStep
  • Первым запросом мы узнаем о доступности опросника конкретному пользователю.

  • Если доступно, то сразу получаем и первый экран с приветственной анимацией и первый экран вопроса.

  • А как только пользователь отправил свой ответ на первый вопрос, фронт дергает POST, мы фиксируем выбор и в ответе фронту отправляем следующий экран вопроса.

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

Как выглядит ответ backend

Ниже не реальный контракт, а упрощенный пример для понимания идеи. Если сценарий недоступен, backend отвечает коротко:

{
  "flow": {
    "available": false
  }
}

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

Если сценарий доступен, backend возвращает стартовый пакет: приветственный экран и первый вопрос.

{
  "flow": {
    "available": true,
    "code": "setup-flow",
    "version": "1.0"
  },
  "intro": {
    "type": "animation",
    "heading": "Давайте настроим сервис под вас",
    "displayDuration": 2500,
    "transition": {
      "type": "auto",
      "target": "currentStep"
    }
  },
  "currentStep": {
    "code": "goal",
    "type": "single-choice",
    "heading": "Что хотите настроить в первую очередь?",
    "hint": "Выберите один вариант",
    "completionRate": 20,
    "navigation": {
      "back": false,
      "primaryAction": "Далее"
    },
    "validation": {
      "min": 1,
      "max": 1,
      "message": "Выберите один вариант"
    },
    "answers": [
      {
        "code": "a1",
        "text": "Главный экран",
        "imageRef": "goal-dashboard"
      },
      {
        "code": "a2",
        "text": "Уведомления",
        "imageRef": "goal-notifications"
      },
      {
        "code": "a3",
        "text": "Подсказки",
        "imageRef": "goal-tips"
      }
    ]
  }
}

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

После выбора клиент отправляет ответ:

{
  "flow": {
    "code": "setup-flow",
    "version": "1.0"
  },
  "answer": {
    "stepCode": "goal",
    "selected": ["a1"]
  }
}

А в ответ получает следующий currentStep в той же модели.

{
  "flow": {
    "available": true,
    "code": "setup-flow",
    "version": "1.0"
  },
  "currentStep": {
    "code": "preferences",
    "type": "multi-choice",
    "heading": "Какие подсказки вам полезнее?",
    "hint": "Можно выбрать несколько вариантов",
    "completionRate": 45,
    "navigation": {
      "back": true,
      "primaryAction": "Далее"
    },
    "validation": {
      "min": 1,
      "max": 3,
      "message": "Выберите хотя бы один вариант"
    },
    "answers": [
      {
        "code": "a1",
        "text": "По настройке сервиса",
        "imageRef": "tips-settings"
      },
      {
        "code": "a2",
        "text": "По новым возможностям",
        "imageRef": "tips-new"
      },
      {
        "code": "a3",
        "text": "По регулярным действиям",
        "imageRef": "tips-regular"
      }
    ]
  }
}

Стартовый GET и последующий POST возвращают клиенту одну и ту же модель: «вот текущее состояние сценария, отрисуй его». Разница только в том, что перед ответом на POST backend ещё сохраняет выбор пользователя и рассчитывает следующий шаг.

Грабли № 3. Сеть может упасть в самый неудобный момент

В мобильном сценарии нельзя считать, что сеть всегда стабильна. Пользователь может нажать «Далее», приложение отправит ответ, а потом не получит ответ.

Ответ сохранился или нет? Нужно повторить запрос? А если повторить, не создадим ли дубль?

Для таких случаев нужна идемпотентная обработка повторной отправки. Если действие уже было принято, backend не должен создавать дубль. Он должен вернуть актуальное состояние сценария.

  1. Клиент повторил последнее действие.

  2. Backend проверил состояние.

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

Грабли № 4. Опросник быстро превращается в огромный граф

Что обычно представляют, когда слышат слово «опросник»? Фиксированный список вопросов, который пользователь проходит сверху вниз. Никакой вариативности, в этом сценарии неважно что именно ты ответишь, список вопросов уже ограничен и отрисован.

В backend‑driven сценарии маршрут может быть другим — следующий может экран зависеть от выбранного ответа, а часть вопросов может вообще не показываться.

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

На схеме видно, что сценарий нелинейный. После первого вопроса пользователь может попасть в разные ветки: пройти через «Вопрос 2», сразу перейти к «Вопросу 3» или пропустить часть шагов и оказаться на «Вопросе 4». Дальше ветки могут расходиться ещё сильнее: один маршрут приведёт к «Вопросу 5» и сразу к финальному экрану, другой — к «Вопросу 6», затем к «Вопросу 9», третий — к «Вопросам 8» и 10.

Для клиента это всё ещё выглядит одинаково: он показывает текущий экран и отправляет выбранный ответ. Карту переходов хранит и рассчитывает backend.

Внутри конфигурации это может быть описано как набор правил маршрутизации:

{
  "step": "goal",
  "routing": [
    { "answer": "a1", "next": "preferences" },
    { "answer": "a2", "next": "notifications" },
    { "answer": "a3", "next": "final-settings" }
  ]
}

Важно, что эта карта переходов не уезжает на клиент целиком. Клиент отправляет выбранный ответ, а backend возвращает уже рассчитанный currentStep.

Грабли № 5. Progress bar в дереве начинает скакать

В линейном сценарии прогресс считать просто: пройденные шаги делим на общее количество шагов.

В дереве маршрутов всё сложнее. Один пользователь может пройти короткую ветку и быстро дойти до финала, другой уйдёт в более длинный маршрут. Если при этом пользователь вернётся назад и изменит ответ, то длина его маршрута может измениться в процессе прохождения.

Из-за этого progress bar начинает вести себя неожиданно: он может быстро расти на короткой ветке, а потом откатываться или скакать при переходе в другую ветку. Технически система работает правильно, но выглядит это странно.

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

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

Грабли № 6. Версии сценариев нельзя смешивать

Ещё один вопрос, который лучше решить заранее: что делать, если сценарий изменился, пока пользователь уже начал его проходить?

Выбрали простой принцип: пользователь допроходит ту версию сценария, с которой стартовал.

  1. Пользователь «A» начал v1, значит завершает v1.

  2. Вышла v2.

  3. Пользователь «B» стартует позже, значит получает v2.

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

Грабли № 7. Состояние прохождения важнее, чем кажется

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

Поэтому нужно хранить не только ответы, но и состояние прохождения. Упрощённо вот так:

Без явного статуса легко попасть в странные ситуации: показать один и тот же опрос повторно, потерять незавершённое прохождение или отправить в аналитику неполные данные как финальные.

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

Грабли № 8. Фича не зависит от релизов (нет)

Самая опасная фраза в BDUI фичах — «теперь всё можно менять без релиза». Нет, не всё.

Backend‑driven UI позволяет быстро менять то, что уже описано в возможностях клиента. Но если клиент не умеет отрисовывать новый тип поведения, backend не сможет научить его этому одной конфигурацией.

Тип изменения

Нужен клиентский релиз?

Текст

Нет

Картинка

Нет

Порядок шагов

Нет

Маршрут между поддерживаемыми экранами

Нет

Новый опросник на существующих типах экранов

Нет

Запуск на новую группу пользователей

Нет

Новый тип экрана, поведения или новый визуальный параметр

Да

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

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

Backend-driven UI ускоряет изменения сценария, но не учит клиент новым компонентам автоматически.

Что получилось?

Главный результат — мы убрали клиентский релиз из большинства изменений опросника.

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

  • Web, iOS и Android получают один и тот же сценарий из общего backend‑слоя.

  • Команда занимается развитием фреймворка, а не постоянной правкой контента.

  • Новые версии сценариев можно запускать без разъезда платформ.

  • Ответы собираются по единой логике и могут сразу использоваться в связанных сценариях аналитики и персонализации.

Backend‑driven UI не делает разработку бесплатной. Он меняет её характер: меньше точечных клиентских доработок, больше работы над общим механизмом.

Чек-лист: где я бы держал фокус в такой задаче с первого дня

В самом начале проработки своей будущей фичи на BDUI рекомендую пройтись по следующим вопросам, а получив ответы сможете отлично проработать логику и корнер-кейсы, уменьшив количество граблей, на которые наступите в самый неожиданный момент.

1. Кто владеет маршрутом?

Если маршрут знает клиент, он быстро начинает становиться владельцем сценария. Для линейной формы это нормально, для ветвящегося backend‑driven сценария — нет.

2. Что считается состоянием прохождения?

Нужно заранее описать статусы: сценарий активен, завершён, отклонён, завис или истёк по времени.

3. Как работает повторный POST?

Мобильная сеть бывает нестабильной, особенно сейчас. Если клиент повторяет последнее действие, то backend должен корректно вернуть актуальное состояние и не создать дубль.

4. Как версионируется сценарий?

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

5. Как устроено ветвление?

Не каждое дерево стоит делать объемным. Чем больше веток, тем сложнее progress, тестирование, аналитика и логичность пользовательского интерфейса.

6. Что клиент уже умеет отображать?

BDUI работает в границах поддерживаемых компонентов. Если нового поведения нет в контракте и реализации клиента, одной конфигурации не хватит. Продумывайте пределы фронта заранее, но не уходите в излишне глубокую проработку, пытаясь перезаложиться особенно на первом этапе фичи.

7. Какие метрики нужны?

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

Вывод

Мы начинали с задачи на несколько экранов: welcome, вопросы, ответы и финал. Можно было бы сделать работу быстро и просто, зашив всё на клиенте.

Но тогда каждая следующая правка снова превращалась бы в синхронизацию web, iOS и Android. Поэтому мы выбрали более сложный путь в начале, но снизили монотонность работ в будущем — сделали сценарий управляемым с backend.

Это не избавило нас от фронтовой разработки полностью. Так не бывает. Но теперь стало проще разделять изменения на два типа.

  1. Если нужно поменять текст, картинку, порядок вопросов или маршрут между уже поддержанными экранами — это обновление сценария.

  2. Если нужен новый тип поведения или новый компонент — это развитие клиента.

Для меня в этом и есть главная польза backend‑driven подхода: он не обещает магию «без релизов вообще», но даёт понятную границу между настройкой сценария и полноценной разработкой.


Подписывайтесь на Телеграм-канал Alfa Digital — там рассказываем о работе в IT, делимся новостями, анонсами митапов и квартирников, рассказываем о технологиях, делимся советами наших экспертов, вакансиями и стажировками, иногда шутим.

По BDUI и SDUI у нас есть большой цикл статей:

Основы BDUI для продуктовых дизайнеров. Шпаргалка

BDUI (Backend Driven User Interface) — это подход к продуктовой разработке, который набирает популяр...

habr.com

Эволюция Server-Driven UI: динамические поля, хэндлеры и многошаг

Server-Driven UI (SDUI) — это подход для динамичного и гибкого пользовательского интерфейса, когда с...

habr.com

Разбираем Remote Compose: как Google предлагает строить BDUI

Технологии Backend-Driven UI уже давно используются во многих компаниях, включая Альфа-Банк. Существ...

habr.com

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

Привет, меня зовут Анна Саботович , я техлид в Альфа-Банке (команда платежей и переводов), отвечаю з...

habr.com

Как мы сокращали размер JSONʼа в SDUI

Проблема большого объёма JSON Представим, что мы отображаем на экране несколько однотипных элементов...

habr.com

SDUI, или Как backend-разработчику почувствовать себя frontend’ером

Вы не стали чаще слышать о таком трендовом явлении, как Server Driven UI? Если вы ещё с ним не сталк...

habr.com

Читайте также:

История вайб‑кодера: «Я был скептиком, но до 4 утра спорил с GLM-5»

Привет! Я Женя, тимлид одной из команд Альфы. В статье расскажу, как наша команда скептиков вайб‑код...

habr.com

AI делает видео за вас? Я попробовал — и расскажу, где нас обманывают

Привет, я Рустам, у меня несколько продуктовых команд. На Хабре я молчал с 2014 года. Но эта история...

habr.com

Как мы с ИИ воскресили игру с кнопочных телефонов

Привет! Меня зовут Абакар, я работаю главным техническим лидером разработки в Альфа-Банке, а в свобо...

habr.com