Как учиться Учиться на чужом коде и внутри рабочих задач
0%

Учиться на чужом коде и внутри рабочих задач

Учиться на чужом коде и внутри рабочих задач

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

Тезис главы, который дальше проверяется:

Чужой код и рабочие задачи — самый доступный учебный материал и одновременно самый бесполезный, если ничего с ними специально не делать. Рабочая задача оптимизирует «закрыть», а не «понять»; чтение кода даёт ощущение понимания раньше, чем понимание. Учебной единицей ни то, ни другое не становится само.

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

Почему «работа сама научит» не работает по умолчанию

Начнём с того, что мешает: с двух убеждений, которые звучат как знание, но им не являются.

Миф первый: 70-20-10

Формула «70 % развития приходит из рабочих задач, 20 % — от других людей, 10 % — из формального обучения» встречается в корпоративных программах развития как установленный факт. Её происхождение: Lombardo M. M., Eichinger R. W. (1996), The Career Architect Development Planner, опирающийся на более раннюю работу Центра креативного лидерства — McCall M. W., Lombardo M. M., Morrison A. M. (1988), The Lessons of Experience.

Что там на самом деле было. Успешных руководителей просили вспомнить ключевые события, повлиявшие на их развитие, и рассказать о них. Ответы кодировали по категориям. Круглые числа 70/20/10 — обобщение долей в этих рассказах.

Чего там не было:

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

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

Миф второй: опыт равен экспертизе

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

Механизм, из-за которого рабочая задача не учит сама:

Обратите внимание: левая ветка — не халтура. Это рациональное поведение при дефиците времени, и в большинстве случаев именно оно и нужно. Проблема начинается, когда левая ветка становится единственной на протяжении лет.

Что при этом измерено

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

  • Xia X., Bao L., Lo D., Xing Z., Hassan A. E., Li S. (2018). Measuring Program Comprehension: A Large-Scale Field Study with Professionals. IEEE TSE, 44(10). Профессиональные разработчики с инструментированной IDE в течение продолжительного периода: на активность, отнесённую к пониманию кода, приходилось около 58 % времени. Оговорка к цифре: категоризация делалась по типу действий в IDE, а не по тому, что человек делал в голове.
  • Minelli R., Mocci A., Lanza M. (2015). I Know What You Did Last Summer. ICPC 2015. Похожая методика, оценка ещё выше — порядка 70 %.
  • Ko A. J., Myers B. A., Coblenz M. J., Aung H. H. (2006). IEEE TSE, 32(12). Значительная доля времени уходит не на чтение как таковое, а на навигацию: поиск релевантных фрагментов и возвращение к ним.
  • Parnin C., Rugaber S. (2011). Resumption strategies for interrupted programming tasks. Software Quality Journal. Восстановление контекста после прерывания — не секунды: по их наблюдениям типично порядка десяти-пятнадцати минут до возобновления редактирования. Отсюда практический вывод, пересекающийся с главой про фокус: чтение кода кусками по десять минут между встречами — это почти чистая трата.

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

Что значит «понять чужой код»

Слово «понял» здесь означает как минимум три разные вещи, и путаница между ними — источник большинства провалов.

Разрыв между текстом и смыслом

Biggerstaff T. J., Mitbander B. G., Webster D. (1993). The Concept Assignment Problem in Program Understanding. ICSE 1993. Задача понимания формулируется как сопоставление концептов: связать фрагменты кода с понятиями предметной области. updateRow в файле sync.go — это «продление подписки» или «частичный возврат»? Ни компилятор, ни grep этого не знают. Именно здесь тратится время, и именно этот шаг нельзя пропустить.

Две модели, а потом третья

Pennington N. (1987). Stimulus Structures and Mental Representations in Expert Comprehension of Computer Programs. Cognitive Psychology, 19(3). Экспериментально различены два типа представления:

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

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

von Mayrhauser A., Vans A. M. (1995). Program Comprehension During Software Maintenance and Evolution. IEEE Computer, 28(8). Интегрированная метамодель добавляет третий слой — ситуационную модель: связку между двумя первыми применительно к конкретной задаче изменения. Именно её отсутствие даёт знакомое ощущение: «код я прочитал, а что менять — не знаю».

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

