
Эту статью я написал лет десять назад, когда только попал в большую студию EA SpB. И забыл бы про нее, но недавно проводил ревизию заметок и набросков на старом HDD и решил, что она до сих пор актуальна, разве что цифры выросли. Тогда проекты под миллион LoC казались гигантами, наверное это и сейчас очень много, но теперь это просто код движка. Но суть не изменилась, просто цифры выросли.
Помню тот день, когда я впервые сел за рабочий стол в офисе, а до этого делал другие проекты и кодовая база размеров 100к строк вместе с либами, движком и логикой казалась - ну очень немаленькой. А тут скачал репозиторий, открыл идеешку, и она минут на пятнадцать подвисла на индексировании файлов. Я смотрел на всё это безобразие и думал: «Это нормально? Мне дали самый отстойный джунский комп? Я что-то сломал уже на онбординге? Мы все умрем?» Нет, всё было нормально, просто я впервые столкнулся с промышленной кодовой базой большого проекта.
Команда в тот момент выкатывала мажор Sims Mobile, который зафейлили почти на месяц и, честно говоря, на нормальный онбординг ресурсов просто не было. ПМ выдал мне простую задачу на разгон, так сказать, чтобы стулья в редакторе дома сохраняли свое положение и размер между запусками, потому что они, как вы наверное догадались - этого не делали и оказывались в дефолтных точках спавна. Звучит элементарно, правда? Сохранил координаты в конфиг, прочитал при старте и таска готова. Вот только я понятия не имел, где искать код этого сохранения конфига, объектов, как называется класс, где лежит конфиг и сами стулья, и есть ли вообще система для таких вещей или надо писать с нуля. Небольшой спойлер, системы не было, все объекты в доме всегда спавнились в тех точках, где их поставил дизайнер, т.е. для редактора дома сейва не было, а для игры был.
В университете нас очень хорошо учили алгоритмам и, внезапно, целых два года мы писали с нуля ОС мечты конкретного преподавателя и холили его желание стать новым Торвальдсом, а не делать всякие там ваши игры, а на предыдущей работе приходилось писать почти весь код с нуля из головы ну или максимум по тому, что принес в клювике штатный алгоритмист-математик - такова специфика разработки охранных систем и разного гидроакустически-подводного софта, можете почитать об этом в записках ездового кота. Ничто из этого не подготовило меня к тому, чтобы открыть проект почти на два миллиона строк без внимания санитаров.
Сейчас это норма, это я не про санитаров, если что. Если вы устроились в студию не на совсем маленький инди-проект, первое, чему придётся научиться это ориентироваться в этом чудовище из унаследованного кода, стремных паттернов и уникальных идей -дцатилетней выдержки, которые писало очень много людей до вас. Неважно, внутренний это движок, лицензированный или что-то собранное из опенсорсовых компонентов, но современные размеры всё равно пугают нежное мировосприятие новоприбывшего и, скорее всего, первые недели вы будете чувствовать себя так, будто вас высадили в незнакомом городе без карты, знакомых и средств связи - вроде и наз��ания улиц знакомые, и перекрестки похожие, но решительно непонятно как пройти в библиотеку в три часа ночи. Хорошие новости - это проходит, либо проходите дальше вы, тут нужно знать, как правильно подойти к задаче.

