10 минут до кода
137 subscribers
85 photos
76 links
Твой ежедневный контекст в мире веб-разработки.

Без скучных уроков и академической нагрузки.
Читаешь канал 10 минут в день — убираешь кашу в голове и становишься программистом.

Просто. Понятно. Каждый день.
Download Telegram
Как чистые функции спасают продакшн

Проблема большинства легаси-проектов не в объеме кода, а в его непредсказуемости. Если вызов getDiscount(100) внезапно роняет базу данных или триггерит рассылку писем, у вас проблемы с архитектурой.

В нормальном инженерном процессе функция должна быть предсказуемой: одни и те же входные данные обязаны возвращать один и тот же результат. Всегда.

Механика чистоты и «ремонт погрузчика»

Чистая функция работает в изоляции. Представьте сотрудника склада, чья задача — упаковать посылку, наклеить номер и положить на ленту. Это детерминированный процесс. Вы даете ему коробку — на выходе получаете готовый к отправке объект. Вы точно знаете, сколько времени это займет и какой будет результат.

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

В коде сайд-эффектами считается любое изменение состояния вне функции:

- Мутация глобальных переменных;
- Запись в базу данных или файловую систему;
- Сетевые запросы;
- Даже банальный console.log — это сайд-эффект, меняющий состояние потока вывода.

Стоимость тестирования и когнитивная нагрузка

Заметная разница между чистым и «грязным» кодом проявляется на этапе тестирования. Чтобы проверить чистую функцию, вам достаточно передать аргументы и сравнить результат. Это доли миллисекунды.

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

Чистый код снижает когнитивную нагрузку. Вы читаете его как книгу: логика линейна, зависимости прозрачны. Вам не нужно держать в голове состояние десяти параллельных вызовов или гадать, не изменится ли значение переменной в середине цикла из-за внешнего запроса.

Архитектурный паттерн: Чистое ядро, грязная оболочка

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

Мы разделяем систему на два слоя:

1. Pure Core (Чистое ядро): Вся бизнес-логика, расчеты, трансформации данных. Здесь живут чистые функции, которые легко тестировать и масштабировать.
2. Impure Shell (Грязная оболочка): Тонкий слой кода, который взаимодействует с внешним миром — базой, API, UI.

Ядро считает — оболочка исполняет. Если расчет скидки отделен от процесса записи в БД, вы можете протестировать миллион сценариев ценообразования, не сделав ни одного лишнего запроса к диску.

Чистый код — это не вопрос эстетики, а вопрос контроля над сложностью. Предсказуемость системы напрямую зависит от того, насколько глубоко вы закопали свои сайд-эффекты.

🔥 — если разделяешь логику и эффекты. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥8
🥳 Квиз день!

Если хотите дополнительный 4-ый вопрос, ставьте 🔥 этому посту.

Наберем 20 — опубликую бонусный квиз 😉

10МДК | ВЕБМастер
🔥2
Команда постоянно ловит Merge Conflicts в классе OrderProcessor, который одновременно считает скидки, лезет в БД и шлет Email. Какое решение устранит причину конфликтов?
Anonymous Quiz
0%
Внедрение LLM-агентов для автоматического слияния веток.
0%
Переход на Trunk-based development с коммитами раз в 15 минут.
85%
Выделение бизнес-логики в Pure Core, отделенный от I/O и БД.
15%
Использование File Locking, чтобы файл правил только один человек.
Изоляция контекста: Почему код не должен знать лишнего

Залитый в Git секрет — это не просто ошибка, это татуировка, которую не свести. Даже если вы удалите API-ключ в следующем коммите, он навсегда останется в истории репозитория.

Вы можете возразить “да кому нужен мой проект и мои секреты?” и я отвечу — ботам. Как только ваш код попадает в публичный доступ, у них уходит от 30 до 60 секунд, чтобы спарсить конфиги и начать выжигать ваш бюджет или сливать базу пользователей.

Инженерный подход заключается в том, чтобы сделать код «слепым» к среде, в которой он работает. Вашему приложению не нужно знать конкретный хэш OpenAI или пароль от базы данных. Ему нужно знать только название переменной, через которую эти данные придут.

Механизм разделения

