Больше месяца ничего тут не писал, потому что был занят и времени на нытье по поводу окружающего бардака не было. Занят я был весьма интересными, приятными и полезными рабочими делами.
Главное занятие - это изучение нового подхода к организации кода вообще и изменениям в юнит-тестировании вследствие этого подхода. Подход называется Testing without mocks, а также Nullables + A-Frame Architecture. В таком способе организации тестов и кода мне очень нравится, во-первых, возможность не зависеть от используемых в продуктовом коде фреймворков и библиотек вроде Mockito, а во-вторых, тот факт, что в любом тесте выполняется весь код приложения вглубь по стеку вызовов вплоть до взаимодействия с внешним миром (файловыми системами, базами данных, сторонними сервисами). За этот месяц пару раз случилось такое, что тесты на высоком/внешнем уровне (вроде контроллеров) отловили нежелательные изменения поведения на уровнях ниже. Это здорово.
Этому процессу изучения и внедрения подхода Nullables в рабочие проекты здорово помогает уже устоявшаяся практика парного программирования. Мы с напарником открыли для себя утилиту mob.sh, которая избавляет от возни с ветками и коммитами при передаче кода друг другу. Работаем по схеме “водитель-штурман”, меняемся ролями раз в 20 минут. Получается, что мы пишем меньше строчек кода в час, чем написали бы, работая отдельно, но явно пишем больше строчек хорошего кода в час. Ну и плюс к этому мы всегда в контексте задачи и проекта в целом, не тратим время на код-ревью, потому что штурман и так постоянно проверяет код.
В общем, жаловаться не на что, я этим двум штукам безумно рад!
Комментарии про парное программирование:
Расскажи про парное программирование, мало где видел такой подход, особенно в распределенных командах. Как это вообще работает.
Чисто технически у нас работает так:
- Оба участника (кстати, можно больше, чем два, но у нас больше нет) переключаются на одну и ту же ветку кода. У нас ветки соответствуют задачам, перед началом работы мы такую ветку создаем.
- “Водитель” шарит экран в Зуме, дает команду
mob start 20
- это создает временную ветку для обмена кодом и запускает таймер на 20 минут. - “Водитель” пишет код, “штурман” говорит, что писать. Ну не в смысле, что диктует по буквам (хотя иногда и такое бывает), а описывает общий ход решения. Тут степень детализации инструкций зависит от уровня квалификации участников и степени “погруженности” в контекст. По моим ощущениям мы с напарником довольно быстро выровнялись по этим параметрам, поэтому инструкции “штурмана” все чаще бывают довольно общими.
- В ходе сессии пишем тесты, чтобы зафиксировать поведение. Иногда (и все чаще) пишем тест сначала, чтобы описать / спроектировать желаемый интерфейс. Имея на экране написанный код (пусть он пока и не компилируется) становится легче обсуждать вопросы дизайна.
- Таймер заканчивается, утилита об этом сигнализирует. “Водитель” отдает команду
mob next
, что приводит к коммиту абсолютно всех изменений во временную ветку. Процесс повторяется, начиная с пункта 2.
После завершения сессии парного программирования делаем mob done
- это переносит все накопленные во временной ветке изменения в ветку задачи. Теперь их можно еще разок просмотреть и закоммитить то, что действительно должно попасть в итоговое решение.
Передача кода с помощью утилиты mob происходит быстро, так что можно меняться ролями достаточно часто. Мы пока не очень хороши в написании тестов, но потихоньку и этому научимся - тогда можно будет устраивать “пинг-понг”, когда первый участник пишет тест, второй добивается его прохождения и пишет следующий тест.
Ok, технически вроде понятно, а как это в джире выглядит? У нас есть спринт. Есть таски, есть стори поинты. Как два разработчика вписываются в тикет?
Хм, мы этим пока вообще не заморачиваемся, потому что у нас сейчас никакие метрики не собираются в разрезе отдельных разработчиков. Поэтому кто первый “рулит”, на того и таску назначаем, как-то так.
Наверное, какие-то сложности могут возникнуть, если кто-то собирает/анализирует метрики по разработчикам (типа “сколько тасков закрыл Вася”) и принимает решения на основе этого анализа (типа “Вася закрыл тасков меньше, чем Петя, поэтому Пете поднимем ЗП, а Васе - нет”). В этом случае я бы порекомендовал обоим разработчикам списывать время на эту таску, а тем, кто собирает метрики - скорректировать принципы сбора и анализа. Собирать метрики по тем, кто затратил время на таску, а не по тому, кто на нее назначен.
В целом, похоже, что ровно такая же проблема возникает и в случае слишком крупных задач (даже без парного программирования). То есть, задачу начинает делать один человек, заболевает/уходит в отпуск/увольняется, и заканчивает задачу другой - как им обоим вписаться в тикет?
Решается такое обычно дроблением задач, чтобы одна таска на одного разработчика укладывалась в один день максимум. Конкретные рамки могут быть любыми, главное, достаточно мелкими, чтобы не думать о подобных проблемах. При парном программировании можно ровно то же самое сделать - разбить таску на две и каждому из пары закрыть свою половину. Но так стоит делать только, если что-то или кто-то зависит от метрик, собираемых из таск-трекера по разработчикам.