Чаще всего мы падаем не на сложных алгоритмах и не на асинхронных гонках. Главные враги — самые простые куски кода, те, на которые даже не хочется тратить внимание. В этой статье я делюсь опытом и наблюдениями о том, как «архитектура сомнений» помогает не доверять очевидному, и почему программисту полезно быть немного параноиком.

Есть у меня привычка: если код кажется слишком простым, я начинаю нервничать. Это как будто идёшь по идеально ровной дороге и ждёшь, что за углом кто-то обязательно выставит грабли.
Помню, как однажды мы упали в продакшене из-за того, что в одном месте забыли проверить входной параметр на null
. Там было всё — ревью, тесты, monitoring. Но не было самой банальной проверки, потому что «да это же очевидно». В итоге очевидность вылилась в трёхдневный пожар и кучу бессонных ночей.
Эффект «это же очевидно»
Есть у меня знакомый, который обожает говорить: «Ну тут всё понятно». Обычно именно его участки кода и ломаются первыми.
Пример, который всплыл у нас на проекте:
def add_items(items=[]):
items.append("X")
return items
print(add_items())
print(add_items())
Когда я впервые увидел этот код в чужом проекте, я даже не придал значения. «Очевидно же, что список будет создаваться заново», — думал я. А потом в логах появился странный баг, и оказалось, что список-то общий. Стыдно признавать, но ловушка с изменяемыми аргументами обманула даже не новичка, а вполне себе уверенного в себе разработчика.
Когда мозг компилирует криво
Мы читаем код не построчно, а блоками. Как будто мозг лениво компилирует «на глазок». И это почти всегда работает… пока не перестаёт.
У меня был момент в Go:
package main
import "fmt"
func main() {
var s string
if s == "" {
fmt.Println("Empty")
}
if s == nil {
fmt.Println("Nil")
}
}
Я реально полчаса искал, почему не работает s == nil
. Мозг упорно отказывался принимать, что строка в Go — не указатель. В какой-то момент я даже поймал себя на том, что злюсь не на язык, а на себя: «Ну я же опытный, как можно было так облажаться?»
Архитектура сомнений как защита
Есть ощущение, что опытные разработчики превращаются в параноиков. Сначала думаешь: «Зачем писать if b == 0
, ну ведь понятно же, что никто так не вызовет». А потом продакшен валится, и ты становишься человеком, который пишет проверки на всё.
Я называю это «архитектурой сомнений». Это не то чтобы набор практик, это скорее внутренняя привычка: не доверять себе и коду, даже если он кажется прозрачным.
Банальные ошибки в банальных условиях
История с реального проекта.
Сервис падал раз в неделю. Мы крутили мониторинг, проверяли асинхронщину, думали про утечки памяти. А оказалось, что всё из-за строчки:
if user.is_admin:
# ...
else:
do_something(user.permissions)Проблема была в том, что в некоторых случаях permissions приходили None. Всё. Никаких тонкостей, просто забытая проверка.
А сколько раз я видел баги из-за деления на ноль? Каждый раз, когда кто-то говорит «ну тут оно никогда ноль», — стоит готовиться к тому, что оно обязательно станет нулём.
Проблема была в том, что в некоторых случаях permissions
приходили None
. Всё. Никаких тонкостей, просто забытая проверка.
А сколько раз я видел баги из-за деления на ноль? Каждый раз, когда кто-то говорит «ну тут оно никогда ноль», — стоит готовиться к тому, что оно обязательно станет нулём.
Очевидное ≠ проверенное
Мой любимый комментарий в коде:
// здесь всё понятно, не трогать
Если видите такое — бегите. Либо правьте, либо хотя бы пишите тест. Потому что «понятно» означает только одно: никто здесь не проверял ничего уже очень давно.
Иллюзия эксперта
Когда я был джуном, я перепроверял каждый if
, потому что боялся ошибиться. Когда стал медлом, я начал лениться и ловить баги на самых простых местах. Когда стал сеньором — понял, что именно простое и есть самое коварное.
Это парадокс: чем больше у тебя опыта, тем больше ты доверяешь своей интуиции. А именно интуиция чаще всего и подводит.
Сомнение как инструмент
Сомнение — это не про неуверенность. Это про инженерный навык.
Вот что я практикую сам:
Читаю код вслух. На слух ошибки ловятся быстрее.
Стараюсь написать тест даже для тривиальных функций.
На ревью представляю, что этот код написал мой заклятый враг.
Если думаю «тут всё понятно» — это триггер остановиться и проверить ещё раз.
Сложные системы из простых ошибок
Как-то на одном проекте у нас упала очередь задач. Все грешили на брокер, искали проблемы в сети. Оказалось, что проблема была в одной строке: неправильно проставленный дефолтный таймаут.
Это смешно и грустно одновременно. Чем больше система, тем чаще она рушится от мелочей. Настоящие катастрофы приходят не от ракетных алгоритмов, а от одного лишнего else
.
Как тренировать внутреннего «параноика»
Я иногда устраиваю себе игру: беру кусок кода и думаю «если бы я был злым демоном, как бы я его сломал?». И сразу появляются десятки сценариев: пустые значения, неожиданные типы, странные символы.
Другой приём: представляю, что код читает не коллега, а студент первого курса. Если даже он сможет сломать мою функцию простым вызовом — значит, защита слабая.
Моментальная уверенность как враг
Бывало у вас такое: посмотрел на код, подумал «ну тут всё ясно» и через пять минут обнаружил, что всё не ясно? У меня такое регулярно. Это состояние моментальной уверенности — главный враг. Именно оно отключает архитектуру сомнений.
Поэтому я научился относиться к своим мыслям «тут понятно» как к багу. Если мозг говорит, что понятно — значит, надо проверить ещё раз.
Ошибки в очевидном — не баги мозга, а его нормальная работа. Мы так устроены: мозг ленится и оптимизирует. Но программист не может позволить себе лениться вместе с ним.
Поэтому сомнение должно стать частью архитектуры. Это не философия, а инженерный инструмент.
Очевидное должно быть проверено первым!