Как читают на самом деле: сверху вниз, гипотезами

Brooks R. (1983). Towards a Theory of the Comprehension of Computer Programs. International Journal of Man-Machine Studies, 18(6). Понимание описывается как построение и проверка гипотез: читатель заранее предполагает, что делает программа, и ищет в тексте подтверждения — маяки (beacons): характерные имена, узнаваемые конструкции, знакомые формы циклов.

Soloway E., Ehrlich K. (1984). Empirical Studies of Programming Knowledge. IEEE TSE, SE-10(5). Эксперты держат в памяти планы — типовые фрагменты решения — и правила дискурса, ожидания о том, как код принято писать. Главный результат этой работы обычно недорассказывают: на программах, написанных вопреки этим ожиданиям, преимущество экспертов над новичками исчезало.

Для читателя чужого кода это не курьёз, а рабочий диагностический признак:

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

Letovsky S. (1986). Cognitive Processes in Program Comprehension. Показал, что процесс оппортунистический: читатель произвольно переключается между движением снизу вверх и сверху вниз, следуя за тем, что подвернулось. Никакой единой правильной стратегии в наблюдениях не нашлось.

Roehm T., Tiarks R., Koschke R., Maalej W. (2012). How Do Professional Developers Comprehend Software? ICSE 2012. Наблюдения за практиками: разработчики стараются избегать полного понимания системы и ограничиваются минимумом под задачу; документацией пользуются мало и не доверяют ей; предпочитают спросить коллегу и прочитать код; формализованные процедуры понимания не применяют.

Граница метода: чего в коде нет

Naur P. (1985). Programming as Theory Building. Microprocessing and Microprogramming, 15(5). Тезис: программа — это остаток от теории, которая была в головах авторов. Теория включает соответствие кода реальному миру, обоснование каждого решения и представление о том, как систему можно развивать. Код и документация — её проекция, из которой исходную теорию полностью восстановить нельзя. Отсюда наблюдение Наура: команда, получившая чужую систему, не восстанавливает её теорию, а строит новую, и с этого момента система живёт иначе.

Это не философия, а практическое ограничение чтения кода:

  • код показывает что делается и почти никогда — почему именно так;
  • отброшенные альтернативы в коде отсутствуют полностью;
  • ограничение, из-за которого решение выглядит странно, могло исчезнуть три года назад, а форма осталась.

Поэтому половина обучения на чужом коде — это не чтение кода: это история изменений, тикеты, обсуждения в pull request, разговор с автором. Как фиксировать восстановленное обоснование, чтобы оно не потерялось снова, — тема глав про ADR и про заметки.

Что реально проверено про обучение на готовых решениях

Здесь есть данные, и они относятся именно к программированию.

Проработанные примеры и разворот экспертизы

База разобрана в главе «Желательные трудности»: Sweller и Cooper (1985) — новичку разбор полного решения даёт больше, чем самостоятельное решение при равном времени; Kalyuga, Ayres, Chandler, Sweller (2003) — эффект разворота экспертизы: тот же приём у подготовленного даёт ноль или минус. Повторять не буду, но зафиксирую следствие для нашей темы: чтение чужого кода — это разбор проработанных примеров, со всеми свойствами этого приёма, включая потолок.

Задачи на достройку: прямое попадание в тему

van Merriënboer J. J. G. (1990). Strategies for Programming Instruction in High School: Program Completion vs. Program Generation. Journal of Educational Computing Research, 6(3). Сравнивались две стратегии обучения программированию: писать программы с нуля против достраивать частично готовые. Стратегия достройки дала лучшие результаты — и по качеству конструирования программ, и по пониманию.

Оговорки, без которых цифру нельзя переносить: школьники, вводный курс, учебный язык, небольшая выборка, конец 1980-х. Механизм, однако, ровно тот, который нас интересует: готовый код рядом снимает часть нагрузки и оставляет ресурс на построение схемы, при этом требуя порождать решение, а не только узнавать его.

Задачи Парсонса

