---

# Стандарт статьи — UE C++ Academy

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

**Главный тест, который стоит выше всех остальных пунктов этого
документа:** каждая статья должна не просто объяснить тему — она
должна изменить способ мышления читателя. Если после статьи человек
пишет такой же код, как писал раньше, значит статья не достигла своей
цели. Если после статьи человек начинает задавать себе другие вопросы
прежде, чем написать код, — статья выполнена успешно. Все разделы
ниже — конкретные, проверяемые способы этого добиться; ни один из них
не самоцель сам по себе.

## Оглавление

- [0. Философия и цель обучения](#sec-0)
- [0.1. Главная цель обучения](#sec-0-1)
- [1. Методика изложения — обязательный порядок](#sec-1)
- [1.5. Правило 30 секунд — как далеко заходить в объяснении](#sec-1-5)
- [1.6. Психология чтения — ритм статьи](#sec-1-6)
- [1.7. Разговор с senior, а не книга](#sec-1-7)
- [2. Обязательные разделы статьи](#sec-2)
- [3. Дополнительные разделы — появляются по необходимости](#sec-3)
- [4. Правила для кода](#sec-4)
- [5. Единый стиль блоков](#sec-5)
- [6. Практические задания](#sec-6)
- [7. FAQ](#sec-7)
- [8. Чек-лист самопроверки перед публикацией статьи](#sec-8)
- [9. Эталонная статья](#sec-9)
- [10. Технические требования к каждой статье (для инфраструктуры)](#sec-10)
- [11. Когда стандарт нарушать можно](#sec-11)
- [12. Проверка понятности и качества объяснения](#sec-12)

<a id="sec-0"></a>
## 0. Философия и цель обучения

Мы не пишем справочник. Справочник отвечает на вопрос «что делает
эта функция». Мы пишем инженерную школу — она отвечает на вопрос
«почему Epic спроектировала именно так, и как я сам приду к такому
же решению в следующий раз, столкнувшись с новой, ещё не описанной
системой движка».

**Каждая статья должна звучать как разговор с опытным разработчиком,
который объясняет сложную тему простыми словами.**

Это означает:
- Ты не читаешь лекцию — ты ведёшь диалог.
- Ты не перечисляешь факты — ты отвечаешь на вопросы.
- Ты не объясняешь "как" — ты показываешь "почему".
- Ты не даёшь готовый ответ — ты помогаешь читателю прийти к нему самому.

**Проверка:** Если после прочтения статьи читатель не может объяснить
тему другому человеку своими словами — статья не достигла цели.

Практическое следствие: после каждой статьи читателя нужно проверять
не на «запомнил ли он факт», а на «сможет ли он объяснить причину
другому человеку и предсказать, как эта причина проявится в
незнакомой ему части движка».

**Главное правило проекта: каждая новая тема должна уменьшать
количество «магии» в Unreal Engine.** После каждой главы читатель
должен уметь назвать конкретную вещь, которая до этой главы казалась
магией движка, а теперь стала логичным следствием архитектуры —
например: после главы про память понятно, почему Unreal почти везде
использует указатели; после Reflection понятно, зачем нужны UCLASS/
UPROPERTY/UFUNCTION; после жизненного цикла объектов понятно, почему
нельзя делать определённые вещи в конструкторе; после Gameplay
Framework очевидно, почему логика разделена между GameMode,
PlayerController, PlayerState и Character. Общий обзор всех таких
«почему» верхнего уровня, независимо от порядка кластеров — на
странице `philosophy.html` («Как мыслит Unreal Engine»); новая статья
не обязана пересказывать её, но обязана явно закрывать вопрос «что
именно здесь перестаёт быть магией» хотя бы один раз где-то в тексте.

<a id="sec-0-1"></a>
### 0.1. Главная цель обучения

Философия выше отвечает на вопрос «как мыслит движок». Этот пункт
отвечает на другой вопрос — «зачем читателю всё это» — и должен
управлять каждым решением о том, что включать в статью, а что нет.

После прохождения курса читатель должен научиться самостоятельно
принимать инженерные решения в Unreal Engine — не помнить факты о
конкретных классах, а уметь:

- самостоятельно проектировать архитектуру новой игровой системы;
- читать исходный код Unreal Engine без страха перед незнакомым
  файлом;
- понимать причины существования любой системы движка, даже той, о
  которой в курсе не написано ни строки;
- находить решение в незнакомой части движка по аналогии с уже
  понятым способом мышления, а не методом проб и ошибок;
- понимать, какие компромиссы стоят за конкретным архитектурным
  решением Epic, и уметь их сформулировать словами, а не просто
  процитировать вывод.

**Главная цель курса — сделать так, чтобы со временем документация
Unreal Engine стала читателю не нужна.** Не потому что он всё
запомнил, а потому что он научился воспроизводить то рассуждение,
которым сама документация была написана.

<a id="sec-1"></a>
## 1. Методика изложения — обязательный порядок

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

1. **Проблема.** Конкретный, воспроизводимый сценарий, в котором
   наивный подход начинает разваливаться. Не абстракция — история
   с конкретными именами классов и конкретными следствиями поломки.
2. **Причина.** Почему проблема вообще возникает — что в природе
   C++, движка реального времени, командной разработки или сетевой
   игры делает наивный подход недостаточным.
3. **Ограничения.** Какие требования нельзя нарушить при поиске
   решения (производительность кадра, совместимость с Blueprint,
   работа в редакторе и рантайме одновременно, сетевая репликация
   и т.д.) — именно ограничения объясняют, почему решение выглядит
   так, а не иначе.
4. **Архитектурное решение.** Что придумала Epic, сформулированное
   как ответ на пункты 1–3, а не как самостоятельный факт.
5. **Реализация.** Как это реализовано в движке и как этим
   пользоваться в собственном коде — только на этом шаге появляется
   API.
6. **Практика.** Задание, которое заставляет читателя воспроизвести
   рассуждение самостоятельно, а не просто скопировать пример.

**Каждый раздел должен начинаться с вопроса.**

**Пример:**

**Плохо:**
> `UPROPERTY()` — это макрос, который размечает поле для Reflection.

**Хорошо:**
> **Зачем вообще нужен UPROPERTY?**
>
> Представь, что ты создал поле в классе. Оно работает. Но движок о нём не знает. А это значит, что...

Вопрос в начале раздела — это якорь для внимания читателя. Он знает,
на какой вопрос сейчас получит ответ.

<a id="sec-1-5"></a>
## 1.5. Правило 30 секунд — как далеко заходить в объяснении

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

**Формальный критерий готовности раздела:** после каждого абзаца
внимательный читатель задаёт себе следующий вопрос через 30 секунд.
Раздел не закончен, пока этот вопрос не закрыт. Если вместо ответа
читатель тянется открывать Google — раздел не прошёл проверку.

**Каждый абзац должен отвечать на вопрос, который естественно возник
после предыдущего.**

**Пример:**

**Плохо:**
> `CreateDefaultSubobject` нужно вызывать в конструкторе.

**Хорошо:**
> `CreateDefaultSubobject` нужно вызывать в конструкторе.
>
> **Почему?**
>
> Потому что только на этом этапе движок ещё строит CDO...

Ответ "почему" должен идти сразу за утверждением, а не через абзац
или раздел.

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

Из этого вытекают шесть конкретных техник, обязательных там, где
применимо:

1. **Дать читателю самому прийти к выводу, а не сообщить вывод.**
   Вместо «компоненты решают проблему комбинаторного взрыва» —
   показать наивный код (один God-класс со всеми способностями),
   затем заставить читателя мысленно скопировать этот класс для
   Monster, NPC, Chest, Door, Turret ещё до того, как в тексте
   появится слово «компонент». Вывод, до которого читатель дошёл
   сам, запоминается; вывод, который ему сообщили, — нет.
2. **История развития мысли, а не готовый ответ.** Проблема → почему
   не решается наследованием → почему не решается интерфейсами →
   почему не решается шаблонами/generics → и только после этого —
   архитектурное решение Epic. Каждый отвергнутый вариант объясняется
   тем, что именно в нём ломается, а не просто помечается как
   «недостаточный».
3. **«Что происходит внутри движка» — пошаговая цепочка, а не
   единственная строчка кода.** Любой ключевой вызов API (например,
   `CreateDefaultSubobject`) сопровождается диаграммой этапов, через
   которые проходит движок между вызовом и результатом (см. раздел 5
   про формат диаграмм). Без этого API выглядит как магия, а не
   инженерное решение.
4. **«Почему не иначе» — сразу после каждого ключевого вызова.** Если
   в тексте появляется `CreateDefaultSubobject`, тут же должно быть
   объяснено, почему не `NewObject` в `BeginPlay` и почему не
   обычный `new` — что конкретно ломается (GC? Reflection? CDO?
   Blueprint?), а не абстрактное «так принято».
5. **«Что если…» — исследование границ через сценарии.** Что будет,
   если сделать это позже? Что если указатель останется nullptr? Что
   если убрать UPROPERTY? Такие сценарии закрывают вопросы читателя
   быстрее, чем абстрактное описание правила, потому что показывают
   границу на конкретном примере поломки.
6. **Практика как мини-проект, а не изолированное задание.** Там, где
   тема — про переиспользуемость (компоненты, интерфейсы,
   DataAsset и т.д.), задание должно заставить применить одно и то же
   решение к нескольким явно разным сущностям (персонаж, сундук,
   враг, башня) и явно зафиксировать момент, когда читатель замечает:
   код самого решения не изменился ни разу. Это и есть тот момент,
   когда абстракция становится очевидной, а не просто прочитанной.

Где уместно и не превращает статью в пересказ исходников — короткий,
явно обозначенный как упрощённый, взгляд на реальную функцию движка
(«Заглянем в исходники движка» — не построчный разбор всего файла, а
2–4 ключевых этапа того, что функция реально делает). Это учит читать
движок, а не только пользоваться им, и относится к тем же
дополнительным разделам, что и «Влияние на GC» — появляется, когда
тема естественно этого просит.

---

<a id="sec-1-6"></a>
## 1.6. Психология чтения — ритм статьи

Раздел 1.5 определял, **насколько глубоко** объяснять каждую мысль.
Этот раздел — про другое измерение: не про содержание, а про то,
**как мозг читателя переваривает информацию**, поданную в этой
глубине. Даже идеально написанная по фактам статья провалится, если
её ритм утомляет.

**Главное правило ритма:** статья должна создавать ощущение, что читатель **продвигается**, а не просто перематывает текст. Если через 30 секунд чтения читатель не чувствует, что понял что-то есть — ритм сбит.

### 1.6.1. Один абзац — одна новая мысль

**Плохо:**
> Reflection позволяет... UHT генерирует... ProcessEvent вызывает... Blueprint VM...

Четыре новые мысли в одном абзаце — мозг перестаёт строить связи.

**Правильно:**
> Reflection — это система, которая позволяет движку узнавать о типах в рантайме.
>
> *(объяснение Reflection)*
>
> Теперь, когда мы понимаем, что такое Reflection, возникает вопрос: как Unreal узнаёт, какие классы должны в него попасть?
>
> *(объяснение UHT)*

Каждый абзац — ровно одна идея. Если в абзаце больше одной новой мысли — разбить.

---

### 1.6.2. Один абзац — максимум один новый термин

Если в абзаце появляется термин `Reflection`, то в этом же абзаце **не должно быть** `Metadata`, `Runtime`, `Dispatcher` и т.д.

Новый термин должен:
- быть выделен (жирным или кодом);
- объяснён в этом же абзаце или в следующем;
- не конкурировать за внимание с другими новыми терминами.

---

### 1.6.3. Ритм — не более 3–4 абзацев текста подряд

Если на странице подряд идёт сплошной текст — мозг устаёт ещё до того, как начал читать.

**Правильный ритм:**

Текст (2–4 абзаца)
↓
Схема / Код / Таблица / Callout
↓
Текст (2–4 абзаца)
↓
Вопрос-переход («Теперь возникает следующий вопрос...»)
↓
Текст

Визуально страница должна «дышать» — чередовать плотный текст с разреженными блоками (код, таблицы, схемы, врезки).

---

### 1.6.4. Каждый раздел — одна эмоциональная вершина

Читатель должен чувствовать, что он **открывает что-то есть**, а не просто читает факты.

Примеры вершин:
- «Вот почему обычный C++ здесь не работает»
- «Именно это Epic и пришлось решать»
- «Теперь становится очевидно, почему...»

Вершина — это момент, когда читатель говорит себе: «Ага, теперь понятно». Без таких моментов статья превращается в поток фактов.

---

### 1.6.5. Микро-награды каждые 30–60 секунд чтения

Читатель должен постоянно чувствовать прогресс:

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

Это создаёт ощущение движения, а не статичного чтения.

---

### 1.6.6. После тяжёлой мысли — пауза

Если абзац был плотным (сложное объяснение, несколько связанных фактов), следующий абзац должен быть коротким — переходом, вопросом, схемой.

**Плохо:**
> (плотное объяснение Reflection)
> (следующее плотное объяснение UHT)

**Хорошо:**
> (плотное объяснение Reflection)
>
> *Теперь, когда мы поняли эту часть, возникает новый вопрос...*
>
> (объяснение UHT)

---

### 1.6.7. Никогда не заканчивать раздел резко

Вместо:
> Таким образом работает Reflection.

Пиши:
> Теперь мы понимаем, как работает Reflection.
>
> Но возникает новый вопрос: как Unreal узнаёт, какие классы вообще должны попасть в Reflection?
>
> Этим занимается Unreal Header Tool.

Разделы должны плавно перетекать друг в друга, создавая ощущение непрерывного рассказа, а не набора фактов.

---

### 1.6.8. Каждая статья — разговор с опытным разработчиком

Это тот же принцип, что задан в разделе 0 («Философия и цель
обучения») — здесь он раскрыт как набор конкретных, проверяемых
признаков стиля.

**Признаки хорошего стиля:**
- Вопросы к читателю ("Представь, что...", "Как ты думаешь, почему...")
- Объяснение причин ("Потому что...")
- Простые аналогии ("Это как...")
- Явные итоги после каждого раздела ("Что это значит для тебя?")

**Признаки плохого стиля:**
- Перечисление фактов ("X делает Y")
- Термины без объяснения
- Отсутствие вопросов
- Сухой, академический тон

---

### 1.6.9. Принцип «понятно без Google»

**Каждый новый термин должен быть объяснён в том месте, где он появляется впервые в курсе.**

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

---

### 1.6.10. Тест на усталость

Перед публикацией статьи автор обязан:
1. Прочитать статью **вслух** — там, где запинаешься, там сбит ритм.
2. Открыть статью и **отойти на 2 метра** от экрана — если страница выглядит как сплошная стена текста, нужно больше визуальных якорей (код, схемы, врезки).
3. Замерить время чтения: если на разбор одного блока уходит больше 3–4 минут без визуальных якорей — блок слишком плотный.

---

### 1.6.11. Опасность терминологического шума

**Правило:** Если в одном абзаце встречается более двух терминов, которые не были объяснены ранее в курсе, статья не готова к публикации.

**Пример терминологического шума:**
> UHT генерирует код для Reflection на основе макросов, который затем используется ProcessEvent для вызова Blueprint-реализаций через vtable-совместимый диспетчер.

Здесь сразу четыре новых понятия: UHT, Reflection, ProcessEvent, vtable, диспетчер. Ни одно не объяснено. Читатель перестаёт понимать уже на втором слове.

**Исправление:**
1. Разбить на несколько абзацев.
2. Объяснить UHT → потом Reflection → потом ProcessEvent → потом диспетчер.
3. Каждый термин закрепить примером.

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

---

### 1.6.12. Правило "главная мысль выделена"

**Каждый раздел должен иметь одну главную мысль. Она должна быть выделена (жирным, блоком, вопросом) и явно сформулирована.**

**Пример:**

> **Главное запомнить**
>
> Если поле хранит указатель на `UObject`, всегда ставь `UPROPERTY()`.
>
> Исключения есть, но они редки и всегда имеют конкретное техническое обоснование.

---

<a id="sec-1-7"></a>
## 1.7. Разговор с senior, а не книга

Есть разница между текстом, который читают, и текстом, в котором
участвуют. Даже при соблюдении всех техник выше статья рискует
звучать как хорошо написанная книга — связный, но односторонний
монолог. Цель — звучать как разговор с опытным разработчиком, который
время от времени замолкает и ждёт, что ответишь ты. Отсюда ещё шесть
техник, которые применяются поверх шести из начала раздела (и поверх
раздела 1.6):

7. **Точка «остановись и подумай».** После того как проблема
   сформулирована, но до того как названо решение — явная пауза с
   конкретным новым кейсом («Завтра дизайнер просит добавить Boat.
   Что ты будешь делать? Новый класс? Копировать Health? Куда
   поместишь Durability? Остановись на минуту, попробуй решить сам»).
   Затем — обычным текстом — ответ. Такая пауза не заменяет обязанность
   объяснить (правило 30 секунд по-прежнему в силе), а даёт читателю
   шанс запустить рассуждение самому, прежде чем текст его запустит
   за него.
8. **Схема, а не таблица, там, где речь о структуре/иерархии.**
   Матрица «сущность × способность» как HTML-таблица достаточна для
   плотных данных, но не передаёт форму дерева. Там, где сравниваются
   наследование и композиция, схема (ASCII-дерево через
   `<pre><span class="diagram">`) явно показывающая ветвление —
   например, отдельное дерево на каждую сущность («Character ├─
   Health ├─ Inventory…», «Monster ├─ Health» рядом) — доносит проблему
   быстрее, чем табличная клетка с галочкой.
9. **Внутренний монолог движка, а не только шаги.** Технической
   диаграммы «Этап 1 → Этап 2 → Этап 3» недостаточно там, где стоит
   задача — показать именно рассуждение, а не последовательность
   событий. Дополнительно, где это усиливает понимание,
   формулируем то же самое от первого лица движка («Окей, я строю
   CDO. Раз вызван CreateDefaultSubobject — значит объект должен
   существовать у всех экземпляров класса. Запоминаю. Добавляю в
   список Default Subobjects…») — это не дублирование технической
   диаграммы, а альтернативная подача того же факта для другого типа
   читательской памяти.
10. **История эволюции идеи в самом движке, если она известна и
    существенна.** Не абстрактное «так исторически сложилось», а
    конкретная связка: какая инженерная проблема была видна ещё в
    ранних версиях движка и как решение менялось версия к версии.
    Использовать только там, где автор действительно уверен в фактах
    — непроверенная история хуже отсутствия истории.
11. **Эскалация в плохом примере, а не единственный late-reveal.**
    Наивный код должен не просто «оказаться плохим», а становиться
    плохим на глазах — по мере роста числа строк, числа include,
    времени компиляции, числа конфликтов при слиянии в команде.
    Каждый шаг эскалации — отдельная, короткая приписка к тому же
    примеру, а не абзац теории.
12. **На один уровень вопросов глубже во внутренностях движка.**
    После того как факт объяснён («Component — это UObject»),
    естественно спросить «а почему у UObject вообще Outer, а не
    Parent?», «а почему Actor тоже UObject?». Такой вопрос не всегда
    имеет место в статье про сам факт (может быть закрыт ссылкой на
    статью про UObject), но там, где он рождается естественно из
    темы — не обрывать на первом слое объяснения.
13. **«Почему НЕ выбрали конкурирующее решение» — более глубокий и
    более обязательный вариант техники 2.** Недостаточно объяснить,
    почему появилось решение Epic — нужно прямо назвать конкретную,
    реально существующую альтернативу вне контекста Unreal и
    объяснить, что именно в ней ломается. Уровень конкретности,
    который здесь требуется: для UPROPERTY — «почему не RTTI языка
    C++»; для ActorComponent — «почему не множественное наследование»;
    для Garbage Collector — «почему не shared_ptr со счётчиком
    ссылок»; для PlayerController — «почему не сделать всё это полями
    Character». Ответ «так было проще» не проходит правило 30 секунд —
    нужна конкретная причина, по которой альтернатива не масштабируется
    или не работает именно в условиях движка реального времени.
14. **«Спроектируй сам» — самостоятельная попытка решения ДО показа
    решения Epic, а не просто пауза «остановись и подумай».** Там, где
    тема — архитектурное решение целого класса задач (Component,
    Interface, Subsystem, GameplayTag и подобные), после раздела
    «Ограничения» и до раздела «Архитектурное решение» размещается
    компонент `.exercise` с тегом «Спроектируй сам»: конкретный список
    сущностей и требований (например: Character, Monster, Chest, Door —
    организуй код так, чтобы Door открывался, Chest хранил предметы, а
    Monster и Character дрались) и явная инструкция попробовать
    спроектировать это самостоятельно, прежде чем читать дальше.
    `<details class="exercise-answer">` в этом случае раскрывает не
    решение Epic целиком (оно и так идёт в тексте статьи следом), а
    разбор — какие естественные варианты обычно предлагают на этом
    месте и что конкретно в них не работает, готовя переход к решению
    Epic как к уже частично предугаданному, а не свалившемуся с
    потолка.
15. **Инженерный компромисс — обязательная связка «Плюсы / Минусы /
    Цена» для любого архитектурного решения уровня целой темы.** Не
    существует бесплатных архитектурных решений — Reflection стоит
    времени компиляции (UHT-проход) и раздутого метаданными бинарника;
    GC стоит непредсказуемых пауз и накладных расходов на отслеживание
    ссылок; Component стоит косвенности вызова и риска забыть
    зарегистрировать компонент. Раздел «Как устроено» (или отдельный
    подраздел «Компромисс» внутри него) обязан явно назвать цену
    решения — не только то, что оно даёт, — и объяснить, почему Epic
    сочла эту цену приемлемой именно для игрового движка реального
    времени. Таблица — естественный формат для этого (см. раздел 5 про
    формат таблиц): три колонки «Плюсы / Минусы / Цена».
16. **Не скрывать цену решения — явный ответ на вопрос «как этим можно
    злоупотребить».** Обязательный раздел «Когда не использовать»
    (раздел 2) должен включать не только сценарии, где решение
    неприменимо в принципе, но и сценарий злоупотребления самим
    решением темы — например: сколько компонентов на одном Actor уже
    признак того, что нужен рефакторинг; когда Subsystem превращается
    в глобальную свалку состояния под другим именем; когда Interface
    используется там, где хватило бы прямого вызова. Любая архитектура,
    доведённая до крайности, превращается в новую версию той самой
    проблемы, которую она решала.
17. **Правило трёх «почему» — минимальная проверка глубины
    объяснения.** Любое ключевое утверждение статьи должно выдержать
    минимум три последовательных вопроса «почему?», прежде чем текст
    сочтёт объяснение законченным. Пример: «Нужен UPROPERTY» → Почему?
    → «Reflection» → Почему нужен Reflection? → «Blueprint, GC,
    сериализация» → Почему для этого не подходит обычный C++? → «В
    языке нет рантайм-информации о произвольных полях класса без RTTI,
    а RTTI не решает Blueprint и сериализацию». Если после третьего
    «почему» у автора нет ответа — объяснение ещё поверхностное, и
    раздел не готов к публикации.

Отдельно от техник изложения — тип врезки `.callout.senior-thoughts`
(«Мысли Senior», см. раздел 5) для афористичных эвристик принятия
решений, и опциональный закрывающий блок «Как объяснил бы архитектор
Epic» — ретроспективный взгляд («если бы мы проектировали движок
сегодня, выбрали бы то же самое? что изменилось за прошедшие годы?»),
уместный там, где у темы есть настоящая историческая дуга, а не
только там, где место под раздел просто есть.

Ещё один тип врезки — `.callout.architect` («🏛 Как думает архитектор»,
см. раздел 5) — контрольные вопросы, которые опыт заставляет задать
себе ДО написания кода, определяющие архитектуру заранее, а не после
того, как код уже написан не так. Уместен там, где тема — не «как
работает эта система», а «как решить, какую систему вообще
использовать» (например, в разделе «Когда использовать» или прямо
перед «Архитектурным решением»).

<a id="sec-2"></a>
## 2. Обязательные разделы статьи

**Каждый раздел должен начинаться с вопроса, на который он отвечает.**

**Пример:**

**Раздел "Что это?"**
> **Зачем нужен UPROPERTY?**

**Раздел "Почему существует?"**
> **Почему один макрос решает три задачи сразу?**

**Раздел "Когда не использовать?"**
> **Когда можно НЕ использовать UPROPERTY?**

Каждая статья без исключений содержит:

1. **Что это?** — одно-два предложения, ориентирующих читателя,
   прежде чем начнётся глубокое погружение.
2. **Почему существует?** — часть арка «Проблема → Причина».
3. **Какую проблему решает?** — конкретный сценарий поломки.
4. **Как устроено?** — часть арка «Ограничения → Решение → Реализация»,
   включая то, что происходит внутри движка и в памяти, если это
   применимо к теме.
5. **Когда использовать?**
6. **Когда не использовать?** — обязателен всегда. Тема без чётких
   границ применимости порождает карго-культ. Включает не только
   сценарии, где решение неприменимо в принципе, но и сценарий
   злоупотребления самим решением — как этим можно перегнуть (см.
   технику 16 раздела 1.7): любая архитектура, доведённая до
   крайности, превращается в новую версию проблемы, которую она
   решала.
7. **Примеры** — полностью рабочий код (см. раздел 4).
8. **Типичные ошибки** — отдельно для новичков и, где это уместно,
   отдельно для middle-разработчиков, с объяснением механизма
   поломки, а не просто «так не делайте».
9. **Практика** — минимум одно задание с разбором ожидаемого решения.
10. **FAQ** — вопросы, которые предсказуемо возникнут у внимательного
    читателя и не были естественным образом закрыты в тексте выше.
11. **Что читатель должен начать замечать** — короткий раздел
    (обычно 3–5 предложений, не список фактов), отвечающий не на
    вопрос «что я узнал», а на вопрос «что я теперь вижу иначе».
    Например: после статьи про Component читатель начинает замечать
    компоненты во всех остальных статьях курса, а не только в той, что
    он только что прочитал; после Reflection он начинает видеть в
    любом макросе `UPROPERTY`/`UFUNCTION` не украшение, а инструкцию
    для UHT; после Gameplay Framework он сам замечает, когда в чужом
    коде здоровье персонажа хранится не там, где должно. Если такой
    раздел получается пересказом «Итогов» другими словами — тема ещё
    не сформулирована как сдвиг мышления, и раздел не готов.
12. **Итоги** — не пересказ фактов и не список утверждений «мы
    сегодня изучили X». Формат — чек-лист того, что читатель теперь
    умеет делать, каждый пункт с «✔» и глаголом действия, а не
    существительным темы. Проверочный вопрос перед публикацией:
    «после этой статьи что теперь умеет делать человек?». Пример
    для статьи про UPROPERTY: «✔ объяснить, почему без UPROPERTY
    объект не переживёт следующую сборку мусора; ✔ объяснить, зачем
    Reflection вообще нужен движку; ✔ написать собственный компонент
    с корректно размеченными полями; ✔ по симптому («поле обнулилось
    само», «поле не видно в Blueprint») понять, какого спецификатора
    не хватает». Это не то же самое, что раздел 11 «Что читатель
    должен начать замечать» — тот про сдвиг восприятия (что теперь
    видно иначе), этот про проверяемое умение (что теперь можно
    сделать руками); statement-версия итогов («X устроено так-то»)
    подходит только там, где тема — чистое знание без применимого
    навыка, и должна быть исключением, а не нормой.

<a id="sec-3"></a>
## 3. Дополнительные разделы — появляются по необходимости

Мы отказались от идеи фиксированных 31 обязательного раздела для
любой темы — навязанные разделы, не относящиеся к теме (например,
«Влияние на Multiplayer» для статьи про `const`), создают шум и
приучают читателя пролистывать нерелевантные куски.

Вместо этого: **раздел появляется тогда и только тогда, когда тема
естественно порождает про него вопрос.** Список типовых
дополнительных разделов (не исчерпывающий):

- Влияние на Garbage Collector
- Влияние на систему Reflection
- Влияние на Blueprint
- Влияние на сериализацию
- Влияние на Multiplayer / репликацию
- Производительность и бюджет кадра
- Работа в редакторе vs рантайме
- Жизненный цикл (если у темы есть заметный жизненный цикл)
- Альтернативные подходы и сравнение
- Инженерный компромисс (Плюсы / Минусы / Цена) — обязателен для
  решений уровня целой темы (см. технику 15 раздела 1.7), опционален
  для более узких, вспомогательных тем
- Пример из реального проекта / Lyra / чтения исходников движка
- Лучшие практики
- Чек-лист

Перед публикацией статьи автор обязан явно проверить каждый пункт
этого списка вопросом «естественно ли читатель спросит об этом
применительно к моей теме?». Если да — раздел обязателен для ЭТОЙ
статьи. Если нет — раздел не добавляется, и повторного объяснения,
почему он пропущен, в тексте статьи не требуется (не превращаем
статью в отчёт о соответствии стандарту).

<a id="sec-4"></a>
## 4. Правила для кода

- Псевдокод запрещён. Каждый пример — реальный, компилируемый
  Unreal C++ актуальной версии движка.
- Каждый содержательный блок кода сопровождается разбором построчно
  (компонент `.line-breakdown`, см. `docs/TEMPLATE.html`), отвечающим
  на четыре вопроса для непонятных с первого взгляда строк: почему
  написано именно так; почему нельзя иначе; что делает эта строка;
  что сломается, если её убрать.
- Если пример почти идентичен паттерну из предыдущей статьи —
  не нужно заново расписывать каждую строку, но нужно явно указать,
  что изменилось и почему.

**Код должен показывать проблему, а не только решение.**

**Пример:**

**Плохо:**
> ```cpp
> UPROPERTY()
> UHealthComponent* Health;
> ```

**Хорошо:**
> **Вот что происходит, если забыть UPROPERTY:**
>
> ```cpp
> AActor* Target; // НЕПРАВИЛЬНО
> ```
>
> Этот код компилируется. Но GC не видит эту ссылку. Через минуту игра крашится...
>
> **А вот правильный вариант:**
>
> ```cpp
> UPROPERTY()
> AActor* Target; // ПРАВИЛЬНО
> ```

<a id="sec-5"></a>
## 5. Единый стиль блоков

Используем ровно шесть типов врезок, определённых в `style.css`,
и не изобретаем новые без обновления этого документа:

| Класс | Когда использовать |
|---|---|
| `.callout.important` | Факт, который нельзя пропустить, чтобы не понять тему неправильно. |
| `.callout.error` | Прямое предупреждение о поломке/краше/уязвимости. |
| `.callout.tip` | Совет опытного разработчика — обязательно с объяснением причины, никогда не «просто делай так». |
| `.callout.senior` | Развёрнутое архитектурное рассуждение senior-уровня — не про синтаксис, а про выбор решения, с примером на пол-абзаца и больше. |
| `.callout.senior-thoughts` | «Мысли Senior» — короткая (2–5 строк), афористичная эвристика принятия решения, не привязанная к конкретному коду из абзаца рядом. Не пересказ того, что уже сказано в тексте, а перенос образа мышления: то, что опытный инженер держит в голове как правило большого пальца. Именно эти блоки читатель должен запоминать и цитировать — если блок длиннее пяти строк или объясняет синтаксис, это `.callout.senior`, а не `.callout.senior-thoughts`. |
| `.callout.architect` | 
«Как думает архитектор» — контрольные вопросы, которые опытный архитектор задаёт себе ДО написания кода, а не после того, как код уже написан не так. Канонический, переиспользуемый (адаптируется под тему, структура остаётся) пример: «Перед тем как писать код, я задаю себе пять вопросов: 
1) Будет ли это использоваться повторно? 
2) Нужно ли это сохранять (Save Game)? 
3) Будет ли Multiplayer? 
4) Будет ли Designer менять это без участия программиста? 
5) Будет ли это существовать в редакторе, а не только в рантайме? 
Ответы на эти пять вопросов уже определяют архитектуру раньше, чем написана первая строчка кода.» 
Уместен в разделе «Когда использовать» или прямо перед «Архитектурным решением». |

Диаграммы «что происходит в памяти» рисуются либо ASCII-схемой
внутри `<pre><span class="diagram">`, либо инлайновым SVG с классом
`diagram-svg` — оба варианта должны работать офлайн, без внешних
сервисов рендеринга.

<a id="sec-6"></a>
## 6. Практические задания

Каждое задание — компонент `.exercise` с обязательным
`<details class="exercise-answer">`, скрывающим не готовый ответ
целиком, а ожидаемый ход рассуждения — так, чтобы читатель сначала
пробовал сам.

<a id="sec-7"></a>
## 7. FAQ

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

<a id="sec-8"></a>
## 8. Чек-лист самопроверки перед публикацией статьи

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

**Понятность текста**
- [ ] Остались ли после прочтения темы логичные вопросы без ответа?
- [ ] Есть ли термины, использованные до того, как они объяснены?
- [ ] Есть ли слишком короткие объяснения («GetOwner() возвращает
      владельца» — это пересказ, а не объяснение)?
- [ ] Есть ли логические скачки — переход к следующему шагу без
      объяснения, почему предыдущий к нему приводит?
- [ ] Хватит ли читателю этой статьи, чтобы НЕ открывать сторонние
      сайты по этой теме?

**Глубина объяснения**
- [ ] Есть ли примеры кода без построчного разбора там, где разбор
      нужен?
- [ ] Есть ли советы без объяснения причины?
- [ ] Есть ли ключевой вызов API без пары «что происходит внутри
      движка» + «почему не иначе» рядом с ним (правило 30 секунд,
      раздел 1.5)?
- [ ] Выдерживает ли центральное утверждение статьи три
      последовательных «почему» (техника 17), или третий ответ
      автору пришлось бы придумывать прямо сейчас?
- [ ] Есть ли архитектурное решение уровня темы без явного ответа на
      «почему не выбрали главную альтернативу» (техника 13)?
- [ ] Есть ли архитектурное решение уровня темы без явной цены —
      таблицы или абзаца «Плюсы / Минусы / Цена» (техника 15)?

**Вовлечение читателя**
- [ ] Сообщён ли центральный вывод статьи читателю напрямую там, где
      его можно было вместо этого дать читателю вывести самому?
- [ ] Если практика — про переиспользуемое решение, применяется ли
      оно в задании более чем к одной явно разной сущности?
- [ ] Есть ли в разделе «Когда не использовать» только «неприменимо»,
      но не «как этим можно злоупотребить» (техника 16)?
- [ ] Есть ли раздел «Что читатель должен начать замечать», и не
      является ли он пересказом «Итогов» другими словами?

**Финальный тест**
- [ ] Прочитав статью ещё раз целиком: меняет ли она то, как читатель
      будет писать код завтра, — или только сообщает факты, которые
      он мог бы найти в документации Epic?

Если хотя бы один ответ на пункты выше — «да» (для финального теста —
«нет»), статья возвращается в работу.

<a id="sec-9"></a>
## 9. Эталонная статья

`articles/engine-vs-framework.html` — золотой стандарт качества и
глубины для всего проекта. Любая новая статья перед публикацией
проверяется вопросом: **«эта статья не хуже эталонной?»** Если
ответ отрицательный — публикация откладывается.

`articles/actor-components.html` — второй эталон, конкретно для
техник из раздела 1.7 (правило 30 секунд, разговор с senior, а не
книга, и с версии, добавленной после ревью читателя, — техники
13–17: «почему не иначе» для глобальных альтернатив, «Спроектируй
сам», инженерный компромисс, предупреждение о злоупотреблении,
правило трёх «почему»): точка «остановись и подумай», деревья вместо
матрицы, внутренний монолог движка рядом с технической диаграммой,
эскалация плохого примера, «Мысли Senior», закрывающая ретроспектива
архитектора Epic. Новая статья, применяющая эти техники, сверяется с
этим файлом как с образцом их исполнения, а не только описанием в
разделе 1.7.

<a id="sec-10"></a>
## 10. Технические требования к каждой статье (для инфраструктуры)

Каждая статья обязана:

1. Подключать `assets/style.css` и `assets/topics.js`,
   `assets/search-index.js`, `assets/app.js` (в этом порядке —
   данные должны быть определены до того, как app.js попытается их
   прочитать).
2. Задавать `window.SITE_ROOT` до подключения `app.js`
   (для статьи в `/articles/` — `"../"`).
3. Задавать `window.PAGE_META` — объект с полями `id`, `title`,
   `cluster`, `tags`, `prereq`, `next`, `related` (см.
   `docs/TEMPLATE.html`).
4. Содержать разметку `#shell > #sidebar + #main`, где `#main`
   содержит `#topbar > #breadcrumbs`, `#page-body > #article + #page-toc`.
5. Все `<h2>`/`<h3>` внутри `#article`, которые должны попасть в
   правый TOC, обязаны иметь атрибут `id`.
6. После добавления или изменения статьи — обновить
   `assets/topics.js` и `assets/search-index.js` вручную либо через
   `assets/build_index.py` (см. комментарии в самом скрипте).
7. `#sidebar-footer` больше не используется — ссылки на
   `philosophy.html`, `graph.html` и `project.html` из футера сайдбара
   убраны по всему сайту. Ссылки на эти страницы остаются доступны
   через основную навигацию (главная страница, сквозной проект и т.д.).

<a id="sec-11"></a>
## 11. Когда стандарт нарушать можно

Никогда молча. Если тема требует нестандартной структуры —
решение фиксируется явно в начале статьи одной строкой в комментарии
HTML `<!-- ОТКЛОНЕНИЕ ОТ СТАНДАРТА: причина -->`, чтобы будущий автор
не спутал сознательное решение с недосмотром.

<a id="sec-12"></a>
## 12. Проверка понятности и качества объяснения

Этот раздел — финальная проверка статьи **после того, как она
написана**. Предыдущие разделы объясняли, *как* писать. Этот раздел
объясняет, *что проверять* перед публикацией.

Level 2 курса — это не справочная статья и не документация API.
Это учебный материал инженерной школы Unreal Engine.

Статья должна быть понятна:
- человеку, который только начинает изучать Unreal Engine;
- разработчику, который уже знаком с Unreal, но хочет систематизировать знания;
- человеку, который впервые сталкивается с внутренней архитектурой движка.

**Главный критерий:** если читатель понимает отдельные предложения,
но теряет смысл всей статьи — статья написана плохо.

---

### 12.1. Один абзац — одна мысль

Каждый абзац должен отвечать только за одну идею.

Если в одном абзаце появляются новые вопросы:
- почему это нужно?
- как это работает?
- где это используется?

значит абзац нужно разделить.

**Плохо:**
> Unreal использует систему Reflection, которая работает через UHT,
> метаданные, GENERATED_BODY и позволяет движку управлять объектами,
> Blueprint и сериализацией.

**Проблема:** в одном предложении четыре разные темы:
- Reflection;
- UHT;
- Metadata;
- Serialization.

Читатель получает набор терминов, но не понимает связи.

**Хорошо:**
> **Почему Unreal вообще нужен Reflection?**
>
> Обычный C++ знает о классах во время компиляции.
> Но Unreal должен работать с объектами уже после запуска игры.
>
> Например, редактор должен показать свойства объекта,
> Blueprint должен вызвать функцию,
> а система сохранений должна понять, какие данные нужно записать.
>
> Для этого движку нужна дополнительная информация о коде.

Сначала объясняется проблема. Потом появляется решение.

---

### 12.2. Каждый новый термин должен быть объяснён

Нельзя использовать термин впервые без объяснения.

**Плохо:**
> Unreal использует Race Condition при работе потоков.

Новичок не знает:
- что такое Race Condition;
- почему это проблема;
- где это происходит.

**Хорошо:**
> **Что такое Race Condition?**
>
> Race Condition — это ситуация, когда два потока программы одновременно
> пытаются изменить одни и те же данные.
>
> Представь, что два разработчика одновременно редактируют один файл.
> Каждый сохраняет свою версию, и результат зависит от того,
> кто нажал кнопку последним.
>
> В программе происходит похожая ситуация.

---

### 12.3. Английские термины и цитаты

Unreal Engine — англоязычный движок.
Английские названия классов, функций и терминов сохраняются.

Но если встречается предложение из документации Epic,
оно должно сопровождаться переводом и пояснением.

**Пример:**

> Epic описывает это так:
>
> *"Create a component or subobject that will be instanced inside all instances of this class"*
>
> **Перевод:**
>
> «Создать компонент или под-объект, который будет существовать внутри
> каждого экземпляра этого класса».
>
> **Проще говоря:**
>
> Если ты создаёшь компонент через этот механизм,
> Unreal заранее знает, что этот компонент должен быть частью каждого объекта
> этого класса.

---

### 12.4. Не использовать незаконченные предложения

Любая фраза должна быть понятна человеку без контекста.

**Плохо:**
> А в проде это уже проблема.

**Проблемы:**
- что такое "прод"?
- какая проблема?
- где именно?

**Хорошо:**
> А в Production-сборке игры это уже становится проблемой.
>
> Production (или Shipping Build) — это финальная версия игры,
> которую получают игроки после завершения разработки.

---

### 12.5. Все сокращения должны расшифровываться

Первое использование сокращения обязательно с расшифровкой.

**Плохо:**
> UBT собирает проект и создаёт модули.

Новичок может не знать UBT.

**Хорошо:**
> UnrealBuildTool (UBT) — это система сборки Unreal Engine.
>
> Она отвечает за описание модулей проекта и подготовку команд
> для компилятора.

После этого можно использовать сокращение:
> Теперь UBT знает...

---

### 12.6. Не объяснять через неизвестные слова

Нельзя объяснять термин через другой термин,
который ещё сложнее.

**Плохо:**
> Reflection — это система метаданных для интроспекции объектов.

Новичок теперь должен искать:
- Metadata;
- Introspection.

**Хорошо:**
> Reflection — это система Unreal Engine,
> которая позволяет движку узнавать информацию о твоих классах
> во время работы игры.
>
> Например:
> - какие есть свойства;
> - какие функции можно вызвать;
> - какие данные можно показать в редакторе.

---

### 12.7. Не использовать сленг без объяснения

Запрещены без пояснения:
- "прод" — Production-сборка;
- "краш" — падение программы;
- "хак" — быстрое, но нестабильное решение;
- "магия" — то, что работает без видимых причин;
- "костыль" — временное решение, которое скрывает проблему.

Если сленг используется, он должен быть объяснён в том же абзаце.

---

### 12.8. Проверка глазами новичка

Перед публикацией автор должен перечитать статью и задать себе вопросы:

- [ ] Смогу ли я понять это без поиска в Google?
- [ ] Объясняется ли каждый новый термин?
- [ ] Понимаю ли я, зачем существует эта система?
- [ ] Понимаю ли я проблему до появления решения?
- [ ] Могу ли я пересказать эту идею своими словами?

Если ответ на любой вопрос — «нет», статья требует переработки.

---

### 12.9. Главная цель статьи

Статья не должна отвечать только:

> "Что делает этот класс?"

Она должна отвечать:

> "Почему Unreal вообще понадобился этот класс?"

После прочтения читатель должен изменить способ мышления.

**Пример:**

После статьи про ActorComponent:

**Плохо:**
> Читатель знает, что есть ActorComponent.

**Хорошо:**
> Читатель начинает замечать:
> "Эту систему не надо добавлять через наследование.
> Возможно, здесь нужен компонент".

---

### 12.10. Финальный чек-лист понятности

Перед публикацией:

- [ ] Нет длинных стен текста.
- [ ] Каждый раздел отвечает на конкретный вопрос.
- [ ] Каждый новый термин объяснён.
- [ ] Английские цитаты переведены.
- [ ] Нет незаконченных фраз.
- [ ] Нет сленга без объяснения.
- [ ] Новичок может понять материал без стороннего поиска.
- [ ] Статья меняет способ мышления, а не просто передаёт факты.

---