Платформенная инженерия Самообслуживание: от заявки в тикете к платформе, которой пользуются
0%

Самообслуживание: от заявки в тикете к платформе, которой пользуются

Самообслуживание: от заявки в тикете к платформе, которой пользуются

Инженеру нужна новая тема в Kafka. Он открывает Jira, находит проект INFRA, выбирает тип «Запрос ресурса», заполняет восемь полей, из которых на четыре он не знает ответа, и нажимает «Создать». Через шесть рабочих дней тема появляется.

Работа на стороне платформы заняла сорок минут: посмотреть заявку, дописать три строки в манифест, применить, проверить. Остальные 47 часов — очередь, уточнения, ожидание окна изменений и время, пока заявитель заметил ответ. Соотношение 40 минут к 48 часам — не признак ленивой платформенной команды, а нормальная физика очередей: когда между потребностью и её удовлетворением стоит человек с собственным списком приоритетов, время ожидания определяется загрузкой этого человека, а не сложностью задачи. И именно это соотношение делает разговор о самообслуживании не вопросом удобства, а вопросом денег.

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

Анатомия заявки: 48 рабочих часов от создания тикета до закрытия, из них 40 минут реальной работы

Что такое самообслуживание и что им не является

Определение стоит дать узкое, иначе термин расползётся до «у нас есть портал».

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

Три слова здесь несут вес.

  • Операции, а не платформы целиком. Самообслуживание — свойство каждой конкретной операции по отдельности, а не бинарное состояние компании. Полностью самообслуживаемый деплой рядом с заявкой на доступ к продовой базе через тикет — нормальная, осознанная конфигурация.
  • Человека в критическом пути, а не человека вообще. Асинхронное ревью, которое не блокирует, — самообслуживание. Проверка политикой, отклоняющая запрос за две секунды, — самообслуживание. Кнопка «Одобрить» у дежурного платформы, без которой ничего не поедет, — тикет с красивым интерфейсом.
  • Результат, а не заявка. Форма, которая создаёт тикет, — это более удобный тикет. Разница видна на метрике: время до результата не изменилось.

Пять уровней, между которыми выбирают

Уровень Как выглядит Время до результата Кто отвечает за корректность
L0. Анархия каждая команда сама делает что хочет прямо в облаке минуты никто; выясняется на аудите
L1. Тикет заявка в очередь, платформа делает руками дни платформа, вручную
L2. Заявка с автоматикой форма запускает пайплайн, но нужно одобрение часы платформа, через одобрение
L3. Самообслуживание с политикой инженер применяет сам, политика проверяет автоматически минуты политика как код
L4. Инвариант ресурс появляется сам из описания сервиса нет запроса вовсе платформа, через шаблон

Важная и неочевидная вещь: L0 не хуже L1 по всем осям. Анархия быстрее тикета и часто быстрее плохого L3. Именно поэтому команды в неё сползают, когда платформа тормозит: обход — не саботаж, а рациональный выбор при заданных издержках. Задача платформы не в том, чтобы закрыть L0 запретом, а в том, чтобы L3 был быстрее и удобнее, чем L0. Это точка воздействия на уровне правил игры, а не на уровне параметров.

Обратите внимание и на L4. Самая дешёвая заявка — та, которой нет. Если у сервиса в описании указано, что он читает из темы orders.v2, тема должна появиться сама вместе с правами и алертами; отдельная операция «создать тему» тут лишняя. Это переход от «запроса ресурса» к декларативному описанию сервиса, и по возможности стоит целиться сразу туда.

Что вообще отдают в самообслуживание

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

Цена заявки: арифметика вместо жалоб

Разговор «у нас всё через тикеты, это плохо» бесполезен, пока у него нет числа. Число получают из журнала тикетов за квартал — данные уже есть в трекере, их достаточно выгрузить.

Возьмём условную компанию: 12 продуктовых команд, около 90 инженеров, платформенная команда из трёх человек. За квартал в очередь платформы пришло 612 заявок. Разложим их по типам операций.