Формат: дан набор перемешанных строк корректной программы, иногда с лишними; нужно собрать рабочий порядок.

  • Ericson B. J., Margulieux L. E., Rick J. (2017). Solving Parsons Problems Versus Fixing and Writing Code. Koli Calling. Решение задач Парсонса занимало заметно меньше времени при сопоставимом приросте обучения по сравнению с написанием и починкой кода.
  • Du Y., Luxton-Reilly A., Denny P. (2020). A Review of Research on Parsons Problems. ACE 2020. Обзор поля: исследований много, но они преимущественно короткие, на вводных курсах, с малыми выборками; данных о переносе за пределы учебных задач мало.

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

Самообъяснение

Самообъяснение (Chi и коллеги, 1989, 1994; метаанализ Bisra и др., 2018) разобрано в главе про перенос. Здесь важна деталь исходной работы 1989 года: сильные студенты, работая с решёнными примерами, спонтанно объясняли себе каждый шаг, слабые — перечитывали и отмечали «понятно». На чтении кода это различие воспроизводится буквально: одни при виде select с default спрашивают «почему неблокирующий вариант и что будет, если убрать default», другие идут дальше.

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

Явные стратегии трассировки

Nelson G. L., Xie B., Ko A. J. (2017). Comprehension First. ICER 2017 и Xie B., Nelson G. L., Ko A. J. (2018). An Explicit Strategy to Scaffold Novice Program Tracing. SIGCSE 2018. Обучение новичков явной пошаговой процедуре трассировки кода — с фиксацией состояния переменных на бумаге — улучшало результаты по сравнению с обычной практикой. Это прямое свидетельство в пользу того, что «читать код» — навык, который можно и нужно ставить отдельно, а не побочный продукт опыта. Ограничение то же: новички, вводные курсы.

Формы работы с чужим кодом и точка, где каждая перестаёт учить

Чего в этой группе данных нет

  • Ни одна из работ не проверялась на кодовой базе в сотни тысяч строк с историей в десять лет.
  • Ни одна не измеряла отдалённый результат — через полгода-год работы в этой базе.
  • Метрики сложности, которыми принято оценивать «трудность» кода, плохо предсказывают реальные усилия на понимание: Peitek N., Apel S., Parnin C., Brechmann A., Siegmund J. (2021). Program Comprehension and Code Complexity Metrics: An fMRI Study. ICSE 2021. Связь есть, но слабее, чем предполагает индустриальная практика; надёжного способа заранее оценить, сколько времени займёт разбор куска, нет.

Приёмы чтения чужого кода

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

1. Читать под вопрос, а не подряд

Sillito J., Murphy G. C., De Volder K. (2008). Asking and Answering Questions During a Program Change Task. IEEE TSE, 34(4). В наблюдениях за реальными задачами изменения собрано 44 типа вопросов, сгруппированных в четыре уровня: найти точку входа; расширить окрестность найденного; понять связный подграф; понять отношения между несколькими подграфами. Существенно, что вопросы верхних уровней инструментами почти не поддерживаются — на них отвечают чтением и разговором.

Как выглядит на практике: перед открытием файла записать вопрос, на который вы ищете ответ. «Где принимается решение о повторной отправке?» — рабочий вопрос. «Разобраться в модуле оплат» — не вопрос, а намерение, и оно порождает бесцельное листание.

Цена. Пара минут и дисциплина не начинать читать раньше. Когда не окупается. Первое знакомство с системой, где вопрос ещё не из чего сформулировать; тогда сначала обзорный проход по точкам входа и тестам.

2. Предсказание до наблюдения

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

Работает по двум причинам сразу: создаёт желательную трудность и превращает наблюдение в проверку гипотезы, то есть в обратную связь. Без записанного предсказания ретроспективное «я так и думал» неотличимо от знания.

Цена. 30–60 секунд на предсказание. Главная стоимость — психологическая: письменно ошибаться неприятно. Когда не окупается. Код, который вы не собираетесь понимать: разовый скрипт, чужая библиотека, куда вы зашли за одной сигнатурой.

3. Сначала исполнить, потом читать

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

