К списку метрик
ENGAGEMENT

Churn Rate (отток)

Churn Rate

Доля пользователей или дохода, переставших использовать продукт за период.

ФОРМУЛА

$$\text{Churn} = \frac{\text{lost in period}}{\text{active at start of period}}$$

ОПИСАНИЕ

Два типа churn:

  1. Customer churn (logo churn) — какая доля юзеров ушла
  2. Revenue churn (MRR churn) — какая доля денег ушла (учитывает downgrades)

Часто MRR churn < customer churn (уходят дешёвые юзеры) или наоборот (уходят дорогие).

Бенчмарки для месячного churn:

КатегорияHealthyConcerning
Consumer subscription5-7%10%+
B2B SaaS small3-5%7%+
B2B SaaS enterprise0.5-1%2%+
Banking apps1-2%5%+
Streaming (Netflix)4-6%10%+

Pitfalls:

  1. Voluntary vs involuntary churn: часть «churn» — это failed payments (карта истекла) — это техническая проблема, не uxperience. Раздели и решай отдельно.

  2. Survivorship bias: «у нас churn 3%» легко звучит хорошо, пока не разделишь на новые vs returning. У новых может быть 20%, у returning 1%. Среднее 3%.

  3. Definition matters: «не залогинился 30 дней» vs «отменил подписку» — совсем разные churn. Выбери понятное и придерживайся.

Расчёт LTV через churn:

$$LTV = \frac{ARPU}{churn}$$

Если churn 5% месяц → клиент живёт 20 месяцев → LTV = 20 × ARPU.

SQL ПРИМЕР
-- Customer churn по cohort
WITH cohorts AS (
  SELECT user_id, date_trunc('month', registered_at)::date AS cohort
  FROM users
),
activity AS (
  SELECT user_id, date_trunc('month', event_time)::date AS active_month
  FROM events
  GROUP BY 1, 2
)
SELECT
  c.cohort,
  COUNT(DISTINCT c.user_id) AS cohort_size,
  COUNT(DISTINCT CASE WHEN a.active_month = c.cohort + INTERVAL '1 month' THEN c.user_id END) AS retained_m1,
  1.0 - COUNT(DISTINCT CASE WHEN a.active_month = c.cohort + INTERVAL '1 month' THEN c.user_id END)::float
        / COUNT(DISTINCT c.user_id) AS month_1_churn
FROM cohorts c
LEFT JOIN activity a ON a.user_id = c.user_id
GROUP BY 1
ORDER BY 1;
PYTHON ПРИМЕР
# По месячным когортам
users["cohort"] = users["registered_at"].dt.to_period("M")
events["active_month"] = events["ts"].dt.to_period("M")

cohort_size = users.groupby("cohort")["id"].nunique()
m1_active = (events.merge(users[["id", "cohort"]], left_on="user_id", right_on="id")
                   .query("active_month == cohort + 1")
                   .groupby("cohort")["user_id"].nunique())
churn_m1 = 1 - (m1_active / cohort_size)
СВЯЗАННЫЕ МЕТРИКИ