Операция Штук за квартал Медиана ожидания Работа платформы Обратима Нужно суждение
Новый сервис из шаблона 22 4,0 дня 90 мин да нет
Тема или очередь 96 1,5 дня 40 мин да нет
Изменение квоты CPU/памяти 143 0,5 дня 15 мин да нет
Превью-стенд на ветку 88 1,0 дня 25 мин да нет
Новый секрет или ротация 71 2,0 дня 20 мин да нет
Дашборд и алерт из шаблона 64 3,0 дня 45 мин да нет
Доступ к логам прода на время 58 1,5 дня 10 мин да частично
Схема в общей БД 41 3,5 дня 60 мин нет да
Новый домен и сертификат 18 2,5 дня 50 мин да нет
Доступ на запись в продовую БД 11 5,0 дней 30 мин нет да

Считать надо две разные величины, и их постоянно путают.

Работа платформы — сколько часов команда платформы тратит на очередь. Здесь это 312 часов за квартал: около 23 % ёмкости команды из трёх человек. Это то, что видит руководитель платформы, и это заметная, но не катастрофическая цифра.

Потерянное время продуктовых команд — сколько ждут заявители. Здесь оно другого порядка. Ожидание само по себе не всегда простой: инженер переключается на другую задачу. Но переключение стоит денег, и незавершённая работа копится. Практичная оценка: считать не всё время ожидания, а стоимость переключения — примерно 20–30 минут потерянного контекста на каждый цикл «отправил заявку — вернулся к другой работе — получил ответ — вспомнил, зачем это было». При двух циклах уточнений на заявку получается около часа на заявку, то есть 612 часов за квартал. Плюс задержка поставки, которую в часах не измерить, но которая видна в lead time изменений (см. DORA-метрики и «Метрики продукта»).

Итого: очередь тикетов стоит компании порядка 920 инженерных часов в квартал, из которых платформа видит только 312. Это и есть главная причина, по которой очередь живёт годами: издержки несёт одна сторона, а решение принимает другая. Классическая локальная оптимизация: у платформы всё в порядке, SLA по тикетам выполняется, 90 % закрыто в срок.

Что автоматизировать первым

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

"""Приоритизация операций для перевода в самообслуживание.
Вход — журнал тикетов за квартал; выход — операции, упорядоченные по тому,
за сколько месяцев окупится разработка самообслуживания для каждой."""
from dataclasses import dataclass

SWITCH_COST_HOURS = 1.0     # потери на переключение контекста, часов на заявку
QUARTERS_PER_YEAR = 4


@dataclass
class Operation:
    name: str
    count_per_quarter: int
    platform_minutes: int    # ручная работа платформы на одну заявку
    build_days: int          # оценка разработки самообслуживания, человеко-дней
    reversible: bool         # можно ли откатить результат без последствий
    needs_judgement: bool    # требуется ли человеческое решение по существу


def annual_savings_hours(op: Operation) -> float:
    """Часы в год, которые вернёт перевод операции в самообслуживание."""
    per_quarter = op.count_per_quarter * (
        op.platform_minutes / 60 + SWITCH_COST_HOURS
    )
    return per_quarter * QUARTERS_PER_YEAR


def payback_months(op: Operation) -> float:
    """За сколько месяцев окупится разработка. inf — не окупится в горизонте."""
    saved = annual_savings_hours(op)
    if saved <= 0:
        return float("inf")
    cost_hours = op.build_days * 8 * 1.6   # 1,6 — поддержка и доработки в первый год
    return cost_hours / (saved / 12)


def rank(ops: list[Operation]) -> list[tuple[Operation, float, float]]:
    """Ранжирование по окупаемости. Исключаются операции, требующие суждения,
    и необратимые: у них автоматизируется подготовка, но не само действие."""
    candidates = [op for op in ops if not op.needs_judgement and op.reversible]
    scored = [(op, annual_savings_hours(op), payback_months(op)) for op in candidates]
    scored.sort(key=lambda row: row[2])     # сначала самые быстро окупаемые
    return scored