Файл .env — это простейшая связка «ключ=значение», которая служит прослойкой между логикой и инфраструктурой. Это позволяет реализовать концепцию, где одна и та же кодовая база ведет себя по-разному в зависимости от контекста:

1. Смена инструментов без переписывания логики. На локалке вы используете SQLite, чтобы не поднимать тяжелые контейнеры. В продакшене у вас развернут Postgres. Если тип базы зашит в коде через if/else, вы плодите технический долг. Если тип базы тянется из переменной DATABASE_TYPE, то ORM просто подхватывает нужный драйвер «под капотом» и ваш код остается девственно чистым.
2. Безопасность через абстракцию. Вместо того чтобы хардкодить API_KEY = "sk-12345...", вы пишете API_KEY = process.env.OPENAI_KEY. Теперь ваша логика работает с абстракцией, а реальный секрет подставляется системой только в момент исполнения.

Правила хорошего тона

Файл .env никогда не должен покидать пределы вашей локальной машины или сервера. Здесь в игру вступает .gitignore. Это ваш «вышибала», который гарантирует, что секреты не улетят в репозиторий. Без этой связки использование переменных окружения теряет смысл — вы просто создаете еще один файл для утечки.

Однако есть проблема: когда новый разработчик клонирует ваш проект, он видит пустую папку без понимания, какие ключи нужны для запуска. Поэтому стандарт здесь — файл .env.example.

Это «пустышка», где указаны только названия ключей:
DB_HOST=
API_TOKEN=
DEBUG_MODE=

Это декларация требований вашей системы. Коллега копирует этот файл, переименовывает в .env и вписывает свои значения.

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

Ставь 🔥 если побежал удалять секреты из git. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥4😁3
Индекс Git: Почему .gitignore не работает задним числом

Многие воспринимают Git как продвинутую «облачную флешку», но на самом деле это база данных состояний. И частая ошибка — думать, что .gitignore обладает магической силой скрывать файлы, которые уже попали в систему. Если вы не понимаете, как работает Индекс, вы рано или поздно зальете в продакшн приватные ключи или тяжелые логи, а потом будете удивляться, и не понимать как же такое произошло.

Индекс как точка невозврата

Когда вы прописываете git init, вы создаете пустую базу данных. На этом этапе Git абсолютно слеп — он не следит за вашими файлами, даже если их в папке тысячи. Чтобы система начала «видеть» данные, их нужно зарегистрировать в Индексе через команду git add.

Индекс — это промежуточный слой между вашим рабочим каталогом и хранилищем коммитов. Как только файл попал в Индекс, он становится «отслеживаемым» (tracked). С этого момента Git превращается в дотошного регистратора: он запоминает каждое изменение, и даже если вы удалите файл физически с диска, Git сможет восстановить его из предыдущего коммита, потому что в базе остался слепок.

Ловушка игнорирования

Главный нюанс, на котором горят джуны: .gitignore работает только для неотслеживаемых файлов. В официальной документации это прописано четко, но это часто пропускают.

Если файл уже находится в Индексе, никакие правила в .gitignore на него не подействуют. Вы можете прописать там путь к своему .env или папке node_modules хоть десять раз — если они уже были добавлены в систему через git add, Git продолжит их трекать и пушить в репозиторий.

Чтобы исправить эту ситуацию и «ослепить» Git в отношении конкретного файла, используется команда git rm. Но и здесь есть нюансы:

1. Просто git rm файл — удалит его и из Индекса, и физически с вашего диска.
2. git rm --cached файл — это хирургическое решение. Флаг -cached приказывает Git удалить запись из базы (Индекса), но оставить сам файл в файловой системе.

Только после этой операции Git «забудет» файл, и правила из .gitignore наконец вступят в силу.

Понимание работы Индекса превращает хаотичное использование команд в осознанное проектирование истории проекта. Git не просто копирует файлы, он управляет их состояниями. Если вы понимаете разницу между рабочей областью, Индексом и коммитом, вы контролируете каждый байт, который улетает в репозиторий.

Ставьте 🔥, если хоть раз вычищали из репозитория случайно залитые конфиги или папки зависимостей. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥8👍2
Почему пустой Catch — это технический суицид

Существует мнение, что Try/Catch — это признак плохого тона. Якобы, если ты используешь эту конструкцию, значит, заранее расписываешься в том, что твой код может упасть с ошибкой.