# Приём: не читать реализацию, а сузить её поведением.
# Гипотеза 1: при 429 запрос повторяется, при 400 — нет.
# Гипотеза 2: задержки растут экспоненциально и ничем не ограничены сверху.
# Гипотеза 3: общий бюджет времени на все попытки не проверяется.

import itertools

def probe(client, status_codes):
    """Одноразовый зонд, а не тест: отвечает на конкретный вопрос
    про конкретный кусок кода и в репозиторий не попадает."""
    attempts = []
    fake = itertools.chain(status_codes, itertools.repeat(200))
    client.transport = lambda req: attempts.append(req) or next(fake)
    client.send({"id": 1})
    return attempts  # количество и интервалы — ответ на гипотезы 1 и 2

Цена. Нужна возможность запустить кусок изолированно. В системах с тяжёлым окружением это отдельная работа на часы. Когда не окупается. Если поднять окружение дороже, чем прочитать; если код детерминирован и короток.

4. Ломать и смотреть

Убрать проверку, инвертировать условие, удалить default из select, снять блокировку, поставить таймаут в ноль — и посмотреть, что упадёт. Каждое такое изменение проверяет один пункт вашей модели: «я считаю, что эта строка отвечает за X».

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

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

5. Достройка: удалить и восстановить

Прямой перенос формата van Merriënboer (1990) на рабочий код. Выбрать функцию, которую вы «понимаете», удалить тело, оставить сигнатуру и тесты — и написать заново. Потом сравнить с оригиналом построчно.

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

Цена. От двадцати минут до часа на функцию. Требует существующих тестов — без них нет оракула и приём вырождается в «написал что-то похожее». Когда не окупается. Тривиальные функции; сгенерированный код; код, который вы никогда не будете менять.

6. Слоями: интерфейс, тесты, реализация

Порядок, снижающий бесполезное чтение: публичный интерфейс и типы данных → тесты → вызывающая сторона → реализация. Тесты обычно оказываются лучшей доступной документацией намерения: они показывают, какие случаи автор считал важными, а какие даже не рассматривал. Отсутствие теста на случай — тоже информация.

Хорошая иллюстрация того, что даёт правильный порядок, — Feathers M. (2004). Working Effectively with Legacy Code: там же приёмы, позволяющие вносить изменения в код, который вы поняли лишь частично. Систематическое руководство по чтению — Spinellis D. (2003). Code Reading: The Open Source Perspective.

Цена. Дисциплина не проваливаться в реализацию сразу. Когда не окупается. Кодовая база без тестов и без внятных интерфейсов — там остаётся только исполнение и история.

7. История как источник намерения

История репозитория содержит то, чего нет в снимке кода: последовательность попыток, откаты, спешные правки. Это ближайшее доступное приближение к теории Наура.

Читая только последний коммит, вы видите простой код с джиттером и не видите, что джиттер появился после инцидента, а до него был откат. git blame покажет автора последней строки — и это чаще всего человек, делавший рефакторинг, а не тот, кто принимал решение.

# Когда и зачем появилась конкретная константа
git log -S 'backoff_cap' --oneline -- internal/retry/
# Как менялся файл, игнорируя форматирующие правки и переносы кода
git log --follow -p -w -- internal/retry/policy.go
git blame -w -C -L 40,80 internal/retry/policy.go
# Найти коммит, после которого поведение изменилось
git bisect start HEAD v1.4.0

Дальше — номер тикета из сообщения коммита, обсуждение в pull request, постмортем инцидента. Технику работы с историей разбирает трек про git, формат разбора инцидентов — глава про постмортемы.

Цена. От пяти минут до получаса. В репозиториях с «squash всего в один коммит» и пустыми сообщениями приём не работает вовсе. Когда не окупается. Молодой код; массовые механические правки; история, переписанная миграцией между системами.

8. Сеанс активного чтения целиком

Собранный цикл, который держит все предыдущие приёмы вместе:

Ключевое свойство цикла — каждый шаг завершается проверкой, а не ощущением. Ощущение «понятно» без шага 5 и 7 — ровно то, что глава про иллюзию понимания называет иллюзией беглости.

Сводка приёмов

