Проектировал электрощит на 108 модулей и понял: современный IT – это скрутка с синей изолентой

от автора

Я выпал из IT на месяц. Причина банальна, но сурова – капитальный ремонт. По квартире проложено пара километров кабеля, из них 600 метров витой пары для шины управления, на полу разложен макет двух щитов по 54 модуля общей стоимостью под 150 тысяч рублей – и чем глубже я лезу в ПУЭ и считаю сечения, тем чётче одна мысль: если бы инженеры-электрики проектировали системы так же, как мы, разработчики, проектируем распределённые приложения – мы бы все давно сгорели заживо.

Три недели по вечерам, по 3–4 часа – я осваивал новую для себя предметную область. Однолинейную схему перерисовывал раз восемь: то появлялись новые линии, то менялись старые. Один щит чисто силовой (ввод, контакторы, УЗО), второй – щит автоматики на базе Wiren Board 8. Я прошёл через все стадии: от «да что тут сложного, три провода воткнуть» до «господи, а откуда здесь ещё одна линия и куда она идёт».

За 10+ лет в IT я вырос от верстальщика до руководителя отдела в диджитал-агентстве. Повидал всякое. Но именно ремонт выбил из головы абстрактную дурь и заставил мыслить категориями физической надёжности. В вебе мы привыкли, что всё мягкое. Упало – поднимем. Течёт – перезапустим. Тормозит – ну, автоскейлинг же. Цена ошибки в 99% случаев – 500-я ошибка в браузере и лишние десятки тысяч в счёте за облако.

В физической инженерии цена ошибки – пожар, затопленные соседи или удар током. И там есть принципы, от которых нельзя отступить – не потому что так написано в методичке, а потому что загорится. Да, в электрике тоже хватает халтуры – но хотя бы там за неё отвечают перед уголовным кодексом, а не перед Jira-тикетом.

Ну и вот – пока я втыкал автоматы в DIN-рейку, в голове сами собой всплывали параллели с тем, что я видел в рабочих проектах. Пойдём по порядку.


1. Селективность УЗО vs каскадные падения

В силовом щите у меня стоит 5 независимых УЗО (устройств защитного отключения) на 63А: отдельно на кухню, отдельно на влажные зоны (ванная, стиралка), отдельно на климат, на розетки и на свет.

Зачем столько, если можно было воткнуть одно на входе и сэкономить кучу денег? Ради селективности.

Современная техника (инверторные кондиционеры, импульсные блоки питания) всегда дает небольшую фоновую утечку тока. Если повесить всю квартиру на одно УЗО, сумма этих микро-утечек выбьет рубильник без всякой аварии. А если авария все же случится – например, ТЭН водонагревателя пробьет на корпус, – у вас погаснет свет во всей квартире, и вы будете искать причину с фонариком в зубах. Разделение на независимые зоны гарантирует: проблема в ванной останется проблемой ванной. Холодильник и сервер продолжат работать.

В IT же про селективность постоянно забывают.

Сервис генерации PDF-отчетов ложится под нагрузкой. Сервис биллинга ждет от него ответа. У биллинга забивается пул соединений к базе данных (потоки висят в ожидании ввода-вывода). Через 30 секунд биллинг начинает обрывать запросы по таймауту. Корзина покупок, которая перед оформлением заказа дергает биллинг, тоже падает. В итоге из-за того, что мы не смогли сгенерировать простую PDF-ку, пользователи не могут купить товар.

Я видел проекты, где 30 микросервисов, свой Helm-чарт на каждый – но ни одного программного предохранителя на внешних вызовах. Да, паттерн Circuit Breaker (автоматический размыкатель цепи) известный. Но покажите мне три боевых проекта подряд, где он реально стоит на каждом вызове. Программный предохранитель – это буквально то же самое, что отдельное УЗО на каждую зону.

# Пример логики предохранителя (Circuit Breaker) на Python# Если 3 вызова подряд падают, рубильник "выбивает" на 10 секунд@circuit_breaker(failure_threshold=3, recovery_timeout=10)def generate_pdf_report():    return make_request_to_pdf_service()try:    report = generate_pdf_report()except CircuitOpenError:    # Селективность в действии: не ждем таймаута, сразу отдаем fallback,    # спасая пул потоков биллинга от каскадного падения    return "Отчет временно недоступен"

