урок 5 из 16

Дифф: главный навык

Читать дифф в правильном порядке, отличать удалённое от добавленного, узнавать шесть красных флагов и пользоваться объёмом диффа как сигналом.

17 мин чтения · 4 главы ·4 задания ·домашняя работа

Как устроен дифф

Дифф — это список изменений: какие строки исчезли и какие появились. Всё. Никакой магии в нём нет, и читать его можно, зная язык программирования весьма поверхностно.

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

src/orders.js
 function discount(order) {
  if (!order.items.length) return 0;
- return order.total * 0.1;
+ const rate = order.total > 5000 ? 0.15 : 0.1;
+ return order.total * rate;
 }

Одна строка удалена, две добавлены. Соседние строки без знака показывают, что это происходит внутри функции скидки.

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

Второе, что стоит знать: дифф не показывает того, чего не изменилось. Это очевидно и тем не менее регулярно подводит. Если агент добавил новую функцию, а старую, которая делает то же самое, оставил на месте, — в диффе будет только добавление. Ничего подозрительного, пока не задашь вопрос «а старая-то куда делась».

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

И четвёртое, самое практичное: у диффа есть размер, и этот размер сам по себе является информацией. К этому вернёмся в последней главе урока.

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

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

git diff
git diff --stat

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

Порядок чтения

Порядок чтения имеет значение. Тот, кто читает дифф сверху вниз, находит меньше, чем тот, кто читает его в правильном порядке, — и это не вопрос внимательности.

1. Список файлов: есть ли неожиданные2. Объём: сколько строк и совпадает ли с задачей3. Удалённые строки: что исчезло4. Добавленные строки: что появилосьудалённые читают раньше добавленных: пропажу заметить труднее
Список файлов читается раньше строк. Файл, которого вы не ждали, отвечает на больше вопросов, чем десять правильных строк в ожидаемом.

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

git diff --stat

 src/cart.js          |  4 +++-
 test/cart.test.js    |  8 ++++++++
 package.json         |  2 +-
 src/format.js        | 12 +++++-------

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

Шаг второй: объём. Сколько всего строк? Сравните с тем, сколько вы ожидали. Про это отдельная глава, здесь достаточно правила: оценку делают до открытия диффа, иначе полученный объём будет казаться правильным сам по себе.

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

Шаг четвёртый: добавленные строки. Теперь можно читать по существу: делает ли новый код то, что нужно, и делает ли он это так, как принято в проекте.

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

Есть и приём, который сильно упрощает третий и четвёртый шаги: читайте дифф с вопросом, а не с чистого листа. Вопрос формулируется до открытия: «где здесь учитывается количество?» Тогда глаз ищет ответ, а не скользит по строкам, и всё, что в ответ не входит, само выделяется как лишнее. Без вопроса чтение превращается в проверку синтаксиса — а синтаксис проверит машина, и проверит лучше.

И практический совет для длинного диффа: читайте его по файлам, а не подряд. Дифф на четыреста строк, прочитанный подряд, превращается в мелькание; тот же дифф, прочитанный по одному файлу за раз с вопросом «что здесь должно было измениться», читается вдвое быстрее и находит вдвое больше.

Красные флаги

Теперь список признаков, каждый из которых означает «остановись». Ни один не означает «неверно» — но каждый обязан быть объяснён, и объяснить его должны вы себе, а не сводка вам.

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

Изменён файл, о котором не просили. Самый частый и самый информативный флаг. Бывает законным: правка расчёта иногда требует правки того, кто его вызывает. Бывает и другим: «заодно привёл к общему стилю».

Правка в тесте вместо кода. Тест упал, и агент поправил тест. Законно ровно в одном случае: если тест действительно проверял неверное. Во всех остальных это подгонка, и она превращает вашу защиту в решето.

test/cart.test.js
- expect(total([{ price: 10, qty: 3 }])).toBe(30);
+ expect(total([{ price: 10, qty: 3 }])).toBe(10);

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

Удалена проверка или условие. Минус на строке, которая что-то проверяла, — всегда вопрос. Возможно, проверка была лишней. Возможно, она мешала, и её убрали.

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

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

Правка в файле настроек. Настройки часто выглядят как код и работают как рычаги. Изменённый параметр может тихо поменять поведение там, где никаких строк не менялось.

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

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

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

Объём как сигнал

Последняя мерка, и самая недооценённая: объём.

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

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

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

Больше, чем ожидали. Самый частый случай, и у него три обычные причины. Агент сделал «заодно» что-то соседнее. Агент выбрал более общее решение, чем требовалось. Агент наткнулся на то, что вы не учли, и это надо услышать словами.

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

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

Есть и полезный приём для тех случаев, когда крупный дифф всё-таки получился. Попросите агента разложить изменения по смыслу: «раздели то, что ты сделал, на отдельные коммиты по смыслу». Триста строк одним куском не читаются; те же триста, разложенные на пять коммитов с понятными названиями, читаются за десять минут.

было: один коммит
 «поправил отчёты» — 312 строк, 9 файлов
стало: пять коммитов
+фильтр по статусу — 18 строк
+тесты на фильтр — 24 строки
+вынесен общий помощник — 60 строк
+переход трёх отчётов на помощник — 190 строк
+обновлено описание — 20 строк

Тот же объём. Но теперь понятно, что читать внимательно, а что пролистать: четвёртый коммит — механический перенос.

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

Задания урока

Каждое проверяется сразу: ответ сверяется с эталоном, и на неверный вариант приходит разбор.

  1. Разложите по тому, что означает знак в диффе.средне
  2. Расставьте шаги чтения диффа по порядку.средне
  3. Какие строки диффа — красные флаги?средне
  4. Объём диффа надо прикидывать до того, как вы его открыли.средне

Практика и материалы по теме курса

Пройти урок с проверкой заданий Курс «Codex» открыт и бесплатен, прогресс сохраняется.
Открыть курс