Приём Что даёт Цена Когда не окупается
Чтение под вопрос отсекает бесцельное листание минуты нет базы для вопроса
Предсказание до наблюдения превращает чтение в обратную связь 30–60 с на итерацию одноразовый код
Сначала исполнить быстрая и честная программная модель подъём окружения тяжёлое окружение, короткий код
Ломать и смотреть проверяет назначение конкретных строк минуты, риск продакшн и общие стенды
Достройка по эталону точный список пробелов 20–60 мин на функцию нет тестов как оракула
Слоями меньше лишнего чтения дисциплина нет интерфейсов и тестов
История восстанавливает намерение 5–30 мин squash-история без сообщений
Самообъяснение письменно вскрывает разрывы в модели 10 мин на кусок материал уже освоен

Рабочая задача как единица обучения

Отдельный приём здесь не нужен: нужна обвязка вокруг задачи, которую вы всё равно делаете. Обвязка состоит из трёх моментов и занимает суммарно 15–25 минут на задачу.

До. Записать предсказание: где, по-вашему, лежит причина; сколько времени займёт; что окажется сложным. Три строчки.

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

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

Без письменной фиксации приём разваливается: «удивление» через два дня выглядит как «ну да, очевидно». Форматы фиксации — следующая глава.

Состояния владения куском системы

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

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

Какие задачи брать ради обучения

Задачи различаются по учебной ценности, и различие предсказуемо.

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

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

Каналы, где чужой код приходит к вам сам

Код-ревью чужих изменений. Как канал обратной связи на ваш код ревью разобрано в главе про обратную связь. Здесь другая сторона: чтение чужих PR — самый дешёвый регулярный поток структурированного чужого кода с уже написанным объяснением намерения. Bacchelli A., Bird C. (2013). Expectations, Outcomes, and Challenges of Modern Code Review. ICSE 2013: заявленная цель ревью — поиск дефектов, а фактически заметная часть отдачи приходится на передачу знаний и осведомлённость о системе; главным препятствием участники называли нехватку контекста у ревьюера. Sadowski C. и др. (2018), ICSE-SEIP: в Google образовательная функция ревью называется в числе явных целей процесса.

Как использовать: раз в неделю разбирать один чужой PR не как ревьюер, а как ученик — с предсказанием до чтения комментариев. Цена — 20–30 минут. Не окупается на косметических диффах и в чужих доменах, где у вас нет опоры.

Парная работа. Здесь нужна честность про цену. Hannay J. E., Dybå T., Arisholm E., Sjøberg D. I. K. (2009). The Effectiveness of Pair Programming: A Meta-Analysis. Information and Software Technology, 51(7): по совокупности экспериментов эффект на качество небольшой положительный, на длительность задачи положительный, а на суммарные трудозатраты — заметно отрицательный, что естественно, раз работают двое. Arisholm E., Gallis H., Dybå T., Sjøberg D. I. K. (2007), IEEE TSE, 33(2): эффект зависит от сложности задачи и квалификации — на сложных задачах пары выигрывают в качестве, на простых выигрыш съедается затратами.

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

Онбординг. Dagenais B., Ossher H., Bellamy R., Robillard M., de Vries J. (2010). Moving into a New Software Project Landscape. ICSE 2010 и Begel A., Simon B. (2008). Novice Software Developers, All Over Again. ICER 2008. Устойчивое наблюдение обеих работ: главная трудность новичка — не язык и не алгоритмы, а ориентация: кого спросить, где границы, какие правила не записаны. Отсюда практический вывод для первых недель: тратить время на карту людей и решений, а не на попытку прочитать всё подряд. Организационная сторона — глава про первые 90 дней и про онбординг со стороны лида.

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

ИИ в этом контуре

Языковая модель объясняет незнакомый код быстро и обычно верно в главном. Это одновременно её польза и её основной риск в этой главе.

Что именно она забирает. По схеме Пеннингтон и Брукса обучение происходит в двух местах: при порождении гипотезы и при её проверке. Готовое объяснение снимает оба шага: вы получаете результат чужого понимания в форме, которая ощущается как собственное понимание. Это то же соотношение, что у проработанных примеров: для новичка в этой кодовой базе — выигрыш, для человека, уже способного строить модель, — разворот экспертизы и потеря тренировки.

