Вы запускаете код, всё работает идеально в вашей IDE, но стоит передать реальные данные от пользователя или развернуть на сервере — и экран заливает красным. Сервер возвращает 500, программа падает. Первая мысль: «Что я сделал не так?» Спокойно. Это не провал, а нормальный этап роста любого разработчика. В реальном мире данные никогда не бывают идеальными: пользователи вводят буквы вместо цифр, файлы могут отсутствовать, сети — отваливаться, а память — иссякать.
За годы работы тимлидом я провёл через эту боль десятки джуниоров и точно знаю: обработка ошибок — не магия, а навык, который превращает хрупкий код в надёжный. В этой статье мы разберём всё по косточкам: от разницы между Error и Exception до паттернов, которые я требую на код-ревью. Вы не просто выучите термины, а поймёте, как писать код, который умеет «умирать красиво» и даже помогает пользователю исправить проблему.
Вы узнаете:
- Чем отличается ошибка (Error) от исключения (Exception) и почему это важно.
- Как правильно использовать конструкции
try-catch,if-elseиfinally. - Какие типы исключений бывают в Python, JavaScript и Java (с примерами).
- Как создавать свои собственные исключения.
- Типичные ошибки новичков, которые приводят к утечкам памяти и «спагетти-коду».
- Чек-лист и пошаговый алгоритм написания надежного кода.
Коротко: эта статья — ваш щит от хаоса. Поехали.
Почему код ломается? Фундамент проблемы
Перед тем как писать try-catch, нужно понять, откуда вообще берутся ошибки. В программировании есть два основных источника проблем, и я постоянно напоминаю о них на код-ревью.
- Проблемы с данными (Input Data).
Пользователь ввёлabcв поле, где должно быть число. Файл, который вы пытаетесь открыть, не существует. Ссылка на API вернула пустой объект вместо списка.
Это самый частый случай. Ваш код логически правильный, но входные данные не соответствуют ожиданиям. В моей практике около 70% багов на проде возникают именно из-за невалидированных данных. - Проблемы с окружением (System Environment).
Недостаточно памяти (Memory Limit). Сеть не отвечает (Connection Timeout). Операционная система заблокировала доступ к файлу.
Это внешние факторы. Вы не можете их контролировать, но должны уметь предсказывать и обрабатывать. Например, в микросервисной архитектуре таймауты сети — это не исключение, а штатная ситуация, под которую мы закладываем retry-логику и circuit breaker.
Важный нюанс: ошибки — это не всегда «сбой». В современной разработке (особенно в микросервисах) ошибки часто являются частью бизнес-логики. Если пользователь попытался купить товар, который уже закончился, сервер должен вернуть ошибку 404 Not Found или 400 Bad Request, а не падать с критическим сбоем. Мы проектируем контракты API так, чтобы клиент мог адекватно отреагировать, а не получал молчаливый 500.
Аналогия из жизни:
Представьте, что вы — водитель. Дорога (ваш код) идеальна. Но вдруг на дороге появляется камень (ошибка данных) или у машины пробило колесо (ошибка окружения).
• Новичок: Врезается в камень, машина ломается, он кричит и бросает руль.
• Профи: Видит камень заранее, тормозит, объезжает его или, если объехать нельзя, аккуратно останавливается, проверяет, что случилось, и продолжает путь (или меняет маршрут).
Обработка исключений — это именно умение «тормозить и объезжать» в коде.
Ошибка (Error) vs Исключение (Exception): В чем разница?
В школьных курсах и в интернете эти слова часто используют как синонимы. Но в профессиональной среде (и в документации языков) между ними есть чёткая граница. Когда я обучаю джуниоров, то прошу запомнить: исключение можно и нужно обрабатывать, а ошибка — сигнал к тому, что программу пора перезапускать или чинить код.
1. Исключение (Exception)
Это событие, которое происходит во время выполнения программы, когда что-то идёт не так, как ожидалось, но программа может продолжать работу, если мы это обработаем.
- Примеры:
FileNotFoundError,ZeroDivisionError,IndexError,NullPointerException. - Суть: Это «мягкая» проблема. Код знает, что может случиться, и готов к этому.
- Действие: Мы «ловим» исключение, делаем что-то полезное (показываем сообщение, перезагружаем файл) и продолжаем работу.
2. Ошибка (Error)
Это критическая проблема, которая обычно указывает на непоправимый сбой в самой программе или в окружении. После такой ошибки программа, как правило, не может продолжать работу.
- Примеры:
SyntaxError(синтаксическая ошибка в коде),MemoryError(исчерпана память),StackOverflowError(слишком глубокий рекурсивный вызов). - Суть: Это «жёсткая» проблема. Код не может справиться с этим в рамках своей логики.
- Действие: Обычно программа просто падает (крашится). Обработка таких ошибок часто требует перезапуска приложения или исправления кода разработчиком.
Таблица сравнения
| Характеристика | Исключение (Exception) | Ошибка (Error) |
|---|---|---|
| Причина | Неверные данные, внешние факторы | Проблемы в коде, ресурсы системы |
| Возможность восстановления | Высокая (можно продолжить) | Низкая (часто требует перезапуска) |
| Пример | FileNotFoundError |
SyntaxError, MemoryError |
| Стратегия | try-catch с логикой восстановления |
Перезапуск, отладка, исправление кода |
| Контроль | Контролируется программистом | Часто неконтролируемо |
Золотое правило новичка:
В 95% случаев вы будете работать с исключениями. Ваша задача — научиться их ловить и обрабатывать. Ошибки (Error) обычно решаются не в коде, а в процессе разработки (написание правильного кода, выделение памяти).
Как работает механизм исключений: Конструкция Try-Catch
В большинстве языков программирования (Python, Java, C#, JavaScript) используется стандартная конструкция для обработки ошибок. Она называется try-catch (или try-except в Python). Я часто вижу, как новички механически оборачивают весь код в try, не понимая внутренней логики. Давайте разложим по полочкам.
Алгоритм работы
try(Попробуй): В этот блок помещается код, который может вызвать ошибку. Программа пытается его выполнить.catch/except(Поймай): Если в блокеtryпроизошла ошибка, программа немедленно перескакивает в этот блок. Код после ошибки вtryне выполняется.- Обработка: Внутри
catchвы пишете код, который должен выполнить действие при ошибке (показать сообщение, сохранить в лог, вернуть дефолтное значение). finally(В любом случае): Этот блок выполняется всегда, независимо от того, была ошибка или нет. Часто используется для очистки ресурсов (закрытие файлов, отключение соединений).
Пример на Python
try:
# Код, который может упасть
result = 10 / 0
print("Эта строка не выполнится")
except ZeroDivisionError:
# Обработка конкретной ошибки
print("Ошибка: Деление на ноль!")
except Exception as e:
# Обработка любой другой ошибки
print(f"Неизвестная ошибка: {e}")
finally:
# Выполнится всегда
print("Блок finally выполнен")
Пример на JavaScript
В JavaScript конструкция называется try-catch, но синтаксис похож.
try {
// Код, который может упасть
let data = JSON.parse("invalid json");
console.log("Эта строка не выполнится");
} catch (error) {
// Обработка ошибки
console.error("Ошибка парсинга JSON:", error.message);
} finally {
// Выполнится всегда
console.log("Блок finally выполнен");
}
Ключевые моменты для запоминания
- Перескок кода: Если в
tryпроизошла ошибка, весь оставшийся код в этом блоке не выполняется. Программа сразу идёт вcatch. - Специфичность: Всегда старайтесь ловить конкретные типы ошибок (
FileNotFoundError,ZeroDivisionError). Ловить «всё подряд» (catch Exception) — это плохая практика, так как вы можете скрыть критические проблемы. Я на ревью сразу отмечаю такие места и прошу переписать. - Множественные catch: Вы можете иметь несколько блоков
catch(илиexcept), каждый для своего типа ошибки. Это позволяет писать разную логику для разных проблем.
Типы исключений: Что ловить и как?
В каждом языке есть стандартный набор исключений. Новичок часто путается: «Какой тип ошибки здесь возник?». Давайте разберём самые популярные типы в Python, JavaScript и Java — с которыми вы будете сталкиваться ежедневно.
1. Python (Python 3.x)
Python очень гибок и имеет понятные имена ошибок. Я рекомендую всегда держать под рукой эту таблицу, пока не запомните.
| Тип исключения | Описание | Когда возникает |
|---|---|---|
ZeroDivisionError |
Деление на ноль | 10 / 0 |
FileNotFoundError |
Файл не найден | open("no_file.txt") |
PermissionError |
Нет прав доступа | Попытка открыть файл без прав |
IndexError |
Неверный индекс | list[10] в списке из 5 элементов |
KeyError |
Неверный ключ | dict["missing_key"] |
TypeError |
Неверный тип | "5" + 5 (строка + число) |
ValueError |
Неверное значение | int("abc") (не число в строке) |
AttributeError |
Неверный атрибут | obj.nonexistent_method() |
ImportError |
Неверный импорт | import nonexistent_module |
MemoryError |
Исчерпана память | (Ошибка, не исключение для обработки) |
Пример обработки нескольких ошибок:
try:
number = int(input("Введите число: "))
result = 100 / number
except ValueError:
print("Вы ввели не число!")
except ZeroDivisionError:
print("На ноль делить нельзя!")
except Exception as e:
print(f"Неизвестная ошибка: {e}")
2. JavaScript (ES6+)
В JS ошибки часто называют Error, но есть и специфические. Важно понимать, что try-catch здесь лучше использовать для непредсказуемых ситуаций (сети, файлы), а для проверки данных — обычные if.
| Тип исключения | Описание | Когда возникает |
|---|---|---|
ReferenceError |
Неизвестная переменная | Использование переменной, которой нет |
TypeError |
Неверный тип | null.method(), 5 + "text" |
SyntaxError |
Синтаксическая ошибка | Неправильный код (часто не ловится в try) |
RangeError |
Неверный диапазон | new Array(-1) |
URIError |
Неверный URI | decodeURIComponent("%") |
EvalError |
Ошибка в eval() |
(Редко используется) |
Важно: В JS часто используется конструкция if-else для проверки данных, а не try-catch, если ошибка предсказуема. try-catch лучше использовать для непредсказуемых ситуаций (сети, файлы).
// Предсказуемая проверка
if (typeof input !== 'string') {
console.error("Ожидалась строка");
return;
}
// Непредсказуемая ситуация
try {
const response = await fetch(url);
const data = await response.json();
} catch (error) {
console.error("Сетевая ошибка:", error);
}
3. Java
Java — язык с жёсткой типизацией, где исключения разделены на Checked (обязательные) и Unchecked (необязательные). Это дисциплинирует, но поначалу вызывает много вопросов у новичков.
| Тип исключения | Описание | Когда возникает |
|---|---|---|
IOException |
Ошибка ввода-вывода | Чтение файла, сеть |
FileNotFoundException |
Файл не найден | new File("no.txt") |
NullPointerException |
Пустой объект | obj.method() когда obj == null |
ArrayIndexOutOfBoundsException |
Неверный индекс | arr[10] |
ArithmeticException |
Математическая ошибка | 10 / 0 |
ClassCastException |
Неверный тип класса | Преобразование типа |
SQLException |
Ошибка SQL | Работа с базой данных |
Checked Exception (Обязательные):
Если метод может вызвать IOException, вы обязаны либо обработать его (try-catch), либо заявить, что метод тоже вызывает эту ошибку (throws IOException). Это заставляет программиста думать об ошибках заранее. Я ценю такой подход, потому что он делает контракт метода явным.
Unchecked Exception (Необязательные):
NullPointerException, ArithmeticException. Их можно не обрабатывать, но программа упадёт, если не обработать. В реальных проектах мы стараемся избегать NullPointerException с помощью Optional или аннотаций вроде @NotNull.
Создание собственных исключений (Custom Exceptions)
Часто стандартные ошибки не описывают вашу бизнес-логику. Например, у вас есть система заказов. Если пользователь пытается заказать товар, который закончился, стандартная ошибка ValueError не подойдёт. Нужно сказать: «Товар закончился».
В этом случае мы создаём собственное исключение. Я обычно советую команде: не плодите исключения ради красоты, но если бизнес-правило нарушено и стандартный тип не передаёт смысла — делайте своё.
Зачем это нужно?
- Читаемость: Код становится понятнее.
except ProductOutOfStockErrorсразу говорит, что случилось. - Специализация: Вы можете добавить в исключение дополнительные данные (например, ID товара, остаток, время).
- Гибкость: Вы можете обрабатывать свои исключения отдельно от стандартных.
Пример на Python
class ProductOutOfStockError(Exception):
"""Товар закончился на складе"""
def __init__(self, product_id, available=0):
self.product_id = product_id
self.available = available
super().__init__(f"Товар {product_id} закончился. Доступно: {available}")
def buy_product(product_id, quantity):
stock = get_stock(product_id) # допустим, возвращает остаток
if stock < quantity:
raise ProductOutOfStockError(product_id, stock)
# ... логика покупки
try:
buy_product("ABC123", 5)
except ProductOutOfStockError as e:
print(f"Не удалось купить: {e}")
# Можно залогировать e.product_id и e.available
Пример на JavaScript
class ProductOutOfStockError extends Error {
constructor(productId, available = 0) {
super(`Товар ${productId} закончился. Доступно: ${available}`);
this.name = "ProductOutOfStockError";
this.productId = productId;
this.available = available;
}
}
function buyProduct(productId, quantity) {
const stock = getStock(productId);
if (stock < quantity) {
throw new ProductOutOfStockError(productId, stock);
}
// ... логика покупки
}
try {
buyProduct("ABC123", 5);
} catch (error) {
if (error instanceof ProductOutOfStockError) {
console.error(`Ошибка заказа: ${error.message}`);
// можно использовать error.productId
} else {
throw error; // пробрасываем неизвестную ошибку дальше
}
}
Совет от тимлида:
Не создавайте свои исключения для каждой мелочи. Используйте стандартные, если они подходят. Свои исключения создавайте только когда они описывают бизнес-логическую ситуацию, которую нельзя описать стандартными типами.
Паттерны обработки ошибок: Как писать правильно?
Новички часто пишут код с ошибками обработки. Давайте разберём правильные паттерны и типичные ошибки. Эти паттерны я выработал за годы ревью и теперь требую их соблюдения в команде.
Паттерн 1: «Предсказание вместо ловли» (Guard Clauses)
Если ошибку можно легко предсказать с помощью if, лучше использовать if, а не try-catch. try-catch — это «тяжёлая» операция, которая может замедлить код. Кроме того, guard clauses делают код линейным и читаемым.
❌ Плохо (использование try-catch для простой проверки):
try:
value = int(user_input)
except ValueError:
value = 0
✅ Хорошо (Guard Clause):
if user_input.isdigit():
value = int(user_input)
else:
value = 0
Почему лучше: Код читается быстрее, нет лишних блоков try, логика ясна. Я часто прошу джуниоров переписать именно так.
Паттерн 2: «Лови конкретно, обрабатывай разумно»
Никогда не используйте catch Exception (или except Exception) для всего, если вы не знаете, что делать.
❌ Плохо:
try:
process_data()
except Exception:
pass # или print("Ошибка")
✅ Хорошо:
try:
process_data()
except FileNotFoundError:
print("Файл не найден, используем значения по умолчанию")
load_defaults()
except ValueError as e:
print(f"Некорректные данные: {e}")
raise # Передаём ошибку выше, если не можем обработать
Почему лучше: Вы точно знаете, что произошло, и можете применить правильное решение. Общий except скрывает проблемы, которые потом всплывут в самый неподходящий момент.
Паттерн 3: «Не скрывать ошибки»
Одна из самых частых ошибок новичков — «заглушить» ошибку, чтобы программа не падала, но не показать пользователю, что случилось.
❌ Плохо:
try:
result = 10 / 0
except:
pass # программа продолжается, но result не определён
✅ Хорошо:
try:
result = 10 / 0
except ZeroDivisionError as e:
logger.error(f"Ошибка деления: {e}")
result = float('inf') # или другое безопасное значение
print("Результат не определён, показано бесконечное значение")
Почему лучше: Пользователь понимает проблему, разработчик видит ошибку в логах, программа не падает. В production-окружении логирование — это must have.
Паттерн 4: «Использование Finally для очистки»
Если вы открываете файл, соединение с сетью или блокируете ресурс, вы обязаны его закрыть. Это делается в блоке finally.
❌ Плохо:
file = open("data.txt")
try:
data = file.read()
except Exception as e:
print(e)
# файл может остаться открытым при ошибке
✅ Хорошо:
file = None
try:
file = open("data.txt")
data = file.read()
except Exception as e:
print(e)
finally:
if file:
file.close()
В Python есть ещё лучший способ: with open(...) as file: автоматически закрывает файл, даже если произошла ошибка. Но принцип finally важен для других языков (Java, C#) и для нетривиальных ресурсов вроде сетевых сокетов или блокировок.
Типичные ошибки новичков (и как их избежать)
Вот список проблем, с которыми я сталкивался как тимлид, когда обучал джуниоров. Почти все они всплывали на код-ревью, и я просил переделать.
1. «Спагетти-код» с обработкой ошибок
Новички пишут код так, что обработка ошибок разбросана по всей функции, и логика становится нечитаемой.
❌ Плохо:
def process_order(order_id):
try:
order = get_order(order_id)
except OrderNotFoundError:
print("Заказ не найден")
return
if order.status != "paid":
print("Заказ не оплачен")
return
try:
ship(order)
except ShippingError:
print("Ошибка доставки")
return
✅ Хорошо (использование раннего возврата и выделенных блоков):
def process_order(order_id):
order = get_order_or_raise(order_id) # бросает исключение, если нет
validate_order_paid(order) # бросает исключение, если не оплачен
ship_order_safely(order) # обрабатывает ShippingError внутри
Я всегда советую выносить проверки в отдельные методы — тогда основная логика остаётся чистой.
2. Ловить всё (catch Exception) и ничего не делать
Как уже говорилось выше, это скрывает проблемы. Если вы ловите всё, вы не знаете, что сломалось. Однажды джун поймал Exception при подключении к базе данных и просто вернул пустой список. Мы потратили полдня, чтобы понять, почему данные не обновляются.
❌ Плохо:
try:
data = fetch_from_api()
except Exception:
data = []
✅ Хорошо:
try:
data = fetch_from_api()
except ConnectionError as e:
logger.error(f"Нет сети: {e}")
data = get_cached_data() # fallback
except ValueError as e:
logger.error(f"Некорректный ответ API: {e}")
raise
3. Использование try-catch вместо if-else для проверки типов
Если вы знаете, что может быть ошибка, и её можно проверить простым условием, не используйте try-catch.
❌ Плохо:
try:
value = int(input_str)
except ValueError:
value = 0
✅ Хорошо:
if input_str.isdigit():
value = int(input_str)
else:
value = 0
4. Игнорирование finally
Ресурсы (файлы, соединения) остаются открытыми, что приводит к утечкам памяти. В одном проекте мы столкнулись с тем, что через неделю работы сервер падал из-за исчерпания файловых дескрипторов — разработчик забыл закрывать файлы в блоке finally.
❌ Плохо:
conn = open_db_connection()
try:
data = conn.query("SELECT ...")
except DBError as e:
print(e)
# соединение не закрыто при ошибке
✅ Хорошо:
conn = None
try:
conn = open_db_connection()
data = conn.query("SELECT ...")
except DBError as e:
logger.error(e)
finally:
if conn:
conn.close()
5. Вывод технических ошибок пользователю
Новички часто пишут print(error), и пользователь видит ZeroDivisionError: division by zero. Это страшно и непонятно. В веб-приложениях это может раскрыть внутреннюю структуру, что небезопасно.
❌ Плохо:
try:
result = 10 / 0
except ZeroDivisionError as e:
print(e) # пользователь видит "division by zero"
✅ Хорошо:
try:
result = 10 / 0
except ZeroDivisionError:
logger.exception("Ошибка деления") # полный трейс в лог
print("Извините, произошла ошибка. Попробуйте позже.")
Чек-лист: Как написать надежный код с обработкой ошибок
Перед тем как сдать код, проверьте его по этому чек-листу. Это поможет вам избежать 90% ошибок. Я сам использую похожий список, когда делаю ревью.
1. Проверка входных данных
- [ ] Все входные параметры проверены на
null,None, пустоту? - [ ] Типы данных проверены (строка, число, объект)?
- [ ] Значения в допустимом диапазоне (например, возраст > 0)?
2. Использование Try-Catch
- [ ] Используется ли
try-catchтолько для непредсказуемых ошибок (сеть, файлы)? - [ ] Ловятся ли конкретные типы ошибок (
FileNotFoundError,TypeError), а неException? - [ ] Есть ли обработка для каждого типа ошибки (разная логика)?
- [ ] Не скрывается ли ошибка (
passили пустойcatch)?
3. Обработка и Логи
- [ ] Пользователь получает понятное сообщение (не технический код ошибки)?
- [ ] Ошибка записана в лог (для разработчика)?
- [ ] Возвращается дефолтное значение или
null, чтобы программа не крашась?
4. Очистка ресурсов
- [ ] Все файлы закрыты (используется
finallyилиwith)? - [ ] Все сетевые соединения отключены?
- [ ] Блоки ресурсов (locks) освобождены?
5. Собственные исключения
- [ ] Если стандартные ошибки не подходят, создан ли свой класс исключения?
- [ ] Содержит ли исключение полезную информацию (ID, остаток, причина)?
6. Код
- [ ] Код не стал «спагетти» (слишком много
ifиtry)? - [ ] Логика обработки ошибок отделена от основной логики (можно использовать выделенные функции)?
Пошаговый алгоритм написания кода с обработкой ошибок
Если вы не знаете, как начать писать функцию с обработкой ошибок, следуйте этому алгоритму. Я даю его каждому новому разработчику в команде, и через пару недель они пишут надёжный код на автомате.
Шаг 1: Определите риски
Подумайте: «Что может сломаться в этом коде?».
- Файл не найден?
- Пользователь ввёл текст вместо числа?
- Сеть отвалилась?
Шаг 2: Добавьте проверки (Guard Clauses)
Для предсказуемых проблем (тип данных, пустота) используйте if-else.
if not user_input:
raise ValueError("Ввод не может быть пустым")
if not isinstance(user_input, str):
raise TypeError("Ожидалась строка")
Шаг 3: Напишите основной код в try
Поместите код, который может вызвать непредсказуемую ошибку, в блок try.
try:
data = read_file("config.yaml")
config = parse_yaml(data)
except FileNotFoundError:
# обработка
Шаг 4: Добавьте except для каждого риска
Для каждого риска из Шага 1 добавьте отдельный блок except.
except FileNotFoundError:
logger.warning("Конфиг не найден, используются значения по умолчанию")
config = default_config
except yaml.YAMLError as e:
logger.error(f"Ошибка парсинга YAML: {e}")
raise
Шаг 5: Добавьте finally для очистки
Если вы открывали файлы или соединения, закройте их в finally.
finally:
if file_handle:
file_handle.close()
Шаг 6: Тестируйте
Запустите код с «неправильными» данными. Убедитесь, что программа не падает, а выдаёт сообщение. В идеале — напишите модульные тесты на каждый сценарий ошибки.
FAQ: Часто задаваемые вопросы
1. Что делать, если я не знаю, какой тип ошибки возник?
Если вы не знаете тип ошибки, можно использовать общий блок except Exception as e. Но обязательно записать ошибку в лог (logger.error(e)) и показать пользователю общее сообщение («Произошла ошибка, попробуйте позже»). Не используйте pass (пустой блок). В production-среде я настраиваю глобальный обработчик, который логирует всё и возвращает 500 с минимальной информацией.
2. Можно ли использовать try-catch для проверки, что файл существует?
Нет. Лучше использовать if os.path.exists("file.txt"). try-catch — это тяжёлая операция, и её не стоит использовать для простых проверок. К тому же, между проверкой существования и открытием файла может пройти время, и файл может исчезнуть — но это уже другой уровень паранойи, который решается атомарными операциями.
3. Почему в Python иногда не нужно использовать try-catch?
В Python есть конструкция with open(...), которая автоматически закрывает файл, даже если произошла ошибка. Это «умный» способ, который заменяет try-finally для файлов. Для других ресурсов (сокеты, блокировки) часто тоже есть контекстные менеджеры, но если их нет — finally ваш друг.
4. Что такое raise и когда его использовать?
raise — это команда, чтобы вызвать ошибку. Вы используете его, когда хотите создать свою ошибку (например, raise ProductOutOfStockError) или передать ошибку дальше (например, raise e в блоке except, чтобы обработать её в другом месте). Часто мы так делаем, чтобы на верхнем уровне принять решение: показать пользователю сообщение или залогировать и пойти дальше.
5. Как обработать ошибку в асинхронном коде (async/await)?
В асинхронном коде (например, в Python или JS) try-catch работает так же, но нужно использовать его внутри async функции.
async function fetchData() {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.error("Ошибка сети:", error);
return null;
}
}
В Python с asyncio аналогично: оборачиваете await в try-except.
6. Что делать, если ошибка критическая и программа должна остановиться?
Если ошибка не может быть обработана (например, MemoryError), лучше не пытаться её «ловить», а позволить программе упасть. Это предотвратит работу с некорректными данными. В веб-разработке это часто приводит к возврату HTTP 500. Главное — чтобы падение было контролируемым: залогировали причину, отправили алерт, и мониторинг сразу показал проблему.
Вывод: Обработка ошибок — это навык, а не магия
Обработка ошибок и исключений — это не просто способ «не дать программе упасть». Это способ написать профессиональный, надёжный код, который уважает пользователя и окружение. Когда я вижу, как джун начинает продумывать не только happy path, но и все возможные отказы, я понимаю: он вырос.
Главные мысли, которые нужно запомнить:
- Ошибки неизбежны. Данные всегда будут неидеальными, сети — отваливаться. Ваш код должен быть готов к этому.
- Различайте Error и Exception. Ошибки (Error) — это критические сбои, исключения (Exception) — это проблемы, которые можно решить.
- Используйте
try-catchправильно. Ловите конкретные ошибки, не скрывайте их, и всегда записывайте в лог. - Предсказывайте, а не ловите. Для простых проверок (тип данных, пустота) используйте
if-else, а неtry-catch. - Думайте о пользователе. Показывайте понятные сообщения, а не стек-трейсы. И всегда освобождайте ресурсы.
Освоив эти принципы, вы перестанете бояться красных строк в консоли и начнёте писать код, который не стыдно показать на ревью. Удачи!
