2 added, 0 removed. Audit A to A.
---
name: bugfix-protocol
version: 1.0.0
type: protocol
author: Lukas Geiger
created: 2026-03-12
updated: 2026-03-12
description: Систематический 6-фазный протокол отладки. Структурированный подход к багам с быстрыми проверками, изолированным тестированием, правилом 20 минут и шаблоном отчета об ошибке.
standalone: true
anthropic_compatible: true
bach_compatible: false
bach_origin: true
category: dev
tags: [debugging, bugfix, protocol, python, pyqt6, systematic]
language: ru
status: active
dependencies: {'tools': [], 'services': [], 'protocols': [], 'python': []}
provenance: {'origin': 'bach', 'origin_path': 'system/skills/workflows/bugfix-protokoll.md', 'origin_version': '1.0.0', 'origin_repo': 'github.com/ellmos-ai/bach', 'last_sync_from_origin': '2026-03-12', 'last_sync_to_origin': None, 'local_changes_since_sync': True}
---
+ <img src="banner.png" width="100%" alt="bugfix-protocol banner">
+
> **Русский** — Официальная русская версия `bugfix-protocol`.
# Bugfix Protocol: Систематическая 6-фазная отладка
Структурированный подход к ошибкам — от анализа симптомов до проверки.
Предотвращает бесцельный метод проб и ошибок и гарантирует устойчивость исправлений.
---
## Обзор и цель
| Фаза | Название | Цель | Макс. время |
|------|----------|------|-------------|
| 1 | Быстрые проверки | Исключить очевидные причины | 2 мин |
| 2 | Диагностика | Локализовать первопричину | 10 мин |
| 3 | Изолированный тест | Сделать баг воспроизводимым | 5 мин |
| 4 | Исправление | Минимальная коррекция | 10 мин |
| 5 | Верификация | Проверить исправление + проверить побочные эффекты | 5 мин |
| 6 | Документирование | Сохранить знания | 2 мин |
**Правило 20 минут:** Если через 20 минут прогресс отсутствует, измените подход или обратитесь за помощью.
---
## Фаза 1: Быстрые проверки (2 мин)
Прежде чем погружаться глубоко — проверьте наиболее частые причины:
### Чек-лист
- [ ] **Синтаксическая ошибка?** Внимательно прочитайте сообщение об ошибке, проверьте строку
- [ ] **Ошибка импорта?** Модуль установлен? Имя правильное? Циклический импорт?
- [ ] **Опечатка?** Правильно ли указаны имена переменных/функций?
- [ ] **Неверный тип данных?** String вместо int? None там, где ожидался объект?
- [ ] **Устаревший кэш?** Удалите `__pycache__`, перезапустите
- [ ] **Неверное окружение?** Активно ли правильное venv? Правильная ли версия Python?
- [ ] **Кодировка?** UTF-8 против cp1252 (классический Windows)
### Быстрые действия
```bash
# Очистить кэш
find . -name "__pycache__" -type d -exec rm -rf {} + 2>&1
find . -name "*.pyc" -delete 2>&1
# Проверить импорты
python -c "import modulename"
# Проверить синтаксис
python -m py_compile file.py
```
---
## Фаза 2: Диагностика (10 мин)
### Стратегия: Снаружи внутрь (Outside-In)
1. **Анализ сообщения об ошибке** — Читайте трассировку стека (traceback) снизу вверх
2. **Проверка последних изменений** — `git diff`, `git log --oneline -10`
3. **Использование диагностических утилит** — Используйте специфичные для проекта инструменты
### Диагностические утилиты (Примеры)
В зависимости от проекта могут пригодиться специализированные скрипты диагностики:
| Инструмент | Назначение |
|------------|------------|
| `import_diagnose.py` | Анализ проблем с импортом |
| `method_analyzer.py` | Проверка сигнатур методов |
| `env_checker.py` | Валидация переменных окружения/путей |
> **Примечание:** Создавайте специфичные для проекта диагностические утилиты или используйте существующие.
> Важен систематический подход, а не конкретный инструмент.
### Методы отладки
```python
# 1. Отладка через print (быстро, но эффективно)
print(f"DEBUG: variable={variable!r}, type={type(variable)}")
# 2. Точка останова (интерактивно)
breakpoint() # Python 3.7+
# 3. Расширенный traceback
import traceback
traceback.print_exc()
# 4. Логирование вместо print
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug(f"State: {state!r}")
```
---
## Фаза 3: Изолированный тест (5 мин)
### Минимальный воспроизводимый пример (MRE)
Цель: Воспроизвести баг с минимальным количеством кода.
```python
# test_bug.py — Минимальный тест воспроизведения
"""
Bug: [Краткое описание]
Expected: [Что должно произойти]
Actual: [Что происходит вместо этого]
"""
# Минимальная настройка
# ... только самое необходимое
# Триггер бага
# ... точный код, вызывающий баг
# Ожидаемый результат
# assert result == expected, f"Got {result}"
```
### Стратегии изоляции
1. **Новый файл:** Воспроизведите баг в отдельном файле
2. **Удаление зависимостей:** По одной, пока баг не исчезнет
3. **Бинарный поиск:** Разделите блок кода пополам, проверьте, в какой половине баг
4. **Git bisect:** `git bisect start`, `git bisect bad`, `git bisect good <commit>`
---
## Фаза 4: Исправление (10 мин)
### Принципы
1. **Минимальность:** Меняйте как можно меньше
2. **Понимание:** Никогда не чините вслепую — поймите, ПОЧЕМУ код сломан
3. **Одна задача:** Одно исправление на коммит, не чините несколько проблем одновременно
4. **Обратная совместимость:** Не ломайте существующий функционал
### Шаблоны исправлений
```python
# ПЛОХО: Лечение симптома
try:
result = broken_function()
except: # Подавление всех исключений
result = default_value
# ХОРОШО: Исправление первопричины
def broken_function():
if input_data is None: # Настоящая причина: отсутствие проверки на None
return default_value
return process(input_data)
```
### Распространенные категории исправлений
| Категория | Типичное исправление |
|-----------|----------------------|
| None/Null | Защитное условие: `if x is None: return default` |
| Ошибка индекса | Проверка границ: `if i < len(lst)` |
| Ошибка типа | Явное приведение: `str(x)`, `int(x)` |
| Ошибка импорта | Исправить путь, установить пакет |
| Кодировка | Явно указать UTF-8: `encoding='utf-8'` |
| Состояние гонки | Блокировка/Мьютекс или изменение порядка |
| Баг состояния | Проверить инициализацию, добавить сброс |
---
## Фаза 5: Верификация (5 мин)
### Чек-лист
- [ ] **Баг исправлен:** Исходная проблема больше не возникает
- [ ] **MRE проходит:** Изолированный тест выполняется успешно
- [ ] **Нет регрессий:** Существующие тесты по-прежнему проходят
- [ ] **Граничные случаи:** Проверены пустой ввод, None, большие объемы данных
- [ ] **Утилиты проекта:** Проверьте директорию утилит проекта на наличие тестов/валидаторов
### Команды тестирования
```bash
# Юнит-тесты
python -m pytest tests/ -v
# Только затронутые тесты
python -m pytest tests/test_module.py -v -k "test_name"
# Проверка типов
python -m mypy file.py
# Линтер
python -m flake8 file.py
```
---
## Фаза 6: Документирование (2 мин)
### Шаблон отчета об ошибке
```markdown
## Bug Report: [Краткий заголовок]
**Date:** YYYY-MM-DD
**Severity:** critical / high / medium / low
**Component:** [Модуль/Файл]
### Symptom
[Что видит пользователь / сообщение об ошибке]
### Root Cause
[Техническая первопричина]
### Fix
[Что было изменено + почему]
### Affected Files
- `file1.py` — [Изменение]
- `file2.py` — [Изменение]
### Prevention
[Как предотвратить подобный баг в будущем?]
```
### Формат сообщения коммита
```
fix: [Краткое описание исправления]
Cause: [Первопричина в одном предложении]
Fix: [Что было изменено]
Test: [Как проверялось]
```
---
## PyQt6 / Отладка GUI — Распространенные ловушки
> Этот раздел актуален для десктопных GUI-проектов на PyQt6/PySide6.
### Топ-5 ловушек PyQt6
| Ловушка | Проблема | Решение |
|---------|----------|---------|
| **Отключение Signal-Slot** | Сигнал подключен, но обработчик не выполняется | `print` в обработчике, проверка сигнатуры |
| **Потокобезопасность** | Обновление GUI из рабочего потока | `QMetaObject.invokeMethod` или использование сигнала |
| **Каскад макета (Layout)** | Виджет не виден / смещен | `widget.show()`, проверка иерархии layout |
| **Блокировка цикла событий** | Зависание GUI | Перенести долгие операции в QThread |
| **Сборка мусора** | Виджет внезапно исчезает | Сохранять ссылку как `self.widget` |
### Вспомогательные функции отладки PyQt6
```python
# Дамп иерархии виджетов
def dump_widget_tree(widget, indent=0):
print(" " * indent + f"{widget.__class__.__name__}: {widget.objectName()}")
for child in widget.findChildren(QWidget):
if child.parent() == widget:
dump_widget_tree(child, indent + 2)
# Отладка сигналов
from PyQt6.QtCore import QObject
original_connect = QObject.connect
def debug_connect(self, *args, **kwargs):
print(f"CONNECT: {self.__class__.__name__} -> {args}")
return original_connect(self, *args, **kwargs)
```
---
## Быстрая справка
```
ОШИБКА НАЙДЕНА?
|
v
[Фаза 1: Быстрые проверки] ───── Очевидно? -> ИСПРАВИТЬ
|
v
[Фаза 2: Диагностика] ────────── Причина ясна? -> Фаза 4
|
v
[Фаза 3: Изолированный тест] ── Воспроизводимо? -> Фаза 4
| |
| Не воспроизводится?
| |
| Добавить логирование,
| ждать повторения
v
[Фаза 4: Исправление] ────────── Минимальное + понятное
|
v
[Фаза 5: Верификация] ───────── Тесты пройдены? -> Фаза 6
| |
| Тесты провалены? -> Назад к Фазе 4
v
[Фаза 6: Документирование] ──── Отчет об ошибке + коммит
```
### Правило 20 минут
Если вы застряли через 20 минут:
1. **Измените подход** — Попробуйте другой метод отладки
2. **Метод утенка** — Объясните проблему вслух (или запишите ее)
3. **Сделайте перерыв** — Отодите на 5 минут, вернитесь со свежим взглядом
4. **Обратитесь за помощью** — Спросите коллегу, проверьте Stack Overflow или документацию
5. **Сброс** — `git stash`, начните с чистого листа