Порядок, который сохраняет обучение и оставляет скорость:

  1. Записать свою гипотезу о том, что делает кусок кода, — три строки.
  2. Спросить модель и сравнить с гипотезой; интересны только расхождения.
  3. Расхождения проверить в рантайме или тестом — модель ошибается уверенно и правдоподобно.
  4. Спросить то, чего в коде нет: «какие альтернативы обычно рассматривают в такой ситуации и чем они хуже». Здесь ИИ силён, а код молчит.

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

Честно про данные: контролируемых исследований влияния объяснений от языковых моделей на долгосрочное понимание кода мне неизвестно. Эксперимент METR (2025) измерял скорость выполнения задач опытными разработчиками, а не обучение; разбор — в главе «Зачем учиться, когда есть ИИ». Развёрнуто про режимы работы с моделью — в главе «Учиться с ИИ»; инженерная сторона — в треках ai-basics и ai-agents.

Цена и когда всё это не окупается

Прямая цена. Обвязка вокруг задачи — 15–25 минут на задачу. Разбор одного чужого PR — 20–30 минут в неделю. Достройка функции — до часа. При десяти задачах в неделю это несколько часов, то есть заметная доля времени, отнятая у поставки.

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

Когда не окупается:

  • Код, который вы никогда не будете менять. Понимание чужой библиотеки на уровне «знаю сигнатуру и поведение на моих входах» — рациональная остановка.
  • Система на выброс. Прототип, эксперимент, миграция, после которой всё будет переписано.
  • Кодовая база без обратной связи. Нет тестов, нет мониторинга, выкатка раз в квартал — оракула нет, и приёмы вырождаются в ритуал.
  • Организационное узкое место. Если рост упирается не в понимание системы, а в то, что вам не дают задач за пределами одного модуля, — это не решается чтением кода.
  • Полное отсутствие базы. Если незнакомо всё сразу — язык, домен, инфраструктура — сначала нужен упорядоченный курс или книга, а не чужой код. Это ровно то, о чём Kirschner, Sweller и Clark (2006) писали применительно к обучению без руководства.

Антипаттерны

  • Читать репозиторий подряд. Файл за файлом, сверху вниз. Даёт ощущение работы и почти нулевой прирост модели: без вопроса ситуационная модель не строится. Родственная ошибка — «разобраться в системе» как задача: формулировка без критерия завершения, которая заменяется списком вопросов с проверяемыми ответами.
  • Считать понятым то, что прочитано. Основной механизм иллюзии; лечится единственным способом — попыткой воспроизвести или изменить.
  • Объяснять непонятность своей глупостью. Иногда код нарушает правила дискурса, и, по данным Soloway и Ehrlich (1984), в этом случае даже эксперт теряет преимущество. Правильная реакция — зафиксировать нарушенное ожидание, а не наращивать усилие.
  • Учиться только на своей подсистеме. Через год это узкая экспертиза, которая обесценивается вместе с подсистемой. Смотрите карту карьеры портала как инструмент поиска пробелов.
  • Копить закладки вместо модели. Сохранённая ссылка на файл — не знание. Про то, чем внешняя память является и чем не является, — следующая глава.
  • Требовать от истории того, чего в ней нет. В репозитории с автоматическим squash и сообщениями «fix» археология не работает; тратить на неё время бессмысленно.
  • Превращать разбор в ритуал. Если после задачи вы пишете три строки, не глядя на предсказание, приём мёртв: работает не запись, а сравнение.