В идеальном мире это, может, и так, но в реальности мы работаем в агрессивной среде. У нас есть внешние API, сторонние библиотеки и сеть, которая имеет свойство отваливаться в самый неподходящий момент. Мы не контролируем всё окружение, поэтому Try/Catch — это не костыль, а механизм выживания системы.

Проблема не в самой конструкции, а в том, как её превращают в «глушитель».

Антипаттерн «Изолента»

Самый лютый грех, который я вижу на ревью — это пустой блок Catch. Разработчик видит, что кусок кода падает, не может (или не хочет) разобраться в причинах и просто оборачивает его в Try, оставляя обработчик ошибки пустым.

Это чистой воды технический суицид. Представьте, что в машине загорелся значок Check Engine. Вместо того чтобы ехать в сервис, вы просто заклеиваете лампочку черной изолентой. Машина продолжает ехать? Да. Но прямо сейчас внутри двигателя может происходить катастрофа, которая через сто километров превратит машину в груду металлолома.

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

Стратегия официанта

Адекватный инженер использует стратегию «официанта». Если на кухне произошел факап и блюдо сгорело, хороший официант не делает вид, что всё в порядке, вынося пустую тарелку. Он подходит, извиняется, объясняет причину и предлагает альтернативу.

В коде это реализуется через три обязательных шага в блоке Catch:

1. Сбор контекста: Логируем всё, что поможет расследовать инцидент — Payload запроса, ID пользователя, состояние переменных.
2. Обработка: Мы либо прокидываем ошибку выше по стеку, если не можем её решить здесь, либо подготавливаем объект-результат, который система сможет адекватно прочитать.
3. Понятный ответ: Если ошибка долетает до интерфейса, пользователь не должен видеть Unexpected variable или NullPointerException. Ему нужно выдать адекватный статус: «Сервис временно недоступен, попробуйте позже».

Исключение vs Валидация

Нужно четко разделять, где нам нужен Try/Catch, а где достаточно обычного if. Try/Catch предназначен для *исключительных* ситуаций: диск переполнен, база данных ушла в ребут, отвалился внешний шлюз.

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

Ставь 🔥, если теперь будешь пользоваться try/catch осознанно. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥7
Клиентская память: Почему архитектура ломается на выборе хранилища

Выбор места для данных на фронтенде — это всегда баланс между безопасностью, временем жизни сессии и скоростью рендеринга. У нас есть три основных хранилища: Cookies, Local Storage и Session Storage. Они появлялись в браузере не одновременно, и каждый закрывает свою специфическую «дыру» в логике приложения.

Cookies: Бейджик для сервера

Куки можно представить как небольшие идентификационные бейджики. Их главная особенность в том, что они — часть HTTP-протокола. Когда сервер присылает ответ, он может «навесить» этот бейджик на браузер, и при каждом следующем запросе браузер будет автоматически отдавать его обратно.

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

Если к вашему фронту подключен сторонний чат-бот, метрики или кривое расширение в браузере пользователя, они не смогут украсть сессию, просто прочитав document.cookie. Данные будут летать только между браузером и бэкендом, минуя уровень исполнения скриптов.

Local Storage: Склад общего доступа

Local Storage — это полноценное хранилище на 5 МБ. Оно персистентно: данные не исчезнут, если закрыть вкладку или перезагрузить компьютер. Это отличный сейф для состояния интерфейса, например, корзины в интернет-магазине.

Но здесь кроется главная проблема: к Local Storage есть доступ у любого скрипта на странице. Это «публичное» место. Хранить здесь JWT-токены или персональные данные — плохая практика. Если злоумышленник прокинет XSS-уязвимость, он заберет всё содержимое одной строчкой кода. Используйте этот склад для того, что не жалко потерять или что не дает прямого доступа к аккаунту: настройки фильтров, черновики сообщений или кэш тяжелых UI-структур.

Session Storage: Черновик внутри вкладки

Session Storage часто путают с Local Storage, но у него есть жесткая изоляция. Он живет ровно до тех пор, пока открыта конкретная вкладка.

1. Закрыли вкладку — данные стерлись.
2. Открыли ту же страницу в соседней вкладке — там «чистый лист».