Без таких паттернов наши микросервисы – дешёвая новогодняя гирлянда: перегорела одна лампочка, и погасла вся ёлка.


2. Отложенная согласованность и физическая кнопка

Для управления светом многие сегодня используют беспроводные реле (Zigbee/Wi-Fi). Нажал на выключатель -> сигнал ушел в Zigbee-шлюз -> оттуда в Home Assistant -> правило автоматизации дернуло Wi-Fi реле в люстре -> свет включился.

Знаете, как это называется в IT? Отложенная согласованность (Eventual Consistency).

Если шлюз завис, если микроволновка дает сильные помехи на частоте 2.4 ГГц, если сервер умного дома ушел в перезагрузку (или обновляется его Docker-контейнер) – вы будете стоять в темноте и отчаянно клацать выключателем.

Поэтому в своем щите автоматики я использую модули реле Wiren Board (WB-MR6C) и классическую топологию «Звезда». Жёсткие силовые кабели (ВВГнг-LS) от ламп идут прямо в щит на реле. А от выключателей в комнатах в щит тянется витая пара. Но самое главное: жилы от выключателей подключаются напрямую к входам iGND самого реле (используется аппаратное прямое управление).

Нажатие кнопки аппаратно замыкает контакт внутри модуля. Свет включается за миллисекунды – без участия софта, без сети, без облака. Чистая механика. Даже если центральный контроллер сгорел, сервер Home Assistant взорвался, а сеть RS-485 оборвана – свет будет работать как обычный тупой выключатель. Это – строгая согласованность.

В разработке мы зачем-то тянем брокеры сообщений и отложенную согласованность туда, где они не нужны.

Разработчик получает задачу: «Списать деньги с баланса и выдать пользователю премиум-статус». А в типичном проекте?

  1. Списывает деньги.

  2. Кидает событие money_deducted в RabbitMQ или Kafka.

  3. Отдельный воркер читает событие и обновляет статус пользователя.

Зачем?! Это одна база данных, один контекст. Если обработчик упадёт или очередь зависнет – пользователь останется без денег и без статуса. Ну ладно, если эквайринг внешний и десять потребителей ждут это событие – куда деваться. Но зачем Kafka между двумя таблицами одной Postgres?

-- Надежно, как прямое замыкание контакта iGNDBEGIN;  UPDATE user_balances SET amount = amount - 100 WHERE user_id = 42 AND amount >= 100;  UPDATE user_roles SET is_premium = TRUE WHERE user_id = 42;COMMIT;

Отложенная согласованность оправдана, когда вы списываете деньги во внешнем эквайринге, а статус выдаёте во внутренней системе. Но плодить брокеры сообщений внутри монолитного бизнес-процесса – это классическое избыточное усложнение.


3. Защита от протечек и единая точка отказа

Модуль WB-MWAC, отвечающий за защиту от протечек, – пожалуй, самая ответственная часть системы. Он управляет шаровыми электрокранами на 24 вольта, причём работает абсолютно автономно: если датчик на полу уловил воду, кран перекроется сам по себе, даже если сервер умного дома отключился. Питается вся эта история через отдельный ИБП Wiren Board (WB-UPS v.3, DIN-рейка, прямо в щите), чтобы исключить зависимость от общей сети.

Логика простая: если прорвёт трубу, вода может залить розетки, от которых питается бытовая техника. Сработает УЗО, электричество в квартире пропадёт – и если краны зависят от общей линии, они превратятся в бесполезные куски металла ровно в тот момент, когда должны были спасти ваш ремонт на несколько миллионов. В моей схеме ИБП подхватит питание, и автоматика спокойно перекроет воду на своих аккумуляторах.