journal = [
    Operation("квота CPU/память", 143, 15, 4, True, False),
    Operation("тема или очередь", 96, 40, 8, True, False),
    Operation("превью-стенд", 88, 25, 15, True, False),
    Operation("секрет и ротация", 71, 20, 10, True, False),
    Operation("дашборд и алерт", 64, 45, 6, True, False),
    Operation("схема в общей БД", 41, 60, 20, False, True),
    Operation("доступ на запись в прод", 11, 30, 12, False, True),
]

for op, saved, months in rank(journal):
    print(f"{op.name:24} {saved:7.0f} ч/год   окупаемость {months:4.1f} мес")

Вывод для этих данных: квоты (715 ч/год, окупаемость 0,9 месяца), темы (640 ч/год, 1,9), дашборды (448 ч/год, 2,1), секреты (379 ч/год, 4,1), превью-стенды (499 ч/год, 4,6). Порядок не совпадает ни с частотой заявок, ни с ощущением «что больше всего бесит»: превью-стенды раздражают сильнее всего, но окупаются впятеро медленнее квот. Схема в БД и доступ на запись отфильтрованы — там нужно суждение, и автоматизировать надо не решение, а подготовку к нему.

Сложность: сортировка даёт O(n log n) по времени и O(n) по памяти, где n — число типов операций (десятки). Вычислительно задача тривиальна; ценность скрипта в том, что он превращает спор о приоритетах в таблицу с числами. Подробнее про эту арифметику — в главе «Рутина и автоматизация» трека SRE, там же разобраны признаки рутины и ловушки её измерения.

Две поправки, без которых числа врут:

  • Автоматизация не убирает работу целиком. Самообслуживание требует поддержки: разбор непонятных отказов, обновления, миграции. Закладывайте 15–25 % исходной ручной работы как остаточную.
  • Часть заявок исчезнет, а часть — размножится. Когда квоту можно поднять за минуту, её начинают поднимать в десять раз чаще. Это не баг: раньше люди терпели неудобство, потому что заявка была дороже. Но в расчёте экономии на это надо закладываться, а в лимитах — предусматривать потолок.

Цена платформенной команды: почему это не бесплатно

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

Окупаемость платформенной команды: экономия растёт с числом команд, но только при высоком принятии

Модель простая, и её стоит держать в голове при каждом решении о новой функции платформы:

  • стоимость — ёмкость платформенной команды: 3 инженера × 150 рабочих часов = 450 часов в месяц. Она почти не зависит от числа обслуживаемых команд;
  • экономия — число команд, реально пользующихся платформой, умноженное на сэкономленные часы на команду в месяц;
  • точка окупаемости — там, где вторая величина превысила первую.

При 30 сэкономленных часах на команду в месяц окупаемость наступает на 15 команде. При принятии в 40 % экономия на команду падает до 12 часов, и точка окупаемости уезжает за 37 команд — то есть в компании из двенадцати команд платформа не окупится никогда, сколько бы функций ни добавили. Наклон линии экономии определяется принятием, а не полнотой функций. Отсюда практический вывод, который в этом треке повторяется многократно: новая функция сдвигает точку окупаемости вправо (растёт стоимость), рост принятия — влево. Если приходится выбирать, выбирайте принятие.

Из той же модели следуют три неудобных вывода.

  1. В маленькой компании выделенная платформенная команда не окупается. При трёх-четырёх продуктовых командах экономия не покроет стоимость даже одного выделенного инженера. Это не значит, что не нужно самообслуживание: нужно, но как побочный продукт работы продуктовых команд — общие шаблоны, общий пайплайн, тонкий слой скриптов. Подробно — в «Платформе в небольшой компании».
  2. Функция, которой пользуется одна команда, почти всегда убыточна. Экономия делится на одну команду, стоимость поддержки — постоянная и навсегда. Такие вещи должна писать сама команда, а платформа — дать точку расширения.
  3. Стоимость владения инструментами входит в стоимость платформы. Кластер Kubernetes — это регулярные обновления control plane, CVE в CNI, миграции API-версий: по опыту это 0,3–1 человека постоянно, независимо от того, сколько сервисов в нём крутится («Kubernetes»). Backstage — это не портал из коробки, а фреймворк на TypeScript, который вы форкаете и поддерживаете: обновления ломают плагины, каталог требует автозаполнения из реальных источников, реалистичная оценка — 1–1,5 человека постоянно (backstage.io/docs). Terraform и Crossplane — это state, дрейф, права и разбор «почему план хочет пересоздать базу» («Terraform и IaC»). Ни один из этих инструментов не плох; плохо брать их, не назвав цену вслух и не заложив её в расчёт.