Современные игровые движки представляют собой гигантские программные системы, и реальный объём исходного кода часто оказывается гораздо больше ожидаемого. Точные цифры редко публикуются официально, поэтому ориентироваться приходится на внешние оценки, исследования и фрагментарные факты.
Оценки размера кодовой базы Unreal Engine колеблются очень значительно даже при наличии открытого репозитория. Разные источники нередко указывают ориентир порядка 2...20+ MLoc, но тут в расчёт входит сам движок, редактор, инструментарий, плагины и вспомогательные модули, и большой разброс скорее объясняется закрытостью большей части кода модулей и плагинов, которые составляют 70% экосистемы движка (хотя сам движок бесплатен и открыт, заметьте). Да и сложно провести корректные расчеты в проектах с тяжёлой генерацией кода, шаблонами и зависимостями.
Несмотря на репутацию «лёгкого» движка, Unity на самом деле также огромен, основная часть движка закрыта (но есть утекший репо 2015 и 2017 годов + личный опыт работы с 5 версией движка), исходники шапрового слоя модулей UnityEngine и UnityEditor содержат несколько сотен KLoc. Плюсовый же движок (runtime + бэкенды + рендеринг + asset pipeline) оценивается примерно в до 1MLoc, сами разработчики где-то выкладывали что чисто с++-ная часть core-систем занимает порядка 500KLoc.
Godot тут самый компактный из «больших» движков, но и он перешагнул отметку в 1MLoc. Быстрый анализ репо дает общий объём около 1MLoc, включая ресурсы и скрипты, Чистый C++ занимает в менее 500KLoc. Относительная компактность Godot достигается не за счёт хорошего кода и архитектуры, а из-за меньшего количества платформ, отсутствия функционального трёхмерного редактора уровня UE и тяжёлых систем наподобие Blueprint.
Dagor - промышленный движок со значительным возрастом и глубокой специализацией, активно развивавшийся более 15 лет. Прямых публичных цифр нет, но по внутренним оценкам разработчиков и масштабам проектов (War Thunder, Enlisted, CR) - объём кодовой базы составляет ~2+MLoc, причём значительная часть оптимизированный C++ под разные платформы, включая консоли, ПК и собственные тулчейны, плюс большое количество инструментария и собственный язык программирования daSlang.
4AEngine, ребята официально не раскрывают размер кодовой базы публично, но по масштабу рендера, инструментов и платформенной матрицы и некоторых инсайдов объём оценивают примерно в 500KLoc, плюс внутренний Level Editor, движок очень компактный по сравнению с UE/Unity, но ориентирован на одну игру, поэтому архитектурная плотность и сложность крайне высокие.
Unreal ████████████████████████████████ 2-20 MLoc
Dagor ████████░░░░░░░░░░░░░░░░░░░░░░░░ ~2+ MLoc
Unity ████████░░░░░░░░░░░░░░░░░░░░░░░░ ~2 MLoc
4A Engine █████░░░░░░░░░░░░░░░░░░░░░░░░░░░ ~2 MLoc
Godot ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ~1 MLoc

Первое, что нужно понять - весь этот код написали такие же люди, как вы, это к сожалению не всегда комплимент, потому что наши с коллегой подходы к проектированию кардинально разные, и "х...к, х..к и в продакшен", я вижу на ревью намного чаще, чем "давайте сядем и подумаем". Они умные (это тоже не всегда комплимент, ибо умные люди часто пишут такой код, от которого все остальные потом страдают), опытные, но, послушайте, вы тоже неглупый человек, иначе вас бы не взяли. На создание этой кодовой базы ушли годы, а может и больше, но в конце концов, каждый начинал в ней новичком, даже тот, кто написал самые первые строки. Никакой магии тут нет, хотя нет, тут я соврал, магии там дофига, и чем старее коммит, тем больше там магии обычно, но даже так это просто программирование, как на ваших маленьких пет-проектах. И тут надо разобраться, как это вообще собирать, у каждого из более чем 10 игровых проектов, где я принимал участие, была своя система сборки (eamake > premake > xmake > meson > jam > cmake > UBT > ninja > make > fastbuild > msbuild) и я чувстую что список далеко не полный. У каждой большой кодовой базы была своя политика сборки зависимостей, и свой набор бубнов и священных ритуалов для настройки окружения сборки. Если вам повезёт, но это бывает редко (мне везло всего два раза из всех случаев), этот процесс автоматизирован, но мне такого ни разу не встречалось, чтобы все вот собралось с полпинка без заморочек.
В идеале это где-то и когда-то было задокументировано, но эта документация, скорее всего, устарела и обычно в обязанности новы�� программистов входит заодно и подправить документ по мере настройки. Если такого документа вообще нет, а это тоже частый кейс, вам может выпасть честь его написать и произвести неизгладимое впечатление на лида. Процесс сборки обычно очень самопальный и заточенный под конкретную студию, так что давать универсальные советы сложно. В зависимости от компании, саппорт могли всё настроить до прихода на рабочее место, но я предпочитаю, чтобы они поставили только базу, а остальное доставлять сам с нужным разрешениями, и это часто спасает кучу времени, потому что в итоге ты оказываешься в привычной рабочей обстановке, а не пластиковой комнате, где все лежит в непривычных местах.
Если не собирается - перечитай документацию, если она есть. Маловероятно, что в репозитории лежит сломанная сборка, если бы это было так, вы бы увидели синьоров, которые ходят вокруг как свежевосставшие зомби в поисках свежих мозгов. Не зазорно спрашивать о проблемах со сборкой в первый месяц, ведь никто не ожидает, что человек со стороны будет знать все эти сакральные знания. Второй момент, люди ценят, когда вы пытаетесь разобраться сами, и редкие пусть и глупые вопросы в целом это хорошая идея, «потому что, я вот вообще без понятия, что такое OpenEXR и где его взять?» вместо общих «оно сломано» будут восприниматься нормально. На конкретный вопрос вы получите кокретный ответ быстрее, и он отнимет меньше времени у того, кто отвечает.