В IT мы наступаем на те же грабли, создавая единые точки отказа:

  • Мониторинг запускаем внутри того же Kubernetes-кластера, который он призван контролировать. У нас так было: Grafana жила в том же K8s, что и продакшн. Когда OOM killer выбил ноду – мы полчаса сидели в тишине, не понимая, почему нет алертов. Потому что алерты лежали вместе с кластером. Узнали о проблеме из чата поддержки – клиент написал, что «ничего не грузится». Это как пожарная сигнализация, которая питается от той же розетки, что и горящий обогреватель.

  • Бэкапы в S3 – но ключи от бакета лежат в том же K8s secret, рядом с данными. Красивая иллюзия надёжности до первого инцидента.

  • Резервные страницы-заглушки для пользователей отдаём с того же Node.js-сервера, который сам уже захлебнулся в нехватке памяти.

По-хорошему, такие вещи должны быть полностью изолированы. Упал бэкенд? Пусть пользователь видит статичную заглушку, отданную напрямую с CDN, а не пытается достучаться до Nginx, который сейчас беспомощно крутит тайм-ауты.


4. Номиналы и лимиты

В щите каждый автоматический выключатель имеет строгий номинал. Розеточная линия – кабель 3×2.5 мм² и автомат на 16А. Световая – 3×1.5 мм² и автомат на 10А. Это не рекомендация – это физический закон: превысь номинал – кабель начнет греться, изоляция оплавится, стена загорится.

Автомат – это буквально аппаратный ограничитель нагрузки. Ток превысил допустимый порог – щёлк, линия обесточена. Никаких переговоров, никакого «ну давай ещё чуть-чуть, может, пронесёт».

А теперь посмотрим на типичный проект. API без каких-либо лимитов. У нас однажды коллега запустил нагрузочный тест. Не на тестовом стенде – на боевом сервере. В рабочее время. Несколько сотен потоков. Через три минуты API лёг, а вместе с ним – все реальные пользователи. Если бы стоял ограничитель запросов – легли бы только его потоки, а клиенты даже не заметили бы.

# Без автомата: один клиент кладёт всех# С автоматом: каждый клиент ограничен своим номиналомfrom ratelimit import limits, sleep_and_retry@sleep_and_retry@limits(calls=100, period=60)  # Автомат на 100 запросов/минdef handle_api_request(client_id, request):    # Превысил лимит? Отключаем линию, остальные продолжают работать    return process(request)

Электрик никогда не повесит кондиционер на 20А на автомат в 32А «с запасом». Почему? Потому что при коротком замыкании автомат будет ждать, пока ток превысит 32А, а к тому моменту кабель, рассчитанный на 20А, уже будет дымить в стене. Точно так же ограничитель нагрузки должен соответствовать реальной пропускной способности сервиса, а не выставляться «на глазок» или, что ещё хуже, отсутствовать вовсе.


5. Заземление и наблюдаемость системы

Заземление – невидимый, но критически важный контур. Жёлто-зеленый проводник PE тянется к каждой розетке и к каждому прибору. При пробое изоляции ток утечки уходит в землю по предсказуемому пути, а УЗО моментально фиксирует разницу токов и отключает линию. Без заземления пробой на корпус превращает стиральную машину в электрический стул – ток пойдет через первого, кто к ней прикоснется.

В IT заземление – это структурированное логирование и наблюдаемость. Когда в системе что-то «пробивает» (ошибка, аномалия, деградация), информация должна уходить по предсказуемому пути в предсказуемое место: в централизованные логи, метрики, трассировки запросов.

В IT же с этим часто беда.

# "Заземление" Поколения JSONprint("here")print("here2")print("wtf why is this None")logger.info(f"user: {user}")  # Весь объект в stdout, удачи в поиске# Инженерный подход: структурированный контурlogger.info("payment_processed", extra={    "user_id": user.id,    "amount": amount,    "currency": "RUB",    "trace_id": request.trace_id,  # Сквозной идентификатор    "latency_ms": elapsed})

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


6. Порядок в проводах и в коде

В моих щитах суммарно больше сотни проводов. Чтобы через год не сойти с ума, пытаясь найти нужную линию, я жестко соблюдаю цветовую маркировку: белая фаза, синий ноль, желто-зеленая земля. Ноли после каждого УЗО – вообще неприкосновенная святыня, под каждое ставится своя отдельная кросс-шина. Стоит случайно перекинуть ноль от розетки ванной на шину кухни – УЗО мгновенно отщелкнет. А чтобы топология читалась глазами, все провода обжаты наконечниками НШВИ и аккуратно разложены по щиту.