Это место для кратковременных данных, которые нужны здесь и сейчас. Но будьте осторожны: если пользователь случайно закроет вкладку с оформлением заказа, где данные лежали только в Session Storage, он вернется к пустой корзине. С точки зрения UX — это обидно. Пользователь скорее закроет сайт совсем, чем будет набивать корзину заново.

Проблема «вспышки» и логика рендеринга

Инженерный подход требует учитывать, когда именно данные становятся доступны. Local Storage доступен только тогда, когда ваш JavaScript уже подгрузился и начал исполняться.

Классический антипаттерн: хранение темы (светлая/темная) в Local Storage.
Пользователь заходит на сайт, браузер парсит HTML/CSS, рендерит белую страницу (по дефолту), и только через 50-100 миллисекунд ваш скрипт добирается до хранилища и переключает тему на темную. Получается неприятная «вспышка», бьющая по глазам.

Если же хранить настройку темы в куках, бэкенд увидит её еще на этапе формирования HTTP-запроса и сразу отдаст HTML с нужным CSS-классом. Никаких прыжков и мерцаний — чистая архитектура.

Но есть еще один тип хранилища в браузере IndexedDB. Ставьте 🔥 если интересно разобраться чем IndexedDB отличается от остальных типов и как его правильно использовать.

А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥6
С чего начинается разработка?

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

Но когда вы начинаете что-то свое, все начинается с одного вопроса: ЗАЧЕМ?

Зачем мы это делаем? Кто пользователь? Какую проблему решаем? Какая главная функция?

Без ответов на эти вопросы любой проект обречен. Вы будете бесконечно модифицировать код, потеряете смысл и просто забросите всё на полпути. Это работает везде: и в серьезных продуктах и в пет-проектах для «набивания руки». Нет цели — нет финала, нет прогресса.

Но что делать когда ответить на этот вопрос сложно?

Используйте нейронки. Не просите их придумать идею, а переключите в режим интервьюера. Пусть они «пытают» вас вопросами и вытаскивают из вас всю нужную информацию.

Но определять вектор проекта — это фундаментальный скилл, который отделяет тех, кто просто пишет код, от тех, кто делает продукт. Без этого вы не сможете даже адекватно разбить проект на этапы. Пока вы будете бесконечно полировать один модуль, кто-то другой выкатит 20 MVP, получив в 20 раз больше опыта.

Ровно эту философию я внедрил в своем клубе «ВЕБМастер». Мы каждый месяц берем одну идею и проходим путь от ТЗ и декомпозиции до деплоя на сервер.

Вчера я открыл набор на этот месяц. Вступить можно только в первую неделю, чтобы все стартовали вместе. Пора превращать накопленные знания в реальные навыки. 💪

Проект текущего месяца я описывал у себя в паблике а войти в ВЕБМастер можно чрез бота клуба 👇

https://t.me/webmaster_club_bot

🎁 Самое приятное: попробовать участие можно всего за 1 рубль.

Буду ждать новых инженеров!

10МДК | ВЕБМастер
🔥3
Каскадное удаление: Целостность данных против логики приложения

Когда впервые натыкаешься на внешние ключи (Foreign Keys) и ON DELETE CASCADE в MySQL, возникает иллюзия полного контроля. Кажется, что ты наконец-то переложил скучную работу по очистке базы на плечи самой СУБД. Но на практике, особенно на этапе активной разработки, эта «магия» быстро превращается в проблему: попытка удалить одну тестовую сущность оборачивается ошибкой целостности, и ты сидишь, вручную отключаешь FOREIGN_KEY_CHECKS, чтобы просто поправить структуру данных.

Механизм «Слепого» удаления

Суть каскада проста: есть родительская таблица (например, projects) и дочерние (tasks, subtasks). Если мы удаляем проект, база автоматически вычищает всё, что к нему привязано. Это путь к идеальной целостности — в базе не остается «сирот» или мусорных записей, которые никуда не ведут. Но здесь кроется ловушка для архитектора.

Когда база удаляет записи каскадом, бэкенд об этом ничего не знает. Для приложения это «событийный разрыв». Если в коде прописана логика «при удалении заказа отправить письмо пользователю или инициировать возврат через API Stripe», то каскад в БД эту логику просто проигнорирует. Код удалил одну строку в users, а миллион связанных orders исчезли в подкапотке СУБД, не вызвав ни одного хука или события в вашем приложении.