Мини-итог

  • Правило 70-20-10 — обобщение ретроспективных рассказов руководителей (Lombardo и Eichinger, 1996; McCall и др., 1988), а не измерение. Как мнемоника допустимо, как обоснование — нет.
  • Понимание кода занимает основную долю рабочего времени: около 58 % по Xia и др. (2018), до 70 % по Minelli и др. (2015). Это описательные данные полевых наблюдений, но они означают, что чтение кода — главный навык, а не подготовка к работе.
  • «Понять» распадается на программную модель, доменную модель и ситуационную (Pennington, 1987; von Mayrhauser и Vans, 1995). Чтение строк наполняет только первую.
  • Понимание идёт гипотезами и маяками (Brooks, 1983), опирается на планы и правила дискурса (Soloway и Ehrlich, 1984); на коде, нарушающем конвенции, преимущество эксперта исчезает.
  • Код — остаток теории, которую полностью восстановить нельзя (Naur, 1985). Поэтому история изменений, тикеты и разговор с автором — не дополнение к чтению, а его обязательная часть.
  • Есть данные в пользу форматов «достройка вместо написания с нуля» (van Merriënboer, 1990), задач Парсонса (Ericson и др., 2017; обзор Du и др., 2020) и явных стратегий трассировки (Xie и др., 2018). Все — на студентах и коротких задачах; переноса на промышленное легаси никто не измерял.
  • Приёмы чтения работают по трём механизмам, разобранным раньше: вопрос вместо потока, предсказание вместо узнавания, изменение вместо чтения. Каждый требует времени и упирается в наличие оракула. Рабочая задача становится учебной от обвязки в 15–25 минут: предсказание до, список удивлений во время, разбор после; без письменной фиксации приём разваливается.
  • Парное программирование не бесплатно: небольшой выигрыш в качестве при заметном росте трудозатрат (Hannay и др., 2009), сильно зависит от сложности задачи и квалификации (Arisholm и др., 2007). Чужие PR дешевле: это регулярный поток чужого кода с уже объяснённым намерением, и передача знаний в ревью зафиксирована эмпирически (Bacchelli и Bird, 2013).
  • ИИ снимает ровно те два шага, на которых происходит обучение, — порождение гипотезы и её проверку. Порядок «сначала своя гипотеза, потом сверка» сохраняет обучение. Контролируемых данных о влиянии ИИ-объяснений на долгосрочное понимание кода нет.