Разговор про деньги — не разовое упражнение перед защитой бюджета, а постоянная практика; ей посвящена глава «Стоимость».

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

Самообслуживание — не «дать всем права». Это пять свойств, и без любого из них конструкция разваливается в предсказуемую сторону.

  1. Декларативный интерфейс. Пользователь описывает желаемое состояние, а не последовательность действий. Императивная команда «создай тему с тремя партициями» не отвечает на вопрос, что делать при повторном вызове, при частичном отказе и при изменении требований. Декларация отвечает: приводим к описанному состоянию, операция идемпотентна.
  2. Политика вместо ревью. Всё, что человек проверял бы глазами, формулируется как автоматическая проверка: лимиты, обязательные метки, запрет публичного доступа, требования к владельцу. Политика отвечает мгновенно и одинаково всем — в отличие от ревьюера, у которого пятница и плохое настроение. Цена: политики надо тестировать и версионировать, иначе однажды они заблокируют выкатку в неудачный момент по причине, которую никто не сможет объяснить.
  3. Обратимость. Ключевой критерий: самообслуживание безопасно ровно настолько, насколько дёшев откат. Создать превью-стенд — обратимо, отдавайте без разговоров. Удалить продовую базу — необратимо, здесь человек в критическом пути оправдан. Между ними — серая зона, где обратимость создаётся инженерно: soft delete, снапшот перед изменением, TTL на ресурс, окно отмены.
  4. Ограничители. Квоты на команду, потолок на размер ресурса, TTL на временные объекты, лимит расхода. Без них первое же самообслуживание превращается в неконтролируемый рост расхода, и следующим шагом менеджмент закрывает всё обратно в тикеты. Ограничитель — не недоверие, а условие, при котором доверие можно отдать без согласования каждый раз.
  5. Наблюдаемость запроса. Пользователь видит, что происходит с его запросом и почему он не прошёл, без похода в чат платформы. Отказ обязан быть читаемым: не admission webhook denied the request, а «в манифесте нет метки owner; добавьте её, см. ссылку». Именно этот пункт отличает работающее самообслуживание от формально существующего: если каждый второй отказ приводит к вопросу в чате, вы построили тикеты с лишним шагом.

Жизненный цикл запроса

Две вещи на этой схеме легко упустить при проектировании, и обе потом стоят дорого.

  • Дрейф. Ресурс, созданный самообслуживанием, кто-нибудь однажды поправит руками через консоль облака. Без периодической сверки описания с фактом платформа теряет знание о происходящем и превращается в источник вранья. Сверка должна либо возвращать к описанию, либо явно помечать ресурс как «под ручным управлением» — оба варианта лучше молчания.
  • Удаление. Самообслуживание без удаления — генератор мусора. TTL на временные ресурсы (превью-стенды, тестовые базы, временные доступы) обязателен с первого дня, иначе через полгода вы будете вручную разбирать 400 забытых стендов и всё это время их оплачивать.

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

Декларация сервиса, из которой выводится всё остальное:

# service.yaml — единственный файл, который пишет продуктовая команда
apiVersion: platform.example.com/v1
kind: Service
metadata:
  name: orders-api
  labels:
    owner: team-checkout          # владелец обязателен: политика отклонит без него
    tier: "1"                     # влияет на SLO, дежурство и лимиты расхода
spec:
  runtime:
    language: go
    cpu: 500m
    memory: 512Mi
    replicas: { min: 2, max: 10 }
  dependencies:
    - kind: KafkaTopic            # тема создастся сама, отдельная заявка не нужна
      name: orders.v2
      partitions: 6
      retention: 7d
    - kind: PostgresDatabase
      name: orders
      size: small                 # small/medium/large — за ними стоят проверенные профили
  observability:
    slo:
      availability: 99.9          # из этого генерируются дашборд и алерты
      latency_p99_ms: 300
  escape:
    raw_terraform: false          # честный флаг выхода за золотой путь, см. ниже