Блокировки и глубокая задумчивость

В продакшене каскад может стать причиной отказа в обслуживании. Представьте пользователя-гиганта, у которого в системе миллион заказов. Когда он решает удалить аккаунт, один простой запрос на удаление инициирует лавину. СУБД уходит в глубокую задумчивость, пытаясь за одну транзакцию вычистить миллионы строк и обновить индексы. В этот момент база вешает блокировки, запросы в очереди растут, и ваш сервис ложится просто потому, что вы решили автоматизировать «уборку».

В инженерной практике для таких случаев используется Batch Delete. Вместо каскада мы пишем контролируемые запросы, которые удаляют данные пачками (например, по 5000 записей за раз) в цикле. Это позволяет базе «дышать» между запросами и не блокирует критически важные таблицы намертво.

Компромисс через SET NULL

Иногда полное выжигание данных — это не то, что нужно бизнесу. Если автор удаляет свой профиль, его статьи в блоге или комментарии не обязательно должны исчезать. Для этого существует ON DELETE SET NULL. Внешний ключ обнуляется, данные остаются, но привязка к сущности пропадает. Пост остается, но автор теперь — «Аноним». Это сохраняет историю и аналитику, избавляя базу от «битых» ссылок без потери полезного контента.

Инженерный взгляд на целостность

Каскад — это инструмент технической чистоты, а не бизнес-логики. Если вам нужно гарантировать, что в базе не останется ошметков данных после технических манипуляций — используйте каскад. Но если процессом должен управлять бизнес (логирование, уведомления, сторонние API), удаление должно быть явным и контролируемым на уровне приложения.

Чистая база данных — это круто, но работающий и предсказуемый продакшн — важнее.

Ставь 🔥, если тоже хоть раз отключал проверку ключей, чтобы просто почистить тестовые данные. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
2🔥2
Ретрай-политики: Как не превратить обработку ошибок в DDoS-атаку на самого себя

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

Внешние API и распределенные системы — это агрессивная среда. Если вы работаете с LLM вроде ChatGPT или Claude, вы не можете гарантировать 100% аптайм или стабильный ответ. Единственное, что вы можете гарантировать — это факт вашей попытки достучаться до эндпоинта.

Ловушка вложенных try-catch

Первое инстинктивное решение — обернуть запрос в try-catch. Если упало — вызываем запрос еще раз внутри блока catch. Но это путь к бесконечной вложенности и «гонке вооружений» с неопределенностью. Вы не знаете, сколько раз упадет сервер: два, три или десять. Дублирование логики обработки ошибок делает код хрупким и невыносимым в дебаге.

Инженерный подход требует выноса логики повторов в отдельный управляемый механизм. Базовый вариант — отложенный повтор. Вместо того чтобы стучать в закрытую дверь каждые 100 миллисекунд, мы должны дать удаленной системе «продышаться» пару секунд. За это время на сервере может инвалидироваться кэш или завершиться процесс очистки памяти, который мешал обработке вашего запроса.

Экспоненциальное ожидание и фактор хаоса

Но простой линейный ретрай (повтор каждые 2 секунды) — это риск само-DDoS’а. Представьте, что у 1000 пользователей одновременно отвалился бэкенд. Если у всех зашит жесткий интервал, то через 2 секунды сервер получит волну из 1000 новых запросов, потом еще одну через 2 секунды. Вместо восстановления вы добьете систему ритмичными ударами.

Решение — Exponential Backoff. Мы увеличиваем паузу после каждой неудачи: 1, 2, 4, 8, 16 секунд. Это разгружает сеть, но оставляет проблему «волн», когда тысячи клиентов синхронно ждут и синхронно бьют в одну и ту же дверь.

Чтобы размазать нагрузку, в формулу добавляется Jitter (джиттер) — контролируемый хаос. Мы берем наше экспоненциальное время и умножаем на случайный коэффициент. В итоге один клиент повторит запрос через 4.1 секунды, второй — через 3.8, а третий — через 4.5. Нагрузка на сервер становится размазанной и плавной, что дает системе реальный шанс вернуться в строй.

Идемпотентность: Главный предохранитель

Ретраи бесполезны и даже опасны без понимания идемпотентности. Это свойство системы выдавать один и тот же результат при повторении идентичного запроса без побочных эффектов.

