| name | debugging |
| description | Structured debugging methodology for systematic problem-solving. Use when user asks to "отладь", "найди баг", "почему не работает", "debug this", "fix bug", "why is this failing", or when code has errors, unexpected behavior, or needs troubleshooting. |
Debugging Skill — структурированная отладка
Систематический подход к поиску и устранению багов через воспроизведение, анализ и проверку гипотез.
Когда использовать
- Код не работает как ожидается
- Есть ошибка или exception
- Непредсказуемое поведение
- Performance проблемы
- Регрессия после изменений
Методология Debugging
Debugging следует научному методу:
- Reproduce — воспроизвести проблему
- Isolate — локализовать источник
- Hypothesize — сформулировать гипотезы
- Test — проверить гипотезы
- Fix — исправить и проверить
- Prevent — предотвратить повторение
Шаг 1: Reproduce (Воспроизведение)
Цель: Получить стабильное воспроизведение бага
Что нужно выяснить
✅ Когда проявляется?
- При каких действиях?
- В какой среде (dev/staging/prod)?
- На каких данных?
- Всегда или иногда?
✅ Как проявляется?
- Что именно идёт не так?
- Какая ошибка выдаётся?
- Что ожидалось vs что получили?
✅ Можно ли воспроизвести?
- Шаги для воспроизведения
- Minimal reproducible example
- Условия окружения
Процесс воспроизведения
## Reproduction Steps
1. [Шаг 1]
2. [Шаг 2]
3. [Шаг 3]
**Expected:** [Что должно произойти]
**Actual:** [Что происходит]
**Environment:**
- OS: [macOS/Linux/Windows]
- Version: [версия приложения]
- Dependencies: [ключевые зависимости]
**Error message:**
[Точный текст ошибки]
**Frequency:** [Always / Sometimes / Rare]
Если не воспроизводится
🔍 Debugging the bug:
- Проверь логи — есть ли дополнительная информация?
- Спроси у пользователя точные шаги
- Проверь различия в окружениях
- Race condition или timing issue?
Шаг 2: Isolate (Локализация)
Цель: Сузить область поиска проблемы
Техники локализации
Binary Search (Деление пополам)
Убирай части кода пока проблема не исчезнет:
[Весь код] → баг есть
[Первая половина] → баг есть?
Да → проблема здесь
Нет → проблема во второй половине
Git Bisect (Поиск коммита)
Если раньше работало:
git bisect start
git bisect bad
git bisect good <hash>
Isolation через тесты
Создай минимальный тест который показывает проблему:
test('minimal reproduction', () => {
const result = buggyFunction(input);
expect(result).toBe(expected);
});
Вопросы для локализации
- В каком файле проблема?
- В какой функции?
- На какой строке возникает ошибка?
- Какие данные вызывают проблему?
- Что было изменено недавно?
Вывод локализации:
## Isolation
**Проблема локализована:**
- Файл: `src/services/payment.ts`
- Функция: `processPayment()`
- Строка: 67
- Условие: Происходит когда `amount > 1000`
**Stack trace:**
Error: Payment processing failed
at processPayment (payment.ts:67)
at handleCheckout (checkout.ts:42)
at onClick (Button.tsx:15)
Шаг 3: Hypothesize (Гипотезы)
Цель: Сформулировать возможные причины
Типичные категории багов
🧮 Logic Errors
- Off-by-one ошибки
- Неправильные условия (if/else)
- Неверная математика
- Edge cases не обработаны
💾 State Issues
- Race conditions
- Stale state
- Uninitialized variables
- Memory leaks
🔄 Async Problems
- Promise not awaited
- Callback hell
- Race conditions
- Timing issues
📊 Data Issues
- Null/undefined не проверены
- Неверный тип данных
- Проблемы сериализации
- Кодировка (encoding)
🔌 Integration Issues
- API изменился
- Network timeout
- Dependency версия
- Environment variables
Формулирование гипотез
Для каждой гипотезы:
### Hypothesis #1: [Название]
**Причина:**
[Что могло вызвать проблему]
**Как проверить:**
[Как подтвердить или опровергнуть]
**Likelihood:** [High / Medium / Low]
Пример:
### Hypothesis #1: Race condition в async операции
**Причина:**
Функция `updateUser` вызывается дважды параллельно,
вторая перезаписывает результат первой
**Как проверить:**
1. Добавить логи до и после `await updateUser()`
2. Проверить что функция выполняется синхронно
3. Добавить debounce/throttle
**Likelihood:** High (90%)
Шаг 4: Test (Проверка гипотез)
Цель: Подтвердить или опровергнуть каждую гипотезу
Методы проверки
Logging & Debugging
console.log('[DEBUG] Input:', input);
console.log('[DEBUG] State before:', state);
console.log('[DEBUG] Result:', result);
debugger;
Assertions
console.assert(user !== null, 'User should not be null');
console.assert(amount > 0, 'Amount should be positive');
Experiments
Измени код чтобы проверить теорию:
const result = await processPayment(data);
const result = processPaymentSync(data);
Tests
Напиши тест который проверяет гипотезу:
test('race condition when called twice', async () => {
const promise1 = updateUser(userId, data1);
const promise2 = updateUser(userId, data2);
const results = await Promise.all([promise1, promise2]);
expect(results).toContainBoth(data1, data2);
});
Проверка гипотез по порядку
Начни с наиболее вероятной (High likelihood):
## Testing Hypotheses
### ✅ Hypothesis #1: CONFIRMED
- Added logs → второй вызов перезаписывает первый
- Timing показывает overlap в выполнении
- **Root cause found!**
### ⏸️ Hypothesis #2: NOT TESTED
- Не нужно — уже нашли причину
### ❌ Hypothesis #3: REJECTED
- Добавил проверку на null → ошибка осталась
Шаг 5: Fix (Исправление)
Цель: Устранить root cause, не симптомы
Принципы исправления
1. Fix the cause, not the symptom
if (result === undefined) {
result = defaultValue;
}
const result = await calculateResult(data);
2. Minimal change
const result = await asyncOperation();
3. Make it obvious
const x = data?.value || 0;
if (isProcessing) return;
isProcessing = true;
try {
await processPayment(data);
} finally {
isProcessing = false;
}
Процесс исправления
## Fix Implementation
**Root Cause:**
Race condition — функция вызывается параллельно
**Solution:**
Добавить механизм блокировки (mutex/semaphore)
**Changes:**
```diff
+ let isProcessing = false;
async function processPayment(data) {
+ if (isProcessing) {
+ throw new Error('Payment already processing');
+ }
+
+ isProcessing = true;
+ try {
const result = await api.charge(data);
return result;
+ } finally {
+ isProcessing = false;
+ }
}
Testing the fix:
---
## Шаг 6: Prevent (Предотвращение)
**Цель:** Избежать повторения проблемы
### Добавить защиту
#### Validation
```javascript
function processPayment(amount, user) {
// Add validation to prevent invalid input
if (!user) throw new Error('User is required');
if (amount <= 0) throw new Error('Amount must be positive');
if (amount > MAX_AMOUNT) throw new Error('Amount too large');
// ... process payment
}
Tests
test('should not allow concurrent payment processing', async () => {
const promise1 = processPayment(data);
const promise2 = processPayment(data);
await expect(promise2).rejects.toThrow('already processing');
});
test('should handle zero amount', () => {
expect(() => processPayment(0, user)).toThrow();
});
Documentation
async function processPayment(data) {
}
Monitoring
logger.info('Payment processing started', {
userId,
amount,
timestamp: Date.now()
});
try {
const result = await processPayment(data);
logger.info('Payment processed successfully', { result });
} catch (error) {
logger.error('Payment processing failed', { error, data });
throw error;
}
Debugging Checklist
Quick Checklist для любого бага
Debugging Tools
Логирование
const debug = require('debug')('app:payment');
debug('Processing payment', { userId, amount });
debug('API response', { status, data });
Breakpoints
debugger;
if (amount > 1000) {
debugger;
}
Assertions
const assert = require('assert');
assert(user !== null, 'User should exist');
assert(amount > 0, 'Amount should be positive');
Stack trace
console.trace('How did we get here?');
const stack = new Error().stack;
console.log(stack);
Network debugging
curl -v https://api.example.com/endpoint
nslookup api.example.com
ping api.example.com
Common Bug Patterns
1. Off-by-one
for (let i = 0; i <= array.length; i++) {
}
for (let i = 0; i < array.length; i++) {
}
2. Async не awaited
async function loadData() {
const data = fetchData();
console.log(data);
}
async function loadData() {
const data = await fetchData();
console.log(data);
}
3. Mutating state
function addItem(array, item) {
array.push(item);
return array;
}
function addItem(array, item) {
return [...array, item];
}
4. Null/undefined не проверены
function getName(user) {
return user.name.toUpperCase();
}
function getName(user) {
return user?.name?.toUpperCase() ?? 'Unknown';
}
5. Race condition
let count = 0;
async function increment() {
const current = count;
await delay(100);
count = current + 1;
}
let count = 0;
const lock = new AsyncLock();
async function increment() {
await lock.acquire('count', async () => {
count = count + 1;
});
}
Best Practices
DO ✅
-
Read error messages carefully
- Полный текст ошибки
- Stack trace до конца
- Warnings тоже важны
-
Use version control
git bisect для поиска коммита
git diff для изменений
git blame для истории
-
Write tests first
- Failing test = reproduction
- Passing test = fix confirmed
-
Document findings
- Причина бага
- Решение
- Почему именно так
DON'T ❌
-
Don't guess randomly
- Не меняй код наугад
- Сформулируй гипотезу сначала
-
Don't fix symptoms
- Найди root cause
- Исправь источник
-
Don't skip tests
- Regression test обязателен
- Иначе баг вернётся
-
Don't leave debug code
- Убери console.log
- Убери debugger
- Убери commented code
Шаблоны запросов
Общий debugging:
Отладь этот код, он не работает как ожидалось
С ошибкой:
Почему возникает ошибка "Cannot read property 'name' of undefined"?
Performance:
Найди почему эта функция работает медленно
Поведение:
Почему кнопка не срабатывает при клике?
Интеграция с другими skills
- Перед debugging → используй research для понимания кода
- После debugging → используй code-review для проверки fix
- Если баг сложный → используй refactoring для улучшения структуры
- Результаты debugging → документируй в комментариях или Todoist