Проверка политикой — вместо ревьюера:

# policy/platform-baseline.yaml — политика как код (синтаксис Kyverno)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: platform-baseline }
spec:
  validationFailureAction: Enforce
  rules:
    - name: owner-label-required
      match:
        any: [{ resources: { kinds: ["platform.example.com/v1/Service"] } }]
      validate:
        # Сообщение пишется для человека, а не для лога: это часть интерфейса
        message: >-
          У сервиса нет метки owner. Укажите команду-владельца:
          docs.example.com/platform/ownership          
        pattern:
          metadata: { labels: { owner: "?*" } }

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

Сессия инженера, который этим пользуется:

# Всё самообслуживание доступно из одной команды — её знают наизусть
$ plat new service orders-api --template go-http --owner team-checkout
  создан каталог orders-api/ с service.yaml, Dockerfile и пайплайном

# Изменение ресурса — тот же путь, что и изменение кода: через pull request
$ vim service.yaml            # replicas.max: 10 -> 20

# Локальная проверка политик до пуша: отказ должен приходить за секунды, а не в CI
$ plat validate
20 реплик × 500m = 10 CPU, квота team-checkout — 8 CPU
    поднять квоту: plat quota request --team team-checkout --cpu 8
    решение автоматическое в пределах бюджета команды

$ plat quota request --team team-checkout --cpu 8
  ✓ квота увеличена: 8 -> 16 CPU (в пределах месячного бюджета, 62 % использовано)

# Что происходит с моим запросом — видно без похода в чат платформы
$ plat status orders-api
  service      orders-api        готов    (обновлён 3 мин назад)
  kafka-topic  orders.v2         готов
  database     orders (small)    создаётся ~2 мин
  slo          99.9 / p99 300ms  дашборд: grafana.example.com/d/orders-api

Ключевое в этой сессии — не команды, а то, что отказ приходит локально, за секунды, с готовым следующим шагом. Отказ в CI через восемь минут — это уже наполовину тикет. Про то, где такие проверки должны жить, — в главе «Конвейер как платформенный сервис».

Четыре поверхности: CLI, PR, API, портал

Самообслуживание доставляется через интерфейсы, и выбор интерфейса — не вопрос вкуса. У каждого своя ниша, своя цена владения и свой типовой провал.

Поверхность Кому и когда Цена владения Как проваливается
CLI ежедневные операции инженера, быстрый цикл сборка под три ОС, версии, автообновление, справка ставится не у всех, версии расходятся, справка врёт
PR в репозиторий изменения, которые нужно ревьюить и хранить в истории шаблоны, боты, время прохождения пайплайна 12-минутный пайплайн убивает цикл правок
API автоматизация, интеграции, скрипты команд версионирование, совместимость, аутентификация нет версий — ломается всё сразу у всех
Портал (UI) редкие операции, новички, обзор и поиск фронтенд как продукт: дизайн, доступность, поддержка делается первым и потому пустует

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

Отдельно про каталог сервисов, который обычно и есть повод завести портал. Каталог полезен ровно до того момента, пока он правдив. Каталог, заполняемый руками, устаревает за квартал и после этого вреден: он не просто бесполезен, он даёт неверные ответы на вопрос «кто владелец» в момент инцидента. Единственный работающий вариант — каталог как производная: владелец, зависимости, SLO и дежурство выводятся из service.yaml в репозитории и из фактических данных выкаток. Никакой отдельной формы «зарегистрируйте свой сервис в портале» быть не должно — это заявка, замаскированная под каталог.

Три способа провалить самообслуживание

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

Провал 1: обёртка над облаком, которая только мешает

Симптом: платформа предоставляет plat create database, за которым стоит вызов Terraform с шестью параметрами из сорока доступных. Команде нужен седьмой — репликация в другой регион. Ответ платформы: «добавим в следующем спринте». Команда идёт в консоль облака и делает руками.

