Open Source
Как писать issues и bug reports
2011 год. Команда V8 три недели охотится за нестабильным JIT-багом. Воспроизводится в 1 случае из 50 запусков. Потом приходит issue от инженера из Сеула: 7 строк JavaScript, которые ломают JIT-оптимизатор стабильно и воспроизводимо. Expected output, actual output, версия V8. Через 48 часов - `FIXED`. Один правильно написанный issue сделал то, что три недели работы трёх инженеров не могли.
- React принимает 2000+ issues в год - большинство без MRE закрываются за недели без фикса
- Chromium: issues с MRE фиксируются в среднем на 60% быстрее по данным ChromeOS project
- Node.js contributing guide явно указывает: issue без minimal repro не будет рассмотрен
- Vite требует репродукцию на StackBlitz - без неё автоматически добавляется метка needs-reproduction
Анатомия хорошего bug report
2011 год. Chromium issue tracker. Инженер из Сеула открывает баг с заголовком: **"Array sort produces wrong results for specific float comparator"**. Прикладывает 7 строк JavaScript, ожидаемый вывод, реальный вывод, версию V8. Через 48 часов трое инженеров Google, которые три недели не могли локализовать нестабильный JIT-баг, закрывают задачу: `FIXED`. Один правильно написанный issue сделал то, что три недели работы трёх инженеров не могли.
На том же трекере живут тысячи issues с заголовком "It doesn't work on my machine". Закрыты через 2 минуты с меткой `needs-info`. Разница не в серьёзности бага - разница в том, что из первого issue можно немедленно запустить отладку, из второго нельзя.
Пять обязательных элементов
Структура не случайна. **Шаги воспроизведения** - это тест, который мейнтейнер запускает у себя. **Expected vs Actual** позволяет мгновенно понять, что сломалось. **Среда** отсекает 80% вопросов "а какая у вас версия?". Без любого из этих элементов мейнтейнер пишет уточняющий комментарий, issue висит открытым ещё неделю.
**Почему regression info ценна:** если указать версию, где баг появился впервые, мейнтейнер может запустить `git bisect` и найти конкретный коммит за 10 итераций вместо ручного поиска по истории. "Работало в 3.1.8, сломалось в 3.2.0" - это уже половина диагностики.
Название issue - отдельное искусство. Формула: `[компонент] действие приводит к результату при условии`. Плохо: `Error with imports`. Хорошо: `[plugin-react] Fast refresh breaks when component uses custom hooks from external package`. Второй вариант сразу говорит: где (plugin-react), что (fast refresh), при каком условии (custom hooks из внешнего пакета). Мейнтейнер за секунду понимает, касается ли это его области.
Issue содержит шаги воспроизведения, expected и actual поведение, версию ПО. Чего не хватает для полного bug report?
Минимальный воспроизводящий пример
Минимальный воспроизводящий пример (MRE) - наименьший кусок кода, который стабильно воспроизводит баг. Слово **минимальный** здесь ключевое. Не "моё приложение на 50 000 строк" - а ровно те 10-20 строк, после которых удаление любой строки убирает проблему.
Интересное свойство процесса: попытка создать MRE решает проблему ещё до открытия issue примерно в 30% случаев. При изоляции бага обнаруживается, что проблема была в собственном коде, а не в библиотеке. Ещё 20% - баг уже известен и закрыт, находится поиском по трекеру. Оставшиеся 50% - настоящие новые баги, и для них MRE ускоряет фикс в 5-10 раз.
Инструменты для быстрого MRE: **CodeSandbox**, **StackBlitz**, **Replit** позволяют создать изолированное окружение за 2 минуты и поделиться ссылкой. Для Node.js-библиотек подходит gist или минимальный репозиторий с одним `package.json` и `index.js`. Для браузерных багов - codepen или jsbin. Любой из этих вариантов лучше, чем ссылка на закрытый репозиторий с инструкцией по настройке на 10 шагов.
**Не прикладывайте весь проект.** Мейнтейнер не будет клонировать 200 MB репозиторий с закрытым кодом чтобы воспроизвести баг. Это правило неформальное, но работает безотказно: если MRE нет, issue ждёт. Ограничение большинства закрытых issues без fix - невозможность воспроизвести.
При создании MRE обнаружилось: при удалении одной зависимости баг исчезает. Что это означает?
Feature request и взгляд мейнтейнера
Feature request - не список желаний. Мейнтейнер видит сотни запросов в год. Принимаются те, что решают реальную проблему достаточного числа людей и вписываются в архитектуру проекта. Запрос "добавьте поддержку X" без контекста получает метку `needs-discussion` и тонет.
Ключевой принцип: описывать **проблему**, а не **решение**. Мейнтейнер может знать лучший подход реализации. Feature request, который начинается с конкретного симптома ("рендер 10 000 строк блокирует UI на 1.8 сек на mid-range телефоне") убедительнее, чем тот, который сразу предлагает флаг. Второй формат позволяет рассмотреть несколько подходов.
Взгляд мейнтейнера: что происходит при триаже
Мейнтейнер крупного проекта обрабатывает 10-30 новых issues в неделю. Первые 30 секунд - решение: воспроизводить или закрыть с запросом информации. Issues без MRE или с непонятным описанием получают метку `needs-info` и ждут. Это не злой умысел - это экономия времени. Хорошо написанный issue уважает время человека, который часто работает бесплатно.
**Поиск дубликатов обязателен.** GitHub поиск по ключевым словам, фильтр по открытым и закрытым issues. Дубликат к уже закрытому issue с решением - потраченное время всех. Если найден похожий issue - лучше прокомментировать его, чем открывать новый. Метки `duplicate`, `wontfix`, `good first issue` - это коммуникационный протокол проекта, читать перед открытием issue.
Feature request: «Добавьте dark mode в библиотеку». Что улучшит этот запрос сильнее всего?
Ключевые идеи
- Bug report: шаги воспроизведения + expected vs actual + среда + MRE - пять обязательных элементов
- MRE - наименьший код, стабильно воспроизводящий баг; создание MRE решает 30% проблем до открытия issue
- Feature request описывает проблему, не решение - мейнтейнер может знать лучший подход
- Название issue: [компонент] действие приводит к результату при условии
- Поиск дубликатов перед открытием - обязательный шаг уважения к времени сообщества
Связанные темы
Issue написан - следующий шаг: превратить его в PR.
- Fork и PR workflow — Технический процесс создания PR после issue
- Анатомия хорошего Pull Request — Хороший issue становится основой описания PR
- Работа с мейнтейнерами — Issues - основной канал коммуникации с мейнтейнерами
Вопросы для размышления
- Возьмите любой закрытый issue в React или Vue с меткой `needs-more-info`. Что конкретно в нём отсутствовало? Как написали бы его правильно?
- Почему процесс создания MRE часто решает проблему до открытия issue? Какой ментальный процесс при этом происходит?
Связанные уроки
- oss-04-fork-pr-workflow — Fork workflow - контекст перед написанием issue
- oss-06-great-pull-request — Хороший issue - первый шаг к хорошему PR
- oss-07-code-review — Code review требует той же точности описания проблемы
- oss-09-maintainers — Работа с мейнтейнерами начинается с правильного issue
- oss-08-community — Этикет сообщества определяет тон issues
- se-02