Да, мне еще пару дней нужно потратить на наведение порядка

Когда же погружаешься в архитектуру типичного современного проекта, часто видишь пучок из 500 голых проводов одного цвета, скрученных изолентой. Перекинуть ноль между шинами – это как подключить сервис авторизации к базе аналитики вместо своей: всё вроде работает, но при первом запросе валится – и непонятно почему:

Неявные зависимости сплетаются в узлы, когда безобидный метод getUserProfile() вдруг делает синхронный HTTP-вызов во внешнюю аналитику.

Разработчик пишет красивое order.getCustomer().getAddress().getCity(), а под капотом база данных ложится от 500 скрытых SQL-запросов из-за ленивой загрузки ORM. Это всё равно что пропустить 50 Ампер через тонкий шнурок от зарядки для телефона.

# 501 запрос к БД (1 + N)orders = Order.objects.all()  # 1 запросfor order in orders:  # 500 итераций    city = order.customer.address.city  # +2 запроса каждый раз (customer, address)    # Итого: 1 + 500*2 = 1001 запрос. На каждый просмотр страницы.# Инженер: 1 запрос с JOINorders = Order.objects.select_related('customer__address').all()for order in orders:    city = order.customer.address.city  # Данные уже в памяти, 0 запросов

А отсутствие индексов в базе – это как щит вообще без маркировки. Чтобы найти нужный заказ в таблице на 10 млн записей, движку приходится выполнять полное сканирование – буквально дергать каждый рубильник по очереди, проверяя, тот ли это.

То, что мы запихнули спагетти-код в Docker-контейнер и развернули через Helm-чарт, не делает нас инженерами. Docker и Helm решают задачу развёртывания, но не задачу проектирования. Мы просто спрятали скрутки меди и алюминия с синей изолентой за красивой гипсокартонной стеной. Рано или поздно оно коротнёт.


Сводная таблица: электрощит vs IT-архитектура

Принцип

Электрощит

IT-аналог

Что делает Поколение JSON

Изоляция сбоев

5 независимых УЗО по зонам

Предохранитель (Circuit Breaker)

Один try/catch на весь запрос

Согласованность

Прямое подключение iGND к реле

Атомарная транзакция БД

Kafka между двумя таблицами одной БД

Автономность

ИБП для кранов протечки

Мониторинг вне кластера

Grafana внутри падающего K8s

Лимиты

Автомат 16А на кабель 2.5 мм²

Ограничитель запросов

API без лимитов: «сервер выдержит»

Наблюдаемость

Заземление PE + маркировка

Логирование + трассировка

console.log("here2")

Порядок

Цветовая маркировка, НШВИ, кросс-шины

Явные контракты, индексы

Спагетти с ленивой загрузкой на 1001 запрос

Вместо итогов

Мы привыкли, что IT – это виртуальный мир, где всё можно откатить командой git revert. Но инфраструктура, вычислительные мощности и, главное, нервы наших пользователей – вполне материальны.

Я вернулся из ремонта с ощущением, которое сложно передать. Когда ты три недели по вечерам перерисовываешь однолинейную схему в восьмой раз, потому что опять появилась новая линия – начинаешь очень иначе смотреть на архитектурные решения в коде. Физическая инженерия отлично проветривает мозги.

Перед тем как втащить в проект очередную модную технологию, распилить монолит на облачные функции или прикрутить брокер сообщений там, где нужен обычный REST API, спросите себя: «А если бы это был мой домашний электрощиток, я бы поставил туда китайское реле с Алиэкспресса, зависящее от пинга до серверов Alibaba?».

Если ответ «нет» – перепишите на скучном, надежном SQL, покройте тестами и идите спокойно спать.


P.S. Этот текст родился во время работы над книгой про «Поколение JSON» – о том, как индустрия забыла фундаментальную инженерию ради хайпа. Если вам близок такой подход (или просто накипело) – заходите в мой Telegram-канал. Там я разбираю архитектурный абсурд и делюсь процессом создания книги. А первую главу можно забрать и почитать бесплатно на сайте json-book.ru.

ссылка на оригинал статьи https://habr.com/ru/articles/1042418/