Механика провала: абстракция без собственной модели. Обёртка не упрощает мышление, она лишь переименовывает поля и при этом отрезает 85 % возможностей исходного инструмента. Пользователь платит полную цену изучения новой абстракции, не получая взамен новых понятий, — и одновременно теряет доступ к документации облака, к StackOverflow и к своему прошлому опыту.

Признаки, что вы строите именно это:

  • каждый новый параметр требует релиза платформы, и в бэклоге платформы очередь из параметров;
  • документация платформы состоит из отсылок «см. документацию AWS/GCP по этому полю»;
  • нет способа выйти за пределы обёртки, кроме как обойти платформу целиком;
  • у абстракции нет ни одного понятия, которого нет в исходном API.

Что делать:

  • Пропускать неизвестные параметры насквозь. Блок raw: в манифесте, куда можно положить любое поле исходного провайдера. Да, это протечка абстракции; альтернатива — что вас обойдут целиком.
  • Скрывать не поля, а решения. Профиль size: small полезен не тем, что прячет три параметра, а тем, что за ним стоит выбор: тип диска, настройки бэкапа, окно обслуживания, лимиты соединений — то, о чём продуктовая команда не хочет думать и не должна. Это содержательная абстракция; подробно — в «Абстракциях».
  • Считать долю пользователей, ушедших в raw:. Если 40 % манифестов используют сырые поля для одного и того же параметра — это не нарушение, это заявка на функцию, поданная голосованием.

Провал 2: портал, которым никто не пользуется

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

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

  • Портал построен вокруг сущностей платформы, а не вокруг задач пользователя. Разделы «Кластеры», «Неймспейсы», «Пайплайны» — это модель платформы. Инженер приходит с задачей «выкатить хотфикс» и не знает, в каком разделе она живёт. Лечится сценариями и задачами пользователя, а не перестановкой меню.
  • Портал делает 60 % пути и бросает. Создать сервис можно, а посмотреть его логи — нельзя, идите в другую систему. Обрыв посередине хуже отсутствия: пользователь запоминает, что здесь всё равно не доделаешь, и перестаёт заходить.
  • Второй логин и третья ролевая модель. Каждый лишний барьер на входе отсекает часть пользователей, и они не возвращаются.
  • Данные врут. Один раз увиденный неверный владелец сервиса или устаревший статус деплоя обнуляет доверие к порталу целиком.
  • Портал построен раньше, чем автоматизация под ним. Кнопка, создающая тикет, — это тикет. Портал — витрина над работающими API; без API он декорация.

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

Про Backstage конкретно, без рекламы и без злорадства. Это открытый фреймворк, из которого можно собрать хороший портал, и в нём есть готовые ответы на каталог, шаблоны и техдоки. Цена: это TypeScript-приложение, которое вы разворачиваете, конфигурируете и обновляете сами; плагины ломаются при обновлениях; часть нужного вам всё равно придётся писать. Реалистичная оценка владения — один-полтора инженера постоянно, плюс несколько месяцев до первой полезной версии. Это разумная сделка при 30+ командах и сомнительная при пяти, где вместо портала лучше работает страница в вики со ссылками и хороший CLI.

Провал 3: абстракция поверх трёх разных потребностей

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

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

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

Что делать:

  • Не абстрагировать раньше третьего похожего случая. До этого — три отдельных решения, пусть с дублированием. Дублирование дешевле неправильной абстракции: его видно и его легко удалить.
  • Разделять по режиму использования, а не по технологии. Не «сервис PostgreSQL», а «база для транзакций сервиса» и «хранилище для аналитики» — даже если под обоими сначала одна и та же СУБД. Выбор технологии — детали реализации платформы, и он может измениться («Выбор и миграция БД»).
  • Считать флаги как техдолг. Каждый новый параметр в общем интерфейсе — сигнал, что модель неверна. Раз в квартал стоит смотреть на список параметров и спрашивать, какие профили за ними прячутся.

Золотой путь: помощь ровно до того момента, пока он путь

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

