Как стать автором
Поиск
Написать публикацию
Обновить

Архитектура сомнений: почему программисты ошибаются даже в очевидном

Уровень сложностиСложный
Время на прочтение4 мин
Количество просмотров1.5K

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

Есть у меня привычка: если код кажется слишком простым, я начинаю нервничать. Это как будто идёшь по идеально ровной дороге и ждёшь, что за углом кто-то обязательно выставит грабли.

Помню, как однажды мы упали в продакшене из-за того, что в одном месте забыли проверить входной параметр на 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.

Как тренировать внутреннего «параноика»

Я иногда устраиваю себе игру: беру кусок кода и думаю «если бы я был злым демоном, как бы я его сломал?». И сразу появляются десятки сценариев: пустые значения, неожиданные типы, странные символы.

Другой приём: представляю, что код читает не коллега, а студент первого курса. Если даже он сможет сломать мою функцию простым вызовом — значит, защита слабая.

Моментальная уверенность как враг

Бывало у вас такое: посмотрел на код, подумал «ну тут всё ясно» и через пять минут обнаружил, что всё не ясно? У меня такое регулярно. Это состояние моментальной уверенности — главный враг. Именно оно отключает архитектуру сомнений.

Поэтому я научился относиться к своим мыслям «тут понятно» как к багу. Если мозг говорит, что понятно — значит, надо проверить ещё раз.

Ошибки в очевидном — не баги мозга, а его нормальная работа. Мы так устроены: мозг ленится и оптимизирует. Но программист не может позволить себе лениться вместе с ним.

Поэтому сомнение должно стать частью архитектуры. Это не философия, а инженерный инструмент.

Очевидное должно быть проверено первым!

Теги:
Хабы:
0
Комментарии1

Публикации

Ближайшие события