Метод утенка: зачем программисты разговаривают с уточкой и как это связано с техникой Фейнмана

Метод утенка: зачем программисты разговаривают с уточкой и как это связано с техникой Фейнмана Полезное

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

Техника Фейнмана — родственный прием, но для учебы, а не для отладки: объяснить тему простыми словами, найти место, где объяснение ломается, вернуться к материалу и закрыть пробел. Ниже — откуда взялась уточка, пример реального бага на Python, который находится объяснением, пошаговая техника Фейнмана и таблица различий. Код проверен на Python 3.14 24.09.2026.

Откуда в IT взялась уточка

Название приему дала книга «Программист-прагматик» (The Pragmatic Programmer) Эндрю Ханта и Дэвида Томаса, вышедшая в 1999 году. Дэйв Томас вспоминает коллегу по Имперскому колледжу Лондона, который несколько месяцев носил с собой маленькую желтую резиновую уточку и ставил ее на терминал во время работы, а сам прием авторы описывают так: объясните проблему кому-то шаг за шагом, и ошибка часто становится видна сама. С тех пор «уточка» стала в IT-среде мемом и настоящим предметом на столах разработчиков.

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

Почему объяснение вслух находит баги

Когда читаешь свой код «про себя», мозг видит намерение, а не текст: пропущенное условие достраивается автоматически. Объяснение заставляет перевести код в последовательность утверждений — «здесь переменная равна тому-то, потому что…» — и каждое утверждение становится проверяемым. Если на слове «потому что» нечего сказать, это и есть подозрительное место.

У этого эффекта есть близкие понятия в когнитивной психологии:

  • эффект самообъяснения (self-explanation) — люди лучше понимают материал, когда объясняют себе каждый шаг, а не просто перечитывают;
  • иллюзия глубины объяснения (illusion of explanatory depth) — человеку кажется, что он понимает механизм, пока его не попросят объяснить подробно;
  • эффект протеже (protege effect) — подготовка к объяснению другому человеку улучшает собственное понимание.

Важная граница: это объясняет, почему метод часто помогает, но не гарантирует, что баг найдется. Уточка хорошо ловит логические ошибки и ложные допущения в вашем собственном коде. Она не заменит отладчик, логи и тесты, когда проблема в окружении, данных или чужой библиотеке.

Пример: баг, который находится объяснением

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

def add_task(title, tasks=[]):
    tasks.append(title)
    return tasks

print(add_task("купить молоко"))
print(add_task("написать отчет"))

Ожидание — два независимых списка по одной задаче. Фактический вывод на Python 3.14:

['купить молоко']
['купить молоко', 'написать отчет']

Вторая задача «прилипла» к первой. Теперь объясним уточке построчно — именно так, как рекомендует метод:

  1. «Функция принимает название задачи и список tasks. Если список не передали, берется значение по умолчанию — пустой список».
  2. «Пустой список создается… когда?» — вот здесь объяснение спотыкается. Честный ответ: выражение [] в значении по умолчанию вычисляется один раз, при определении функции, а не при каждом вызове.
  3. «Значит, все вызовы без второго аргумента получают один и тот же объект списка, и append дописывает в него».

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

def add_task(title, tasks=[]):
    tasks.append(title)
    return tasks

add_task("a")
add_task("b")
print(add_task.__defaults__)

Вывод: (['a', 'b'],) — список по умолчанию действительно накапливает данные между вызовами. Исправление — использовать None как маркер «аргумент не передан» и создавать список внутри функции:

def add_task(title, tasks=None):
    if tasks is None:
        tasks = []
    tasks.append(title)
    return tasks

print(add_task("купить молоко"))
print(add_task("написать отчет"))
print(add_task.__defaults__)

Вывод:

['купить молоко']
['написать отчет']
(None,)

Обратите внимание: при чтении «про себя» строка tasks=[] читается как «пустой список», и ошибку легко не заметить. Ее выдал вопрос «когда именно это вычисляется?», который появился только при попытке объяснить.

Как «разговаривать с уточкой»: пошагово

  1. Сформулируйте ожидание. Одной фразой: что должно происходить и что происходит на самом деле, с конкретным входом («при втором вызове ожидаю список из одной задачи, получаю из двух»).
  2. Идите по коду в порядке выполнения, а не сверху вниз по файлу. Для каждой строки — что она делает и какие значения у переменных после нее.
  3. Не пропускайте «очевидные» строки. Баг чаще прячется именно там, где кажется, что объяснять нечего.
  4. Отмечайте каждое «вроде бы» и «наверное». Это непроверенное допущение: проверьте его выводом значения, отладчиком или тестом.
  5. Говорите вслух или пишите. Если вслух неудобно (open space), подойдет текст: черновик вопроса в чат команды часто работает так же — половина вопросов не отправляется, потому что ответ находится при написании.

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