Разница не в технике, а в том, что происходит с командой, которой путь не подходит.

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

  • команда берёт на себя эксплуатацию: своё дежурство, свои алерты, свой SLO («SLI и SLO»);
  • обязательства по безопасности и соответствию остаются в силе — они не часть золотого пути, а часть периметра компании («Безопасность по умолчанию»);
  • в каталоге появляется метка: этот сервис вне золотого пути, при инциденте будить его команду;
  • назначается дата пересмотра — обычно через два квартала: возможно, платформа к тому времени закроет потребность.

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

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

Полезная граница: золотой путь описывает как удобно, периметр безопасности описывает что нельзя. Их смешение — главная причина, по которой золотой путь превращается в забор. «Нельзя выкатывать без ревью безопасности» — периметр. «Мы поддерживаем Go и Kotlin» — путь; команда на Rust не преступник, она просто дальше от готовых инструментов и знает об этом.

Метрики принятия: что считать вместо функций

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

Метрика Определение Ловушка Откуда данные
Доля операций без человека операции, прошедшие без вмешательства платформы / все операции считать только успешные — тогда отказы прячутся журнал событий платформы
Время до результата (p50/p90) от запроса до готового ресурса считать только p50: боль живёт в хвосте тот же журнал
Принятие сервисов на золотом пути / всех продовых сервисов считать команды, а не сервисы: команда с 20 сервисами и одним на платформе засчитывается каталог + фактические выкатки
Время до первого успеха от старта новой команды до первой выкатки в прод измеряется редко, поэтому шумно; берите медиану за полгода онбординг новых команд
Возвраты в тикеты заявок по операциям, у которых есть самообслуживание нулевое значение подозрительно: значит, люди сдались и делают руками трекер
Обходы ресурсов, созданных мимо платформы требует сверки с облаком; без неё метрика оптимистична инвентаризация облака
Удержание команды, активные в платформе месяц назад и сегодня новые команды маскируют уход старых — считайте когортами журнал событий
Успешность операций доля операций, завершившихся без ошибки 100 % означает, что операций мало журнал событий

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

Считается всё это из одной таблицы событий, которую надо завести с первого дня:

-- События платформы: одна строка на операцию, независимо от поверхности
-- (CLI, портал, API, пайплайн). Без этой таблицы метрик принятия не существует.
CREATE TABLE platform_events (
    id            bigserial PRIMARY KEY,
    ts            timestamptz NOT NULL DEFAULT now(),
    team          text        NOT NULL,        -- владелец, из метки owner
    service       text,
    operation     text        NOT NULL,        -- create_topic, deploy, quota_change, ...
    surface       text        NOT NULL,        -- cli | portal | api | pipeline
    outcome       text        NOT NULL,        -- ok | policy_denied | error | escalated
    human_needed  boolean     NOT NULL,        -- потребовалось ли вмешательство платформы
    duration_ms   integer     NOT NULL         -- от запроса до готового результата
);

-- Главный отчёт: доля операций без человека и время до результата по типам
SELECT
    operation,
    count(*)                                                   AS total,
    round(100.0 * avg((NOT human_needed)::int), 1)             AS self_service_pct,
    round(100.0 * avg((outcome = 'ok')::int), 1)               AS success_pct,
    percentile_cont(0.5) WITHIN GROUP (ORDER BY duration_ms)   AS p50_ms,
    percentile_cont(0.9) WITHIN GROUP (ORDER BY duration_ms)   AS p90_ms
FROM platform_events
WHERE ts >= now() - interval '30 days'
GROUP BY operation
ORDER BY total DESC;

-- Удержание: команда была активна в прошлом месяце и молчит в этом.
-- Уходящие команды видно здесь раньше, чем в любом опросе.
SELECT team, max(ts) AS last_seen
FROM platform_events
WHERE ts >= now() - interval '60 days'
GROUP BY team
HAVING max(ts) < now() - interval '30 days';

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

Про измерение опыта разработчика в целом — в «Опыте разработчика»; там же разбирается, почему одни только системные метрики врут и зачем к ним нужны восприятие и поток (подход SPACE, «DevEx: What Actually Drives Productivity», ACM Queue, 2023).

Когда самообслуживание не нужно

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