Если вы ретраите запрос на получение данных (GET) — это безопасно. Но если вы ретраите списание средств или создание заказа без ключа идемпотентности, вы рискуете списать деньги дважды или создать дублирующие записи в базе. Бэкенд должен уметь определять: «Так, этот запрос с ID #555 я уже видел и успешно обработал, просто верну старый результат», вместо того чтобы запускать логику транзакции заново.

Инженер не просто повторяет действие, пока оно не сработает. Он проектирует систему так, чтобы повторы не разрушали целостность данных.

🔥 — если пост был понятен. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥4
Хирургия кода: Почему швейцарский нож убивает архитектуру

Single Responsibility Principle (SRP) — это база, которую цитируют все, но на практике часто превращают в абсурд. Многие воспринимают SRP как требование дробить код до атомарного состояния, когда описание функции занимает больше строк, чем само тело с единственным вызовом сторонней библиотеки. Это перегиб. Истинный смысл SRP не в минимизации строк, а в четкой функциональной деятельности.

Штопор в операционной: Опасность универсальных инструментов

Представьте хирурга, который оперирует пациента швейцарским ножом. В теории — это нож, он может резать. Но на практике, пока врач делает надрез, он рискует зацепить пациента открытым штопором. В коде это выглядит так: вы меняете логику расчета скидки, а у вас внезапно «отваливается» генерация PDF-отчетов.

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

Лингвистический тест на чистоту реализации

Самый быстрый способ продиагностировать код на нарушение SRP — это проговорить вслух, что делает конкретный метод. Если в описании появляется союз «и», вы нарушили принцип.

- «Эта функция проверяет права доступа И достает данные из базы И форматирует их в JSON».

Здесь как минимум три разных зоны ответственности. Каждое «и» — это вектор для выделения логики в отдельную сущность. Разделение этих действий упрощает не только дебаг, но и переиспользование. Вам может понадобиться достать те же данные для другого эндпоинта, но уже без проверки прав в этом конкретном месте или с другим форматированием. Если всё зашито в одну «швейцарскую» функцию, вам придется либо дублировать код, либо городить костыли.

Экономика инженерных решений

Соблюдение Single Responsibility напрямую влияет на стоимость поддержки и регрессионного тестирования. В большой системе даже минорная правка может потребовать перепроверки всех связностей. И вот фикс “на 5 минут” растягивается на пару часов.

Когда же функции изолированы и выполняют одну задачу, зона поражения при изменениях минимальна. Мы точно знаем: правка логики расчета не заденет транспортный слой или генерацию документов.

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

🔥 — если теперь понимаешь SRP. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥5
🥳 Квиз день!

Если хотите дополнительный 4-ый вопрос, ставьте 🔥 этому посту.

Наберем 20 — опубликую бонусный квиз 😉

10МДК | ВЕБМастер
🔥6
Сервис обработки заказов должен вызвать API логистической службы. API временно недоступно (503). Как реализовать логику повторов, чтобы минимизировать нагрузку на шлюз и сохранить целостность данных?
Anonymous Quiz
7%
Цикл while(true) с немедленным повтором запроса до получения кода 200
33%
Рекурсивный вызов в блоке catch с фиксированным интервалом в 1 секунду
60%
Exponential Backoff с добавлением Jitter и передача ключа идемпотентности
0%
Использование каскадного удаления (ON DELETE CASCADE) для очистки транзакции
Лифт против микроволновки: Как проектировать предсказуемые системы

Слово «идемпотентность» звучит как заклинание, которое сложно выговорить с первого раза без запинки. Но за этим тяжеловесным термином скрывается максимально примитивная логика. Инженер должен гарантировать: сколько бы раз мы ни дергали один и тот же ендпоинт с одинаковыми данными, состояние системы изменится только один раз.

Представьте кнопку вызова 10-го этажа в лифте. Вы можете нажать её один раз, а можете в приступе нетерпения ударить по ней сотню раз. Лифт всё равно приедет на 10-й этаж и откроет двери один раз. Он не будет кататься вверх-вниз или хлопать дверьми пропорционально количеству нажатий. Эта кнопка — идемпотентна.