import inspect

def duck_review(func):
    lines, start = inspect.getsourcelines(func)
    for number, line in enumerate(lines, start=start):
        print(f"{number:>3} | {line.rstrip()}")
        print("    уточка: что делает эта строка и почему?")

def add_task(title, tasks=None):
    if tasks is None:
        tasks = []
    tasks.append(title)
    return tasks

duck_review(add_task)

Сохраните скрипт в файл целиком, как он показан, и запустите (python3 duck.py). inspect.getsourcelines возвращает строки исходника и номер первой из них: в этом файле add_task начинается на 9-й строке. Если исходника нет, функция упадет: для встроенной функции на C (например, math.sqrt) — TypeError, для функции, чей .py-файл недоступен (только .pyc), — OSError. Первые строки вывода:

  9 | def add_task(title, tasks=None):
    уточка: что делает эта строка и почему?
 10 |     if tasks is None:
    уточка: что делает эта строка и почему?

Если уточка не помогла

  • Объяснение сходится, а код все равно ведет себя иначе — значит, неверен один из «фактов» о среде: версия библиотеки, входные данные, конфигурация. Выведите реальные значения (print, логи, отладчик) вместо того, чтобы их предполагать.
  • Не получается объяснить, что делает строка — это пробел в знаниях, а не в коде. Здесь метод утенка переходит в технику Фейнмана: откройте документацию и разберитесь с конструкцией.
  • Кода слишком много для построчного разбора — сузьте область: минимальный воспроизводящий пример на 10-20 строк сам по себе часто показывает причину.

Техника Фейнмана: то же объяснение, но для учебы

Ричард Фейнман (1918-1988) — американский физик, лауреат Нобелевской премии 1965 года, известный умением объяснять сложное простыми словами. Четыре шага, которые сегодня называют «техникой Фейнмана», — это популярная реконструкция его подхода к учебе, а не схема, которую он сам оформил как метод. Порядок такой:

  1. Выберите тему и запишите все, что о ней знаете. Например, «чем список отличается от кортежа в Python».
  2. Объясните ее простыми словами — так, чтобы понял человек без подготовки. Без жаргона: если без термина никак, объясните и его.
  3. Найдите, где объяснение ломается. Места, где хочется сказать «ну, это сложно» или повторить заученную фразу, — пробелы. Вернитесь к источникам и закройте их.
  4. Упростите и проверьте на слушателе. Сократите объяснение, добавьте пример или аналогию и расскажите реальному человеку. Его вопросы покажут, что еще осталось неясным.

Для программиста удобная проверка шага 2 — объяснить тему кодом: написать минимальный пример и предсказать его вывод до запуска. Если предсказание не совпало с реальным выводом, как с tasks=[] выше, пробел найден точно.

Граница метода: простое объяснение не должно становиться неверным. Упрощение нормально, если вы понимаете, где оно перестает работать, и можете это назвать.

Метод утенка и техника Фейнмана: в чем разница

Метод утенка Техника Фейнмана
Задача найти ошибку в конкретном коде понять и запомнить тему
Что объясняем свой код, строка за строкой понятие или механизм целиком
Слушатель уточка, коллега, черновик вопроса воображаемый новичок, затем реальный человек
Признак успеха найдено расхождение «думал — происходит» тему можно объяснить просто и без пробелов
Когда не хватает проблема в окружении или данных нужна практика, а не только понимание

Общий механизм у них один: объяснение превращает смутное «я вроде понимаю» в набор утверждений, которые можно проверить.

Выводы

  • Метод утенка — отладка через построчное объяснение кода собеседнику, который ничего не знает о задаче; уточка на столе — удобная замена коллеге.
  • Название пришло из книги «Программист-прагматик» (1999); принцип работает потому, что объяснение вскрывает непроверенные допущения.
  • Классический пример — изменяемое значение по умолчанию tasks=[] в Python: баг находится вопросом «когда вычисляется этот список?».
  • Техника Фейнмана применяет тот же принцип к учебе: объяснить просто, найти пробел, вернуться к материалу, упростить.
  • Оба приема не заменяют отладчик, тесты и практику, а дополняют их.

Где применяется / связь с практикой

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

Освойте тему на практике

Если вы изучаете Python и хотите разбирать такие ситуации с преподавателем и ревью кода, посмотрите курс «Python-разработчик. Базовый уровень». Попробовать формат занятий бесплатно можно на открытых уроках Otus.

FAQ

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

Можно ли использовать вместо уточки ИИ-ассистента?
Можно, но это уже другой прием: ассистент отвечает и может сразу предложить исправление. Эффект метода утенка дает именно ваша формулировка проблемы, поэтому полезно сначала описать ее полностью самому, а затем сверить с ответом и проверить его запуском.

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

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)