Признак операции Пример Что делать вместо самообслуживания
Редкая: 1–3 раза в год новый регион присутствия документированный чек-лист, делает платформа
Необратимая удаление продовой БД, отзыв домена человек в критическом пути осознанно
Требует суждения по существу схема данных, влияющая на смежные сервисы автоматизировать подготовку и проверки, решение оставить людям
Требует переговоров вне инженерии доступ к персональным данным процесс согласования, автоматизировать выдачу после решения
Высокая цена ошибки при низкой частоте смена корневого сертификата репетиция и парная работа, не кнопка

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

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

Типичные ошибки

  • Построить портал первым. Портал — витрина над работающими API. Без них это страница с формами, создающими тикеты.
  • Считать функции вместо принятия. Двадцать функций при принятии 30 % — убыток, посчитанный как достижение.
  • Не считать стоимость платформенной команды. Без модели окупаемости платформа превращается в налог, который никто не решается отменить.
  • Отдавать необратимое. Самообслуживание безопасно ровно настолько, насколько дёшев откат; начинать надо с обратимого.
  • Забыть про удаление и TTL. Самообслуживание без удаления — генератор мусора и счетов.
  • Наказывать за обход. Обходы не исчезнут, исчезнет информация о них. Обход — это заявка на функцию.
  • Абстрагировать после первого клиента. До третьего похожего случая дублирование дешевле неверной абстракции.
  • Отказ без следующего шага. denied by policy без объяснения превращает самообслуживание в тикет с лишним шагом.
  • Брать инструмент, не назвав цену владения. Kubernetes, Backstage, Crossplane — обязательства на годы, а не решения; закладывайте человеко-месяцы поддержки в расчёт.
  • Молча менять интерфейс. Платформа — продукт с пользователями; ломающее изменение без миграционного пути стоит доверия, которое восстанавливается годами («Миграции»).

Мини-итог

  • Самообслуживание — свойство отдельной операции: результат без человека из другой команды в критическом пути. Это шкала от анархии до инварианта, а не выключатель.
  • Очередь тикетов имеет цену, и она считается: у условной компании из 12 команд — порядка 920 инженерных часов в квартал, из которых платформа видит только 312. Издержки несёт одна сторона, решение принимает другая.
  • Платформенная команда обязана окупаться сэкономленным временем. Точка окупаемости определяется принятием, а не полнотой функций: при принятии 40 % платформа не окупается никогда.
  • Пять свойств делают самообслуживание работающим: декларативность, политика вместо ревью, обратимость, ограничители, наблюдаемость запроса. Безопасность отдачи операции равна дешевизне отката.
  • Поверхности строятся в порядке CLI и PR → API → портал. Портал — витрина, а не фундамент; каталог полезен, только пока он производная от реальных данных.
  • Три типовых провала: обёртка без собственной модели, портал вокруг сущностей платформы, абстракция как объединение требований трёх разных клиентов.
  • Золотой путь остаётся путём, пока съезд с него возможен, оформлен и не стыден. Каждый съезд — вход в бэклог платформы.
  • Метрики: доля операций без человека, время до результата по p50/p90, принятие по сервисам, удержание когортами, обходы. Не метрики: регистрации, число функций, объём созданных ресурсов.

Источники

  • Evan Bottcher. What I Talk About When I Talk About Platforms — определение платформы через проложенную дорогу и самообслуживание.
  • Matthew Skelton, Manuel Pais. Team Topologies (IT Revolution, 2019), teamtopologies.com — платформенная команда как тип, «тончайшая жизнеспособная платформа», когнитивная нагрузка.
  • CNCF TAG App Delivery. Platforms White Paper и Platform Engineering Maturity Model — уровни зрелости и набор возможностей платформы.
  • Google SRE. Eliminating Toil — определение рутины и арифметика её устранения.
  • Noda, Storey, Forsgren, Greiler. DevEx: What Actually Drives Productivity, ACM Queue, 2023 — что измерять в опыте разработчика.
  • DORA — lead time изменений, частота выкаток и связь с практиками доставки.
  • Backstage documentation — что это на самом деле: фреймворк, а не готовый портал.
  • Crossplane docs и Terraform docs — декларативное управление инфраструктурой.
  • Open Policy Agent и Kyverno — политика как код вместо ручного ревью.

Что дальше

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

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов