# Стандарт уроков курса «Кликстрим на ClickHouse»

> Дата: 2026-06-01. Поправка: 2026-06-05 (по итогам уроков 0–2 — добавлены §2 «Регистр
> и голос», описание шапки урока и §5 «Грабли, на которые мы уже наступили»).
> Назначение: рабочий чеклист, по которому пишется **каждый** урок. Открывается при
> создании урока. Рамка курса (зачем/что/скоуп) — в `PRD.md`; карта уроков и
> маршрут — в `LEARNING_PLAN.md`.

---

## 1. Шаблон урока

Каждый урок строится по единому шаблону:

1. **Зачем и где в проде** — что за паттерн и где он встречается в обычной
   кликстрим-аналитике (без привязки к конкретной компании).
2. **Руки** — что запустить и что понаблюдать (Kafka UI / ClickHouse / Grafana).
3. **Загляни внутрь** — чтение отполированного эталонного кода с пояснениями.
4. **Управляемая правка** — одно-два маленьких изменения с **видимым результатом**.
   Например: добавить поле в Materialized View и увидеть его в `*_raw`; «сломать»
   запись и увидеть `+1` в таблице ошибок `parse_errors`; сменить `kafka_group_name`
   и увидеть, как топик читается заново. Менти меняет — видит эффект — объясняет.
   **Верни как было** — каждая правка завершается явным шагом отката к чистому
   состоянию (откатить изменение либо пройти
   [канонический сброс](./README.md#подготовка-и-канонический-сброс)), чтобы
   самостоятельный менти не застрял со сломанным стендом без ментора.
   (Урок 0 — без этого шага, только наблюдение.)
5. **Проверь себя** — самопроверка (раздел 4).
6. **Что должно получиться** — **конкретный видимый результат**, который менти проверяет
   сам: скрин эффекта правки (например, красный DAG или `+1` в таблице ошибок) плюс один
   абзац «своими словами» про паттерн урока или запрос, который пришлось написать. Это и
   есть самопроверка (раздел 4); те же вопросы «своими словами» менти разбирает с ментором
   на еженедельном созвоне. (Урок 0 — без правки, поэтому результат здесь — что менти
   увидел при наблюдении.)

### Шапка урока

Кроме шести секций, каждый урок открывается единым блоком-цитатой (`>`) сверху — он задаёт
рамку до того, как менти дойдёт до первой секции:

- **Формат:** одной фразой, что менти делает, — `**практика** — будешь сам запускать…`
  или `**наблюдение** — ничего не запускаем и не меняем…`. Старые пометки «Статус: черновик»
  и «Режим: руки» не возвращаем: менти читал их как «материал не готов» и «непонятно, как
  понимать»;
- **Пререквизит:** какой урок пройден и что менти уже умеет;
- **Эталонный путь:** ссылка(и) на разбираемый файл;
- **Поток данных одной строкой:** та же стрелочная схема, что стоит в начале эталонного файла;
- **О чём урок простыми словами:** 1–2 фразы совсем простым языком, без терминов, — чтобы
  менти с первой строки понял, о чём речь.

В конце урока — короткий **мост к следующему**: какой честный вопрос остаётся открытым и
как его закроет следующий урок.

## 2. Регистр и голос

Этот раздел — про то, *как* написан урок. Он появился по итогам уроков 0–2: мы начали со
сжатого регистра и переписывали — дешевле сразу писать мягко.

**Кто читатель.** Менти прошёл курсовую, уровень — «обзорно» (понять идею и потрогать, а не
стать экспертом). Поддержка — еженедельный созвон с ментором, а не «сессия»; на нём менти
разбирает вопросы «своими словами».

**Правила регистра:**
- **термин расшифровываем на первом употреблении** — коротко, своими словами (как `ODS`,
  `DQ-split`, `Materialized View`, `*OrNull` в уроках 1–2). Не оставляем слово голым;
- **человеческий тон, без жаргона ради жаргона** — короткие предложения, прямое обращение
  к менти («открой», «посмотри», «помнишь из урока 0»);
- **`###`-подзаголовки внутри длинных секций** — особенно в «Загляни внутрь»: каждый
  смысловой кусок под своим заголовком, а не сплошной стеной;
- **плотные абзацы-«стены» бьём на пункты** с конкретикой;
- **разжёвывать, а не сжимать.** Урок может выйти заметно длиннее первого черновика — для
  «обзорно» это норма, а не повод резать.

**House style (оставляем осознанно, `ai-text-lint` на это не правим):** тире «—», буква «ё»,
жирные зачины абзацев, двоеточие перед списком.

**Перед финалом** — прогнать текст урока через `/ai-text-lint` (контекст `article`) и убрать
настоящие AI-маркеры (например, слова вроде «ключевой», «является»), но house style выше
не снимать.

## 3. Стандарт качества эталонного кода

Эталонные пути полируем до **учебного качества**; остальной код стенда остаётся под
капотом — его не трогаем.

**Чеклист (минимум).** Файл доведён, если:
1. комментарии на русском объясняют **зачем**, а не пересказывают код;
2. шаги явные и названы своими именами, без неочевидных трюков;
3. в начале файла — поток данных одной строкой (как в STG:
   `Kafka → kafka_*_raw → MV → *_raw`);
4. ошибки и проверки качества данных видны — понятно, куда смотреть;
5. нет мусора: мёртвого кода, закомментированных экспериментов, разнобоя в именах.

**Тест одного прохода (главный критерий и защита от громоздкости).**
> Менти читает файл один раз сверху вниз и может пересказать своими словами,
> *что* происходит и *зачем* — не прыгая по файлу, не гугля, не расшифровывая трюки.

Если тест не проходит — правим строго одним из трёх способов: (1) упростить код;
(2) добавить ровно одну строку «зачем» там, где код честно неочевиден; (3) убрать
лишнее под капот или в короткую пометку. **Добавить ещё комментариев — не способ.**

**Две защиты от раздувания:**
- комментируем только неочевидное «зачем» (очевидную строку комментировать нельзя —
  лишний текст сам по себе мешает понимать);
- «близко к проду» ≠ «вся прод-сложность прямо в коде урока»: один прод-паттерн на
  урок показываем чисто, остальные прод-заботы — короткой пометкой «в проде иначе: …».

Вердикты аудита по конкретным путям — в `LEARNING_PLAN.md`.

## 4. Самопроверка (по-простому)

Отдельный тест-фреймворк не строим. Опираемся на то, что в стенде уже есть:
- штатные быстрые проверки (smoke) из `docs/TEST_PLAN.md`;
- встроенные проверки в `etl_pipeline_dag.py` (DAG падает на пустой витрине или
  нарушении целостности);
- [канонический сброс](./README.md#подготовка-и-канонический-сброс) для чистого
  возврата и повтора штатного пути.

В каждом уроке — маленькая табличка самопроверки в формате
**действие → где смотреть → что ожидать**, а для управляемой правки — какой
видимый результат должен появиться.

## 5. Грабли, на которые мы уже наступили

Короткий список ошибок из уроков 0–2 — чтобы следующий урок их не повторил.

- **Проверяй на стенде, а не «на глаз».** Любой категоричный claim про числа или целостность
  подтверждай командой на стенде. Так нашёлся баг с `kafka_ts` в уроке 1 (значение типа
  `DateTime64` молча резалось через `toInt64`, и время по всему стенду уехало в `1970`) и
  подтвердилось, что меньший размер таблиц контекста в уроке 2 — это схлопывание повторов
  по `click_id` (проверяется через `uniqExact`), а не потеря данных.
- **Имена таблиц и колонок в тексте сверяй с реальным DDL.** То, что написано в уроке, должно
  совпадать с `sql/ddl/...`, иначе менти запутается, когда выполнит запрос на стенде.
- **Один паттерн на урок; будущие темы только анонсируй.** Не раскрывай то, что относится к
  следующим урокам (пример: жёсткий гейт на «сирот» — это урок 4, в уроке 3 мы их только
  вводим как понятие).
- **Спорные API ClickHouse сверяй через MCP Context7** (зачем и в каком порядке — в
  `AGENTS.md`), не полагайся на память: именно типы и поведение функций уже один раз подвели.