Источники

  • Naur P. (1985). Programming as Theory Building. Microprocessing and Microprogramming, 15(5).
  • Brooks R. (1983). Towards a Theory of the Comprehension of Computer Programs. International Journal of Man-Machine Studies, 18(6); Letovsky S. (1986). Cognitive Processes in Program Comprehension. Empirical Studies of Programmers; расширенная версия — Journal of Systems and Software, 1987.
  • Soloway E., Ehrlich K. (1984). Empirical Studies of Programming Knowledge. IEEE Transactions on Software Engineering, SE-10(5).
  • Pennington N. (1987). Stimulus Structures and Mental Representations in Expert Comprehension of Computer Programs. Cognitive Psychology, 19(3); von Mayrhauser A., Vans A. M. (1995). Program Comprehension During Software Maintenance and Evolution. IEEE Computer, 28(8).
  • Biggerstaff T. J., Mitbander B. G., Webster D. (1993). The Concept Assignment Problem in Program Understanding. ICSE 1993.
  • Vessey I. (1985). Expertise in Debugging Computer Programs: A Process Analysis. International Journal of Man-Machine Studies, 23(5); ранее — Gould J. D. (1975). Some Psychological Evidence on How People Debug Computer Programs. Там же, 7(2).
  • Robillard M. P., Coelho W., Murphy G. C. (2004). How Effective Developers Investigate Source Code. IEEE TSE, 30(12); LaToza T. D., Venolia G., DeLine R. (2006). Maintaining Mental Models: A Study of Developer Work Habits. ICSE 2006.
  • Ko A. J., Myers B. A., Coblenz M. J., Aung H. H. (2006). An Exploratory Study of How Developers Seek, Relate, and Collect Relevant Information During Software Maintenance Tasks. IEEE TSE, 32(12); Sillito J., Murphy G. C., De Volder K. (2008). Asking and Answering Questions During a Program Change Task. Там же, 34(4).
  • Roehm T., Tiarks R., Koschke R., Maalej W. (2012). How Do Professional Developers Comprehend Software? ICSE 2012; см. также Maalej W., Tiarks R., Roehm T., Koschke R. (2014). On the Comprehension of Program Comprehension. ACM TOSEM, 23(4).
  • Xia X., Bao L., Lo D., Xing Z., Hassan A. E., Li S. (2018). Measuring Program Comprehension: A Large-Scale Field Study with Professionals. IEEE TSE, 44(10).
  • Minelli R., Mocci A., Lanza M. (2015). I Know What You Did Last Summer — An Investigation of How Developers Spend Their Time. ICPC 2015.
  • Parnin C., Rugaber S. (2011). Resumption Strategies for Interrupted Programming Tasks. Software Quality Journal, 19(1).
  • Siegmund J. и др. (2014). Understanding Understanding Source Code with Functional Magnetic Resonance Imaging. ICSE 2014; Peitek N., Apel S., Parnin C., Brechmann A., Siegmund J. (2021). Program Comprehension and Code Complexity Metrics: An fMRI Study. ICSE 2021. van Merriënboer J. J. G. (1990). Strategies for Programming Instruction in High School: Program Completion vs. Program Generation. Journal of Educational Computing Research, 6(3).
  • Ericson B. J., Margulieux L. E., Rick J. (2017). Solving Parsons Problems Versus Fixing and Writing Code. Koli Calling 2017; Du Y., Luxton-Reilly A., Denny P. (2020). A Review of Research on Parsons Problems. ACE 2020. Nelson G. L., Xie B., Ko A. J. (2017). Comprehension First: Evaluating a Novel Pedagogy and Tutoring System for Program Tracing in CS1. ICER 2017; Xie B., Nelson G. L., Ko A. J. (2018). An Explicit Strategy to Scaffold Novice Program Tracing. SIGCSE 2018.
  • Sweller J., Cooper G. A. (1985). The Use of Worked Examples as a Substitute for Problem Solving in Learning Algebra. Cognition and Instruction, 2(1); Kalyuga S., Ayres P., Chandler P., Sweller J. (2003). The Expertise Reversal Effect. Educational Psychologist, 38(1); Kirschner P., Sweller J., Clark R. (2006). Why Minimal Guidance During Instruction Does Not Work. Educational Psychologist, 41(2).
  • Chi M. T. H., Bassok M., Lewis M. W., Reimann P., Glaser R. (1989). Self-Explanations. Cognitive Science, 13(2); Chi M. T. H. и др. (1994). Eliciting Self-Explanations Improves Understanding. Там же, 18(3); Bisra K., Liu Q., Nesbit J. C., Salimi F., Winne P. H. (2018). Inducing Self-Explanation: A Meta-Analysis. Educational Psychology Review, 30.
  • Bacchelli A., Bird C. (2013). Expectations, Outcomes, and Challenges of Modern Code Review. ICSE 2013. Текст у Microsoft Research
  • Sadowski C., Söderberg E., Church L., Sipko M., Bacchelli A. (2018). Modern Code Review: A Case Study at Google. ICSE-SEIP 2018.
  • Hannay J. E., Dybå T., Arisholm E., Sjøberg D. I. K. (2009). The Effectiveness of Pair Programming: A Meta-Analysis. Information and Software Technology, 51(7); Arisholm E., Gallis H., Dybå T., Sjøberg D. I. K. (2007). Evaluating Pair Programming with Respect to System Complexity and Programmer Expertise. IEEE TSE, 33(2).
  • Dagenais B., Ossher H., Bellamy R., Robillard M., de Vries J. (2010). Moving into a New Software Project Landscape. ICSE 2010; Begel A., Simon B. (2008). Novice Software Developers, All Over Again. ICER 2008.
  • Lombardo M. M., Eichinger R. W. (1996). The Career Architect Development Planner; McCall M. W., Lombardo M. M., Morrison A. M. (1988). The Lessons of Experience — источник формулы 70-20-10.
  • Feathers M. (2004). Working Effectively with Legacy Code. Prentice Hall; Spinellis D. (2003). Code Reading: The Open Source Perspective. Addison-Wesley; Détienne F. (2002). Software Design — Cognitive Aspects. Springer.
  • METR (2025). Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity.

Что дальше

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

Заметки и внешняя память: инструмент, а не ритуал

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

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

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

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