← Назад к вопросам

Умеешь ли искать ошибки в программе

2.7 Senior🔥 151 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Debugging: искусство находить ошибки

Да, это один из моих ключевых навыков после 10 лет опыта.

Методология Debug-а

1. Понять проблему

  • Что сломалось?
  • Когда началось?
  • Как воспроизвести?

2. Выдвинуть гипотезы

  • Это может быть A, B или C
  • Какая вероятность каждого?

3. Тестировать гипотезы

  • Проверить логи
  • Запустить в debugger
  • Добавить временный logging

4. Найти root cause

  • Это не症状, это причина

5. Fix и verify

  • Исправить
  • Убедиться, что работает
  • Убедиться, что не сломал ничего другого

Инструменты, которые я использую

1. Логирование (первое, что проверяю)

logger.error("Payment failed", exception);

// Логи показывают стек трейс

awk 'grep error /var/log/app.log | tail -100'

// 100 последних ошибок

2. IDE Debugger (IntelliJ IDEA)

  • Breakpoint на строку кода
  • Пошаговое выполнение (Step Into, Step Over)
  • Inspect variables
  • Evaluate expressions
  • Watch expressions

3. Database queries

SELECT * FROM transactions WHERE user_id = 123 ORDER BY created_at DESC LIMIT 10;

-- Проверяю: правильно ли данные в БД?

4. Network requests (curl, Postman)

curl -v http://api/users/123

// -v показывает headers, status, response body

5. System monitoring

jps -l // список Java процессов jstack <pid> // все потоки и их stack traces jmap -heap <pid> // memory usage

6. Production tools

  • Datadog / New Relic (performance monitoring)
  • ELK stack (логи)
  • Prometheus + Grafana (метрики)

Пример 1: API возвращает 500 ошибку

Шаг 1: Логи

ERROR [2024-03-20 14:32:10] NullPointerException at PaymentService:145
  at com.example.payment.PaymentService.processPayment(...)
  at com.example.controller.PaymentController.pay(...)

Шаг 2: Посмотри код на строке 145

BigDecimal amount = order.getDiscount(); // NPE здесь?
amount = amount.add(tax); // Если amount = null

Шаг 3: Проверь данные в БД

SELECT * FROM orders WHERE id = 12345;
-- discount column NULL в некоторых заказах

Шаг 4: Fix

BigDecimal discount = order.getDiscount() != null ? 
    order.getDiscount() : BigDecimal.ZERO;
BigDecimal amount = discount.add(tax);

Шаг 5: Verify

grep "NullPointerException" /var/log/app.log | wc -l
# Было: 150 ошибок в день
# Теперь: 0

Пример 2: Запрос выполняется 30 секунд

Шаг 1: Профилируй

API /api/users/search
Total time: 30 000ms
  Database query: 29 500ms (98%)
  JSON serialization: 400ms
  Other: 100ms

Проблема в БД!

Шаг 2: EXPLAIN ANALYZE

EXPLAIN ANALYZE
SELECT * FROM users WHERE email LIKE 'john%' OR phone LIKE 'john%';

-- Результат: Seq Scan, 500M rows
-- Нет индекса!

Шаг 3: Fix

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_phone ON users(phone);

Шаг 4: Verify

API /api/users/search: 30 000ms → 150ms (200x быстрее)

Пример 3: Memory утечка (heap grows)

Шаг 1: Мониторинг

Day 1: Heap 500MB
Day 2: Heap 600MB
Day 3: Heap 700MB
Day 7: Heap 3000MB → OutOfMemoryError

Шаг 2: Dump heap

jmap -dump:live,format=b,file=heap.bin <pid>
jhat -J-Xmx4g heap.bin

Шаг 3: Analyze

Top objects:
1. String[]: 1000000 instances, 1.5GB memory
2. All same string: "cached_item"

Шаг 4: Find in code

private static List<String> cache = new ArrayList<>();

public void addToCache(String item) {
    cache.add(item); // Добавляем, но никогда не удаляем!
}

Шаг 5: Fix

private static LoadingCache<String, String> cache =
    CacheBuilder.newBuilder()
        .maximumSize(10_000)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build(...);

Пример 4: Race condition

Симптом: Случайные ошибки при высокой нагрузке

Шаг 1: Воспроизведи stress test

ab -n 10000 -c 100 http://localhost:8080/api/transfer

# Результаты нестабильны
# Иногда: 200 OK
# Иногда: 500 Error

Шаг 2: Добавь debugging

public void transfer(Long fromId, Long toId, BigDecimal amount) {
    logger.info("Transfer start: from={}, to={}, amount={}", fromId, toId, amount);
    
    Account from = getAccount(fromId);
    logger.info("From balance: {}", from.getBalance());
    
    Account to = getAccount(toId);
    
    // Здесь может быть race condition
    Thread.sleep(100); // Искусственная задержка для воспроизведения
    
    from.setBalance(from.getBalance().subtract(amount));
    save(from);
}

Шаг 3: Видишь проблему? Два потока читают одинаковый баланс, оба отнимают, один перезаписывает.

Шаг 4: Fix

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transfer(...) {
    // Теперь thread-safe
}

Debugging Tips & Tricks

1. Bisect метод (binary search для bug-а)

Если не знаешь где баг:

  • Comment out половину кода
  • Работает ли?
  • Если да: баг во второй половине
  • Если нет: баг в первой половине
  • Повтори до конца

2. Add logging strategically

logger.debug("Starting processing");
logger.debug("Step 1 done, result: {}", result);
logger.debug("Step 2 done, result: {}", result);
logger.error("Processing failed", exception);

3. Use assertions в development

assert amount.compareTo(BigDecimal.ZERO) > 0 : "Amount must be positive";
assert account != null : "Account not found";

4. Unit tests для reproduction

@Test
void testNegativeAmount() {
    // Reproduce bug
    assertThrows(IllegalArgumentException.class, () ->
        service.transfer(account1, account2, BigDecimal.valueOf(-100))
    );
}

Мой процесс

  1. Сохраняю спокойствие — паника не помогает
  2. Понимаю масштаб — это production? critical?
  3. Собираю информацию — логи, метрики, воспроизведение
  4. Формирую гипотезы — обычно это 3-5 вариантов
  5. Тестирую — один за другим
  6. Документирую — для future reference
  7. Делаю post-mortem — как предотвратить в future

Главный вывод

Debugging это skill, которым нужно владеть как Senior Developer. Это умение отличает хорошего разработчика от отличного.

Умеешь ли искать ошибки в программе | PrepBro