А теперь возьмём кнопку «+30 секунд» на микроволновке. Каждое нажатие меняет состояние системы: было 30 секунд, стало 60, потом 90. Нажмете 100 раз — и вместо разогретого обеда получите угли. Это пример неидемпотентного поведения.

Когда вы проектируете логику эндпоинта, всегда задавайте себе вопрос: на что это больше похоже — на лифт или на микроволновку?

Сценарий боли: Сбой связи в метро

В идеальном мире индемпотентность вообще не нужна. Но мы, увы, работаем в неидеальном мире, и наш код крутится в такой же среде.

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

Что видит пользователь? Бесконечный лодер.
Что он делает? Обновляет страницу и жмет «Оплатить» еще раз.

Если ваша система не идемпотентна, вы спишете деньги дважды за один и тот же заказ. Это превращается в технический инцидент, который вам же и придется разгребать.

Механика: Идемпотентный ключ

Чтобы бэкэнд понимал, что запрос пришел повторно, нам нужен «якорь» — Idempotency Key. Это уникальный идентификатор операции, который мы генерируем, чтобы отличать запросы и транзакции.

Как это работает под капотом:

1. Клиент отправляет запрос с заголовком Idempotency-Key: uuid-123-456.
2. Бэкэнд проверяет: «Я уже видел этот ключ?».
3. Если нет — обрабатывает запрос, сохраняет результат в базу или кеш и отдает ответ.
4. Если ключ уже есть в системе — бэкэнд просто достает из базы старый успешный ответ и отдает его клиенту, даже не пытаясь заново проводить оплату или менять данные.

Клиент думает, что всё прошло прямо сейчас, а на самом деле мы просто вернули ему результат первой попытки.

Стандарты и «костыли»

В вебе есть базовый этикет. По стандарту HTTP-методы GET, PUT и DELETE должны быть идемпотентными. Сколько ни удаляй один и тот же пост по его ID (DELETE), он останется удаленным. Сколько ни запрашивай профиль юзера (GET), данные не должны мутировать.

Проблема всегда в POST. По своей природе он не идемпотентен, так как обычно создает новую сущность. Именно поэтому для всех критичных POST-запросов (платежи, заказы, переводы) нам нужны свои инженерные «костыли» в виде ключей идемпотентности.

Инженерный чеклист:
Если операция меняет баланс, создает заказ или отправляет письмо — она обязана быть идемпотентной. Иначе вы просто ждете момента, когда кто-то в метро нажмет кнопку дважды. 😉

🔥 — если теперь можешь объяснить идемпотентность бабушке. А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥4
GraphQL: Свобода фронтенда и ловушка производительности

В REST архитектура диктует правила: один эндпоинт — один ресурс. Если тебе нужно собрать данные для профиля пользователя, где висят его последние заказы, ты идешь в /users/{id}, забираешь данные, а потом отдельным запросом стучишься в /orders?user_id={id}. Два запроса, два ожидания, лишний оверхед на сетевые задержки.

GraphQL меняет парадигму. Вместо того чтобы бегать по разным «ручкам», клиент обращается к единому порталу.

Если REST — это классический магазин, где ты точно знаешь, на какой полке лежат яблоки, а на какой хлеб, то GraphQL — это окно выдачи. Ты просто диктуешь список покупок в одно окно, и тебе выезжает собранный пакет. Один запрос — и в ответе сразу и юзер, и его заказы, и даже статус программы лояльности.

Схема как жесткий контракт

Вся магия держится на схеме(Schema). Бэкенд жестко описывает типы данных и связи между ними. Для фронтенд-инженеров это превращается в идеальный рабочий процесс: Тебе не нужно выпрашивать документацию у бэкэндера или гадать, какое поле придет в ответе. Ты открываешь специальный софт (Apollo Studio) идешь там в песочницу, где работает автодополнение, и сам как из кубиков лего собираешь запрос.

Схема — это живая документация.

Иллюзия скорости и проблема N+1

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

Когда фронтенд запрашивает дерево связанных объектов, бэкенд может свалиться в проблему N+1. Если ваш бэкэнд написан не оптимально, сервер на один запрос клиента сделает один запрос в БД за пользователем и еще сто запросов за каждым его заказом по отдельности. В итоге один «красивый» запрос в GraphQL может выполняться дольше, чем пять параллельных запросов в REST.

Инженерные компромиссы: Кэширование и Трафик