Поздравляю! Это наконец собралось на вашей машине, и возможно, заняло всего 40 минут, а возможно три дня и забрало в жертву клавиатуру, но вы справились и теперь перед вами стоит задача найти все-же конфиг этих стульев и добавить туда сохранение. Звучит просто, правда? Осталось только найти этот чёртов конфиг и обслуживающую систему в кодовой базе на пару миллионов строк. В этот момент возникает почти непреодолимый соблазн подойти к ближайшему сеньору и спросить: "Привет, а где у нас стулья хранятся?" В этот момент синьор-Александр, не отрываясь от дебаггера (где он в данный момент разбирается, почему домик глючит только на плойке, только по четвергам, и только если игрок носит красную шляпу, мгновенно ответит: "А, это в Game/Play/Home/HomeInventory/KesloInventoryIndoor.cpp, строка 2847 и нет буковку r я не забыл. Только не забудь зарегистрировать обработчик в HomeManager, иначе система будет работать везде, кроме этого стула. Да, и проверь, что ты не сломал расположение стульев вне дома, потому есть KesloInventoryOutdoor.cpp"
Пять секунд и у вас будет ответ на вопрос, поиск которого мог занять -дцать минут. Магия? Телепатия? Фотографическая память, развитая годами кранчей и просмотра логов крашей в 3 часа ночи? Спойлер - нет, синьоры не помнят всё наизусть. Это физически невозможно, когда у вас 2+ миллионов строк кода, 2К+ модулей движка, 3К+ UI элементов, 150к+ ассетов и 470 разных систем, которые взаимодействует с 230 другими. Добавьте к этому легаси-код, который писали люди, покинувшие студию в 2013 году, и комментарии на трёх разных языках — английский, русский, и мой любимый: "что я курил, когда это писал на плюсах".
Сеньоры не знают код наизусть. Они просто хорошо умеют искать. И это навык, которому можно и нужно научиться. Я может открою какой-то секрет, но обычно синьоры-помидоры заняты тушением пожаров в проде, ревью пулл-реквестов, которые намереваются сломать половину игры, оптимизацией кода, который тормозит на консолях последние 5 лет, и объяснением дизайнерам, что фича "за пару часиков" на самом деле займёт месяц или попытками понять почему физика вдруг начала вести себя... креативно. Помогать найти вам файл с конфигом стула стоит на самой нижней ступени приоритетов на сегодня, но они тоже люди и не чужды человечности, поэтому вы таки получите ответ на свой вопрос.
Через пару тройку месяцев самостоятельных поисков вы будете находить нужное за пять минут вместо двадцати, узнаете архитектуру п��оекта лучше любой документации и перестанете бояться огромной кодовой базы и сами станете тем человеком с "энциклопедическими" знаниями. И наконец магия — она же опыт. Через полгода работы с проектом вы просто думаете: "Стул... это же, внезапно, инвентарь дома!" — и сразу открываете Game/Play/Inventory/InventoryHome.cpp. Там оно и есть, работает в 95% случаев, занимает 10 секунд, но требует 6+ месяцев работы с проектом.

Умение искать самому не значит "никогда не задавать вопросы". Спрашивайте если искали 20+ минут безуспешно, это вероятно та граница, когда новичку стоит просить помощи, возможно код в неочевидном месте или используется странный нейминг, который вы не смогли предугадать. Спрашивайте если код явно выглядит неправильно и вы подозреваете баг. Спрашивайте когда нужно принять архитектурное решение или расширить существующий функционал, и точно надо спрашивать если код покрыт комментариями // TODO: Ask John about this а Джон уволился 3 года назад. А когда вы все переискали и придёте к сеньору с вопросом "Я искал тот стул 30 минут, пробовал поиск по всему проекту, смотрел в холодильнике, проверял home классы, но ничего не нашёл. Где может быть FooBazChair?" , то сеньор не только ответит на вопрос, но и возможно даст совет как надо было искать. Возможно код назывался FooBazKreslo, а не FooBazChair в силу исторических причин.
Один полезный приём при поиске частей кода, которые относятся к редактору, моделям, или несистемным фичам, это искать по строкам, которые видны пользователю. Редактор мало ориентирован на профессиональных программистов и больше на его пользователей - дизайнеров, артистов, звукачей и поэтому документация по нему зачастую оказывается в куда лучшем состоянии, чем та, что доступна нам, программистам и особенно в лицензируемых движках, да и умение пользоваться самим редактором и игрой помогает лучше понять соответствующий код. Возвращаясь к примеру с FooBazChair мне часто помогало запустить, редактор найти там эти злополучные стулья, посмотреть их свойства или видимые компоненты, затем вернуться в код движка и посмотреть файлы, содержащие эти названия. Возможно, оно лежит в таблице строк и это ключ-значение, значит, ищем затем по ключу, что в итоге приводит нас к нужной системе.
Важно помнить, что названия, которые видит игрок, не всегда совпадают с именами в коде. На ранних этапах разработки функции и системы вообще часто получают временные внутренние имена, а официальные называния появляются позже. Поэтому FooBazKreslo будет долгое время преследовать вас в кошмарах, и в коде будет крайне редко использоваться FooBazChair в силу исторических причин. По таким вот незаметным семантическим нюансам очень легко детектится, какую часть кода писали восточно-европейские ребята, а какую нет, как впрочем легко детектируется индусский, австралийский и азиатские семантические стили. Поэтому если поиски результата по очевидным ключевым словам ни к чему не приводят, это не повод бросаться рефакторить половину движка, возможно, вам просто не хватает части внутреннего контекста (магии), и это хороший момент, чтобы уточнить детали у коллег.

История версий все еще остается самым недооценённым инструментом изучения кода. Тот же git содержит подробные изменения по каждому файлу, что позволяет понять, когда появилась конкретная функция, кто её писал, какие файлы изменялись вместе с ней, когда у Маши была свадьба и почему Джона уволили. Это особенно полезно, если вы делаете похожую задачу: можно посмотреть, какие части проекта были затронуты ранее, и не пропустить возможные зависимости. Другая типичная ошибка новичков в больших кодовых базах, помимо избегания поиска по истории коммитов, это изобретение велосипеда. Поверьте, все возможные виды велосипедов с цветными педалями и кривыми рулями уже были изобретены до вас. Но получив задачу, требующую стандартного математического или утилитарного решения, мы почему-то стараемся писать свою реализацию, не подозревая, что такая функция уже существует в проекте.
В лучшем случае это всплывёт на ревью, если туда придет тимлид и ведающий синьор, и придётся переделывать работу, в худшем это дублирование попадёт в основной код, и проект станет сложнее. Поэтому в крупных движках стоит всегда исходить из предположения, что кто-то уже решал и решил похожую задачу, и сначала искать готовый код. Даже в случае со стулом FooBazKreslo проще изучить, как другие комоды сохраняют свою позицию, чем писать логику с нуля. Иногда вообще бывает полезно пройтись по этим участкам в отладчике, чтобы разобраться в механизме.
Студийные кодстайлы вообще отдельная часть культуры проекта. У каждого (каждого, Карл) разработчика есть свои предпочтения, но в существующей кодовой базе необходимо придерживаться уже принятого стиля. И это явно не то место, где стоит проявлять личные творческие амбиции - какая бы система ни была, её нужно изучить и соблюдать. Свобода экспериментов остаётся для домашних проектов, а в рабочем коде важна единообразная структура.
Наконец, нужно помнить, что исходный код всегда будет последней инстанцией истины. Документы устаревают, вики врет, комментарии не совпадают с реальностью, а ответы коллег иногда основаны на воспоминаниях, а не на фактах. Если нужно точно понять «что», «где» и «когда», это всё есть только в коде. Единственный вопрос, который не всегда можно вывести из текста программы, это «на...япочему», и за точным ответом и направлением движения действительно придётся идти на поклон к долгожителям.

---
З.Ы. Уставший синьор после 73-го вопроса "а где лежит..." на этой неделе. Если эта статья помогла вам - поделитесь ею с коллегами, особенно с теми, кто только что присоединился к проекту.
З.З.Ы Приходите на вебинар https://pvs-studio.ru/ru/webinar/25/ , расскажу как мы тут над строками в игровых движках издеваемся.