GraphQL так же ломает привычные инструменты оптимизации и работы с вебом.

1. Смерть HTTP-кэширования: В REST каждый URL уникален, его легко закэшировать на уровне браузера или CDN. В GraphQL у тебя всегда один эндпоинт (обычно /graphql) и всегда метод POST. Стандартные механизмы кэширования тут бесполезны — инженерам приходится внедрять сложные решения на уровне объектов внутри приложения.
2. Операции: Вместо стандартных методов (GET, POST, PUT) мы используем query для получения данных, mutation для их изменения и subscriptions, когда нужно подписаться на события через вебсокеты.

Когда это оправдано?

GraphQL — это спасение, когда у тебя один бэкенд и зоопарк клиентов: мобильное приложение, веб-сайт, десктоп и админка. Каждому из них нужны свои наборы полей из одних и тех же сущностей. Вместо того чтобы плодить десятки специфических эндпоинтов под каждый чих фронтенда, ты даешь им возможность самим выбирать состав данных.

Но если у тебя простое приложение с парой страниц, внедрение GraphQL — это стрельба из пушки по воробьям, которая принесет больше проблем с производительностью и кэшированием, чем реальной пользы.

🔥 — если работали с GraphQL и ловили N+1
🤔 — если REST кажется надежнее

А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥3
Ловушка зависимостей: Почему чужой код — это арендное жилье

Скорость выхода на рынок часто диктует свои правила: вместо того чтобы изобретать велосипеды, мы подтягиваем готовые пакеты. Это логично — пока ты будешь писать, тестировать и фиксить свои баги, конкуренты уже соберут все деньги. Но у этой медали есть обратная сторона, которая вскрывается в самый неподходящий момент.

Использование стороннего пакета — это долгосрочная аренда. Вы заезжаете в готовую квартиру, платите символическую цену и живете в комфорте, пока не прорвет трубу. Если хозяин квартиры «морозится» и не отвечает на звонки, вы оказываетесь в патовой ситуации. Вы не можете просто починить трубу, потому что это чужая собственность и вне вашей зоны ответственности. Но и съехать моментально не получится — вы уже обжились, перевезли вещи и настроили быт.

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

Инфраструктурный тупик и цена «бесплатного» кода

Прямо сейчас я наблюдаю этот сценарий в реальном проекте. Система критически зависит от библиотеки, на которой построен весь админ-интерфейс. Пока стек был актуальным, всё работало идеально. Но пришло время обновлять версию PHP и фреймворка, чтобы соответствовать современным стандартам безопасности и производительности.

Результат — инфраструктурный ад. Библиотека заброшена, версии конфликтуют, обновиться невозможно. У бизнеса теперь три плохих варианта:

1. Искать замену и переписывать огромный пласт UI.
2. Форкать чужой код, фиксить его самостоятельно и поддерживать вечно.
3. Копировать логику к себе и превращать её в собственное легаси.

Любой из этих путей — это прямые убытки: лишние деньги, часы разработки и риск сломать то, что работало годами. Когда вы тащите в проект очередную зависимость, вы подписываете контракт, условия которого могут измениться в любой момент без вашего участия.

Архитектурный щит: Anti-Corruption Layer

Если без стороннего пакета не обойтись, ваша задача как инженера — минимизировать площадь соприкосновения. Нельзя позволять чужому коду прорастать сквозь всю архитектуру приложения.

Решение — Anti-Corruption Layer (ACL). Это защитный слой, состоящий из ваших собственных интерфейсов, оберток и адаптеров.

- Ваше приложение никогда не вызывает сторонний пакет напрямую.
- Оно общается с вашим интерфейсом (Wrapper/Adapter).
- Ваш код внутри адаптера уже дергает методы библиотеки.

В такой конфигурации пакет становится заменяемым модулем. Если либа «протухла», скомпрометирована или мешает обновлению инфраструктуры, вы просто пишете новый адаптер под другой инструмент. Логика самого приложения при этом остается нетронутой. Это стоит времени на старте, но избавляет от технического дефолта в будущем.

Ставь 🔥 если не думал что от сторонних библиотек может быть столько проблем.

А если остались вопросы или что-то звучит слишком абстрактно — пиши в комменты, обязательно разберем!

10МДК | ВЕБМастер
🔥4