Files
progcode/web/data/articles.json
T
huncode 44c99a2640
Build and deploy / deploy (push) Successful in 21s
upgrade January 2018 Bitrix articles
2026-07-31 01:44:45 +03:00

5194 lines
1.5 MiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"slug": "editorial-2027-12-field-author-manifesto",
"title": "Манифест инженерного письма: кейс с ограничениями и выводами",
"date": "2027-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автор",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как после десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «манифест инженерного письма». Типичная ситуация выглядит так: после десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Зафиксировать принципы блога: личный опыт, проверка, ограничения и уважение к читателю.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «манифест инженерного письма» вернётся в следующем релизе под другим именем. Техническая статья соединяет факт, модель, действие и честное описание неопределённости.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-12-mechanism-author-manifesto",
"title": "Манифест инженерного письма: как принять инженерное решение",
"date": "2027-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автор",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему техническая статья соединяет факт, модель, действие и честное описание неопределённости — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «манифест инженерного письма». Техническая статья соединяет факт, модель, действие и честное описание неопределённости. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-12-practice-author-manifesto",
"title": "Манифест инженерного письма: практический маршрут",
"date": "2027-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автор",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как зафиксировать принципы блога: личный опыт, проверка, ограничения и уважение к читателю. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «манифест инженерного письма». Цель заметки — зафиксировать принципы блога: личный опыт, проверка, ограничения и уважение к читателю. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Техническая статья соединяет факт, модель, действие и честное описание неопределённости.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: зафиксировать принципы блога: личный опыт, проверка, ограничения и уважение к читателю.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «манифест инженерного письма» не в сложном синтаксисе, а в неявных предположениях. После десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-11-field-mistakes-revisions",
"title": "Пересмотр старых советов: кейс с ограничениями и выводами",
"date": "2027-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Ретроспектива",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «пересмотр старых советов». Типичная ситуация выглядит так: старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Исправлять собственные публикации и показывать, что новое знание меняет практику.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «пересмотр старых советов» вернётся в следующем релизе под другим именем. Инженерная зрелость проявляется не в безошибочности, а в способности обновить вывод при новых данных.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-11-mechanism-mistakes-revisions",
"title": "Пересмотр старых советов: как принять инженерное решение",
"date": "2027-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Ретроспектива",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему инженерная зрелость проявляется не в безошибочности, а в способности обновить вывод при новых данных — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «пересмотр старых советов». Инженерная зрелость проявляется не в безошибочности, а в способности обновить вывод при новых данных. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-11-practice-mistakes-revisions",
"title": "Пересмотр старых советов: практический маршрут",
"date": "2027-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Ретроспектива",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как исправлять собственные публикации и показывать, что новое знание меняет практику. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «пересмотр старых советов». Цель заметки — исправлять собственные публикации и показывать, что новое знание меняет практику. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Инженерная зрелость проявляется не в безошибочности, а в способности обновить вывод при новых данных.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: исправлять собственные публикации и показывать, что новое знание меняет практику.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «пересмотр старых советов» не в сложном синтаксисе, а в неявных предположениях. Старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-10-field-long-form-interview",
"title": "Длинное техническое интервью: кейс с ограничениями и выводами",
"date": "2027-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Интервью",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «длинное техническое интервью». Типичная ситуация выглядит так: спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Раскрыть решение через реальные ограничения, а не через набор победных формулировок.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «длинное техническое интервью» вернётся в следующем релизе под другим именем. История технологии становится полезной, когда в ней видны сомнения, отказы и критерии выбора.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-10-mechanism-long-form-interview",
"title": "Длинное техническое интервью: как принять инженерное решение",
"date": "2027-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Интервью",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему история технологии становится полезной, когда в ней видны сомнения, отказы и критерии выбора — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «длинное техническое интервью». История технологии становится полезной, когда в ней видны сомнения, отказы и критерии выбора. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-10-practice-long-form-interview",
"title": "Длинное техническое интервью: практический маршрут",
"date": "2027-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Интервью",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как раскрыть решение через реальные ограничения, а не через набор победных формулировок. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «длинное техническое интервью». Цель заметки — раскрыть решение через реальные ограничения, а не через набор победных формулировок. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. История технологии становится полезной, когда в ней видны сомнения, отказы и критерии выбора.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: раскрыть решение через реальные ограничения, а не через набор победных формулировок.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «длинное техническое интервью» не в сложном синтаксисе, а в неявных предположениях. Спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-09-field-mentor-series",
"title": "Серия для инженера, который растёт: кейс с ограничениями и выводами",
"date": "2027-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наставничество",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «серия для инженера, который растёт». Типичная ситуация выглядит так: junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Дать человеку не список технологий, а способ ставить вопросы и проверять выводы.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «серия для инженера, который растёт» вернётся в следующем релизе под другим именем. Рост инженера ускоряется через малые эксперименты, обратную связь и письменную фиксацию знания.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-09-mechanism-mentor-series",
"title": "Серия для инженера, который растёт: как принять инженерное решение",
"date": "2027-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наставничество",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему рост инженера ускоряется через малые эксперименты, обратную связь и письменную фиксацию знания — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «серия для инженера, который растёт». Рост инженера ускоряется через малые эксперименты, обратную связь и письменную фиксацию знания. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-09-practice-mentor-series",
"title": "Серия для инженера, который растёт: практический маршрут",
"date": "2027-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наставничество",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как дать человеку не список технологий, а способ ставить вопросы и проверять выводы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «серия для инженера, который растёт». Цель заметки — дать человеку не список технологий, а способ ставить вопросы и проверять выводы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Рост инженера ускоряется через малые эксперименты, обратную связь и письменную фиксацию знания.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: дать человеку не список технологий, а способ ставить вопросы и проверять выводы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «серия для инженера, который растёт» не в сложном синтаксисе, а в неявных предположениях. Junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-08-field-security-capstone",
"title": "Большой разбор безопасности: кейс с ограничениями и выводами",
"date": "2027-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Кейс"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «большой разбор безопасности». Типичная ситуация выглядит так: одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Показать защиту как последовательность решений от формы до production-окружения.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «большой разбор безопасности» вернётся в следующем релизе под другим именем. Безопасность требует слоёв: проверка ввода, права, сессия, журналирование и безопасная доставка.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-08-mechanism-security-capstone",
"title": "Большой разбор безопасности: как принять инженерное решение",
"date": "2027-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Кейс"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему безопасность требует слоёв: проверка ввода, права, сессия, журналирование и безопасная доставка — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «большой разбор безопасности». Безопасность требует слоёв: проверка ввода, права, сессия, журналирование и безопасная доставка. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-08-practice-security-capstone",
"title": "Большой разбор безопасности: практический маршрут",
"date": "2027-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Кейс"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как показать защиту как последовательность решений от формы до production-окружения. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «большой разбор безопасности». Цель заметки — показать защиту как последовательность решений от формы до production-окружения. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Безопасность требует слоёв: проверка ввода, права, сессия, журналирование и безопасная доставка.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: показать защиту как последовательность решений от формы до production-окружения.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «большой разбор безопасности» не в сложном синтаксисе, а в неявных предположениях. Одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-07-field-reliability-capstone",
"title": "Большой разбор надёжности: кейс с ограничениями и выводами",
"date": "2027-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как при недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «большой разбор надёжности». Типичная ситуация выглядит так: при недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Собрать timeout, retry, очередь, мониторинг и восстановление в один сценарий.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «большой разбор надёжности» вернётся в следующем релизе под другим именем. Надёжность появляется из контролируемого поведения при сбоях, а не из надежды на отсутствие сбоев.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-07-mechanism-reliability-capstone",
"title": "Большой разбор надёжности: как принять инженерное решение",
"date": "2027-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему надёжность появляется из контролируемого поведения при сбоях, а не из надежды на отсутствие сбоев — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «большой разбор надёжности». Надёжность появляется из контролируемого поведения при сбоях, а не из надежды на отсутствие сбоев. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: при недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-07-practice-reliability-capstone",
"title": "Большой разбор надёжности: практический маршрут",
"date": "2027-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как собрать timeout, retry, очередь, мониторинг и восстановление в один сценарий. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «большой разбор надёжности». Цель заметки — собрать timeout, retry, очередь, мониторинг и восстановление в один сценарий. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Надёжность появляется из контролируемого поведения при сбоях, а не из надежды на отсутствие сбоев.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: собрать timeout, retry, очередь, мониторинг и восстановление в один сценарий.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: при недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «большой разбор надёжности» не в сложном синтаксисе, а в неявных предположениях. При недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-06-field-performance-capstone",
"title": "Большой разбор производительности: кейс с ограничениями и выводами",
"date": "2027-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «большой разбор производительности». Типичная ситуация выглядит так: команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Показать путь от жалобы пользователя до ограничения, которое реально изменило время ответа.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «большой разбор производительности» вернётся в следующем релизе под другим именем. Производительность — это последовательность измерений и гипотез, а не коллекция популярных оптимизаций.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-06-mechanism-performance-capstone",
"title": "Большой разбор производительности: как принять инженерное решение",
"date": "2027-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему производительность — это последовательность измерений и гипотез, а не коллекция популярных оптимизаций — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «большой разбор производительности». Производительность — это последовательность измерений и гипотез, а не коллекция популярных оптимизаций. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-06-practice-performance-capstone",
"title": "Большой разбор производительности: практический маршрут",
"date": "2027-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Кейс"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как показать путь от жалобы пользователя до ограничения, которое реально изменило время ответа. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «большой разбор производительности». Цель заметки — показать путь от жалобы пользователя до ограничения, которое реально изменило время ответа. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Производительность — это последовательность измерений и гипотез, а не коллекция популярных оптимизаций.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: показать путь от жалобы пользователя до ограничения, которое реально изменило время ответа.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «большой разбор производительности» не в сложном синтаксисе, а в неявных предположениях. Команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-05-field-http-tls-guide",
"title": "Полевой справочник HTTP и TLS: кейс с ограничениями и выводами",
"date": "2027-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «полевой справочник HTTP и TLS». Типичная ситуация выглядит так: ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Связать статусы, кеш, таймауты, сертификаты и поведение клиента в одной модели.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «полевой справочник HTTP и TLS» вернётся в следующем релизе под другим именем. Протокол становится понятным, когда каждый симптом сопоставлен с уровнем, который его создаёт.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-05-mechanism-http-tls-guide",
"title": "Полевой справочник HTTP и TLS: как принять инженерное решение",
"date": "2027-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему протокол становится понятным, когда каждый симптом сопоставлен с уровнем, который его создаёт — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «полевой справочник HTTP и TLS». Протокол становится понятным, когда каждый симптом сопоставлен с уровнем, который его создаёт. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-05-practice-http-tls-guide",
"title": "Полевой справочник HTTP и TLS: практический маршрут",
"date": "2027-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как связать статусы, кеш, таймауты, сертификаты и поведение клиента в одной модели. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «полевой справочник HTTP и TLS». Цель заметки — связать статусы, кеш, таймауты, сертификаты и поведение клиента в одной модели. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Протокол становится понятным, когда каждый симптом сопоставлен с уровнем, который его создаёт.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: связать статусы, кеш, таймауты, сертификаты и поведение клиента в одной модели.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «полевой справочник HTTP и TLS» не в сложном синтаксисе, а в неявных предположениях. Ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-04-field-build-evolution",
"title": "Эволюция frontend-сборки: кейс с ограничениями и выводами",
"date": "2027-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Сборка"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «эволюция frontend-сборки». Типичная ситуация выглядит так: новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отделить полезные идеи модульности и кеширования от конкретной моды на инструмент.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «эволюция frontend-сборки» вернётся в следующем релизе под другим именем. Сборка существует, чтобы создать предсказуемый артефакт с понятным графом зависимостей.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-04-mechanism-build-evolution",
"title": "Эволюция frontend-сборки: как принять инженерное решение",
"date": "2027-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Сборка"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему сборка существует, чтобы создать предсказуемый артефакт с понятным графом зависимостей — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «эволюция frontend-сборки». Сборка существует, чтобы создать предсказуемый артефакт с понятным графом зависимостей. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-04-practice-build-evolution",
"title": "Эволюция frontend-сборки: практический маршрут",
"date": "2027-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Сборка"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как отделить полезные идеи модульности и кеширования от конкретной моды на инструмент. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «эволюция frontend-сборки». Цель заметки — отделить полезные идеи модульности и кеширования от конкретной моды на инструмент. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сборка существует, чтобы создать предсказуемый артефакт с понятным графом зависимостей.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отделить полезные идеи модульности и кеширования от конкретной моды на инструмент.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «эволюция frontend-сборки» не в сложном синтаксисе, а в неявных предположениях. Новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-03-field-d-lessons",
"title": "Уроки D для прикладного инженера: кейс с ограничениями и выводами",
"date": "2027-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Производительность"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Кейс о том, как проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «уроки D для прикладного инженера». Типичная ситуация выглядит так: проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Перенести привычку измерять память, типы и время выполнения в любой язык.</p>\n<pre><code>dub test\ndub build --build=release\n# Затем измеряем latency, память и профиль аллокаций.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «уроки D для прикладного инженера» вернётся в следующем релизе под другим именем. Системное мышление полезно даже в высокоуровневом стеке: оно задаёт вопросы о цене абстракции.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-03-mechanism-d-lessons",
"title": "Уроки D для прикладного инженера: как принять инженерное решение",
"date": "2027-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Производительность"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Разбираем, почему системное мышление полезно даже в высокоуровневом стеке: оно задаёт вопросы о цене абстракции — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «уроки D для прикладного инженера». Системное мышление полезно даже в высокоуровневом стеке: оно задаёт вопросы о цене абстракции. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>import core.memory : GC;\n\nGC.disable();\nscope(exit) GC.enable();\n\n// Использовать только в измеренном локальном участке.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-03-practice-d-lessons",
"title": "Уроки D для прикладного инженера: практический маршрут",
"date": "2027-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Производительность"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Практическая заметка о том, как перенести привычку измерять память, типы и время выполнения в любой язык. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «уроки D для прикладного инженера». Цель заметки — перенести привычку измерять память, типы и время выполнения в любой язык. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Системное мышление полезно даже в высокоуровневом стеке: оно задаёт вопросы о цене абстракции.</p>\n<pre><code>enum table = buildLookupTable();\nstatic assert(table.length == 256);\n\nvoid handleRequest() {\n // В горячем пути сначала измеряем аллокации.\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: перенести привычку измерять память, типы и время выполнения в любой язык.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «уроки D для прикладного инженера» не в сложном синтаксисе, а в неявных предположениях. Проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-02-field-bitrix-lessons",
"title": "Уроки Bitrix для legacy-разработки: кейс с ограничениями и выводами",
"date": "2027-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Legacy"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Кейс о том, как новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «уроки Bitrix для legacy-разработки». Типичная ситуация выглядит так: новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Превратить опыт работы с большой CMS в общие принципы сопровождения зрелых систем.</p>\n<pre><code>$required = [&#039;IBLOCK_ID&#039;, &#039;NAME&#039;];\nforeach ($required as $field) {\n if (empty($fields[$field])) {\n throw new InvalidArgumentException($field . &#039; is required&#039;);\n }\n}</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «уроки Bitrix для legacy-разработки» вернётся в следующем релизе под другим именем. Ценность legacy-системы в её реальных правилах и интеграциях, которые нельзя увидеть по одной папке исходников.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-02-mechanism-bitrix-lessons",
"title": "Уроки Bitrix для legacy-разработки: как принять инженерное решение",
"date": "2027-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Legacy"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Разбираем, почему ценность legacy-системы в её реальных правилах и интеграциях, которые нельзя увидеть по одной папке исходников — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «уроки Bitrix для legacy-разработки». Ценность legacy-системы в её реальных правилах и интеграциях, которые нельзя увидеть по одной папке исходников. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>$id = $element-&gt;Add($fields);\nif ($id === false) {\n error_log(&#039;Bitrix error: &#039; . $element-&gt;LAST_ERROR);\n return null;\n}\nreturn (int) $id;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-02-practice-bitrix-lessons",
"title": "Уроки Bitrix для legacy-разработки: практический маршрут",
"date": "2027-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Legacy"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Практическая заметка о том, как превратить опыт работы с большой CMS в общие принципы сопровождения зрелых систем. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «уроки Bitrix для legacy-разработки». Цель заметки — превратить опыт работы с большой CMS в общие принципы сопровождения зрелых систем. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Ценность legacy-системы в её реальных правилах и интеграциях, которые нельзя увидеть по одной папке исходников.</p>\n<pre><code>CModule::IncludeModule(&#039;iblock&#039;);\n$element = new CIBlockElement();\n$id = $element-&gt;Add([\n &#039;IBLOCK_ID&#039; =&gt; 12,\n &#039;NAME&#039; =&gt; $name,\n &#039;ACTIVE&#039; =&gt; &#039;Y&#039;,\n &#039;PROPERTY_VALUES&#039; =&gt; $properties,\n]);\nif (!$id) {\n throw new RuntimeException($element-&gt;LAST_ERROR);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: превратить опыт работы с большой CMS в общие принципы сопровождения зрелых систем.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «уроки Bitrix для legacy-разработки» не в сложном синтаксисе, а в неявных предположениях. Новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-01-field-debugging-decade",
"title": "Десять лет web-диагностики: кейс с ограничениями и выводами",
"date": "2027-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Диагностика",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как после многих часов правки кода выясняется, что проблема была в конфигурации прокси. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Этот разбор собирает повторяющийся паттерн из многих проектов, не выдавая его за универсальную истину. Тема: «десять лет web-диагностики». Типичная ситуация выглядит так: после многих часов правки кода выясняется, что проблема была в конфигурации прокси. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Собрать общий алгоритм поиска причины от симптома до проверяемой гипотезы.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «десять лет web-диагностики» вернётся в следующем релизе под другим именем. Диагностика движется по слоям: пользовательский эффект, запрос, приложение, зависимость, среда.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2027-01-mechanism-debugging-decade",
"title": "Десять лет web-диагностики: как принять инженерное решение",
"date": "2027-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Диагностика",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему диагностика движется по слоям: пользовательский эффект, запрос, приложение, зависимость, среда — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Зрелая техническая заметка должна оставить читателю не только команду, но и модель для следующей незнакомой ситуации. Разберём «десять лет web-диагностики». Диагностика движется по слоям: пользовательский эффект, запрос, приложение, зависимость, среда. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после многих часов правки кода выясняется, что проблема была в конфигурации прокси. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2027-01-practice-debugging-decade",
"title": "Десять лет web-диагностики: практический маршрут",
"date": "2027-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Диагностика",
"Развитие"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как собрать общий алгоритм поиска причины от симптома до проверяемой гипотезы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>После десяти лет заметок я всё чаще начинаю не с инструмента, а с наблюдаемого эффекта и способа его измерить. Тема этой практики: «десять лет web-диагностики». Цель заметки — собрать общий алгоритм поиска причины от симптома до проверяемой гипотезы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Диагностика движется по слоям: пользовательский эффект, запрос, приложение, зависимость, среда.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: собрать общий алгоритм поиска причины от симптома до проверяемой гипотезы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после многих часов правки кода выясняется, что проблема была в конфигурации прокси.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «десять лет web-диагностики» не в сложном синтаксисе, а в неявных предположениях. После многих часов правки кода выясняется, что проблема была в конфигурации прокси. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Пусть это будет не финальный ответ, а хороший вопрос и проверяемый следующий шаг.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-12-field-portfolio-case",
"title": "Инженерный кейс от проблемы до результата: кейс с ограничениями и выводами",
"date": "2026-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как в презентации есть новая архитектура, но нет данных, какую проблему она реально решила. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «инженерный кейс от проблемы до результата». Типичная ситуация выглядит так: в презентации есть новая архитектура, но нет данных, какую проблему она реально решила. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Показать не только красивый финал, но и путь измерения, решения и последствий.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «инженерный кейс от проблемы до результата» вернётся в следующем релизе под другим именем. Хороший кейс сохраняет исходные условия и критерии успеха, иначе его нельзя перенести в другой контекст.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-12-mechanism-portfolio-case",
"title": "Инженерный кейс от проблемы до результата: как принять инженерное решение",
"date": "2026-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему хороший кейс сохраняет исходные условия и критерии успеха, иначе его нельзя перенести в другой контекст — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «инженерный кейс от проблемы до результата». Хороший кейс сохраняет исходные условия и критерии успеха, иначе его нельзя перенести в другой контекст. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в презентации есть новая архитектура, но нет данных, какую проблему она реально решила. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-12-practice-portfolio-case",
"title": "Инженерный кейс от проблемы до результата: практический маршрут",
"date": "2026-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как показать не только красивый финал, но и путь измерения, решения и последствий. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «инженерный кейс от проблемы до результата». Цель заметки — показать не только красивый финал, но и путь измерения, решения и последствий. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хороший кейс сохраняет исходные условия и критерии успеха, иначе его нельзя перенести в другой контекст.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: показать не только красивый финал, но и путь измерения, решения и последствий.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в презентации есть новая архитектура, но нет данных, какую проблему она реально решила.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «инженерный кейс от проблемы до результата» не в сложном синтаксисе, а в неявных предположениях. В презентации есть новая архитектура, но нет данных, какую проблему она реально решила. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-11-field-technology-evaluation",
"title": "Сравнение технологий без хайпа: кейс с ограничениями и выводами",
"date": "2026-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «сравнение технологий без хайпа». Типичная ситуация выглядит так: команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбирать инструмент по задаче, зрелости команды, ограничениям и стоимости выхода.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «сравнение технологий без хайпа» вернётся в следующем релизе под другим именем. Технология не бывает лучшей вообще: у неё есть сильные стороны, риски и цена сопровождения.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-11-mechanism-technology-evaluation",
"title": "Сравнение технологий без хайпа: как принять инженерное решение",
"date": "2026-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему технология не бывает лучшей вообще: у неё есть сильные стороны, риски и цена сопровождения — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «сравнение технологий без хайпа». Технология не бывает лучшей вообще: у неё есть сильные стороны, риски и цена сопровождения. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-11-practice-technology-evaluation",
"title": "Сравнение технологий без хайпа: практический маршрут",
"date": "2026-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как выбирать инструмент по задаче, зрелости команды, ограничениям и стоимости выхода. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «сравнение технологий без хайпа». Цель заметки — выбирать инструмент по задаче, зрелости команды, ограничениям и стоимости выхода. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Технология не бывает лучшей вообще: у неё есть сильные стороны, риски и цена сопровождения.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбирать инструмент по задаче, зрелости команды, ограничениям и стоимости выхода.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «сравнение технологий без хайпа» не в сложном синтаксисе, а в неявных предположениях. Команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-10-field-code-review-standard",
"title": "Стандарт code review: кейс с ограничениями и выводами",
"date": "2026-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как в PR много комментариев по форматированию, но никто не заметил необратимую миграцию. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «стандарт code review». Типичная ситуация выглядит так: в PR много комментариев по форматированию, но никто не заметил необратимую миграцию. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать ревью способом снизить риск и передать контекст, а не соревнованием в замечаниях.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «стандарт code review» вернётся в следующем релизе под другим именем. Хорошее ревью проверяет цель изменения, границы, тесты, наблюдение и будущую поддержку.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-10-mechanism-code-review-standard",
"title": "Стандарт code review: как принять инженерное решение",
"date": "2026-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему хорошее ревью проверяет цель изменения, границы, тесты, наблюдение и будущую поддержку — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «стандарт code review». Хорошее ревью проверяет цель изменения, границы, тесты, наблюдение и будущую поддержку. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в PR много комментариев по форматированию, но никто не заметил необратимую миграцию. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-10-practice-code-review-standard",
"title": "Стандарт code review: практический маршрут",
"date": "2026-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать ревью способом снизить риск и передать контекст, а не соревнованием в замечаниях. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «стандарт code review». Цель заметки — сделать ревью способом снизить риск и передать контекст, а не соревнованием в замечаниях. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хорошее ревью проверяет цель изменения, границы, тесты, наблюдение и будущую поддержку.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать ревью способом снизить риск и передать контекст, а не соревнованием в замечаниях.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в PR много комментариев по форматированию, но никто не заметил необратимую миграцию.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «стандарт code review» не в сложном синтаксисе, а в неявных предположениях. В PR много комментариев по форматированию, но никто не заметил необратимую миграцию. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-09-field-frontend-backend-boundary",
"title": "Граница frontend и backend: кейс с ограничениями и выводами",
"date": "2026-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Backend"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «граница frontend и backend». Типичная ситуация выглядит так: правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Разделить представление, контракт и бизнес-правила без дублирования логики.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «граница frontend и backend» вернётся в следующем релизе под другим именем. Граница определяется ответственностью за данные и решение, а не только HTTP-вызовом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-09-mechanism-frontend-backend-boundary",
"title": "Граница frontend и backend: как принять инженерное решение",
"date": "2026-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Backend"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему граница определяется ответственностью за данные и решение, а не только HTTP-вызовом — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «граница frontend и backend». Граница определяется ответственностью за данные и решение, а не только HTTP-вызовом. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-09-practice-frontend-backend-boundary",
"title": "Граница frontend и backend: практический маршрут",
"date": "2026-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Backend"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как разделить представление, контракт и бизнес-правила без дублирования логики. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «граница frontend и backend». Цель заметки — разделить представление, контракт и бизнес-правила без дублирования логики. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Граница определяется ответственностью за данные и решение, а не только HTTP-вызовом.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: разделить представление, контракт и бизнес-правила без дублирования логики.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «граница frontend и backend» не в сложном синтаксисе, а в неявных предположениях. Правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-08-field-end-to-end-observability",
"title": "End-to-end наблюдаемость: кейс с ограничениями и выводами",
"date": "2026-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Тестирование"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как e2e-тест упал, но невозможно быстро найти соответствующий backend-запрос. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «end-to-end наблюдаемость». Типичная ситуация выглядит так: e2e-тест упал, но невозможно быстро найти соответствующий backend-запрос. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Связать пользовательское действие, браузерный тест, API и инфраструктурный trace.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «end-to-end наблюдаемость» вернётся в следующем релизе под другим именем. Сигналы полезны, когда общий идентификатор сохраняется через все границы системы.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-08-mechanism-end-to-end-observability",
"title": "End-to-end наблюдаемость: как принять инженерное решение",
"date": "2026-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Тестирование"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему сигналы полезны, когда общий идентификатор сохраняется через все границы системы — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «end-to-end наблюдаемость». Сигналы полезны, когда общий идентификатор сохраняется через все границы системы. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: e2e-тест упал, но невозможно быстро найти соответствующий backend-запрос. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-08-practice-end-to-end-observability",
"title": "End-to-end наблюдаемость: практический маршрут",
"date": "2026-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Тестирование"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как связать пользовательское действие, браузерный тест, API и инфраструктурный trace. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «end-to-end наблюдаемость». Цель заметки — связать пользовательское действие, браузерный тест, API и инфраструктурный trace. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сигналы полезны, когда общий идентификатор сохраняется через все границы системы.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: связать пользовательское действие, браузерный тест, API и инфраструктурный trace.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: e2e-тест упал, но невозможно быстро найти соответствующий backend-запрос.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «end-to-end наблюдаемость» не в сложном синтаксисе, а в неявных предположениях. E2e-тест упал, но невозможно быстро найти соответствующий backend-запрос. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-07-field-migration-playbook",
"title": "План миграции: кейс с ограничениями и выводами",
"date": "2026-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Миграции",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как перенос авторизации уже начался, но команда не знает, как откатиться при сбое. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «план миграции». Типичная ситуация выглядит так: перенос авторизации уже начался, но команда не знает, как откатиться при сбое. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать переход обратимым и контролируемым через этапы и сигналы.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «план миграции» вернётся в следующем релизе под другим именем. Миграция безопасна, когда старая и новая схема могут сосуществовать, а переключение измеримо.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-07-mechanism-migration-playbook",
"title": "План миграции: как принять инженерное решение",
"date": "2026-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Миграции",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему миграция безопасна, когда старая и новая схема могут сосуществовать, а переключение измеримо — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «план миграции». Миграция безопасна, когда старая и новая схема могут сосуществовать, а переключение измеримо. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: перенос авторизации уже начался, но команда не знает, как откатиться при сбое. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-07-practice-migration-playbook",
"title": "План миграции: практический маршрут",
"date": "2026-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Миграции",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать переход обратимым и контролируемым через этапы и сигналы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «план миграции». Цель заметки — сделать переход обратимым и контролируемым через этапы и сигналы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Миграция безопасна, когда старая и новая схема могут сосуществовать, а переключение измеримо.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать переход обратимым и контролируемым через этапы и сигналы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: перенос авторизации уже начался, но команда не знает, как откатиться при сбое.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «план миграции» не в сложном синтаксисе, а в неявных предположениях. Перенос авторизации уже начался, но команда не знает, как откатиться при сбое. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-06-field-multi-runtime",
"title": "PHP, JavaScript и D в одном ландшафте: кейс с ограничениями и выводами",
"date": "2026-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"JavaScript",
"DLang"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Кейс о том, как часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «PHP, JavaScript и D в одном ландшафте». Типичная ситуация выглядит так: часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбирать язык по границе задачи, а не пытаться сделать один стек универсальным.</p>\n<pre><code>dub test\ndub build --build=release\n# Затем измеряем latency, память и профиль аллокаций.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «PHP, JavaScript и D в одном ландшафте» вернётся в следующем релизе под другим именем. Разные runtime полезны, если контракты, наблюдение и владение системой остаются едиными.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-06-mechanism-multi-runtime",
"title": "PHP, JavaScript и D в одном ландшафте: как принять инженерное решение",
"date": "2026-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"JavaScript",
"DLang"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Разбираем, почему разные runtime полезны, если контракты, наблюдение и владение системой остаются едиными — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «PHP, JavaScript и D в одном ландшафте». Разные runtime полезны, если контракты, наблюдение и владение системой остаются едиными. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>import core.memory : GC;\n\nGC.disable();\nscope(exit) GC.enable();\n\n// Использовать только в измеренном локальном участке.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-06-practice-multi-runtime",
"title": "PHP, JavaScript и D в одном ландшафте: практический маршрут",
"date": "2026-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"JavaScript",
"DLang"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Практическая заметка о том, как выбирать язык по границе задачи, а не пытаться сделать один стек универсальным. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «PHP, JavaScript и D в одном ландшафте». Цель заметки — выбирать язык по границе задачи, а не пытаться сделать один стек универсальным. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Разные runtime полезны, если контракты, наблюдение и владение системой остаются едиными.</p>\n<pre><code>enum table = buildLookupTable();\nstatic assert(table.length == 256);\n\nvoid handleRequest() {\n // В горячем пути сначала измеряем аллокации.\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбирать язык по границе задачи, а не пытаться сделать один стек универсальным.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «PHP, JavaScript и D в одном ландшафте» не в сложном синтаксисе, а в неявных предположениях. Часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-05-field-systems-performance",
"title": "Производительность системы: кейс с ограничениями и выводами",
"date": "2026-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как график CPU низкий, но пользователь всё равно ждёт страницу десять секунд. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «производительность системы». Типичная ситуация выглядит так: график CPU низкий, но пользователь всё равно ждёт страницу десять секунд. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Искать узкое место по измерению, а не оптимизировать самый знакомый компонент.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «производительность системы» вернётся в следующем релизе под другим именем. Задержка запроса складывается из очереди, сети, CPU, диска, базы и внешних вызовов.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-05-mechanism-systems-performance",
"title": "Производительность системы: как принять инженерное решение",
"date": "2026-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему задержка запроса складывается из очереди, сети, CPU, диска, базы и внешних вызовов — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «производительность системы». Задержка запроса складывается из очереди, сети, CPU, диска, базы и внешних вызовов. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: график CPU низкий, но пользователь всё равно ждёт страницу десять секунд. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-05-practice-systems-performance",
"title": "Производительность системы: практический маршрут",
"date": "2026-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как искать узкое место по измерению, а не оптимизировать самый знакомый компонент. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «производительность системы». Цель заметки — искать узкое место по измерению, а не оптимизировать самый знакомый компонент. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Задержка запроса складывается из очереди, сети, CPU, диска, базы и внешних вызовов.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: искать узкое место по измерению, а не оптимизировать самый знакомый компонент.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: график CPU низкий, но пользователь всё равно ждёт страницу десять секунд.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «производительность системы» не в сложном синтаксисе, а в неявных предположениях. График CPU низкий, но пользователь всё равно ждёт страницу десять секунд. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-04-field-modern-web-security",
"title": "Современная безопасность веба: кейс с ограничениями и выводами",
"date": "2026-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «современная безопасность веба». Типичная ситуация выглядит так: исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Соединить защиту браузера, API, сессии и цепочки доставки в одну модель.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «современная безопасность веба» вернётся в следующем релизе под другим именем. Угроза проходит через границы: ввод, браузер, сеть, сервис, зависимость и оператор.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-04-mechanism-modern-web-security",
"title": "Современная безопасность веба: как принять инженерное решение",
"date": "2026-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему угроза проходит через границы: ввод, браузер, сеть, сервис, зависимость и оператор — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «современная безопасность веба». Угроза проходит через границы: ввод, браузер, сеть, сервис, зависимость и оператор. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-04-practice-modern-web-security",
"title": "Современная безопасность веба: практический маршрут",
"date": "2026-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как соединить защиту браузера, API, сессии и цепочки доставки в одну модель. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «современная безопасность веба». Цель заметки — соединить защиту браузера, API, сессии и цепочки доставки в одну модель. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Угроза проходит через границы: ввод, браузер, сеть, сервис, зависимость и оператор.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: соединить защиту браузера, API, сессии и цепочки доставки в одну модель.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «современная безопасность веба» не в сложном синтаксисе, а в неявных предположениях. Исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-03-field-data-contracts",
"title": "Контракты данных: кейс с ограничениями и выводами",
"date": "2026-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как поле status одинаково называется в двух сервисах, но означает разные стадии процесса. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «контракты данных». Типичная ситуация выглядит так: поле status одинаково называется в двух сервисах, но означает разные стадии процесса. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Зафиксировать смысл полей, владельца и правила совместимости между системами.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «контракты данных» вернётся в следующем релизе под другим именем. Схема без семантики не защищает от неверной трактовки значения и времени его актуальности.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-03-mechanism-data-contracts",
"title": "Контракты данных: как принять инженерное решение",
"date": "2026-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему схема без семантики не защищает от неверной трактовки значения и времени его актуальности — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «контракты данных». Схема без семантики не защищает от неверной трактовки значения и времени его актуальности. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: поле status одинаково называется в двух сервисах, но означает разные стадии процесса. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-03-practice-data-contracts",
"title": "Контракты данных: практический маршрут",
"date": "2026-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как зафиксировать смысл полей, владельца и правила совместимости между системами. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «контракты данных». Цель заметки — зафиксировать смысл полей, владельца и правила совместимости между системами. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Схема без семантики не защищает от неверной трактовки значения и времени его актуальности.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: зафиксировать смысл полей, владельца и правила совместимости между системами.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: поле status одинаково называется в двух сервисах, но означает разные стадии процесса.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «контракты данных» не в сложном синтаксисе, а в неявных предположениях. Поле status одинаково называется в двух сервисах, но означает разные стадии процесса. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-02-field-resilience",
"title": "Устойчивость к отказам: кейс с ограничениями и выводами",
"date": "2026-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «устойчивость к отказам». Типичная ситуация выглядит так: внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проектировать ожидаемые сбои зависимостей как штатный сценарий.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «устойчивость к отказам» вернётся в следующем релизе под другим именем. Timeout, retry, limit и fallback работают только вместе с наблюдением и ограничением нагрузки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-02-mechanism-resilience",
"title": "Устойчивость к отказам: как принять инженерное решение",
"date": "2026-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему timeout, retry, limit и fallback работают только вместе с наблюдением и ограничением нагрузки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «устойчивость к отказам». Timeout, retry, limit и fallback работают только вместе с наблюдением и ограничением нагрузки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-02-practice-resilience",
"title": "Устойчивость к отказам: практический маршрут",
"date": "2026-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как проектировать ожидаемые сбои зависимостей как штатный сценарий. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «устойчивость к отказам». Цель заметки — проектировать ожидаемые сбои зависимостей как штатный сценарий. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Timeout, retry, limit и fallback работают только вместе с наблюдением и ограничением нагрузки.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проектировать ожидаемые сбои зависимостей как штатный сценарий.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «устойчивость к отказам» не в сложном синтаксисе, а в неявных предположениях. Внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-01-field-platform-api",
"title": "API платформенной команды: кейс с ограничениями и выводами",
"date": "2026-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"API"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Кейс о том, как команды начинают обходить внутренний сервис напрямую через базу данных. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Ниже не универсальный рецепт, а воспроизводимый способ исследовать решение с его ограничениями. Тема: «API платформенной команды». Типичная ситуация выглядит так: команды начинают обходить внутренний сервис напрямую через базу данных. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать внутренний сервис удобным для потребителя и управляемым для владельца.</p>\n<pre><code>1. Указать владельца API.\n2. Зафиксировать SLO и документацию.\n3. Включить наблюдение до запуска.\n4. Продумать путь отката.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «API платформенной команды» вернётся в следующем релизе под другим именем. Платформенный API — это контракт, документация, поддержка изменений и измеряемый уровень сервиса.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2026-01-mechanism-platform-api",
"title": "API платформенной команды: как принять инженерное решение",
"date": "2026-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"API"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Разбираем, почему платформенный API — это контракт, документация, поддержка изменений и измеряемый уровень сервиса — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Системное объяснение должно связывать локальный код с тем, что увидит пользователь и оператор. Разбираем «API платформенной команды». Платформенный API — это контракт, документация, поддержка изменений и измеряемый уровень сервиса. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>kubectl apply -f deployment.yaml\nkubectl rollout status deployment/app --timeout=10m\nkubectl rollout history deployment/app\nkubectl rollout undo deployment/app</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команды начинают обходить внутренний сервис напрямую через базу данных. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2026-01-practice-platform-api",
"title": "API платформенной команды: практический маршрут",
"date": "2026-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"API"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Практическая заметка о том, как сделать внутренний сервис удобным для потребителя и управляемым для владельца. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>На стыке frontend, backend и платформы решение нужно оценивать как часть целой системы. Практический разбор темы «API платформенной команды». Цель заметки — сделать внутренний сервис удобным для потребителя и управляемым для владельца. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Платформенный API — это контракт, документация, поддержка изменений и измеряемый уровень сервиса.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: app\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: app\n image: registry.example/app:sha</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать внутренний сервис удобным для потребителя и управляемым для владельца.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команды начинают обходить внутренний сервис напрямую через базу данных.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «API платформенной команды» не в сложном синтаксисе, а в неявных предположениях. Команды начинают обходить внутренний сервис напрямую через базу данных. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Именно так отдельная практика превращается в переносимый инженерный метод.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-12-field-year-synthesis",
"title": "Синтез инженерного года: кейс с ограничениями и выводами",
"date": "2025-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «синтез инженерного года». Типичная ситуация выглядит так: статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выделить несколько устойчивых принципов из десятков частных задач.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «синтез инженерного года» вернётся в следующем релизе под другим именем. Синтез не заменяет детали, но показывает повторяющийся механизм за разными инцидентами.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-12-mechanism-year-synthesis",
"title": "Синтез инженерного года: как принять инженерное решение",
"date": "2025-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему синтез не заменяет детали, но показывает повторяющийся механизм за разными инцидентами — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «синтез инженерного года». Синтез не заменяет детали, но показывает повторяющийся механизм за разными инцидентами. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-12-practice-year-synthesis",
"title": "Синтез инженерного года: практический маршрут",
"date": "2025-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Развитие",
"Автор"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как выделить несколько устойчивых принципов из десятков частных задач. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «синтез инженерного года». Цель заметки — выделить несколько устойчивых принципов из десятков частных задач. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Синтез не заменяет детали, но показывает повторяющийся механизм за разными инцидентами.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выделить несколько устойчивых принципов из десятков частных задач.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «синтез инженерного года» не в сложном синтаксисе, а в неявных предположениях. Статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-11-field-research-method",
"title": "Проверка источников: кейс с ограничениями и выводами",
"date": "2025-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Исследование",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как популярный ответ из поиска повторяют годами, хотя API давно поменялся. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «проверка источников». Типичная ситуация выглядит так: популярный ответ из поиска повторяют годами, хотя API давно поменялся. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Разделять официальную документацию, наблюдение в проекте и личную гипотезу.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «проверка источников» вернётся в следующем релизе под другим именем. Дата, версия и контекст источника меняют смысл даже технически верного совета.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-11-mechanism-research-method",
"title": "Проверка источников: как принять инженерное решение",
"date": "2025-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Исследование",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему дата, версия и контекст источника меняют смысл даже технически верного совета — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «проверка источников». Дата, версия и контекст источника меняют смысл даже технически верного совета. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: популярный ответ из поиска повторяют годами, хотя API давно поменялся. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-11-practice-research-method",
"title": "Проверка источников: практический маршрут",
"date": "2025-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Исследование",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как разделять официальную документацию, наблюдение в проекте и личную гипотезу. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «проверка источников». Цель заметки — разделять официальную документацию, наблюдение в проекте и личную гипотезу. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Дата, версия и контекст источника меняют смысл даже технически верного совета.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: разделять официальную документацию, наблюдение в проекте и личную гипотезу.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: популярный ответ из поиска повторяют годами, хотя API давно поменялся.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «проверка источников» не в сложном синтаксисе, а в неявных предположениях. Популярный ответ из поиска повторяют годами, хотя API давно поменялся. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-10-field-teaching-engineering",
"title": "Объяснение сложной темы: кейс с ограничениями и выводами",
"date": "2025-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Обучение",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как после статьи читатель копирует команду, но не знает, что делать при другой ошибке. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «объяснение сложной темы». Типичная ситуация выглядит так: после статьи читатель копирует команду, но не знает, что делать при другой ошибке. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Вести читателя от наблюдаемого симптома к модели и самостоятельной проверке.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «объяснение сложной темы» вернётся в следующем релизе под другим именем. Обучающая заметка ценна, когда человек может повторить результат и понять, почему он получился.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-10-mechanism-teaching-engineering",
"title": "Объяснение сложной темы: как принять инженерное решение",
"date": "2025-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Обучение",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему обучающая заметка ценна, когда человек может повторить результат и понять, почему он получился — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «объяснение сложной темы». Обучающая заметка ценна, когда человек может повторить результат и понять, почему он получился. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после статьи читатель копирует команду, но не знает, что делать при другой ошибке. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-10-practice-teaching-engineering",
"title": "Объяснение сложной темы: практический маршрут",
"date": "2025-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Обучение",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как вести читателя от наблюдаемого симптома к модели и самостоятельной проверке. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «объяснение сложной темы». Цель заметки — вести читателя от наблюдаемого симптома к модели и самостоятельной проверке. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Обучающая заметка ценна, когда человек может повторить результат и понять, почему он получился.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: вести читателя от наблюдаемого симптома к модели и самостоятельной проверке.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после статьи читатель копирует команду, но не знает, что делать при другой ошибке.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «объяснение сложной темы» не в сложном синтаксисе, а в неявных предположениях. После статьи читатель копирует команду, но не знает, что делать при другой ошибке. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-09-field-engineering-interviews",
"title": "Инженерное интервью: кейс с ограничениями и выводами",
"date": "2025-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Команда",
"Интервью"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «инженерное интервью». Типичная ситуация выглядит так: эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Собирать опыт через конкретный контекст, решение и последствия, а не через абстрактный совет.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «инженерное интервью» вернётся в следующем релизе под другим именем. Хорошее интервью раскрывает ход мысли и ограничения, поэтому полезно другим инженерам.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-09-mechanism-engineering-interviews",
"title": "Инженерное интервью: как принять инженерное решение",
"date": "2025-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Команда",
"Интервью"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему хорошее интервью раскрывает ход мысли и ограничения, поэтому полезно другим инженерам — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «инженерное интервью». Хорошее интервью раскрывает ход мысли и ограничения, поэтому полезно другим инженерам. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-09-practice-engineering-interviews",
"title": "Инженерное интервью: практический маршрут",
"date": "2025-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Команда",
"Интервью"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как собирать опыт через конкретный контекст, решение и последствия, а не через абстрактный совет. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «инженерное интервью». Цель заметки — собирать опыт через конкретный контекст, решение и последствия, а не через абстрактный совет. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хорошее интервью раскрывает ход мысли и ограничения, поэтому полезно другим инженерам.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: собирать опыт через конкретный контекст, решение и последствия, а не через абстрактный совет.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «инженерное интервью» не в сложном синтаксисе, а в неявных предположениях. Эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-08-field-tool-ux-research",
"title": "Исследование UX внутреннего инструмента: кейс с ограничениями и выводами",
"date": "2025-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инструменты",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «исследование UX внутреннего инструмента». Типичная ситуация выглядит так: команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Наблюдать реальную работу коллег вместо предположения, где им неудобно.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «исследование UX внутреннего инструмента» вернётся в следующем релизе под другим именем. Внутренний интерфейс тоже продукт: у него есть пользователи, сценарии, ошибки и стоимость времени.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-08-mechanism-tool-ux-research",
"title": "Исследование UX внутреннего инструмента: как принять инженерное решение",
"date": "2025-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инструменты",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему внутренний интерфейс тоже продукт: у него есть пользователи, сценарии, ошибки и стоимость времени — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «исследование UX внутреннего инструмента». Внутренний интерфейс тоже продукт: у него есть пользователи, сценарии, ошибки и стоимость времени. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-08-practice-tool-ux-research",
"title": "Исследование UX внутреннего инструмента: практический маршрут",
"date": "2025-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инструменты",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как наблюдать реальную работу коллег вместо предположения, где им неудобно. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «исследование UX внутреннего инструмента». Цель заметки — наблюдать реальную работу коллег вместо предположения, где им неудобно. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Внутренний интерфейс тоже продукт: у него есть пользователи, сценарии, ошибки и стоимость времени.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: наблюдать реальную работу коллег вместо предположения, где им неудобно.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «исследование UX внутреннего инструмента» не в сложном синтаксисе, а в неявных предположениях. Команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-07-field-product-metrics",
"title": "Метрики продукта для инженера: кейс с ограничениями и выводами",
"date": "2025-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"Аналитика"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как конверсия выросла, но поддержка получила вдвое больше обращений. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «метрики продукта для инженера». Типичная ситуация выглядит так: конверсия выросла, но поддержка получила вдвое больше обращений. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Использовать метрику как сигнал поведения, а не как повод оптимизировать цифру в вакууме.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «метрики продукта для инженера» вернётся в следующем релизе под другим именем. Метрика нужна вместе с сегментом, периодом, ограничениями и возможной побочной ценой.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-07-mechanism-product-metrics",
"title": "Метрики продукта для инженера: как принять инженерное решение",
"date": "2025-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"Аналитика"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему метрика нужна вместе с сегментом, периодом, ограничениями и возможной побочной ценой — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «метрики продукта для инженера». Метрика нужна вместе с сегментом, периодом, ограничениями и возможной побочной ценой. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: конверсия выросла, но поддержка получила вдвое больше обращений. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-07-practice-product-metrics",
"title": "Метрики продукта для инженера: практический маршрут",
"date": "2025-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"Аналитика"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как использовать метрику как сигнал поведения, а не как повод оптимизировать цифру в вакууме. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «метрики продукта для инженера». Цель заметки — использовать метрику как сигнал поведения, а не как повод оптимизировать цифру в вакууме. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Метрика нужна вместе с сегментом, периодом, ограничениями и возможной побочной ценой.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: использовать метрику как сигнал поведения, а не как повод оптимизировать цифру в вакууме.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: конверсия выросла, но поддержка получила вдвое больше обращений.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «метрики продукта для инженера» не в сложном синтаксисе, а в неявных предположениях. Конверсия выросла, но поддержка получила вдвое больше обращений. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-06-field-developer-experience",
"title": "Удобство внутреннего инструмента: кейс с ограничениями и выводами",
"date": "2025-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DX",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как новичок тратит два дня на запуск проекта, хотя сама задача занимает час. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «удобство внутреннего инструмента». Типичная ситуация выглядит так: новичок тратит два дня на запуск проекта, хотя сама задача занимает час. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Измерять путь разработчика от клона репозитория до первого полезного изменения.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «удобство внутреннего инструмента» вернётся в следующем релизе под другим именем. DX складывается из документации, скорости обратной связи, локальной среды и понятных ошибок.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-06-mechanism-developer-experience",
"title": "Удобство внутреннего инструмента: как принять инженерное решение",
"date": "2025-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DX",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему DX складывается из документации, скорости обратной связи, локальной среды и понятных ошибок — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «удобство внутреннего инструмента». DX складывается из документации, скорости обратной связи, локальной среды и понятных ошибок. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новичок тратит два дня на запуск проекта, хотя сама задача занимает час. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-06-practice-developer-experience",
"title": "Удобство внутреннего инструмента: практический маршрут",
"date": "2025-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DX",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как измерять путь разработчика от клона репозитория до первого полезного изменения. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «удобство внутреннего инструмента». Цель заметки — измерять путь разработчика от клона репозитория до первого полезного изменения. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. DX складывается из документации, скорости обратной связи, локальной среды и понятных ошибок.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: измерять путь разработчика от клона репозитория до первого полезного изменения.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новичок тратит два дня на запуск проекта, хотя сама задача занимает час.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «удобство внутреннего инструмента» не в сложном синтаксисе, а в неявных предположениях. Новичок тратит два дня на запуск проекта, хотя сама задача занимает час. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-05-field-engineering-automation",
"title": "Автоматизация рутины: кейс с ограничениями и выводами",
"date": "2025-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автоматизация",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «автоматизация рутины». Типичная ситуация выглядит так: скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбрать процесс, который повторяется достаточно часто и имеет проверяемый результат.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «автоматизация рутины» вернётся в следующем релизе под другим именем. Автоматизация безопасна, когда у неё есть вход, выход, идемпотентность и возможность отмены.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-05-mechanism-engineering-automation",
"title": "Автоматизация рутины: как принять инженерное решение",
"date": "2025-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автоматизация",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему автоматизация безопасна, когда у неё есть вход, выход, идемпотентность и возможность отмены — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «автоматизация рутины». Автоматизация безопасна, когда у неё есть вход, выход, идемпотентность и возможность отмены. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-05-practice-engineering-automation",
"title": "Автоматизация рутины: практический маршрут",
"date": "2025-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Автоматизация",
"Разработка"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как выбрать процесс, который повторяется достаточно часто и имеет проверяемый результат. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «автоматизация рутины». Цель заметки — выбрать процесс, который повторяется достаточно часто и имеет проверяемый результат. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Автоматизация безопасна, когда у неё есть вход, выход, идемпотентность и возможность отмены.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбрать процесс, который повторяется достаточно часто и имеет проверяемый результат.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «автоматизация рутины» не в сложном синтаксисе, а в неявных предположениях. Скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-04-field-ai-data-privacy",
"title": "Данные и приватность в AI-инструментах: кейс с ограничениями и выводами",
"date": "2025-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Безопасность"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Кейс о том, как в prompt попал фрагмент production-лога с персональными данными. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «данные и приватность в AI-инструментах». Типичная ситуация выглядит так: в prompt попал фрагмент production-лога с персональными данными. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. До отправки контекста понять, какие данные можно передавать внешнему сервису.</p>\n<pre><code>1. Убрать секреты и персональные данные из контекста.\n2. Проверить утверждения по источнику.\n3. Запустить тесты и негативные сценарии.\n4. Сохранить решение и причину принятия.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «данные и приватность в AI-инструментах» вернётся в следующем релизе под другим именем. Данные, права доступа, срок хранения и журналирование должны быть частью выбора инструмента.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-04-mechanism-ai-data-privacy",
"title": "Данные и приватность в AI-инструментах: как принять инженерное решение",
"date": "2025-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Безопасность"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Разбираем, почему данные, права доступа, срок хранения и журналирование должны быть частью выбора инструмента — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «данные и приватность в AI-инструментах». Данные, права доступа, срок хранения и журналирование должны быть частью выбора инструмента. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>source -&gt; retrieval -&gt; generated draft -&gt; test -&gt; human decision\n\nДля каждого шага нужны:\nвладелец, ограничение данных и способ проверить результат.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в prompt попал фрагмент production-лога с персональными данными. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-04-practice-ai-data-privacy",
"title": "Данные и приватность в AI-инструментах: практический маршрут",
"date": "2025-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Безопасность"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Практическая заметка о том, как до отправки контекста понять, какие данные можно передавать внешнему сервису. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «данные и приватность в AI-инструментах». Цель заметки — до отправки контекста понять, какие данные можно передавать внешнему сервису. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Данные, права доступа, срок хранения и журналирование должны быть частью выбора инструмента.</p>\n<pre><code>task: &quot;Измени функцию расчёта скидки&quot;\nconstraints: [&quot;не менять публичный API&quot;, &quot;добавить негативные тесты&quot;]\nevidence: [&quot;diff&quot;, &quot;tests&quot;, &quot;security review&quot;]\n\n# Ответ модели — вход в ревью, не доказательство корректности.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: до отправки контекста понять, какие данные можно передавать внешнему сервису.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в prompt попал фрагмент production-лога с персональными данными.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «данные и приватность в AI-инструментах» не в сложном синтаксисе, а в неявных предположениях. В prompt попал фрагмент production-лога с персональными данными. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-03-field-knowledge-retrieval",
"title": "Поиск по инженерной базе знаний: кейс с ограничениями и выводами",
"date": "2025-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Документация"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Кейс о том, как ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «поиск по инженерной базе знаний». Типичная ситуация выглядит так: ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать источник и дату важнее уверенного пересказа.</p>\n<pre><code>1. Убрать секреты и персональные данные из контекста.\n2. Проверить утверждения по источнику.\n3. Запустить тесты и негативные сценарии.\n4. Сохранить решение и причину принятия.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «поиск по инженерной базе знаний» вернётся в следующем релизе под другим именем. Retrieval полезен только при явном корпусе, ссылке на источник и механизме обновления знаний.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-03-mechanism-knowledge-retrieval",
"title": "Поиск по инженерной базе знаний: как принять инженерное решение",
"date": "2025-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Документация"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Разбираем, почему retrieval полезен только при явном корпусе, ссылке на источник и механизме обновления знаний — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «поиск по инженерной базе знаний». Retrieval полезен только при явном корпусе, ссылке на источник и механизме обновления знаний. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>source -&gt; retrieval -&gt; generated draft -&gt; test -&gt; human decision\n\nДля каждого шага нужны:\nвладелец, ограничение данных и способ проверить результат.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-03-practice-knowledge-retrieval",
"title": "Поиск по инженерной базе знаний: практический маршрут",
"date": "2025-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Документация"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Практическая заметка о том, как сделать источник и дату важнее уверенного пересказа. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «поиск по инженерной базе знаний». Цель заметки — сделать источник и дату важнее уверенного пересказа. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Retrieval полезен только при явном корпусе, ссылке на источник и механизме обновления знаний.</p>\n<pre><code>task: &quot;Измени функцию расчёта скидки&quot;\nconstraints: [&quot;не менять публичный API&quot;, &quot;добавить негативные тесты&quot;]\nevidence: [&quot;diff&quot;, &quot;tests&quot;, &quot;security review&quot;]\n\n# Ответ модели — вход в ревью, не доказательство корректности.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать источник и дату важнее уверенного пересказа.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «поиск по инженерной базе знаний» не в сложном синтаксисе, а в неявных предположениях. Ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-02-field-ai-code-verification",
"title": "Проверка сгенерированного кода: кейс с ограничениями и выводами",
"date": "2025-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Качество"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Кейс о том, как ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «проверка сгенерированного кода». Типичная ситуация выглядит так: ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Строить тесты и ревью вокруг риска, а не вокруг доверия к красивому ответу.</p>\n<pre><code>1. Убрать секреты и персональные данные из контекста.\n2. Проверить утверждения по источнику.\n3. Запустить тесты и негативные сценарии.\n4. Сохранить решение и причину принятия.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «проверка сгенерированного кода» вернётся в следующем релизе под другим именем. Проверка включает контракт, негативные сценарии, безопасность и воспроизводимый запуск.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-02-mechanism-ai-code-verification",
"title": "Проверка сгенерированного кода: как принять инженерное решение",
"date": "2025-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Качество"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Разбираем, почему проверка включает контракт, негативные сценарии, безопасность и воспроизводимый запуск — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «проверка сгенерированного кода». Проверка включает контракт, негативные сценарии, безопасность и воспроизводимый запуск. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>source -&gt; retrieval -&gt; generated draft -&gt; test -&gt; human decision\n\nДля каждого шага нужны:\nвладелец, ограничение данных и способ проверить результат.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-02-practice-ai-code-verification",
"title": "Проверка сгенерированного кода: практический маршрут",
"date": "2025-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Качество"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Практическая заметка о том, как строить тесты и ревью вокруг риска, а не вокруг доверия к красивому ответу. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «проверка сгенерированного кода». Цель заметки — строить тесты и ревью вокруг риска, а не вокруг доверия к красивому ответу. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Проверка включает контракт, негативные сценарии, безопасность и воспроизводимый запуск.</p>\n<pre><code>task: &quot;Измени функцию расчёта скидки&quot;\nconstraints: [&quot;не менять публичный API&quot;, &quot;добавить негативные тесты&quot;]\nevidence: [&quot;diff&quot;, &quot;tests&quot;, &quot;security review&quot;]\n\n# Ответ модели — вход в ревью, не доказательство корректности.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: строить тесты и ревью вокруг риска, а не вокруг доверия к красивому ответу.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «проверка сгенерированного кода» не в сложном синтаксисе, а в неявных предположениях. Ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-01-field-ai-coding-assistant",
"title": "Помощник для написания кода: кейс с ограничениями и выводами",
"date": "2025-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Разработка"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Кейс о том, как сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «помощник для написания кода». Типичная ситуация выглядит так: сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Использовать генерацию как ускоритель черновика, не передавая ей ответственность за решение.</p>\n<pre><code>1. Убрать секреты и персональные данные из контекста.\n2. Проверить утверждения по источнику.\n3. Запустить тесты и негативные сценарии.\n4. Сохранить решение и причину принятия.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «помощник для написания кода» вернётся в следующем релизе под другим именем. Модель может предложить форму кода, но требования, тест и проверка остаются задачей инженера.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2025-01-mechanism-ai-coding-assistant",
"title": "Помощник для написания кода: как принять инженерное решение",
"date": "2025-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Разработка"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Разбираем, почему модель может предложить форму кода, но требования, тест и проверка остаются задачей инженера — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «помощник для написания кода». Модель может предложить форму кода, но требования, тест и проверка остаются задачей инженера. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>source -&gt; retrieval -&gt; generated draft -&gt; test -&gt; human decision\n\nДля каждого шага нужны:\nвладелец, ограничение данных и способ проверить результат.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2025-01-practice-ai-coding-assistant",
"title": "Помощник для написания кода: практический маршрут",
"date": "2025-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"AI",
"Разработка"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "Практическая заметка о том, как использовать генерацию как ускоритель черновика, не передавая ей ответственность за решение. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «помощник для написания кода». Цель заметки — использовать генерацию как ускоритель черновика, не передавая ей ответственность за решение. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Модель может предложить форму кода, но требования, тест и проверка остаются задачей инженера.</p>\n<pre><code>task: &quot;Измени функцию расчёта скидки&quot;\nconstraints: [&quot;не менять публичный API&quot;, &quot;добавить негативные тесты&quot;]\nevidence: [&quot;diff&quot;, &quot;tests&quot;, &quot;security review&quot;]\n\n# Ответ модели — вход в ревью, не доказательство корректности.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: использовать генерацию как ускоритель черновика, не передавая ей ответственность за решение.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «помощник для написания кода» не в сложном синтаксисе, а в неявных предположениях. Сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST: Generative AI Profile</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-12-field-maintenance-retro",
"title": "Год сопровождения системы: диагностика, решение и проверка",
"date": "2024-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Сопровождение",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «год сопровождения системы». Типичная ситуация выглядит так: команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Собрать повторяющиеся проблемы в план улучшений вместо списка раздражителей.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «год сопровождения системы» вернётся в следующем релизе под другим именем. Сопровождение — это данные об инцидентах, времени изменений и качестве обратной связи.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-12-mechanism-maintenance-retro",
"title": "Год сопровождения системы: модель, ограничения и границы",
"date": "2024-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Сопровождение",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему сопровождение — это данные об инцидентах, времени изменений и качестве обратной связи — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «год сопровождения системы». Сопровождение — это данные об инцидентах, времени изменений и качестве обратной связи. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-12-practice-maintenance-retro",
"title": "Год сопровождения системы: минимальная инженерная схема",
"date": "2024-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Сопровождение",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как собрать повторяющиеся проблемы в план улучшений вместо списка раздражителей. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «год сопровождения системы». Цель заметки — собрать повторяющиеся проблемы в план улучшений вместо списка раздражителей. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сопровождение — это данные об инцидентах, времени изменений и качестве обратной связи.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: собрать повторяющиеся проблемы в план улучшений вместо списка раздражителей.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «год сопровождения системы» не в сложном синтаксисе, а в неявных предположениях. Команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-11-field-deprecation",
"title": "Удаление устаревшего: диагностика, решение и проверка",
"date": "2024-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"API"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «удаление устаревшего». Типичная ситуация выглядит так: старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Вывести старый API или модуль без внезапного обрыва для потребителей.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «удаление устаревшего» вернётся в следующем релизе под другим именем. Депрекация включает инвентаризацию, предупреждение, миграционный путь и конечную дату.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-11-mechanism-deprecation",
"title": "Удаление устаревшего: модель, ограничения и границы",
"date": "2024-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"API"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему депрекация включает инвентаризацию, предупреждение, миграционный путь и конечную дату — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «удаление устаревшего». Депрекация включает инвентаризацию, предупреждение, миграционный путь и конечную дату. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-11-practice-deprecation",
"title": "Удаление устаревшего: минимальная инженерная схема",
"date": "2024-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"API"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как вывести старый API или модуль без внезапного обрыва для потребителей. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «удаление устаревшего». Цель заметки — вывести старый API или модуль без внезапного обрыва для потребителей. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Депрекация включает инвентаризацию, предупреждение, миграционный путь и конечную дату.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: вывести старый API или модуль без внезапного обрыва для потребителей.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «удаление устаревшего» не в сложном синтаксисе, а в неявных предположениях. Старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-10-field-capacity-cost",
"title": "Ёмкость и стоимость: диагностика, решение и проверка",
"date": "2024-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инфраструктура",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «ёмкость и стоимость». Типичная ситуация выглядит так: счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Измерять потребление ресурсов до того, как оптимизация станет срочной.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «ёмкость и стоимость» вернётся в следующем релизе под другим именем. Стоимость растёт из нагрузки, хранения, трафика и операционной сложности.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-10-mechanism-capacity-cost",
"title": "Ёмкость и стоимость: модель, ограничения и границы",
"date": "2024-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инфраструктура",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему стоимость растёт из нагрузки, хранения, трафика и операционной сложности — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «ёмкость и стоимость». Стоимость растёт из нагрузки, хранения, трафика и операционной сложности. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-10-practice-capacity-cost",
"title": "Ёмкость и стоимость: минимальная инженерная схема",
"date": "2024-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Инфраструктура",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как измерять потребление ресурсов до того, как оптимизация станет срочной. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «ёмкость и стоимость». Цель заметки — измерять потребление ресурсов до того, как оптимизация станет срочной. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Стоимость растёт из нагрузки, хранения, трафика и операционной сложности.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: измерять потребление ресурсов до того, как оптимизация станет срочной.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «ёмкость и стоимость» не в сложном синтаксисе, а в неявных предположениях. Счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-09-field-adr-decisions",
"title": "Запись инженерного решения: диагностика, решение и проверка",
"date": "2024-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как в коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «запись инженерного решения». Типичная ситуация выглядит так: в коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сохранить контекст выбора, чтобы через год не спорить с неизвестным прошлым.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «запись инженерного решения» вернётся в следующем релизе под другим именем. ADR описывает проблему, принятое решение, альтернативы и последствия.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-09-mechanism-adr-decisions",
"title": "Запись инженерного решения: модель, ограничения и границы",
"date": "2024-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему ADR описывает проблему, принятое решение, альтернативы и последствия — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «запись инженерного решения». ADR описывает проблему, принятое решение, альтернативы и последствия. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-09-practice-adr-decisions",
"title": "Запись инженерного решения: минимальная инженерная схема",
"date": "2024-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Документация"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сохранить контекст выбора, чтобы через год не спорить с неизвестным прошлым. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «запись инженерного решения». Цель заметки — сохранить контекст выбора, чтобы через год не спорить с неизвестным прошлым. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. ADR описывает проблему, принятое решение, альтернативы и последствия.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сохранить контекст выбора, чтобы через год не спорить с неизвестным прошлым.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «запись инженерного решения» не в сложном синтаксисе, а в неявных предположениях. В коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-08-field-feature-flags",
"title": "Feature flags: диагностика, решение и проверка",
"date": "2024-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как новая функция работает у сотрудников, но должна быть выключена для всех клиентов. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «feature flags». Типичная ситуация выглядит так: новая функция работает у сотрудников, но должна быть выключена для всех клиентов. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отделить выкладку кода от включения поведения для аудитории.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «feature flags» вернётся в следующем релизе под другим именем. Флаг — это временный контракт с владельцем, условиями включения и датой удаления.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-08-mechanism-feature-flags",
"title": "Feature flags: модель, ограничения и границы",
"date": "2024-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему флаг — это временный контракт с владельцем, условиями включения и датой удаления — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «feature flags». Флаг — это временный контракт с владельцем, условиями включения и датой удаления. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новая функция работает у сотрудников, но должна быть выключена для всех клиентов. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-08-practice-feature-flags",
"title": "Feature flags: минимальная инженерная схема",
"date": "2024-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как отделить выкладку кода от включения поведения для аудитории. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «feature flags». Цель заметки — отделить выкладку кода от включения поведения для аудитории. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Флаг — это временный контракт с владельцем, условиями включения и датой удаления.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отделить выкладку кода от включения поведения для аудитории.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новая функция работает у сотрудников, но должна быть выключена для всех клиентов.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «feature flags» не в сложном синтаксисе, а в неявных предположениях. Новая функция работает у сотрудников, но должна быть выключена для всех клиентов. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-07-field-release-engineering",
"title": "Инженерия релиза: диагностика, решение и проверка",
"date": "2024-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Релизы",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Кейс о том, как код в main уже другой, чем образ, который фактически работает в production. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «инженерия релиза». Типичная ситуация выглядит так: код в main уже другой, чем образ, который фактически работает в production. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать выпуск версией с проверяемым содержимым и планом отката.</p>\n<pre><code>1. Указать владельца API.\n2. Зафиксировать SLO и документацию.\n3. Включить наблюдение до запуска.\n4. Продумать путь отката.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «инженерия релиза» вернётся в следующем релизе под другим именем. Релиз связывает commit, артефакт, конфигурацию, миграции и наблюдение после выкладки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-07-mechanism-release-engineering",
"title": "Инженерия релиза: модель, ограничения и границы",
"date": "2024-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Релизы",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Разбираем, почему релиз связывает commit, артефакт, конфигурацию, миграции и наблюдение после выкладки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «инженерия релиза». Релиз связывает commit, артефакт, конфигурацию, миграции и наблюдение после выкладки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>kubectl apply -f deployment.yaml\nkubectl rollout status deployment/app --timeout=10m\nkubectl rollout history deployment/app\nkubectl rollout undo deployment/app</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: код в main уже другой, чем образ, который фактически работает в production. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-07-practice-release-engineering",
"title": "Инженерия релиза: минимальная инженерная схема",
"date": "2024-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Релизы",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Практическая заметка о том, как сделать выпуск версией с проверяемым содержимым и планом отката. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «инженерия релиза». Цель заметки — сделать выпуск версией с проверяемым содержимым и планом отката. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Релиз связывает commit, артефакт, конфигурацию, миграции и наблюдение после выкладки.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: app\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: app\n image: registry.example/app:sha</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать выпуск версией с проверяемым содержимым и планом отката.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: код в main уже другой, чем образ, который фактически работает в production.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «инженерия релиза» не в сложном синтаксисе, а в неявных предположениях. Код в main уже другой, чем образ, который фактически работает в production. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-06-field-container-orchestration",
"title": "Управление контейнерной нагрузкой: диагностика, решение и проверка",
"date": "2024-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Kubernetes",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Кейс о том, как после обновления образа часть pod-ов работает на старой версии слишком долго. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «управление контейнерной нагрузкой». Типичная ситуация выглядит так: после обновления образа часть pod-ов работает на старой версии слишком долго. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отделить описание желаемого состояния сервиса от ручного запуска процессов.</p>\n<pre><code>1. Указать владельца API.\n2. Зафиксировать SLO и документацию.\n3. Включить наблюдение до запуска.\n4. Продумать путь отката.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «управление контейнерной нагрузкой» вернётся в следующем релизе под другим именем. Deployment поддерживает нужное количество реплик и контролируемо заменяет старые версии.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-06-mechanism-container-orchestration",
"title": "Управление контейнерной нагрузкой: модель, ограничения и границы",
"date": "2024-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Kubernetes",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Разбираем, почему Deployment поддерживает нужное количество реплик и контролируемо заменяет старые версии — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «управление контейнерной нагрузкой». Deployment поддерживает нужное количество реплик и контролируемо заменяет старые версии. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>kubectl apply -f deployment.yaml\nkubectl rollout status deployment/app --timeout=10m\nkubectl rollout history deployment/app\nkubectl rollout undo deployment/app</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после обновления образа часть pod-ов работает на старой версии слишком долго. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-06-practice-container-orchestration",
"title": "Управление контейнерной нагрузкой: минимальная инженерная схема",
"date": "2024-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Kubernetes",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Практическая заметка о том, как отделить описание желаемого состояния сервиса от ручного запуска процессов. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «управление контейнерной нагрузкой». Цель заметки — отделить описание желаемого состояния сервиса от ручного запуска процессов. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Deployment поддерживает нужное количество реплик и контролируемо заменяет старые версии.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: app\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: app\n image: registry.example/app:sha</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отделить описание желаемого состояния сервиса от ручного запуска процессов.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после обновления образа часть pod-ов работает на старой версии слишком долго.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «управление контейнерной нагрузкой» не в сложном синтаксисе, а в неявных предположениях. После обновления образа часть pod-ов работает на старой версии слишком долго. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-05-field-platform-templates",
"title": "Шаблоны для команд: диагностика, решение и проверка",
"date": "2024-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Кейс о том, как каждая новая команда вручную копирует CI, Dockerfile и healthcheck. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «шаблоны для команд». Типичная ситуация выглядит так: каждая новая команда вручную копирует CI, Dockerfile и healthcheck. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Убрать повторяемую настройку без превращения платформы в обязательный лабиринт.</p>\n<pre><code>1. Указать владельца API.\n2. Зафиксировать SLO и документацию.\n3. Включить наблюдение до запуска.\n4. Продумать путь отката.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «шаблоны для команд» вернётся в следующем релизе под другим именем. Платформенный шаблон должен давать безопасный старт и оставлять понятные точки расширения.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-05-mechanism-platform-templates",
"title": "Шаблоны для команд: модель, ограничения и границы",
"date": "2024-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Разбираем, почему платформенный шаблон должен давать безопасный старт и оставлять понятные точки расширения — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «шаблоны для команд». Платформенный шаблон должен давать безопасный старт и оставлять понятные точки расширения. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>kubectl apply -f deployment.yaml\nkubectl rollout status deployment/app --timeout=10m\nkubectl rollout history deployment/app\nkubectl rollout undo deployment/app</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: каждая новая команда вручную копирует CI, Dockerfile и healthcheck. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-05-practice-platform-templates",
"title": "Шаблоны для команд: минимальная инженерная схема",
"date": "2024-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Платформа",
"DevOps"
],
"cover": "/assets/illustrations/vibe-api.svg",
"excerpt": "Практическая заметка о том, как убрать повторяемую настройку без превращения платформы в обязательный лабиринт. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «шаблоны для команд». Цель заметки — убрать повторяемую настройку без превращения платформы в обязательный лабиринт. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Платформенный шаблон должен давать безопасный старт и оставлять понятные точки расширения.</p>\n<pre><code>apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: app\nspec:\n replicas: 2\n template:\n spec:\n containers:\n - name: app\n image: registry.example/app:sha</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: убрать повторяемую настройку без превращения платформы в обязательный лабиринт.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: каждая новая команда вручную копирует CI, Dockerfile и healthcheck.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «шаблоны для команд» не в сложном синтаксисе, а в неявных предположениях. Каждая новая команда вручную копирует CI, Dockerfile и healthcheck. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-04-field-data-migrations",
"title": "Безопасная миграция данных: диагностика, решение и проверка",
"date": "2024-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Миграции"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Кейс о том, как новая колонка появилась, но старый сервис ещё не умеет её заполнять. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «безопасная миграция данных». Типичная ситуация выглядит так: новая колонка появилась, но старый сервис ещё не умеет её заполнять. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Изменить схему и данные так, чтобы старая и новая версия приложения могли жить рядом.</p>\n<pre><code>CREATE INDEX CONCURRENTLY products_category_updated_idx\nON products (category_id, updated_at DESC);\n\n-- После этого снова смотрим EXPLAIN ANALYZE.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «безопасная миграция данных» вернётся в следующем релизе под другим именем. Хорошая миграция разделяет добавление, заполнение, переключение чтения и удаление старого.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-04-mechanism-data-migrations",
"title": "Безопасная миграция данных: модель, ограничения и границы",
"date": "2024-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Миграции"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Разбираем, почему хорошая миграция разделяет добавление, заполнение, переключение чтения и удаление старого — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «безопасная миграция данных». Хорошая миграция разделяет добавление, заполнение, переключение чтения и удаление старого. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>BEGIN;\nUPDATE stock SET amount = amount - 1 WHERE product_id = $1;\nINSERT INTO orders (product_id, user_id) VALUES ($1, $2);\nCOMMIT;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новая колонка появилась, но старый сервис ещё не умеет её заполнять. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-04-practice-data-migrations",
"title": "Безопасная миграция данных: минимальная инженерная схема",
"date": "2024-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Миграции"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Практическая заметка о том, как изменить схему и данные так, чтобы старая и новая версия приложения могли жить рядом. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «безопасная миграция данных». Цель заметки — изменить схему и данные так, чтобы старая и новая версия приложения могли жить рядом. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хорошая миграция разделяет добавление, заполнение, переключение чтения и удаление старого.</p>\n<pre><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name\nFROM products\nWHERE category_id = $1\nORDER BY updated_at DESC\nLIMIT 20;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: изменить схему и данные так, чтобы старая и новая версия приложения могли жить рядом.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новая колонка появилась, но старый сервис ещё не умеет её заполнять.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «безопасная миграция данных» не в сложном синтаксисе, а в неявных предположениях. Новая колонка появилась, но старый сервис ещё не умеет её заполнять. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-03-field-package-boundaries",
"title": "Границы пакетов: диагностика, решение и проверка",
"date": "2024-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Архитектура"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как один helper тянет в frontend половину backend-зависимостей. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «границы пакетов». Типичная ситуация выглядит так: один helper тянет в frontend половину backend-зависимостей. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Не дать общему пакету превратиться в свалку случайных утилит.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «границы пакетов» вернётся в следующем релизе под другим именем. Публичный API пакета должен быть меньше его внутреннего устройства.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-03-mechanism-package-boundaries",
"title": "Границы пакетов: модель, ограничения и границы",
"date": "2024-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Архитектура"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему публичный API пакета должен быть меньше его внутреннего устройства — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «границы пакетов». Публичный API пакета должен быть меньше его внутреннего устройства. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: один helper тянет в frontend половину backend-зависимостей. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-03-practice-package-boundaries",
"title": "Границы пакетов: минимальная инженерная схема",
"date": "2024-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Архитектура"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как не дать общему пакету превратиться в свалку случайных утилит. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «границы пакетов». Цель заметки — не дать общему пакету превратиться в свалку случайных утилит. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Публичный API пакета должен быть меньше его внутреннего устройства.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: не дать общему пакету превратиться в свалку случайных утилит.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: один helper тянет в frontend половину backend-зависимостей.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «границы пакетов» не в сложном синтаксисе, а в неявных предположениях. Один helper тянет в frontend половину backend-зависимостей. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-02-field-modular-monolith",
"title": "Модульный монолит: диагностика, решение и проверка",
"date": "2024-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Backend"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как две команды меняют одну таблицу и случайно ломают друг другу сценарии. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «модульный монолит». Типичная ситуация выглядит так: две команды меняют одну таблицу и случайно ломают друг другу сценарии. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать внутренние границы явными до выделения сервисов.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «модульный монолит» вернётся в следующем релизе под другим именем. Модуль определяется владением данными, API и зависимостями, а не папкой в репозитории.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-02-mechanism-modular-monolith",
"title": "Модульный монолит: модель, ограничения и границы",
"date": "2024-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Backend"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему модуль определяется владением данными, API и зависимостями, а не папкой в репозитории — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «модульный монолит». Модуль определяется владением данными, API и зависимостями, а не папкой в репозитории. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: две команды меняют одну таблицу и случайно ломают друг другу сценарии. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-02-practice-modular-monolith",
"title": "Модульный монолит: минимальная инженерная схема",
"date": "2024-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Backend"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать внутренние границы явными до выделения сервисов. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «модульный монолит». Цель заметки — сделать внутренние границы явными до выделения сервисов. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Модуль определяется владением данными, API и зависимостями, а не папкой в репозитории.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать внутренние границы явными до выделения сервисов.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: две команды меняют одну таблицу и случайно ломают друг другу сценарии.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «модульный монолит» не в сложном синтаксисе, а в неявных предположениях. Две команды меняют одну таблицу и случайно ломают друг другу сценарии. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-01-field-legacy-modernization",
"title": "Модернизация legacy-системы: диагностика, решение и проверка",
"date": "2024-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как проекту десять лет, и мысль о полном переписывании повторяется каждый квартал. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>В зрелом проекте полезнее описывать не один удачный ход, а контекст, альтернативы и последствия. Кейс: «модернизация legacy-системы». Типичная ситуация выглядит так: проекту десять лет, и мысль о полном переписывании повторяется каждый квартал. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбрать узкий участок для улучшения, не останавливая весь продукт.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «модернизация legacy-системы» вернётся в следующем релизе под другим именем. Старую систему меняют через границы и совместимые переходы, а не через одномоментную замену.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2024-01-mechanism-legacy-modernization",
"title": "Модернизация legacy-системы: модель, ограничения и границы",
"date": "2024-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему старую систему меняют через границы и совместимые переходы, а не через одномоментную замену — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>За названием инструмента обычно скрывается договор о данных, времени и ответственности. В теме «модернизация legacy-системы». Старую систему меняют через границы и совместимые переходы, а не через одномоментную замену. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: проекту десять лет, и мысль о полном переписывании повторяется каждый квартал. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2024-01-practice-legacy-modernization",
"title": "Модернизация legacy-системы: минимальная инженерная схема",
"date": "2024-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Рефакторинг",
"Архитектура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как выбрать узкий участок для улучшения, не останавливая весь продукт. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Сопровождаемая система выигрывает не от самой модной технологии, а от ясной границы и понятной проверки. Разберём «модернизация legacy-системы». Цель заметки — выбрать узкий участок для улучшения, не останавливая весь продукт. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Старую систему меняют через границы и совместимые переходы, а не через одномоментную замену.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбрать узкий участок для улучшения, не останавливая весь продукт.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: проекту десять лет, и мысль о полном переписывании повторяется каждый квартал.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «модернизация legacy-системы» не в сложном синтаксисе, а в неявных предположениях. Проекту десять лет, и мысль о полном переписывании повторяется каждый квартал. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Зафиксированный контракт экономит команде больше времени, чем ещё один удачный хак.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-12-field-security-audit",
"title": "Практический аудит веб-проекта: диагностика, решение и проверка",
"date": "2023-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как команда получила длинный список предупреждений и не знает, что исправлять первым. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «практический аудит веб-проекта». Типичная ситуация выглядит так: команда получила длинный список предупреждений и не знает, что исправлять первым. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Пройти по поверхности атаки системно и зафиксировать приоритеты исправления.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «практический аудит веб-проекта» вернётся в следующем релизе под другим именем. Аудит — это инвентаризация, проверка контроля и доказательство результата, а не разовый сканер.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-12-mechanism-security-audit",
"title": "Практический аудит веб-проекта: модель, ограничения и границы",
"date": "2023-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему аудит — это инвентаризация, проверка контроля и доказательство результата, а не разовый сканер — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «практический аудит веб-проекта». Аудит — это инвентаризация, проверка контроля и доказательство результата, а не разовый сканер. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда получила длинный список предупреждений и не знает, что исправлять первым. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-12-practice-security-audit",
"title": "Практический аудит веб-проекта: минимальная инженерная схема",
"date": "2023-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как пройти по поверхности атаки системно и зафиксировать приоритеты исправления. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «практический аудит веб-проекта». Цель заметки — пройти по поверхности атаки системно и зафиксировать приоритеты исправления. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Аудит — это инвентаризация, проверка контроля и доказательство результата, а не разовый сканер.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: пройти по поверхности атаки системно и зафиксировать приоритеты исправления.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда получила длинный список предупреждений и не знает, что исправлять первым.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «практический аудит веб-проекта» не в сложном синтаксисе, а в неявных предположениях. Команда получила длинный список предупреждений и не знает, что исправлять первым. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-11-field-postmortem",
"title": "Postmortem без поиска виноватого: диагностика, решение и проверка",
"date": "2023-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как в отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «postmortem без поиска виноватого». Типичная ситуация выглядит так: в отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать из сбоя улучшение системы, а не публичную казнь.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «postmortem без поиска виноватого» вернётся в следующем релизе под другим именем. Разбор должен объяснить, почему защита не сработала, и что изменится в коде или процессе.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-11-mechanism-postmortem",
"title": "Postmortem без поиска виноватого: модель, ограничения и границы",
"date": "2023-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему разбор должен объяснить, почему защита не сработала, и что изменится в коде или процессе — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «postmortem без поиска виноватого». Разбор должен объяснить, почему защита не сработала, и что изменится в коде или процессе. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-11-practice-postmortem",
"title": "Postmortem без поиска виноватого: минимальная инженерная схема",
"date": "2023-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать из сбоя улучшение системы, а не публичную казнь. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «postmortem без поиска виноватого». Цель заметки — сделать из сбоя улучшение системы, а не публичную казнь. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Разбор должен объяснить, почему защита не сработала, и что изменится в коде или процессе.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать из сбоя улучшение системы, а не публичную казнь.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «postmortem без поиска виноватого» не в сложном синтаксисе, а в неявных предположениях. В отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-10-field-sli-slo",
"title": "SLI и SLO: диагностика, решение и проверка",
"date": "2023-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как команда считает доступность по разным формулам и спорит, был ли инцидент. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «SLI и SLO». Типичная ситуация выглядит так: команда считает доступность по разным формулам и спорит, был ли инцидент. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Согласовать, какое качество сервиса действительно важно для пользователя.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «SLI и SLO» вернётся в следующем релизе под другим именем. Метрика становится целью только после того, как определены окно измерения и допустимый уровень ошибки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-10-mechanism-sli-slo",
"title": "SLI и SLO: модель, ограничения и границы",
"date": "2023-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему метрика становится целью только после того, как определены окно измерения и допустимый уровень ошибки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «SLI и SLO». Метрика становится целью только после того, как определены окно измерения и допустимый уровень ошибки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда считает доступность по разным формулам и спорит, был ли инцидент. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-10-practice-sli-slo",
"title": "SLI и SLO: минимальная инженерная схема",
"date": "2023-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как согласовать, какое качество сервиса действительно важно для пользователя. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «SLI и SLO». Цель заметки — согласовать, какое качество сервиса действительно важно для пользователя. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Метрика становится целью только после того, как определены окно измерения и допустимый уровень ошибки.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: согласовать, какое качество сервиса действительно важно для пользователя.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда считает доступность по разным формулам и спорит, был ли инцидент.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «SLI и SLO» не в сложном синтаксисе, а в неявных предположениях. Команда считает доступность по разным формулам и спорит, был ли инцидент. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-09-field-telemetry-signals",
"title": "Логи, метрики и трассы: диагностика, решение и проверка",
"date": "2023-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как график показывает рост ошибок, но не говорит, где именно теряется запрос. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «логи, метрики и трассы». Типичная ситуация выглядит так: график показывает рост ошибок, но не говорит, где именно теряется запрос. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Использовать три сигнала вместе вместо бесконечного поиска в одном журнале.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «логи, метрики и трассы» вернётся в следующем релизе под другим именем. Логи объясняют событие, метрики показывают тенденцию, а trace связывает путь запроса.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-09-mechanism-telemetry-signals",
"title": "Логи, метрики и трассы: модель, ограничения и границы",
"date": "2023-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему логи объясняют событие, метрики показывают тенденцию, а trace связывает путь запроса — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «логи, метрики и трассы». Логи объясняют событие, метрики показывают тенденцию, а trace связывает путь запроса. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: график показывает рост ошибок, но не говорит, где именно теряется запрос. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-09-practice-telemetry-signals",
"title": "Логи, метрики и трассы: минимальная инженерная схема",
"date": "2023-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как использовать три сигнала вместе вместо бесконечного поиска в одном журнале. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «логи, метрики и трассы». Цель заметки — использовать три сигнала вместе вместо бесконечного поиска в одном журнале. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Логи объясняют событие, метрики показывают тенденцию, а trace связывает путь запроса.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: использовать три сигнала вместе вместо бесконечного поиска в одном журнале.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: график показывает рост ошибок, но не говорит, где именно теряется запрос.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «логи, метрики и трассы» не в сложном синтаксисе, а в неявных предположениях. График показывает рост ошибок, но не говорит, где именно теряется запрос. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-08-field-e2e-stability",
"title": "Стабильные e2e-тесты: диагностика, решение и проверка",
"date": "2023-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как падение теста невозможно повторить, потому что оно зависит от порядка запуска. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «стабильные e2e-тесты». Типичная ситуация выглядит так: падение теста невозможно повторить, потому что оно зависит от порядка запуска. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проверять сценарий без sleep, случайных селекторов и зависимости от чужих данных.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «стабильные e2e-тесты» вернётся в следующем релизе под другим именем. Устойчивость теста строится на изоляции данных, пользовательских локаторах и наблюдаемом результате.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-08-mechanism-e2e-stability",
"title": "Стабильные e2e-тесты: модель, ограничения и границы",
"date": "2023-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему устойчивость теста строится на изоляции данных, пользовательских локаторах и наблюдаемом результате — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «стабильные e2e-тесты». Устойчивость теста строится на изоляции данных, пользовательских локаторах и наблюдаемом результате. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: падение теста невозможно повторить, потому что оно зависит от порядка запуска. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-08-practice-e2e-stability",
"title": "Стабильные e2e-тесты: минимальная инженерная схема",
"date": "2023-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как проверять сценарий без sleep, случайных селекторов и зависимости от чужих данных. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «стабильные e2e-тесты». Цель заметки — проверять сценарий без sleep, случайных селекторов и зависимости от чужих данных. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Устойчивость теста строится на изоляции данных, пользовательских локаторах и наблюдаемом результате.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проверять сценарий без sleep, случайных селекторов и зависимости от чужих данных.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: падение теста невозможно повторить, потому что оно зависит от порядка запуска.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «стабильные e2e-тесты» не в сложном синтаксисе, а в неявных предположениях. Падение теста невозможно повторить, потому что оно зависит от порядка запуска. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-07-field-contract-tests",
"title": "Контрактные тесты API: диагностика, решение и проверка",
"date": "2023-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"API"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как backend поменял nullable-поле, а frontend узнал об этом только после выкладки. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «контрактные тесты API». Типичная ситуация выглядит так: backend поменял nullable-поле, а frontend узнал об этом только после выкладки. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Ловить расхождение producer и consumer до общего релиза.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «контрактные тесты API» вернётся в следующем релизе под другим именем. Контракт фиксирует поля, статусы и примеры, которые обе стороны считают допустимыми.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-07-mechanism-contract-tests",
"title": "Контрактные тесты API: модель, ограничения и границы",
"date": "2023-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"API"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему контракт фиксирует поля, статусы и примеры, которые обе стороны считают допустимыми — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «контрактные тесты API». Контракт фиксирует поля, статусы и примеры, которые обе стороны считают допустимыми. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: backend поменял nullable-поле, а frontend узнал об этом только после выкладки. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-07-practice-contract-tests",
"title": "Контрактные тесты API: минимальная инженерная схема",
"date": "2023-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"API"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как ловить расхождение producer и consumer до общего релиза. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «контрактные тесты API». Цель заметки — ловить расхождение producer и consumer до общего релиза. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Контракт фиксирует поля, статусы и примеры, которые обе стороны считают допустимыми.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: ловить расхождение producer и consumer до общего релиза.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: backend поменял nullable-поле, а frontend узнал об этом только после выкладки.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «контрактные тесты API» не в сложном синтаксисе, а в неявных предположениях. Backend поменял nullable-поле, а frontend узнал об этом только после выкладки. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-06-field-secrets-supply-chain",
"title": "Секреты и цепочка поставки: диагностика, решение и проверка",
"date": "2023-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как токен тестовой среды случайно сохранился в образе контейнера. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «секреты и цепочка поставки». Типичная ситуация выглядит так: токен тестовой среды случайно сохранился в образе контейнера. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Не давать токенам попасть в логи, образы, историю git и временные файлы CI.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «секреты и цепочка поставки» вернётся в следующем релизе под другим именем. Секрет безопасен только пока ограничены его распространение, срок жизни и область действия.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-06-mechanism-secrets-supply-chain",
"title": "Секреты и цепочка поставки: модель, ограничения и границы",
"date": "2023-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему секрет безопасен только пока ограничены его распространение, срок жизни и область действия — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «секреты и цепочка поставки». Секрет безопасен только пока ограничены его распространение, срок жизни и область действия. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: токен тестовой среды случайно сохранился в образе контейнера. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-06-practice-secrets-supply-chain",
"title": "Секреты и цепочка поставки: минимальная инженерная схема",
"date": "2023-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как не давать токенам попасть в логи, образы, историю git и временные файлы CI. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «секреты и цепочка поставки». Цель заметки — не давать токенам попасть в логи, образы, историю git и временные файлы CI. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Секрет безопасен только пока ограничены его распространение, срок жизни и область действия.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: не давать токенам попасть в логи, образы, историю git и временные файлы CI.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: токен тестовой среды случайно сохранился в образе контейнера.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «секреты и цепочка поставки» не в сложном синтаксисе, а в неявных предположениях. Токен тестовой среды случайно сохранился в образе контейнера. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-05-field-static-analysis",
"title": "Статический анализ: диагностика, решение и проверка",
"date": "2023-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"JavaScript"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как одна и та же ошибка с null и необработанным Promise появляется в разных модулях. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «статический анализ». Типичная ситуация выглядит так: одна и та же ошибка с null и необработанным Promise появляется в разных модулях. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Ловить повторяемые ошибки до запуска приложения.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «статический анализ» вернётся в следующем релизе под другим именем. Линтер и анализатор полезны там, где правила однозначны и проверяются автоматически.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-05-mechanism-static-analysis",
"title": "Статический анализ: модель, ограничения и границы",
"date": "2023-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"JavaScript"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему линтер и анализатор полезны там, где правила однозначны и проверяются автоматически — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «статический анализ». Линтер и анализатор полезны там, где правила однозначны и проверяются автоматически. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: одна и та же ошибка с null и необработанным Promise появляется в разных модулях. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-05-practice-static-analysis",
"title": "Статический анализ: минимальная инженерная схема",
"date": "2023-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Качество",
"JavaScript"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как ловить повторяемые ошибки до запуска приложения. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «статический анализ». Цель заметки — ловить повторяемые ошибки до запуска приложения. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Линтер и анализатор полезны там, где правила однозначны и проверяются автоматически.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: ловить повторяемые ошибки до запуска приложения.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: одна и та же ошибка с null и необработанным Promise появляется в разных модулях.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «статический анализ» не в сложном синтаксисе, а в неявных предположениях. Одна и та же ошибка с null и необработанным Promise появляется в разных модулях. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-04-field-dependency-security",
"title": "Безопасность зависимостей: диагностика, решение и проверка",
"date": "2023-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Зависимости"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как уязвимость найдена в пакете, который уже не используется в исходном коде напрямую. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «безопасность зависимостей». Типичная ситуация выглядит так: уязвимость найдена в пакете, который уже не используется в исходном коде напрямую. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Понимать, какие библиотеки действительно попадают в production и как их обновлять.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «безопасность зависимостей» вернётся в следующем релизе под другим именем. Зависимость — это часть цепочки поставки, поэтому версия, источник и обновление имеют значение.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-04-mechanism-dependency-security",
"title": "Безопасность зависимостей: модель, ограничения и границы",
"date": "2023-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Зависимости"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему зависимость — это часть цепочки поставки, поэтому версия, источник и обновление имеют значение — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «безопасность зависимостей». Зависимость — это часть цепочки поставки, поэтому версия, источник и обновление имеют значение. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: уязвимость найдена в пакете, который уже не используется в исходном коде напрямую. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-04-practice-dependency-security",
"title": "Безопасность зависимостей: минимальная инженерная схема",
"date": "2023-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Зависимости"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как понимать, какие библиотеки действительно попадают в production и как их обновлять. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «безопасность зависимостей». Цель заметки — понимать, какие библиотеки действительно попадают в production и как их обновлять. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Зависимость — это часть цепочки поставки, поэтому версия, источник и обновление имеют значение.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: понимать, какие библиотеки действительно попадают в production и как их обновлять.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: уязвимость найдена в пакете, который уже не используется в исходном коде напрямую.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «безопасность зависимостей» не в сложном синтаксисе, а в неявных предположениях. Уязвимость найдена в пакете, который уже не используется в исходном коде напрямую. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-03-field-csrf-cors",
"title": "CSRF и CORS: диагностика, решение и проверка",
"date": "2023-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"HTTP"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «CSRF и CORS». Типичная ситуация выглядит так: сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Не открывать доступ к пользовательской сессии только потому, что API нужен frontend-виджету.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «CSRF и CORS» вернётся в следующем релизе под другим именем. CORS управляет доступом браузерного кода, а CSRF защищает действие от подделанного запроса.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-03-mechanism-csrf-cors",
"title": "CSRF и CORS: модель, ограничения и границы",
"date": "2023-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"HTTP"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему CORS управляет доступом браузерного кода, а CSRF защищает действие от подделанного запроса — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «CSRF и CORS». CORS управляет доступом браузерного кода, а CSRF защищает действие от подделанного запроса. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-03-practice-csrf-cors",
"title": "CSRF и CORS: минимальная инженерная схема",
"date": "2023-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"HTTP"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как не открывать доступ к пользовательской сессии только потому, что API нужен frontend-виджету. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «CSRF и CORS». Цель заметки — не открывать доступ к пользовательской сессии только потому, что API нужен frontend-виджету. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. CORS управляет доступом браузерного кода, а CSRF защищает действие от подделанного запроса.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: не открывать доступ к пользовательской сессии только потому, что API нужен frontend-виджету.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «CSRF и CORS» не в сложном синтаксисе, а в неявных предположениях. Сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-02-field-sessions-auth",
"title": "Аутентификация и сессии: диагностика, решение и проверка",
"date": "2023-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Аутентификация"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «аутентификация и сессии». Типичная ситуация выглядит так: пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Не путать подтверждение личности с правом выполнить конкретное действие.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «аутентификация и сессии» вернётся в следующем релизе под другим именем. Сессия, cookie, срок жизни и серверная проверка образуют один контур доверия.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-02-mechanism-sessions-auth",
"title": "Аутентификация и сессии: модель, ограничения и границы",
"date": "2023-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Аутентификация"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему сессия, cookie, срок жизни и серверная проверка образуют один контур доверия — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «аутентификация и сессии». Сессия, cookie, срок жизни и серверная проверка образуют один контур доверия. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-02-practice-sessions-auth",
"title": "Аутентификация и сессии: минимальная инженерная схема",
"date": "2023-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Аутентификация"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как не путать подтверждение личности с правом выполнить конкретное действие. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «аутентификация и сессии». Цель заметки — не путать подтверждение личности с правом выполнить конкретное действие. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сессия, cookie, срок жизни и серверная проверка образуют один контур доверия.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: не путать подтверждение личности с правом выполнить конкретное действие.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «аутентификация и сессии» не в сложном синтаксисе, а в неявных предположениях. Пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-01-field-threat-model",
"title": "Модель угроз: диагностика, решение и проверка",
"date": "2023-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «модель угроз». Типичная ситуация выглядит так: команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Обсудить, что именно защищаем, от кого и какими границами.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «модель угроз» вернётся в следующем релизе под другим именем. Модель угроз связывает актив, актёра, поверхность атаки и меру защиты.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2023-01-mechanism-threat-model",
"title": "Модель угроз: модель, ограничения и границы",
"date": "2023-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему модель угроз связывает актив, актёра, поверхность атаки и меру защиты — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «модель угроз». Модель угроз связывает актив, актёра, поверхность атаки и меру защиты. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2023-01-practice-threat-model",
"title": "Модель угроз: минимальная инженерная схема",
"date": "2023-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как обсудить, что именно защищаем, от кого и какими границами. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «модель угроз». Цель заметки — обсудить, что именно защищаем, от кого и какими границами. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Модель угроз связывает актив, актёра, поверхность атаки и меру защиты.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: обсудить, что именно защищаем, от кого и какими границами.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «модель угроз» не в сложном синтаксисе, а в неявных предположениях. Команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-12-field-user-research",
"title": "Исследование пользовательской проблемы: диагностика, решение и проверка",
"date": "2022-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «исследование пользовательской проблемы». Типичная ситуация выглядит так: команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Связать техническое изменение с наблюдаемым сценарием человека.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «исследование пользовательской проблемы» вернётся в следующем релизе под другим именем. Инженерная гипотеза полезна, когда у неё есть пользователь, контекст, сигнал и способ проверки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-12-mechanism-user-research",
"title": "Исследование пользовательской проблемы: модель, ограничения и границы",
"date": "2022-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему инженерная гипотеза полезна, когда у неё есть пользователь, контекст, сигнал и способ проверки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «исследование пользовательской проблемы». Инженерная гипотеза полезна, когда у неё есть пользователь, контекст, сигнал и способ проверки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-12-practice-user-research",
"title": "Исследование пользовательской проблемы: минимальная инженерная схема",
"date": "2022-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Продукт",
"UX"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как связать техническое изменение с наблюдаемым сценарием человека. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «исследование пользовательской проблемы». Цель заметки — связать техническое изменение с наблюдаемым сценарием человека. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Инженерная гипотеза полезна, когда у неё есть пользователь, контекст, сигнал и способ проверки.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: связать техническое изменение с наблюдаемым сценарием человека.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «исследование пользовательской проблемы» не в сложном синтаксисе, а в неявных предположениях. Команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-11-field-bitrix-performance",
"title": "Производительность Bitrix-страницы: диагностика, решение и проверка",
"date": "2022-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Производительность"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Кейс о том, как страница каталога тормозит, но разработчики спорят только о размере JavaScript. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «производительность Bitrix-страницы». Типичная ситуация выглядит так: страница каталога тормозит, но разработчики спорят только о размере JavaScript. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Найти запрос, шаблон или кеш, который реально влияет на время ответа.</p>\n<pre><code>$required = [&#039;IBLOCK_ID&#039;, &#039;NAME&#039;];\nforeach ($required as $field) {\n if (empty($fields[$field])) {\n throw new InvalidArgumentException($field . &#039; is required&#039;);\n }\n}</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «производительность Bitrix-страницы» вернётся в следующем релизе под другим именем. Быстрый сайт требует измерять цепочку целиком: база, PHP, кеш, HTML и браузер.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-11-mechanism-bitrix-performance",
"title": "Производительность Bitrix-страницы: модель, ограничения и границы",
"date": "2022-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Производительность"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Разбираем, почему быстрый сайт требует измерять цепочку целиком: база, PHP, кеш, HTML и браузер — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «производительность Bitrix-страницы». Быстрый сайт требует измерять цепочку целиком: база, PHP, кеш, HTML и браузер. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>$id = $element-&gt;Add($fields);\nif ($id === false) {\n error_log(&#039;Bitrix error: &#039; . $element-&gt;LAST_ERROR);\n return null;\n}\nreturn (int) $id;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: страница каталога тормозит, но разработчики спорят только о размере JavaScript. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-11-practice-bitrix-performance",
"title": "Производительность Bitrix-страницы: минимальная инженерная схема",
"date": "2022-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Производительность"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Практическая заметка о том, как найти запрос, шаблон или кеш, который реально влияет на время ответа. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «производительность Bitrix-страницы». Цель заметки — найти запрос, шаблон или кеш, который реально влияет на время ответа. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Быстрый сайт требует измерять цепочку целиком: база, PHP, кеш, HTML и браузер.</p>\n<pre><code>CModule::IncludeModule(&#039;iblock&#039;);\n$element = new CIBlockElement();\n$id = $element-&gt;Add([\n &#039;IBLOCK_ID&#039; =&gt; 12,\n &#039;NAME&#039; =&gt; $name,\n &#039;ACTIVE&#039; =&gt; &#039;Y&#039;,\n &#039;PROPERTY_VALUES&#039; =&gt; $properties,\n]);\nif (!$id) {\n throw new RuntimeException($element-&gt;LAST_ERROR);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: найти запрос, шаблон или кеш, который реально влияет на время ответа.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: страница каталога тормозит, но разработчики спорят только о размере JavaScript.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «производительность Bitrix-страницы» не в сложном синтаксисе, а в неявных предположениях. Страница каталога тормозит, но разработчики спорят только о размере JavaScript. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-10-field-ui-tests",
"title": "Проверки пользовательского сценария: диагностика, решение и проверка",
"date": "2022-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как тест кликает по кнопке, но иногда падает из-за случайного тайминга. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «проверки пользовательского сценария». Типичная ситуация выглядит так: тест кликает по кнопке, но иногда падает из-за случайного тайминга. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Автоматизировать путь человека через интерфейс, а не набор технических селекторов.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «проверки пользовательского сценария» вернётся в следующем релизе под другим именем. Устойчивый e2e-тест ждёт наблюдаемое состояние и проверяет результат действия.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-10-mechanism-ui-tests",
"title": "Проверки пользовательского сценария: модель, ограничения и границы",
"date": "2022-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему устойчивый e2e-тест ждёт наблюдаемое состояние и проверяет результат действия — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «проверки пользовательского сценария». Устойчивый e2e-тест ждёт наблюдаемое состояние и проверяет результат действия. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: тест кликает по кнопке, но иногда падает из-за случайного тайминга. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-10-practice-ui-tests",
"title": "Проверки пользовательского сценария: минимальная инженерная схема",
"date": "2022-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Тестирование",
"Frontend"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как автоматизировать путь человека через интерфейс, а не набор технических селекторов. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «проверки пользовательского сценария». Цель заметки — автоматизировать путь человека через интерфейс, а не набор технических селекторов. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Устойчивый e2e-тест ждёт наблюдаемое состояние и проверяет результат действия.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: автоматизировать путь человека через интерфейс, а не набор технических селекторов.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: тест кликает по кнопке, но иногда падает из-за случайного тайминга.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «проверки пользовательского сценария» не в сложном синтаксисе, а в неявных предположениях. Тест кликает по кнопке, но иногда падает из-за случайного тайминга. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-09-field-mobile-ux",
"title": "Мобильный сценарий: диагностика, решение и проверка",
"date": "2022-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как десктопная таблица на телефоне превращается в стену мелкого текста. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «мобильный сценарий». Типичная ситуация выглядит так: десктопная таблица на телефоне превращается в стену мелкого текста. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проектировать действие под палец, маленький экран и краткое внимание.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «мобильный сценарий» вернётся в следующем релизе под другим именем. Мобильный интерфейс ограничен не только размером, но и сетью, вводом и контекстом пользователя.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-09-mechanism-mobile-ux",
"title": "Мобильный сценарий: модель, ограничения и границы",
"date": "2022-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему мобильный интерфейс ограничен не только размером, но и сетью, вводом и контекстом пользователя — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «мобильный сценарий». Мобильный интерфейс ограничен не только размером, но и сетью, вводом и контекстом пользователя. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: десктопная таблица на телефоне превращается в стену мелкого текста. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-09-practice-mobile-ux",
"title": "Мобильный сценарий: минимальная инженерная схема",
"date": "2022-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как проектировать действие под палец, маленький экран и краткое внимание. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «мобильный сценарий». Цель заметки — проектировать действие под палец, маленький экран и краткое внимание. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Мобильный интерфейс ограничен не только размером, но и сетью, вводом и контекстом пользователя.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проектировать действие под палец, маленький экран и краткое внимание.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: десктопная таблица на телефоне превращается в стену мелкого текста.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «мобильный сценарий» не в сложном синтаксисе, а в неявных предположениях. Десктопная таблица на телефоне превращается в стену мелкого текста. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-08-field-media-performance",
"title": "Изображения и медиа: диагностика, решение и проверка",
"date": "2022-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как карточка товара занимает большую часть трафика, хотя изображение выводится маленьким. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «изображения и медиа». Типичная ситуация выглядит так: карточка товара занимает большую часть трафика, хотя изображение выводится маленьким. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отдавать нужный размер файла и не заставлять браузер скачивать лишнее.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «изображения и медиа» вернётся в следующем релизе под другим именем. Медиа влияет на сеть, layout и визуальную стабильность страницы.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-08-mechanism-media-performance",
"title": "Изображения и медиа: модель, ограничения и границы",
"date": "2022-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему медиа влияет на сеть, layout и визуальную стабильность страницы — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «изображения и медиа». Медиа влияет на сеть, layout и визуальную стабильность страницы. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: карточка товара занимает большую часть трафика, хотя изображение выводится маленьким. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-08-practice-media-performance",
"title": "Изображения и медиа: минимальная инженерная схема",
"date": "2022-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как отдавать нужный размер файла и не заставлять браузер скачивать лишнее. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «изображения и медиа». Цель заметки — отдавать нужный размер файла и не заставлять браузер скачивать лишнее. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Медиа влияет на сеть, layout и визуальную стабильность страницы.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отдавать нужный размер файла и не заставлять браузер скачивать лишнее.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: карточка товара занимает большую часть трафика, хотя изображение выводится маленьким.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «изображения и медиа» не в сложном синтаксисе, а в неявных предположениях. Карточка товара занимает большую часть трафика, хотя изображение выводится маленьким. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-07-field-poor-network",
"title": "Работа при плохой сети: диагностика, решение и проверка",
"date": "2022-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Web",
"UX"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как кнопка сохранения нажимается дважды, пока пользователь не видит ответа. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «работа при плохой сети». Типичная ситуация выглядит так: кнопка сохранения нажимается дважды, пока пользователь не видит ответа. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать интерфейс честным при медленном, нестабильном или отсутствующем соединении.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «работа при плохой сети» вернётся в следующем релизе под другим именем. Сеть может потерять запрос после отправки, поэтому UI должен различать ожидание, ошибку и неизвестный результат.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-07-mechanism-poor-network",
"title": "Работа при плохой сети: модель, ограничения и границы",
"date": "2022-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Web",
"UX"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему сеть может потерять запрос после отправки, поэтому UI должен различать ожидание, ошибку и неизвестный результат — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «работа при плохой сети». Сеть может потерять запрос после отправки, поэтому UI должен различать ожидание, ошибку и неизвестный результат. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: кнопка сохранения нажимается дважды, пока пользователь не видит ответа. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-07-practice-poor-network",
"title": "Работа при плохой сети: минимальная инженерная схема",
"date": "2022-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Web",
"UX"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как сделать интерфейс честным при медленном, нестабильном или отсутствующем соединении. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «работа при плохой сети». Цель заметки — сделать интерфейс честным при медленном, нестабильном или отсутствующем соединении. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сеть может потерять запрос после отправки, поэтому UI должен различать ожидание, ошибку и неизвестный результат.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать интерфейс честным при медленном, нестабильном или отсутствующем соединении.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: кнопка сохранения нажимается дважды, пока пользователь не видит ответа.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «работа при плохой сети» не в сложном синтаксисе, а в неявных предположениях. Кнопка сохранения нажимается дважды, пока пользователь не видит ответа. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-06-field-form-errors",
"title": "Ошибки формы: диагностика, решение и проверка",
"date": "2022-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как после серверной ошибки форма очищается и человек не понимает, что исправлять. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «ошибки формы». Типичная ситуация выглядит так: после серверной ошибки форма очищается и человек не понимает, что исправлять. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Вернуть пользователю действие, а не только факт неудачи.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «ошибки формы» вернётся в следующем релизе под другим именем. Хорошая ошибка привязана к полю, объясняет причину и не теряет уже введённые данные.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-06-mechanism-form-errors",
"title": "Ошибки формы: модель, ограничения и границы",
"date": "2022-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему хорошая ошибка привязана к полю, объясняет причину и не теряет уже введённые данные — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «ошибки формы». Хорошая ошибка привязана к полю, объясняет причину и не теряет уже введённые данные. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после серверной ошибки форма очищается и человек не понимает, что исправлять. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-06-practice-form-errors",
"title": "Ошибки формы: минимальная инженерная схема",
"date": "2022-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"UX",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как вернуть пользователю действие, а не только факт неудачи. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «ошибки формы». Цель заметки — вернуть пользователю действие, а не только факт неудачи. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хорошая ошибка привязана к полю, объясняет причину и не теряет уже введённые данные.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: вернуть пользователю действие, а не только факт неудачи.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после серверной ошибки форма очищается и человек не понимает, что исправлять.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «ошибки формы» не в сложном синтаксисе, а в неявных предположениях. После серверной ошибки форма очищается и человек не понимает, что исправлять. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-05-field-design-system",
"title": "Маленькая дизайн-система: диагностика, решение и проверка",
"date": "2022-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Дизайн-система"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как у трёх форм разные обязательные поля, цвета ошибок и логика disabled. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «маленькая дизайн-система». Типичная ситуация выглядит так: у трёх форм разные обязательные поля, цвета ошибок и логика disabled. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Снизить число случайных вариантов кнопок, ошибок и полей в продукте.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «маленькая дизайн-система» вернётся в следующем релизе под другим именем. Компонент становится полезным, когда у него есть контракт поведения, состояний и доступности.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-05-mechanism-design-system",
"title": "Маленькая дизайн-система: модель, ограничения и границы",
"date": "2022-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Дизайн-система"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему компонент становится полезным, когда у него есть контракт поведения, состояний и доступности — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «маленькая дизайн-система». Компонент становится полезным, когда у него есть контракт поведения, состояний и доступности. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: у трёх форм разные обязательные поля, цвета ошибок и логика disabled. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-05-practice-design-system",
"title": "Маленькая дизайн-система: минимальная инженерная схема",
"date": "2022-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Дизайн-система"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как снизить число случайных вариантов кнопок, ошибок и полей в продукте. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «маленькая дизайн-система». Цель заметки — снизить число случайных вариантов кнопок, ошибок и полей в продукте. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Компонент становится полезным, когда у него есть контракт поведения, состояний и доступности.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: снизить число случайных вариантов кнопок, ошибок и полей в продукте.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: у трёх форм разные обязательные поля, цвета ошибок и логика disabled.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «маленькая дизайн-система» не в сложном синтаксисе, а в неявных предположениях. У трёх форм разные обязательные поля, цвета ошибок и логика disabled. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-04-field-accessible-interface",
"title": "Доступный интерфейс: диагностика, решение и проверка",
"date": "2022-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как форма красивая, но screen reader не связывает подпись с полем. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «доступный интерфейс». Типичная ситуация выглядит так: форма красивая, но screen reader не связывает подпись с полем. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проверять фокус, подписи, контраст и сценарий без мыши как обычную часть разработки.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «доступный интерфейс» вернётся в следующем релизе под другим именем. Доступность строится на семантике и ожидаемом поведении, а не на отдельном виджете для проверки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-04-mechanism-accessible-interface",
"title": "Доступный интерфейс: модель, ограничения и границы",
"date": "2022-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему доступность строится на семантике и ожидаемом поведении, а не на отдельном виджете для проверки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «доступный интерфейс». Доступность строится на семантике и ожидаемом поведении, а не на отдельном виджете для проверки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: форма красивая, но screen reader не связывает подпись с полем. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-04-practice-accessible-interface",
"title": "Доступный интерфейс: минимальная инженерная схема",
"date": "2022-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как проверять фокус, подписи, контраст и сценарий без мыши как обычную часть разработки. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «доступный интерфейс». Цель заметки — проверять фокус, подписи, контраст и сценарий без мыши как обычную часть разработки. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Доступность строится на семантике и ожидаемом поведении, а не на отдельном виджете для проверки.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проверять фокус, подписи, контраст и сценарий без мыши как обычную часть разработки.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: форма красивая, но screen reader не связывает подпись с полем.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «доступный интерфейс» не в сложном синтаксисе, а в неявных предположениях. Форма красивая, но screen reader не связывает подпись с полем. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-03-field-browser-rendering",
"title": "Как браузер рисует страницу: диагностика, решение и проверка",
"date": "2022-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как простая анимация начинает дёргаться после добавления большого списка. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «как браузер рисует страницу». Типичная ситуация выглядит так: простая анимация начинает дёргаться после добавления большого списка. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Понимать, почему изменение DOM иногда дорого, а правильный порядок ресурсов важен.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «как браузер рисует страницу» вернётся в следующем релизе под другим именем. Парсинг, стили, layout, paint и JavaScript влияют друг на друга через критический путь.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-03-mechanism-browser-rendering",
"title": "Как браузер рисует страницу: модель, ограничения и границы",
"date": "2022-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему парсинг, стили, layout, paint и JavaScript влияют друг на друга через критический путь — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «как браузер рисует страницу». Парсинг, стили, layout, paint и JavaScript влияют друг на друга через критический путь. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: простая анимация начинает дёргаться после добавления большого списка. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-03-practice-browser-rendering",
"title": "Как браузер рисует страницу: минимальная инженерная схема",
"date": "2022-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как понимать, почему изменение DOM иногда дорого, а правильный порядок ресурсов важен. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «как браузер рисует страницу». Цель заметки — понимать, почему изменение DOM иногда дорого, а правильный порядок ресурсов важен. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Парсинг, стили, layout, paint и JavaScript влияют друг на друга через критический путь.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: понимать, почему изменение DOM иногда дорого, а правильный порядок ресурсов важен.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: простая анимация начинает дёргаться после добавления большого списка.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «как браузер рисует страницу» не в сложном синтаксисе, а в неявных предположениях. Простая анимация начинает дёргаться после добавления большого списка. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-02-field-performance-budget",
"title": "Бюджет производительности: диагностика, решение и проверка",
"date": "2022-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «бюджет производительности». Типичная ситуация выглядит так: каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Превратить пожелание «быстрее» в проверяемые ограничения для страницы.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «бюджет производительности» вернётся в следующем релизе под другим именем. Бюджет фиксирует допустимые размеры, задержки и сценарии измерения до регрессии.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-02-mechanism-performance-budget",
"title": "Бюджет производительности: модель, ограничения и границы",
"date": "2022-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему бюджет фиксирует допустимые размеры, задержки и сценарии измерения до регрессии — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «бюджет производительности». Бюджет фиксирует допустимые размеры, задержки и сценарии измерения до регрессии. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-02-practice-performance-budget",
"title": "Бюджет производительности: минимальная инженерная схема",
"date": "2022-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Производительность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как превратить пожелание «быстрее» в проверяемые ограничения для страницы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «бюджет производительности». Цель заметки — превратить пожелание «быстрее» в проверяемые ограничения для страницы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Бюджет фиксирует допустимые размеры, задержки и сценарии измерения до регрессии.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: превратить пожелание «быстрее» в проверяемые ограничения для страницы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «бюджет производительности» не в сложном синтаксисе, а в неявных предположениях. Каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-01-field-ssr-csr",
"title": "Границы SSR и CSR: диагностика, решение и проверка",
"date": "2022-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как контент важен для первого экрана, но виджет требует браузерного API. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «границы SSR и CSR». Типичная ситуация выглядит так: контент важен для первого экрана, но виджет требует браузерного API. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбрать, что показать пользователю сразу, а что оставить клиентскому коду.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «границы SSR и CSR» вернётся в следующем релизе под другим именем. Серверный и клиентский рендер решают разные проблемы времени ответа, интерактивности и кеширования.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2022-01-mechanism-ssr-csr",
"title": "Границы SSR и CSR: модель, ограничения и границы",
"date": "2022-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему серверный и клиентский рендер решают разные проблемы времени ответа, интерактивности и кеширования — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «границы SSR и CSR». Серверный и клиентский рендер решают разные проблемы времени ответа, интерактивности и кеширования. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: контент важен для первого экрана, но виджет требует браузерного API. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2022-01-practice-ssr-csr",
"title": "Границы SSR и CSR: минимальная инженерная схема",
"date": "2022-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Frontend",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как выбрать, что показать пользователю сразу, а что оставить клиентскому коду. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «границы SSR и CSR». Цель заметки — выбрать, что показать пользователю сразу, а что оставить клиентскому коду. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Серверный и клиентский рендер решают разные проблемы времени ответа, интерактивности и кеширования.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбрать, что показать пользователю сразу, а что оставить клиентскому коду.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: контент важен для первого экрана, но виджет требует браузерного API.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «границы SSR и CSR» не в сложном синтаксисе, а в неявных предположениях. Контент важен для первого экрана, но виджет требует браузерного API. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-12-field-architecture-review",
"title": "Разбор: Архитектурный разбор решения в реальном проекте",
"date": "2021-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как команда спорит о технологии, но не сформулировала, какую проблему выбирает решать. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «архитектурный разбор решения». Типичная ситуация выглядит так: команда спорит о технологии, но не сформулировала, какую проблему выбирает решать. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Зафиксировать ключевые допущения до того, как они станут скрытым долгом.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «архитектурный разбор решения» вернётся в следующем релизе под другим именем. Архитектурное решение — это контекст, вариант, последствия и дата пересмотра.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-12-mechanism-architecture-review",
"title": "Под капотом: Архитектурный разбор решения",
"date": "2021-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему архитектурное решение — это контекст, вариант, последствия и дата пересмотра — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «архитектурный разбор решения». Архитектурное решение — это контекст, вариант, последствия и дата пересмотра. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: команда спорит о технологии, но не сформулировала, какую проблему выбирает решать. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-12-practice-architecture-review",
"title": "Архитектура. Как работать с темой: Архитектурный разбор решения",
"date": "2021-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как зафиксировать ключевые допущения до того, как они станут скрытым долгом. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «архитектурный разбор решения». Цель заметки — зафиксировать ключевые допущения до того, как они станут скрытым долгом. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Архитектурное решение — это контекст, вариант, последствия и дата пересмотра.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: зафиксировать ключевые допущения до того, как они станут скрытым долгом.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: команда спорит о технологии, но не сформулировала, какую проблему выбирает решать.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «архитектурный разбор решения» не в сложном синтаксисе, а в неявных предположениях. Команда спорит о технологии, но не сформулировала, какую проблему выбирает решать. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Deployments</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-11-field-load-testing",
"title": "Разбор: Нагрузочное тестирование в реальном проекте",
"date": "2021-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Нагрузка",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как на стенде всё быстро, а в день акции время ответа растёт в несколько раз. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «нагрузочное тестирование». Типичная ситуация выглядит так: на стенде всё быстро, а в день акции время ответа растёт в несколько раз. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проверять систему по сценарию пользователя, а не по одному красивому RPS.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «нагрузочное тестирование» вернётся в следующем релизе под другим именем. Нагрузка раскрывает очереди, лимиты соединений, блокировки и узкие места зависимостей.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-11-mechanism-load-testing",
"title": "Под капотом: Нагрузочное тестирование",
"date": "2021-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Нагрузка",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему нагрузка раскрывает очереди, лимиты соединений, блокировки и узкие места зависимостей — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «нагрузочное тестирование». Нагрузка раскрывает очереди, лимиты соединений, блокировки и узкие места зависимостей. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: на стенде всё быстро, а в день акции время ответа растёт в несколько раз. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-11-practice-load-testing",
"title": "Нагрузка. Как работать с темой: Нагрузочное тестирование",
"date": "2021-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Нагрузка",
"Надёжность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как проверять систему по сценарию пользователя, а не по одному красивому RPS. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «нагрузочное тестирование». Цель заметки — проверять систему по сценарию пользователя, а не по одному красивому RPS. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Нагрузка раскрывает очереди, лимиты соединений, блокировки и узкие места зависимостей.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проверять систему по сценарию пользователя, а не по одному красивому RPS.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: на стенде всё быстро, а в день акции время ответа растёт в несколько раз.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «нагрузочное тестирование» не в сложном синтаксисе, а в неявных предположениях. На стенде всё быстро, а в день акции время ответа растёт в несколько раз. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-10-field-d-runtime-service",
"title": "Разбор: Runtime D в сервисе в реальном проекте",
"date": "2021-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Backend"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Кейс о том, как сервис быстрый в тесте, но под нагрузкой появляются редкие паузы. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «runtime D в сервисе». Типичная ситуация выглядит так: сервис быстрый в тесте, но под нагрузкой появляются редкие паузы. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Использовать сильные стороны D, не игнорируя работу GC и границы ввода-вывода.</p>\n<pre><code>dub test\ndub build --build=release\n# Затем измеряем latency, память и профиль аллокаций.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «runtime D в сервисе» вернётся в следующем релизе под другим именем. Низкоуровневая модель полезна тогда, когда измерена, а не включена из любопытства.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-10-mechanism-d-runtime-service",
"title": "Под капотом: Runtime D в сервисе",
"date": "2021-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Backend"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Разбираем, почему низкоуровневая модель полезна тогда, когда измерена, а не включена из любопытства — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «runtime D в сервисе». Низкоуровневая модель полезна тогда, когда измерена, а не включена из любопытства. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>import core.memory : GC;\n\nGC.disable();\nscope(exit) GC.enable();\n\n// Использовать только в измеренном локальном участке.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: сервис быстрый в тесте, но под нагрузкой появляются редкие паузы. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-10-practice-d-runtime-service",
"title": "D. Как работать с темой: Runtime D в сервисе",
"date": "2021-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Backend"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "Практическая заметка о том, как использовать сильные стороны D, не игнорируя работу GC и границы ввода-вывода. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «runtime D в сервисе». Цель заметки — использовать сильные стороны D, не игнорируя работу GC и границы ввода-вывода. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Низкоуровневая модель полезна тогда, когда измерена, а не включена из любопытства.</p>\n<pre><code>enum table = buildLookupTable();\nstatic assert(table.length == 256);\n\nvoid handleRequest() {\n // В горячем пути сначала измеряем аллокации.\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: использовать сильные стороны D, не игнорируя работу GC и границы ввода-вывода.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: сервис быстрый в тесте, но под нагрузкой появляются редкие паузы.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «runtime D в сервисе» не в сложном синтаксисе, а в неявных предположениях. Сервис быстрый в тесте, но под нагрузкой появляются редкие паузы. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dlang.org/spec/garbage.html\" target=\"_blank\" rel=\"noopener\">D: automatic memory management</a></li><li><a href=\"https://dlang.org/library/core/memory.html\" target=\"_blank\" rel=\"noopener\">D: core.memory</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-09-field-api-versioning",
"title": "Разбор: Версионирование API в реальном проекте",
"date": "2021-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Архитектура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как мобильное приложение ещё отправляет поле, которое backend уже переименовал. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «версионирование API». Типичная ситуация выглядит так: мобильное приложение ещё отправляет поле, которое backend уже переименовал. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Изменять контракт так, чтобы старые клиенты не ломались внезапно.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «версионирование API» вернётся в следующем релизе под другим именем. Версия — это договор об обратной совместимости, а не только сегмент URL.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-09-mechanism-api-versioning",
"title": "Под капотом: Версионирование API",
"date": "2021-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Архитектура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему версия — это договор об обратной совместимости, а не только сегмент URL — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «версионирование API». Версия — это договор об обратной совместимости, а не только сегмент URL. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: мобильное приложение ещё отправляет поле, которое backend уже переименовал. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-09-practice-api-versioning",
"title": "API. Как работать с темой: Версионирование API",
"date": "2021-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Архитектура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как изменять контракт так, чтобы старые клиенты не ломались внезапно. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «версионирование API». Цель заметки — изменять контракт так, чтобы старые клиенты не ломались внезапно. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Версия — это договор об обратной совместимости, а не только сегмент URL.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: изменять контракт так, чтобы старые клиенты не ломались внезапно.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: мобильное приложение ещё отправляет поле, которое backend уже переименовал.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «версионирование API» не в сложном синтаксисе, а в неявных предположениях. Мобильное приложение ещё отправляет поле, которое backend уже переименовал. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-08-field-storage-contracts",
"title": "Разбор: Контракт хранилища в реальном проекте",
"date": "2021-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Инфраструктура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как в базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «контракт хранилища». Типичная ситуация выглядит так: в базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Выбрать место для файла, записи и временного объекта по их жизненному циклу.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «контракт хранилища» вернётся в следующем релизе под другим именем. Способ хранения определяется доступом, сроком жизни, размером, резервированием и правами.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-08-mechanism-storage-contracts",
"title": "Под капотом: Контракт хранилища",
"date": "2021-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Инфраструктура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему способ хранения определяется доступом, сроком жизни, размером, резервированием и правами — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «контракт хранилища». Способ хранения определяется доступом, сроком жизни, размером, резервированием и правами. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-08-practice-storage-contracts",
"title": "Данные. Как работать с темой: Контракт хранилища",
"date": "2021-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Инфраструктура"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как выбрать место для файла, записи и временного объекта по их жизненному циклу. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «контракт хранилища». Цель заметки — выбрать место для файла, записи и временного объекта по их жизненному циклу. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Способ хранения определяется доступом, сроком жизни, размером, резервированием и правами.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: выбрать место для файла, записи и временного объекта по их жизненному циклу.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «контракт хранилища» не в сложном синтаксисе, а в неявных предположениях. В базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-07-field-search-indexing",
"title": "Разбор: Индексация и поиск в реальном проекте",
"date": "2021-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Поиск",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Кейс о том, как новый товар есть в карточке, но не находится через поиск несколько минут. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «индексация и поиск». Типичная ситуация выглядит так: новый товар есть в карточке, но не находится через поиск несколько минут. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отделить поиск по тексту от транзакционного хранения сущностей.</p>\n<pre><code>CREATE INDEX CONCURRENTLY products_category_updated_idx\nON products (category_id, updated_at DESC);\n\n-- После этого снова смотрим EXPLAIN ANALYZE.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «индексация и поиск» вернётся в следующем релизе под другим именем. Индекс оптимизирован под поиск, поэтому обновляется по своему контракту и может отставать.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-07-mechanism-search-indexing",
"title": "Под капотом: Индексация и поиск",
"date": "2021-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Поиск",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Разбираем, почему индекс оптимизирован под поиск, поэтому обновляется по своему контракту и может отставать — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «индексация и поиск». Индекс оптимизирован под поиск, поэтому обновляется по своему контракту и может отставать. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>BEGIN;\nUPDATE stock SET amount = amount - 1 WHERE product_id = $1;\nINSERT INTO orders (product_id, user_id) VALUES ($1, $2);\nCOMMIT;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: новый товар есть в карточке, но не находится через поиск несколько минут. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-07-practice-search-indexing",
"title": "Поиск. Как работать с темой: Индексация и поиск",
"date": "2021-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Поиск",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Практическая заметка о том, как отделить поиск по тексту от транзакционного хранения сущностей. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «индексация и поиск». Цель заметки — отделить поиск по тексту от транзакционного хранения сущностей. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Индекс оптимизирован под поиск, поэтому обновляется по своему контракту и может отставать.</p>\n<pre><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name\nFROM products\nWHERE category_id = $1\nORDER BY updated_at DESC\nLIMIT 20;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отделить поиск по тексту от транзакционного хранения сущностей.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: новый товар есть в карточке, но не находится через поиск несколько минут.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «индексация и поиск» не в сложном синтаксисе, а в неявных предположениях. Новый товар есть в карточке, но не находится через поиск несколько минут. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-06-field-event-driven",
"title": "Разбор: Событийная интеграция в реальном проекте",
"date": "2021-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"События"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как добавление нового письма требует менять основной код оформления заказа. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «событийная интеграция». Типичная ситуация выглядит так: добавление нового письма требует менять основной код оформления заказа. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать уведомление о факте отдельным от реакции конкретного потребителя.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «событийная интеграция» вернётся в следующем релизе под другим именем. Событие описывает уже произошедший факт и может быть обработано несколькими независимыми получателями.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-06-mechanism-event-driven",
"title": "Под капотом: Событийная интеграция",
"date": "2021-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"События"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему событие описывает уже произошедший факт и может быть обработано несколькими независимыми получателями — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «событийная интеграция». Событие описывает уже произошедший факт и может быть обработано несколькими независимыми получателями. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: добавление нового письма требует менять основной код оформления заказа. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-06-practice-event-driven",
"title": "Архитектура. Как работать с темой: Событийная интеграция",
"date": "2021-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"События"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как сделать уведомление о факте отдельным от реакции конкретного потребителя. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «событийная интеграция». Цель заметки — сделать уведомление о факте отдельным от реакции конкретного потребителя. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Событие описывает уже произошедший факт и может быть обработано несколькими независимыми получателями.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать уведомление о факте отдельным от реакции конкретного потребителя.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: добавление нового письма требует менять основной код оформления заказа.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «событийная интеграция» не в сложном синтаксисе, а в неявных предположениях. Добавление нового письма требует менять основной код оформления заказа. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-05-field-distributed-locks",
"title": "Разбор: Блокировки и конкуренция в реальном проекте",
"date": "2021-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Конкурентность"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как две вкладки одновременно уменьшают один и тот же остаток. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «блокировки и конкуренция». Типичная ситуация выглядит так: две вкладки одновременно уменьшают один и тот же остаток. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Защитить операцию, которую нельзя выполнить одновременно дважды.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «блокировки и конкуренция» вернётся в следующем релизе под другим именем. Блокировка имеет область, срок жизни и сценарий потери владельца.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-05-mechanism-distributed-locks",
"title": "Под капотом: Блокировки и конкуренция",
"date": "2021-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Конкурентность"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему блокировка имеет область, срок жизни и сценарий потери владельца — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «блокировки и конкуренция». Блокировка имеет область, срок жизни и сценарий потери владельца. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: две вкладки одновременно уменьшают один и тот же остаток. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-05-practice-distributed-locks",
"title": "Backend. Как работать с темой: Блокировки и конкуренция",
"date": "2021-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Конкурентность"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как защитить операцию, которую нельзя выполнить одновременно дважды. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «блокировки и конкуренция». Цель заметки — защитить операцию, которую нельзя выполнить одновременно дважды. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Блокировка имеет область, срок жизни и сценарий потери владельца.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: защитить операцию, которую нельзя выполнить одновременно дважды.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: две вкладки одновременно уменьшают один и тот же остаток.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «блокировки и конкуренция» не в сложном синтаксисе, а в неявных предположениях. Две вкладки одновременно уменьшают один и тот же остаток. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-04-field-data-consistency",
"title": "Разбор: Согласованность данных между сервисами в реальном проекте",
"date": "2021-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Данные"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как пользователь видит заказ, но уведомление о нём приходит через несколько минут. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «согласованность данных между сервисами». Типичная ситуация выглядит так: пользователь видит заказ, но уведомление о нём приходит через несколько минут. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Не обещать мгновенную синхронность там, где система работает асинхронно.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «согласованность данных между сервисами» вернётся в следующем релизе под другим именем. Между локальной транзакцией и результатом в другом сервисе существует временная граница.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-04-mechanism-data-consistency",
"title": "Под капотом: Согласованность данных между сервисами",
"date": "2021-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Данные"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему между локальной транзакцией и результатом в другом сервисе существует временная граница — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «согласованность данных между сервисами». Между локальной транзакцией и результатом в другом сервисе существует временная граница. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: пользователь видит заказ, но уведомление о нём приходит через несколько минут. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-04-practice-data-consistency",
"title": "Архитектура. Как работать с темой: Согласованность данных между сервисами",
"date": "2021-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Архитектура",
"Данные"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как не обещать мгновенную синхронность там, где система работает асинхронно. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «согласованность данных между сервисами». Цель заметки — не обещать мгновенную синхронность там, где система работает асинхронно. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Между локальной транзакцией и результатом в другом сервисе существует временная граница.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: не обещать мгновенную синхронность там, где система работает асинхронно.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: пользователь видит заказ, но уведомление о нём приходит через несколько минут.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «согласованность данных между сервисами» не в сложном синтаксисе, а в неявных предположениях. Пользователь видит заказ, но уведомление о нём приходит через несколько минут. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-03-field-queues",
"title": "Разбор: Очереди задач в реальном проекте",
"date": "2021-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Очереди",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как один worker завис, а задачи после него перестали двигаться. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «очереди задач». Типичная ситуация выглядит так: один worker завис, а задачи после него перестали двигаться. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Распределить работу между worker-процессами и не потерять задачу при падении.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «очереди задач» вернётся в следующем релизе под другим именем. Очередь отделяет приём работы от её выполнения и требует подтверждения обработки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-03-mechanism-queues",
"title": "Под капотом: Очереди задач",
"date": "2021-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Очереди",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему очередь отделяет приём работы от её выполнения и требует подтверждения обработки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «очереди задач». Очередь отделяет приём работы от её выполнения и требует подтверждения обработки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: один worker завис, а задачи после него перестали двигаться. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-03-practice-queues",
"title": "Очереди. Как работать с темой: Очереди задач",
"date": "2021-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Очереди",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как распределить работу между worker-процессами и не потерять задачу при падении. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «очереди задач». Цель заметки — распределить работу между worker-процессами и не потерять задачу при падении. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Очередь отделяет приём работы от её выполнения и требует подтверждения обработки.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: распределить работу между worker-процессами и не потерять задачу при падении.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: один worker завис, а задачи после него перестали двигаться.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «очереди задач» не в сложном синтаксисе, а в неявных предположениях. Один worker завис, а задачи после него перестали двигаться. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-02-field-cache-invalidation",
"title": "Разбор: Инвалидация кеша в реальном проекте",
"date": "2021-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Кеширование",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как пользователь поменял профиль, а соседняя страница ещё час показывает старое имя. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «инвалидация кеша». Типичная ситуация выглядит так: пользователь поменял профиль, а соседняя страница ещё час показывает старое имя. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Ускорить чтение и не сделать старые данные постоянной правдой.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «инвалидация кеша» вернётся в следующем релизе под другим именем. Кеш всегда требует владельца, ключа, срока жизни и события обновления.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-02-mechanism-cache-invalidation",
"title": "Под капотом: Инвалидация кеша",
"date": "2021-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Кеширование",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему кеш всегда требует владельца, ключа, срока жизни и события обновления — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «инвалидация кеша». Кеш всегда требует владельца, ключа, срока жизни и события обновления. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: пользователь поменял профиль, а соседняя страница ещё час показывает старое имя. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-02-practice-cache-invalidation",
"title": "Кеширование. Как работать с темой: Инвалидация кеша",
"date": "2021-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Кеширование",
"Backend"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как ускорить чтение и не сделать старые данные постоянной правдой. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «инвалидация кеша». Цель заметки — ускорить чтение и не сделать старые данные постоянной правдой. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Кеш всегда требует владельца, ключа, срока жизни и события обновления.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: ускорить чтение и не сделать старые данные постоянной правдой.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: пользователь поменял профиль, а соседняя страница ещё час показывает старое имя.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «инвалидация кеша» не в сложном синтаксисе, а в неявных предположениях. Пользователь поменял профиль, а соседняя страница ещё час показывает старое имя. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-01-field-transactions",
"title": "Разбор: Границы транзакции в реальном проекте",
"date": "2021-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PostgreSQL",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Кейс о том, как заказ создан, а строка остатка на складе не обновилась. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Начинаю такой разбор с фактов, временной шкалы и границ ответственности. Сегодняшний случай: «границы транзакции». Типичная ситуация выглядит так: заказ создан, а строка остатка на складе не обновилась. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сохранить согласованность нескольких изменений данных.</p>\n<pre><code>CREATE INDEX CONCURRENTLY products_category_updated_idx\nON products (category_id, updated_at DESC);\n\n-- После этого снова смотрим EXPLAIN ANALYZE.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «границы транзакции» вернётся в следующем релизе под другим именем. Транзакция фиксирует набор изменений целиком или откатывает его при ошибке.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2021-01-mechanism-transactions",
"title": "Под капотом: Границы транзакции",
"date": "2021-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PostgreSQL",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Разбираем, почему транзакция фиксирует набор изменений целиком или откатывает его при ошибке — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Полезная инженерная модель должна объяснять не только нормальную работу, но и отказ. Рассмотрим «границы транзакции». Транзакция фиксирует набор изменений целиком или откатывает его при ошибке. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>BEGIN;\nUPDATE stock SET amount = amount - 1 WHERE product_id = $1;\nINSERT INTO orders (product_id, user_id) VALUES ($1, $2);\nCOMMIT;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: заказ создан, а строка остатка на складе не обновилась. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2021-01-practice-transactions",
"title": "PostgreSQL. Как работать с темой: Границы транзакции",
"date": "2021-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PostgreSQL",
"Данные"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Практическая заметка о том, как сохранить согласованность нескольких изменений данных. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Когда система растёт, локальная правка начинает затрагивать данные, сеть и ожидания пользователей. Ниже — практическая схема для темы «границы транзакции». Цель заметки — сохранить согласованность нескольких изменений данных. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Транзакция фиксирует набор изменений целиком или откатывает его при ошибке.</p>\n<pre><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name\nFROM products\nWHERE category_id = $1\nORDER BY updated_at DESC\nLIMIT 20;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сохранить согласованность нескольких изменений данных.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: заказ создан, а строка остатка на складе не обновилась.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «границы транзакции» не в сложном синтаксисе, а в неявных предположениях. Заказ создан, а строка остатка на складе не обновилась. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Это не гарантирует отсутствие ошибок, но делает их обнаруживаемыми и управляемыми.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-12-field-incident-review",
"title": "Разбор: Разбор инцидента в реальном проекте",
"date": "2020-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как сервис восстановили, но через месяц та же ошибка повторилась. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «разбор инцидента». Типичная ситуация выглядит так: сервис восстановили, но через месяц та же ошибка повторилась. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Извлечь из сбоя изменение процесса, а не найти виноватого.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «разбор инцидента» вернётся в следующем релизе под другим именем. Хороший разбор связывает симптом, временную шкалу, защитные механизмы и действие.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-12-mechanism-incident-review",
"title": "Под капотом: Разбор инцидента",
"date": "2020-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему хороший разбор связывает симптом, временную шкалу, защитные механизмы и действие — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «разбор инцидента». Хороший разбор связывает симптом, временную шкалу, защитные механизмы и действие. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: сервис восстановили, но через месяц та же ошибка повторилась. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-12-practice-incident-review",
"title": "Надёжность. Как работать с темой: Разбор инцидента",
"date": "2020-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Надёжность",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как извлечь из сбоя изменение процесса, а не найти виноватого. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «разбор инцидента». Цель заметки — извлечь из сбоя изменение процесса, а не найти виноватого. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Хороший разбор связывает симптом, временную шкалу, защитные механизмы и действие.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: извлечь из сбоя изменение процесса, а не найти виноватого.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: сервис восстановили, но через месяц та же ошибка повторилась.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «разбор инцидента» не в сложном синтаксисе, а в неявных предположениях. Сервис восстановили, но через месяц та же ошибка повторилась. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-11-field-security-baseline",
"title": "Разбор: Минимальная защита веб-приложения в реальном проекте",
"date": "2020-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Кейс о том, как старый административный endpoint доступен без CSRF-защиты. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «минимальная защита веб-приложения». Типичная ситуация выглядит так: старый административный endpoint доступен без CSRF-защиты. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Закрыть наиболее очевидные дыры в аутентификации, вводе и настройках.</p>\n<pre><code>Asset -&gt; actor -&gt; entry point -&gt; control -&gt; evidence\n\nЕсли для контроля нет доказательства, считаем его непроверенным.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «минимальная защита веб-приложения» вернётся в следующем релизе под другим именем. Безопасность — это серия независимых барьеров, а не одна «волшебная» проверка.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-11-mechanism-security-baseline",
"title": "Под капотом: Минимальная защита веб-приложения",
"date": "2020-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Разбираем, почему безопасность — это серия независимых барьеров, а не одна «волшебная» проверка — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «минимальная защита веб-приложения». Безопасность — это серия независимых барьеров, а не одна «волшебная» проверка. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>allow-list extension: jpg, png, webp\nvalidate content independently\ngenerate server filename\nstore outside web root\nserve through authorized handler</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: старый административный endpoint доступен без CSRF-защиты. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-11-practice-security-baseline",
"title": "Безопасность. Как работать с темой: Минимальная защита веб-приложения",
"date": "2020-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"Web"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Практическая заметка о том, как закрыть наиболее очевидные дыры в аутентификации, вводе и настройках. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «минимальная защита веб-приложения». Цель заметки — закрыть наиболее очевидные дыры в аутентификации, вводе и настройках. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Безопасность — это серия независимых барьеров, а не одна «волшебная» проверка.</p>\n<pre><code>if (!sameOrigin(request) || !validCsrfToken(request)) {\n return response.status(403).end();\n}\n\nif (!user.can(&#039;profile:update&#039;)) {\n return response.status(403).end();\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: закрыть наиболее очевидные дыры в аутентификации, вводе и настройках.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: старый административный endpoint доступен без CSRF-защиты.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «минимальная защита веб-приложения» не в сложном синтаксисе, а в неявных предположениях. Старый административный endpoint доступен без CSRF-защиты. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: CSRF Prevention Cheat Sheet</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-10-field-backup-recovery",
"title": "Разбор: Резервное копирование и восстановление в реальном проекте",
"date": "2020-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Надёжность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Кейс о том, как резервная копия есть, но никто не пробовал поднять из неё тестовую базу. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «резервное копирование и восстановление». Типичная ситуация выглядит так: резервная копия есть, но никто не пробовал поднять из неё тестовую базу. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проверять не наличие бэкапа, а возможность вернуть данные в рабочее состояние.</p>\n<pre><code>CREATE INDEX CONCURRENTLY products_category_updated_idx\nON products (category_id, updated_at DESC);\n\n-- После этого снова смотрим EXPLAIN ANALYZE.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «резервное копирование и восстановление» вернётся в следующем релизе под другим именем. Копия ценна только после регулярной проверки восстановления и понятного RPO/RTO.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-10-mechanism-backup-recovery",
"title": "Под капотом: Резервное копирование и восстановление",
"date": "2020-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Надёжность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Разбираем, почему копия ценна только после регулярной проверки восстановления и понятного RPO/RTO — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «резервное копирование и восстановление». Копия ценна только после регулярной проверки восстановления и понятного RPO/RTO. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>BEGIN;\nUPDATE stock SET amount = amount - 1 WHERE product_id = $1;\nINSERT INTO orders (product_id, user_id) VALUES ($1, $2);\nCOMMIT;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: резервная копия есть, но никто не пробовал поднять из неё тестовую базу. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-10-practice-backup-recovery",
"title": "Данные. Как работать с темой: Резервное копирование и восстановление",
"date": "2020-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Данные",
"Надёжность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Практическая заметка о том, как проверять не наличие бэкапа, а возможность вернуть данные в рабочее состояние. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «резервное копирование и восстановление». Цель заметки — проверять не наличие бэкапа, а возможность вернуть данные в рабочее состояние. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Копия ценна только после регулярной проверки восстановления и понятного RPO/RTO.</p>\n<pre><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name\nFROM products\nWHERE category_id = $1\nORDER BY updated_at DESC\nLIMIT 20;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проверять не наличие бэкапа, а возможность вернуть данные в рабочее состояние.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: резервная копия есть, но никто не пробовал поднять из неё тестовую базу.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «резервное копирование и восстановление» не в сложном синтаксисе, а в неявных предположениях. Резервная копия есть, но никто не пробовал поднять из неё тестовую базу. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-09-field-tracing-basics",
"title": "Разбор: Трассировка запроса в реальном проекте",
"date": "2020-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"HTTP"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «трассировка запроса». Типичная ситуация выглядит так: один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Увидеть путь одного запроса через frontend, API и внешнюю систему.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «трассировка запроса» вернётся в следующем релизе под другим именем. Trace связывает операции общим контекстом и показывает задержки между компонентами.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-09-mechanism-tracing-basics",
"title": "Под капотом: Трассировка запроса",
"date": "2020-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"HTTP"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему trace связывает операции общим контекстом и показывает задержки между компонентами — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «трассировка запроса». Trace связывает операции общим контекстом и показывает задержки между компонентами. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-09-practice-tracing-basics",
"title": "Наблюдаемость. Как работать с темой: Трассировка запроса",
"date": "2020-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"HTTP"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как увидеть путь одного запроса через frontend, API и внешнюю систему. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «трассировка запроса». Цель заметки — увидеть путь одного запроса через frontend, API и внешнюю систему. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Trace связывает операции общим контекстом и показывает задержки между компонентами.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: увидеть путь одного запроса через frontend, API и внешнюю систему.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «трассировка запроса» не в сложном синтаксисе, а в неявных предположениях. Один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-08-field-metrics-basics",
"title": "Разбор: Метрики приложения в реальном проекте",
"date": "2020-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Производительность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как жалобы пользователей есть, а в логах нет явной аварии. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «метрики приложения». Типичная ситуация выглядит так: жалобы пользователей есть, а в логах нет явной аварии. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отличать отдельную ошибку от ухудшения поведения всей системы.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «метрики приложения» вернётся в следующем релизе под другим именем. Метрика показывает изменение во времени, поэтому важны единицы, лейблы и базовая линия.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-08-mechanism-metrics-basics",
"title": "Под капотом: Метрики приложения",
"date": "2020-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Производительность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему метрика показывает изменение во времени, поэтому важны единицы, лейблы и базовая линия — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «метрики приложения». Метрика показывает изменение во времени, поэтому важны единицы, лейблы и базовая линия. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: жалобы пользователей есть, а в логах нет явной аварии. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-08-practice-metrics-basics",
"title": "Наблюдаемость. Как работать с темой: Метрики приложения",
"date": "2020-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Производительность"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как отличать отдельную ошибку от ухудшения поведения всей системы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «метрики приложения». Цель заметки — отличать отдельную ошибку от ухудшения поведения всей системы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Метрика показывает изменение во времени, поэтому важны единицы, лейблы и базовая линия.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отличать отдельную ошибку от ухудшения поведения всей системы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: жалобы пользователей есть, а в логах нет явной аварии.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «метрики приложения» не в сложном синтаксисе, а в неявных предположениях. Жалобы пользователей есть, а в логах нет явной аварии. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-07-field-structured-logs",
"title": "Разбор: Структурированные логи в реальном проекте",
"date": "2020-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Кейс о том, как в журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «структурированные логи». Типичная ситуация выглядит так: в журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Находить нужный запрос по идентификатору, а не читать тысячи строк вручную.</p>\n<pre><code>1. Видим ухудшение по метрике.\n2. Находим trace конкретного запроса.\n3. Читаем логи нужного span.\n4. Проверяем исправление тем же сигналом.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «структурированные логи» вернётся в следующем релизе под другим именем. Лог — это событие с полями, уровнем и контекстом, а не просто текст для консоли.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-07-mechanism-structured-logs",
"title": "Под капотом: Структурированные логи",
"date": "2020-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Разбираем, почему лог — это событие с полями, уровнем и контекстом, а не просто текст для консоли — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «структурированные логи». Лог — это событие с полями, уровнем и контекстом, а не просто текст для консоли. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>trace: путь запроса\nmetric: число или распределение во времени\nlog: контекст конкретного события\n\nСвязываем их общим request_id или trace_id.</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: в журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-07-practice-structured-logs",
"title": "Наблюдаемость. Как работать с темой: Структурированные логи",
"date": "2020-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Наблюдаемость",
"Backend"
],
"cover": "/assets/illustrations/gc-small-pools.svg",
"excerpt": "Практическая заметка о том, как находить нужный запрос по идентификатору, а не читать тысячи строк вручную. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «структурированные логи». Цель заметки — находить нужный запрос по идентификатору, а не читать тысячи строк вручную. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Лог — это событие с полями, уровнем и контекстом, а не просто текст для консоли.</p>\n<pre><code>request_id=8f2d operation=checkout status=202 duration_ms=42\n\n# Одно событие — одна строка с полями,\n# по которым его можно найти и агрегировать.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: находить нужный запрос по идентификатору, а не читать тысячи строк вручную.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: в журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «структурированные логи» не в сложном синтаксисе, а в неявных предположениях. В журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://opentelemetry.io/docs/concepts/signals/\" target=\"_blank\" rel=\"noopener\">OpenTelemetry: signals</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-06-field-retry-idempotency",
"title": "Разбор: Повтор запросов и идемпотентность в реальном проекте",
"date": "2020-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Надёжность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как клиент не получил ответ и отправил ту же команду ещё раз. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «повтор запросов и идемпотентность». Типичная ситуация выглядит так: клиент не получил ответ и отправил ту же команду ещё раз. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Повторять временные сбои, не создавая второй платёж, заказ или письмо.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «повтор запросов и идемпотентность» вернётся в следующем релизе под другим именем. Повтор безопасен только тогда, когда операция имеет ключ идемпотентности и понятный результат.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-06-mechanism-retry-idempotency",
"title": "Под капотом: Повтор запросов и идемпотентность",
"date": "2020-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Надёжность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему повтор безопасен только тогда, когда операция имеет ключ идемпотентности и понятный результат — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «повтор запросов и идемпотентность». Повтор безопасен только тогда, когда операция имеет ключ идемпотентности и понятный результат. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: клиент не получил ответ и отправил ту же команду ещё раз. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-06-practice-retry-idempotency",
"title": "Backend. Как работать с темой: Повтор запросов и идемпотентность",
"date": "2020-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"Надёжность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как повторять временные сбои, не создавая второй платёж, заказ или письмо. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «повтор запросов и идемпотентность». Цель заметки — повторять временные сбои, не создавая второй платёж, заказ или письмо. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Повтор безопасен только тогда, когда операция имеет ключ идемпотентности и понятный результат.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: повторять временные сбои, не создавая второй платёж, заказ или письмо.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: клиент не получил ответ и отправил ту же команду ещё раз.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «повтор запросов и идемпотентность» не в сложном синтаксисе, а в неявных предположениях. Клиент не получил ответ и отправил ту же команду ещё раз. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-05-field-background-jobs",
"title": "Разбор: Фоновые задачи в реальном проекте",
"date": "2020-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Очереди"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Кейс о том, как экспорт отчёта занимает минуту и держит браузер пользователя в ожидании. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «фоновые задачи». Типичная ситуация выглядит так: экспорт отчёта занимает минуту и держит браузер пользователя в ожидании. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Вынести долгую работу из пользовательского HTTP-запроса.</p>\n<pre><code>1. Записать задачу с ключом идемпотентности.\n2. Взять задачу worker-ом.\n3. Сохранить результат.\n4. Подтвердить обработку только после успеха.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «фоновые задачи» вернётся в следующем релизе под другим именем. Запрос должен принять намерение, а worker — выполнить тяжёлую часть с отдельным состоянием.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-05-mechanism-background-jobs",
"title": "Под капотом: Фоновые задачи",
"date": "2020-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Очереди"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Разбираем, почему запрос должен принять намерение, а worker — выполнить тяжёлую часть с отдельным состоянием — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «фоновые задачи». Запрос должен принять намерение, а worker — выполнить тяжёлую часть с отдельным состоянием. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>cache key: profile:42\nowner: profile service\nttl: 5m\ninvalidate on: ProfileUpdated\nfallback: read primary storage</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: экспорт отчёта занимает минуту и держит браузер пользователя в ожидании. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-05-practice-background-jobs",
"title": "Backend. Как работать с темой: Фоновые задачи",
"date": "2020-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Backend",
"Очереди"
],
"cover": "/assets/illustrations/vibe-async.svg",
"excerpt": "Практическая заметка о том, как вынести долгую работу из пользовательского HTTP-запроса. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «фоновые задачи». Цель заметки — вынести долгую работу из пользовательского HTTP-запроса. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Запрос должен принять намерение, а worker — выполнить тяжёлую часть с отдельным состоянием.</p>\n<pre><code>XADD jobs * type export report_id 42\nXGROUP CREATE jobs exports $ MKSTREAM\nXREADGROUP GROUP exports worker-1 COUNT 1 BLOCK 5000 STREAMS jobs &gt;\nXACK jobs exports 1700000000000-0</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: вынести долгую работу из пользовательского HTTP-запроса.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: экспорт отчёта занимает минуту и держит браузер пользователя в ожидании.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «фоновые задачи» не в сложном синтаксисе, а в неявных предположениях. Экспорт отчёта занимает минуту и держит браузер пользователя в ожидании. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://redis.io/docs/latest/develop/data-types/streams/\" target=\"_blank\" rel=\"noopener\">Redis Streams</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-04-field-reverse-proxy",
"title": "Разбор: Reverse proxy перед приложением в реальном проекте",
"date": "2020-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Инфраструктура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как приложение считает запрос HTTPS, а в логах почему-то видит http. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «reverse proxy перед приложением». Типичная ситуация выглядит так: приложение считает запрос HTTPS, а в логах почему-то видит http. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Разобраться, где завершается TLS и кто отвечает за таймауты и заголовки.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «reverse proxy перед приложением» вернётся в следующем релизе под другим именем. Прокси отделяет внешнее HTTP-соединение от процесса приложения и задаёт границу ответственности.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-04-mechanism-reverse-proxy",
"title": "Под капотом: Reverse proxy перед приложением",
"date": "2020-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Инфраструктура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему прокси отделяет внешнее HTTP-соединение от процесса приложения и задаёт границу ответственности — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «reverse proxy перед приложением». Прокси отделяет внешнее HTTP-соединение от процесса приложения и задаёт границу ответственности. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: приложение считает запрос HTTPS, а в логах почему-то видит http. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-04-practice-reverse-proxy",
"title": "HTTP. Как работать с темой: Reverse proxy перед приложением",
"date": "2020-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Инфраструктура"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как разобраться, где завершается TLS и кто отвечает за таймауты и заголовки. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «reverse proxy перед приложением». Цель заметки — разобраться, где завершается TLS и кто отвечает за таймауты и заголовки. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Прокси отделяет внешнее HTTP-соединение от процесса приложения и задаёт границу ответственности.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: разобраться, где завершается TLS и кто отвечает за таймауты и заголовки.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: приложение считает запрос HTTPS, а в логах почему-то видит http.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «reverse proxy перед приложением» не в сложном синтаксисе, а в неявных предположениях. Приложение считает запрос HTTPS, а в логах почему-то видит http. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-03-field-ci-pipeline",
"title": "Разбор: Минимальный pipeline в реальном проекте",
"date": "2020-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"CI/CD",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как локально тесты запускались, но после merge приложение не собирается. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «минимальный pipeline». Типичная ситуация выглядит так: локально тесты запускались, но после merge приложение не собирается. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Превратить проверку кода в повторяемую последовательность шагов.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «минимальный pipeline» вернётся в следующем релизе под другим именем. CI ценен не скоростью сам по себе, а тем, что одинаково проверяет каждое изменение.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-03-mechanism-ci-pipeline",
"title": "Под капотом: Минимальный pipeline",
"date": "2020-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"CI/CD",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему CI ценен не скоростью сам по себе, а тем, что одинаково проверяет каждое изменение — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «минимальный pipeline». CI ценен не скоростью сам по себе, а тем, что одинаково проверяет каждое изменение. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: локально тесты запускались, но после merge приложение не собирается. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-03-practice-ci-pipeline",
"title": "CI/CD. Как работать с темой: Минимальный pipeline",
"date": "2020-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"CI/CD",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как превратить проверку кода в повторяемую последовательность шагов. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «минимальный pipeline». Цель заметки — превратить проверку кода в повторяемую последовательность шагов. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. CI ценен не скоростью сам по себе, а тем, что одинаково проверяет каждое изменение.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: превратить проверку кода в повторяемую последовательность шагов.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: локально тесты запускались, но после merge приложение не собирается.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «минимальный pipeline» не в сложном синтаксисе, а в неявных предположениях. Локально тесты запускались, но после merge приложение не собирается. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://playwright.dev/docs/best-practices\" target=\"_blank\" rel=\"noopener\">Playwright: best practices</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-02-field-configs-secrets",
"title": "Разбор: Настройки и секреты в реальном проекте",
"date": "2020-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как боевой токен случайно оказался в JSON-ответе об ошибке. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «настройки и секреты». Типичная ситуация выглядит так: боевой токен случайно оказался в JSON-ответе об ошибке. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Разделить код, параметры окружения и чувствительные данные.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «настройки и секреты» вернётся в следующем релизе под другим именем. Конфигурация меняется между средами, а секрет не должен попадать в репозиторий или лог.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-02-mechanism-configs-secrets",
"title": "Под капотом: Настройки и секреты",
"date": "2020-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему конфигурация меняется между средами, а секрет не должен попадать в репозиторий или лог — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «настройки и секреты». Конфигурация меняется между средами, а секрет не должен попадать в репозиторий или лог. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: боевой токен случайно оказался в JSON-ответе об ошибке. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-02-practice-configs-secrets",
"title": "Конфигурация. Как работать с темой: Настройки и секреты",
"date": "2020-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Безопасность",
"DevOps"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как разделить код, параметры окружения и чувствительные данные. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «настройки и секреты». Цель заметки — разделить код, параметры окружения и чувствительные данные. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Конфигурация меняется между средами, а секрет не должен попадать в репозиторий или лог.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: разделить код, параметры окружения и чувствительные данные.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: боевой токен случайно оказался в JSON-ответе об ошибке.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «настройки и секреты» не в сложном синтаксисе, а в неявных предположениях. Боевой токен случайно оказался в JSON-ответе об ошибке. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-01-field-docker-local",
"title": "Разбор: Локальная разработка в контейнере в реальном проекте",
"date": "2020-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Docker",
"Инфраструктура"
],
"cover": "/assets/illustrations/vibe-cover.svg",
"excerpt": "Кейс о том, как проект требует старую версию PHP, которой нет в системе разработчика. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «локальная разработка в контейнере». Типичная ситуация выглядит так: проект требует старую версию PHP, которой нет в системе разработчика. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Запускать приложение с одинаковыми зависимостями у всей команды.</p>\n<pre><code>docker compose config\ndocker compose up --build\ndocker compose logs --tail=100 app\ndocker compose exec app php -v</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «локальная разработка в контейнере» вернётся в следующем релизе под другим именем. Образ описывает окружение, а контейнер запускает его как изолированный процесс.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2020-01-mechanism-docker-local",
"title": "Под капотом: Локальная разработка в контейнере",
"date": "2020-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Docker",
"Инфраструктура"
],
"cover": "/assets/illustrations/vibe-cover.svg",
"excerpt": "Разбираем, почему образ описывает окружение, а контейнер запускает его как изолированный процесс — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «локальная разработка в контейнере». Образ описывает окружение, а контейнер запускает его как изолированный процесс. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>services:\n app:\n build: .\n environment:\n APP_ENV: development\n ports:\n - &quot;8080:8080&quot;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: проект требует старую версию PHP, которой нет в системе разработчика. Поэтому в production нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2020-01-practice-docker-local",
"title": "Docker. Как работать с темой: Локальная разработка в контейнере",
"date": "2020-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Docker",
"Инфраструктура"
],
"cover": "/assets/illustrations/vibe-cover.svg",
"excerpt": "Практическая заметка о том, как запускать приложение с одинаковыми зависимостями у всей команды. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «локальная разработка в контейнере». Цель заметки — запускать приложение с одинаковыми зависимостями у всей команды. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Образ описывает окружение, а контейнер запускает его как изолированный процесс.</p>\n<pre><code>FROM php:8.3-cli\nWORKDIR /app\nCOPY composer.json composer.lock ./\nRUN composer install --no-dev --prefer-dist\nCOPY . .\nCMD [&quot;php&quot;, &quot;public/index.php&quot;]</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: запускать приложение с одинаковыми зависимостями у всей команды.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: проект требует старую версию PHP, которой нет в системе разработчика.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «локальная разработка в контейнере» не в сложном синтаксисе, а в неявных предположениях. Проект требует старую версию PHP, которой нет в системе разработчика. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-12-field-code-ownership",
"title": "Разбор: Ответственность за код в реальном проекте",
"date": "2019-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Разработка",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как автор старой интеграции ушёл, а никто не понимает, какие поля обязательны. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «ответственность за код». Типичная ситуация выглядит так: автор старой интеграции ушёл, а никто не понимает, какие поля обязательны. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать передачу модуля понятной, а не держать знания только в голове автора.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «ответственность за код» вернётся в следующем релизе под другим именем. Сопровождаемость начинается там, где другой человек способен воспроизвести решение.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-12-mechanism-code-ownership",
"title": "Под капотом: Ответственность за код",
"date": "2019-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Разработка",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему сопровождаемость начинается там, где другой человек способен воспроизвести решение — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «ответственность за код». Сопровождаемость начинается там, где другой человек способен воспроизвести решение. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: автор старой интеграции ушёл, а никто не понимает, какие поля обязательны. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-12-practice-code-ownership",
"title": "Команда. Как работать с темой: Ответственность за код",
"date": "2019-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Разработка",
"Команда"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать передачу модуля понятной, а не держать знания только в голове автора. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «ответственность за код». Цель заметки — сделать передачу модуля понятной, а не держать знания только в голове автора. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сопровождаемость начинается там, где другой человек способен воспроизвести решение.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать передачу модуля понятной, а не держать знания только в голове автора.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: автор старой интеграции ушёл, а никто не понимает, какие поля обязательны.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «ответственность за код» не в сложном синтаксисе, а в неявных предположениях. Автор старой интеграции ушёл, а никто не понимает, какие поля обязательны. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-11-field-reproducible-builds",
"title": "Разбор: Воспроизводимая сборка в реальном проекте",
"date": "2019-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как после npm install у одного разработчика появляется ошибка, которой нет у остальных. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «воспроизводимая сборка». Типичная ситуация выглядит так: после npm install у одного разработчика появляется ошибка, которой нет у остальных. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Получать одинаковый результат на локальной машине и в CI.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «воспроизводимая сборка» вернётся в следующем релизе под другим именем. Lockfile, версии среды и явная конфигурация превращают сборку из ритуала в процесс.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-11-mechanism-reproducible-builds",
"title": "Под капотом: Воспроизводимая сборка",
"date": "2019-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему lockfile, версии среды и явная конфигурация превращают сборку из ритуала в процесс — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «воспроизводимая сборка». Lockfile, версии среды и явная конфигурация превращают сборку из ритуала в процесс. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после npm install у одного разработчика появляется ошибка, которой нет у остальных. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-11-practice-reproducible-builds",
"title": "Сборка. Как работать с темой: Воспроизводимая сборка",
"date": "2019-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как получать одинаковый результат на локальной машине и в CI. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «воспроизводимая сборка». Цель заметки — получать одинаковый результат на локальной машине и в CI. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Lockfile, версии среды и явная конфигурация превращают сборку из ритуала в процесс.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: получать одинаковый результат на локальной машине и в CI.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после npm install у одного разработчика появляется ошибка, которой нет у остальных.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «воспроизводимая сборка» не в сложном синтаксисе, а в неявных предположениях. После npm install у одного разработчика появляется ошибка, которой нет у остальных. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-10-field-typescript-migration",
"title": "Разбор: Переход на TypeScript в реальном проекте",
"date": "2019-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"TypeScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как старый JavaScript-модуль используют в десятке страниц с разными аргументами. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «переход на TypeScript». Типичная ситуация выглядит так: старый JavaScript-модуль используют в десятке страниц с разными аргументами. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Добавить типы без переписывания всей кодовой базы.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «переход на TypeScript» вернётся в следующем релизе под другим именем. Типы полезнее всего на границах данных: API, форма, конфигурация и публичная функция.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-10-mechanism-typescript-migration",
"title": "Под капотом: Переход на TypeScript",
"date": "2019-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"TypeScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему типы полезнее всего на границах данных: API, форма, конфигурация и публичная функция — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «переход на TypeScript». Типы полезнее всего на границах данных: API, форма, конфигурация и публичная функция. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: старый JavaScript-модуль используют в десятке страниц с разными аргументами. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-10-practice-typescript-migration",
"title": "JavaScript. Как работать с темой: Переход на TypeScript",
"date": "2019-10-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"TypeScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как добавить типы без переписывания всей кодовой базы. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «переход на TypeScript». Цель заметки — добавить типы без переписывания всей кодовой базы. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Типы полезнее всего на границах данных: API, форма, конфигурация и публичная функция.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: добавить типы без переписывания всей кодовой базы.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: старый JavaScript-модуль используют в десятке страниц с разными аргументами.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «переход на TypeScript» не в сложном синтаксисе, а в неявных предположениях. Старый JavaScript-модуль используют в десятке страниц с разными аргументами. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-09-field-accessibility-basics",
"title": "Разбор: Базовая доступность в реальном проекте",
"date": "2019-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как красивая кнопка работает мышью, но недоступна с клавиатуры. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «базовая доступность». Типичная ситуация выглядит так: красивая кнопка работает мышью, но недоступна с клавиатуры. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Дать интерфейсу нормальную семантику, фокус и текстовые подписи.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «базовая доступность» вернётся в следующем релизе под другим именем. Браузер строит не только DOM, но и дерево доступности для вспомогательных технологий.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-09-mechanism-accessibility-basics",
"title": "Под капотом: Базовая доступность",
"date": "2019-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему браузер строит не только DOM, но и дерево доступности для вспомогательных технологий — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «базовая доступность». Браузер строит не только DOM, но и дерево доступности для вспомогательных технологий. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: красивая кнопка работает мышью, но недоступна с клавиатуры. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-09-practice-accessibility-basics",
"title": "Frontend. Как работать с темой: Базовая доступность",
"date": "2019-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Доступность",
"Frontend"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как дать интерфейсу нормальную семантику, фокус и текстовые подписи. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «базовая доступность». Цель заметки — дать интерфейсу нормальную семантику, фокус и текстовые подписи. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Браузер строит не только DOM, но и дерево доступности для вспомогательных технологий.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: дать интерфейсу нормальную семантику, фокус и текстовые подписи.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: красивая кнопка работает мышью, но недоступна с клавиатуры.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «базовая доступность» не в сложном синтаксисе, а в неявных предположениях. Красивая кнопка работает мышью, но недоступна с клавиатуры. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-08-field-frontend-performance",
"title": "Разбор: Производительность первой загрузки в реальном проекте",
"date": "2019-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как страница быстро отвечает сервером, но визуально остаётся пустой. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «производительность первой загрузки». Типичная ситуация выглядит так: страница быстро отвечает сервером, но визуально остаётся пустой. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Измерить, что именно мешает странице стать полезной пользователю.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «производительность первой загрузки» вернётся в следующем релизе под другим именем. Сеть, парсинг, выполнение JavaScript и рендеринг образуют одну цепочку задержки.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-08-mechanism-frontend-performance",
"title": "Под капотом: Производительность первой загрузки",
"date": "2019-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему сеть, парсинг, выполнение JavaScript и рендеринг образуют одну цепочку задержки — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «производительность первой загрузки». Сеть, парсинг, выполнение JavaScript и рендеринг образуют одну цепочку задержки. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: страница быстро отвечает сервером, но визуально остаётся пустой. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-08-practice-frontend-performance",
"title": "Frontend. Как работать с темой: Производительность первой загрузки",
"date": "2019-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Производительность"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как измерить, что именно мешает странице стать полезной пользователю. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «производительность первой загрузки». Цель заметки — измерить, что именно мешает странице стать полезной пользователю. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Сеть, парсинг, выполнение JavaScript и рендеринг образуют одну цепочку задержки.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: измерить, что именно мешает странице стать полезной пользователю.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: страница быстро отвечает сервером, но визуально остаётся пустой.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «производительность первой загрузки» не в сложном синтаксисе, а в неявных предположениях. Страница быстро отвечает сервером, но визуально остаётся пустой. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/Performance\" target=\"_blank\" rel=\"noopener\">MDN: Web performance</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-07-field-sql-indexes",
"title": "Разбор: Индексы и планы запросов в реальном проекте",
"date": "2019-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"SQL",
"Производительность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Кейс о том, как страница каталога медленно открывается только при большом количестве товаров. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «индексы и планы запросов». Типичная ситуация выглядит так: страница каталога медленно открывается только при большом количестве товаров. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Ускорить нужный запрос, а не поставить индекс на каждую колонку подряд.</p>\n<pre><code>CREATE INDEX CONCURRENTLY products_category_updated_idx\nON products (category_id, updated_at DESC);\n\n-- После этого снова смотрим EXPLAIN ANALYZE.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «индексы и планы запросов» вернётся в следующем релизе под другим именем. Индекс ускоряет поиск ценой записи, места и усложнения плана.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-07-mechanism-sql-indexes",
"title": "Под капотом: Индексы и планы запросов",
"date": "2019-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"SQL",
"Производительность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Разбираем, почему индекс ускоряет поиск ценой записи, места и усложнения плана — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «индексы и планы запросов». Индекс ускоряет поиск ценой записи, места и усложнения плана. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>BEGIN;\nUPDATE stock SET amount = amount - 1 WHERE product_id = $1;\nINSERT INTO orders (product_id, user_id) VALUES ($1, $2);\nCOMMIT;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: страница каталога медленно открывается только при большом количестве товаров. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-07-practice-sql-indexes",
"title": "SQL. Как работать с темой: Индексы и планы запросов",
"date": "2019-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"SQL",
"Производительность"
],
"cover": "/assets/illustrations/vibe-db.svg",
"excerpt": "Практическая заметка о том, как ускорить нужный запрос, а не поставить индекс на каждую колонку подряд. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «индексы и планы запросов». Цель заметки — ускорить нужный запрос, а не поставить индекс на каждую колонку подряд. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Индекс ускоряет поиск ценой записи, места и усложнения плана.</p>\n<pre><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT id, name\nFROM products\nWHERE category_id = $1\nORDER BY updated_at DESC\nLIMIT 20;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: ускорить нужный запрос, а не поставить индекс на каждую колонку подряд.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: страница каталога медленно открывается только при большом количестве товаров.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «индексы и планы запросов» не в сложном синтаксисе, а в неявных предположениях. Страница каталога медленно открывается только при большом количестве товаров. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.postgresql.org/docs/current/indexes.html\" target=\"_blank\" rel=\"noopener\">PostgreSQL: Indexes</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-06-field-rest-api",
"title": "Разбор: Контракт REST API в реальном проекте",
"date": "2019-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"HTTP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как одна и та же ошибка то приходит строкой, то пустым массивом. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «контракт REST API». Типичная ситуация выглядит так: одна и та же ошибка то приходит строкой, то пустым массивом. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать интерфейс предсказуемым для формы, скрипта и внешнего клиента.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «контракт REST API» вернётся в следующем релизе под другим именем. Контракт включает не только URL, но и статусы, ошибки, идемпотентность и формат полей.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-06-mechanism-rest-api",
"title": "Под капотом: Контракт REST API",
"date": "2019-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"HTTP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему контракт включает не только URL, но и статусы, ошибки, идемпотентность и формат полей — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «контракт REST API». Контракт включает не только URL, но и статусы, ошибки, идемпотентность и формат полей. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: одна и та же ошибка то приходит строкой, то пустым массивом. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-06-practice-rest-api",
"title": "API. Как работать с темой: Контракт REST API",
"date": "2019-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"API",
"HTTP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как сделать интерфейс предсказуемым для формы, скрипта и внешнего клиента. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «контракт REST API». Цель заметки — сделать интерфейс предсказуемым для формы, скрипта и внешнего клиента. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Контракт включает не только URL, но и статусы, ошибки, идемпотентность и формат полей.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать интерфейс предсказуемым для формы, скрипта и внешнего клиента.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: одна и та же ошибка то приходит строкой, то пустым массивом.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «контракт REST API» не в сложном синтаксисе, а в неявных предположениях. Одна и та же ошибка то приходит строкой, то пустым массивом. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-05-field-http-caching",
"title": "Разбор: Кеширование HTTP-ответов в реальном проекте",
"date": "2019-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Производительность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как после деплоя клиент продолжает получать старый JavaScript-файл. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «кеширование HTTP-ответов». Типичная ситуация выглядит так: после деплоя клиент продолжает получать старый JavaScript-файл. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Ускорить повторные запросы, не показывая пользователю устаревшие данные.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «кеширование HTTP-ответов» вернётся в следующем релизе под другим именем. Кеш — это договор о свежести, ключе и инвалидировании, а не только заголовок max-age.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-05-mechanism-http-caching",
"title": "Под капотом: Кеширование HTTP-ответов",
"date": "2019-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Производительность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему кеш — это договор о свежести, ключе и инвалидировании, а не только заголовок max-age — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «кеширование HTTP-ответов». Кеш — это договор о свежести, ключе и инвалидировании, а не только заголовок max-age. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после деплоя клиент продолжает получать старый JavaScript-файл. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-05-practice-http-caching",
"title": "HTTP. Как работать с темой: Кеширование HTTP-ответов",
"date": "2019-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"Производительность"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как ускорить повторные запросы, не показывая пользователю устаревшие данные. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «кеширование HTTP-ответов». Цель заметки — ускорить повторные запросы, не показывая пользователю устаревшие данные. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Кеш — это договор о свежести, ключе и инвалидировании, а не только заголовок max-age.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: ускорить повторные запросы, не показывая пользователю устаревшие данные.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после деплоя клиент продолжает получать старый JavaScript-файл.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «кеширование HTTP-ответов» не в сложном синтаксисе, а в неявных предположениях. После деплоя клиент продолжает получать старый JavaScript-файл. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-04-field-forms-validation",
"title": "Разбор: Валидация форм в реальном проекте",
"date": "2019-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"UX"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Кейс о том, как поле подсвечено зелёным, а API всё равно отказывается принимать значение. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «валидация форм». Типичная ситуация выглядит так: поле подсвечено зелёным, а API всё равно отказывается принимать значение. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Дать человеку понятную ошибку и всё равно проверить данные на сервере.</p>\n<pre><code>1. Пройти сценарий с клавиатуры.\n2. Проверить состояние loading и error.\n3. Проверить медленную сеть.\n4. Записать метрику до и после изменения.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «валидация форм» вернётся в следующем релизе под другим именем. Клиентская валидация улучшает UX, но сервер остаётся источником истины.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-04-mechanism-forms-validation",
"title": "Под капотом: Валидация форм",
"date": "2019-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"UX"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Разбираем, почему клиентская валидация улучшает UX, но сервер остаётся источником истины — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «валидация форм». Клиентская валидация улучшает UX, но сервер остаётся источником истины. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>new PerformanceObserver((list) =&gt; {\n for (const entry of list.getEntries()) {\n console.log(entry.name, entry.duration);\n }\n}).observe({ type: &#039;navigation&#039;, buffered: true });</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: поле подсвечено зелёным, а API всё равно отказывается принимать значение. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-04-practice-forms-validation",
"title": "Frontend. Как работать с темой: Валидация форм",
"date": "2019-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"UX"
],
"cover": "/assets/illustrations/vibe-web.svg",
"excerpt": "Практическая заметка о том, как дать человеку понятную ошибку и всё равно проверить данные на сервере. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «валидация форм». Цель заметки — дать человеку понятную ошибку и всё равно проверить данные на сервере. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Клиентская валидация улучшает UX, но сервер остаётся источником истины.</p>\n<pre><code>&lt;label for=&quot;email&quot;&gt;Почта&lt;/label&gt;\n&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;email&quot; autocomplete=&quot;email&quot;&gt;\n&lt;button type=&quot;submit&quot;&gt;Сохранить&lt;/button&gt;</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: дать человеку понятную ошибку и всё равно проверить данные на сервере.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: поле подсвечено зелёным, а API всё равно отказывается принимать значение.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «валидация форм» не в сложном синтаксисе, а в неявных предположениях. Поле подсвечено зелёным, а API всё равно отказывается принимать значение. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.w3.org/TR/WCAG22/\" target=\"_blank\" rel=\"noopener\">W3C: WCAG 2.2</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-03-field-event-loop",
"title": "Разбор: Event loop и асинхронность в реальном проекте",
"date": "2019-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Асинхронность"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как кнопка реагирует только после обработки большого массива в then. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «event loop и асинхронность». Типичная ситуация выглядит так: кнопка реагирует только после обработки большого массива в then. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Понять, почему обработчик интерфейса «подвисает», хотя запросы асинхронные.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «event loop и асинхронность» вернётся в следующем релизе под другим именем. Обещание не делает CPU-вычисление параллельным: главный поток всё ещё один.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-03-mechanism-event-loop",
"title": "Под капотом: Event loop и асинхронность",
"date": "2019-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Асинхронность"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему обещание не делает CPU-вычисление параллельным: главный поток всё ещё один — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «event loop и асинхронность». Обещание не делает CPU-вычисление параллельным: главный поток всё ещё один. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: кнопка реагирует только после обработки большого массива в then. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-03-practice-event-loop",
"title": "JavaScript. Как работать с темой: Event loop и асинхронность",
"date": "2019-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Асинхронность"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как понять, почему обработчик интерфейса «подвисает», хотя запросы асинхронные. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «event loop и асинхронность». Цель заметки — понять, почему обработчик интерфейса «подвисает», хотя запросы асинхронные. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Обещание не делает CPU-вычисление параллельным: главный поток всё ещё один.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: понять, почему обработчик интерфейса «подвисает», хотя запросы асинхронные.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: кнопка реагирует только после обработки большого массива в then.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «event loop и асинхронность» не в сложном синтаксисе, а в неявных предположениях. Кнопка реагирует только после обработки большого массива в then. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-02-field-es-modules",
"title": "Разбор: ES-модули в браузере в реальном проекте",
"date": "2019-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как страница работает на сервере, но падает при открытии через file://. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «ES-модули в браузере». Типичная ситуация выглядит так: страница работает на сервере, но падает при открытии через file://. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Разделить код на файлы без хаоса в глобальной области видимости.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «ES-модули в браузере» вернётся в следующем релизе под другим именем. Модуль загружается как отдельный ресурс и подчиняется правилам CORS и графа импортов.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-02-mechanism-es-modules",
"title": "Под капотом: ES-модули в браузере",
"date": "2019-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему модуль загружается как отдельный ресурс и подчиняется правилам CORS и графа импортов — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «ES-модули в браузере». Модуль загружается как отдельный ресурс и подчиняется правилам CORS и графа импортов. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: страница работает на сервере, но падает при открытии через file://. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-02-practice-es-modules",
"title": "JavaScript. Как работать с темой: ES-модули в браузере",
"date": "2019-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как разделить код на файлы без хаоса в глобальной области видимости. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>В рабочем коде важен не только результат на локальной машине, но и возможность повторить его в другой среде. Рассмотрим «ES-модули в браузере». Цель заметки — разделить код на файлы без хаоса в глобальной области видимости. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Модуль загружается как отдельный ресурс и подчиняется правилам CORS и графа импортов.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: разделить код на файлы без хаоса в глобальной области видимости.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: страница работает на сервере, но падает при открытии через file://.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «ES-модули в браузере» не в сложном синтаксисе, а в неявных предположениях. Страница работает на сервере, но падает при открытии через file://. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2019-01-field-jquery-webpack",
"title": "Разбор: JQuery в Webpack в реальном проекте",
"date": "2019-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Webpack"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как после миграции сборки старый inputmask перестаёт видеть jQuery. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>Разбор подобных случаев удобнее строить не вокруг виноватого компонента, а вокруг цепочки наблюдаемых фактов. Тема: «jQuery в Webpack». Типичная ситуация выглядит так: после миграции сборки старый inputmask перестаёт видеть jQuery. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать зависимость явной и не потерять плагины, ожидающие window.jQuery.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «jQuery в Webpack» вернётся в следующем релизе под другим именем. Модульная система и глобальные переменные имеют разные правила видимости.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2019-01-mechanism-jquery-webpack",
"title": "Под капотом: JQuery в Webpack",
"date": "2019-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Webpack"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему модульная система и глобальные переменные имеют разные правила видимости — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Перед настройкой полезно договориться о модели происходящего. Разберём «jQuery в Webpack». Модульная система и глобальные переменные имеют разные правила видимости. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после миграции сборки старый inputmask перестаёт видеть jQuery. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Так задача перестаёт быть одноразовым исправлением и становится проверяемым процессом.</p>",
"readingMinutes": 3
},
{
"slug": "использование-jquery-в-webpack",
"title": "JavaScript. Использование jQuery в Webpack",
"date": "2019-01-12T21:54:18+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Webpack является одним из самых мощных и гибких инструментов для сборки фронтенд-проектов. Иногда необходимо включить в проект Webpack одну из самых популярных JavaScript библиотек jQuery. Для начала необходимо установить jQuery из репозитория npm командой: np",
"contentHtml": "<p>Webpack является одним из самых мощных и гибких инструментов для сборки фронтенд-проектов. Иногда необходимо включить в проект Webpack одну из самых популярных JavaScript библиотек jQuery.</p>\n<p>Для начала необходимо установить jQuery из репозитория npm командой:</p>\n<pre><code>npm i jquery</code></pre>\n<p>либо (если используем менеджер пакетов Yarn):</p>\n<pre><code>yarn add jquery</code></pre>\n<p>Чтобы jQuery стал доступным в глобальной области видимости в «бандле» (собираемом пакете, от bundle) можно использовать ProvidePlugin (см. официальную документацию <a href=\"https://webpack.js.org/plugins/provide-plugin/\" target=\"_blank\" rel=\"noopener\">https://webpack.js.org/plugins/provide-plugin/</a>):</p>\n\n<div><div><div><div><div><div><div><pre><code>module.exports = {\n plugins: [\n new webpack.ProvidePlugin({\n $: 'jquery',\n jQuery: 'jquery'\n }),\n ]\n};</code></pre></div></div></div></div></div></div></div>\n\n<p>Так библиотека jQuery будет доступна через глобальные переменные $, jQuery, window.jQuery.<br />\nИногда в проектах Webpack собираются несколько бандлов. Например, есть финальный скрипт, который подключается на все страницы и в нём необходима библиотека jQuery. А в других скриптах она уже не нужна, так как она будет уже подключена глобально. В таком случае только в модуле, который будет глобальным, необходимо подключить jQuery (для импорта модулей будем использовать стандарт ES6/ES2015):</p>\n\n<div><div><div><div><div><div><div><pre><code>import $ from 'jquery';\n \nglobal.jQuery = $;\nglobal.$ = $;</code></pre></div></div></div></div></div></div></div>\n\n<p>Также jQuery можно подключить в проект Webpack через CDN с помощью плагина html-webpack-externals-plugin (<a href=\"https://www.npmjs.com/package/html-webpack-externals-plugin\" target=\"_blank\" rel=\"noopener\">https://www.npmjs.com/package/html-webpack-externals-plugin</a>):</p>\n\n<div><div><div><div><div><div><div><pre><code>module.exports = {\n plugins: [\n new HtmlWebpackExternalsPlugin({ // optional plugin: inject cdn\n externals: [\n {\n module: 'jquery',\n entry: 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'\n }\n ],\n }),\n ]\n};</code></pre></div></div></div></div></div></div></div>\n\n<p>После чего можно использовать jQuery библиотеки, подключая их следующим образом:</p>\n\n<div><div><div><div><div><div><div><pre><code>require(&quot;inputmask/dist/inputmask/jquery.inputmask.js&quot;);</code></pre></div></div></div></div></div></div></div>\n<h2>Итоговая схема подключения</h2>\n<p>Для старого проекта jQuery в Webpack лучше подключать явно: установить пакет, импортировать его в точке входа и отдельно решить вопрос с глобальными переменными. ProvidePlugin удобен, когда в модулях встречаются свободные идентификаторы <em>$</em> или <em>jQuery</em>. Но если старый плагин лезет именно в <em>window.jQuery</em>, одного ProvidePlugin может быть мало — нужно положить jQuery в <em>window</em> самостоятельно.</p>\n<pre><code>import $ from 'jquery';\n\nwindow.$ = $;\nwindow.jQuery = $;</code></pre>\n<p>После этого можно добавить ProvidePlugin, чтобы не писать импорт в каждом файле:</p>\n<pre><code>const webpack = require('webpack');\n\nmodule.exports = {\n plugins: [\n new webpack.ProvidePlugin({\n $: 'jquery',\n jQuery: 'jquery',\n }),\n ],\n};</code></pre>\n<p>Порядок подключения имеет значение. Сначала в entry-файле импортируем jQuery и кладём его в <em>window</em>, потом импортируем старые плагины, которым нужен глобальный объект. Если плагин подключается через <em>require</em>, делаем это после установки <em>window.jQuery</em>.</p>\n<pre><code>import './jquery-global';\nimport 'inputmask/dist/jquery.inputmask';\nimport './app';</code></pre>\n<p>Проверка простая: в браузерной консоли должны существовать <em>window.$</em> и <em>window.jQuery</em>, а в собранном bundle не должно быть двух разных копий jQuery. Если проект новый, лучше не тащить jQuery без необходимости. Если проект старый и плагины уже написаны под jQuery, такая схема делает зависимость явной и предсказуемой.</p>",
"readingMinutes": 2
},
{
"slug": "editorial-2018-12-field-legacy-refactoring",
"title": "Разработка. Рефакторинг Bitrix-проекта: разбор типичной ошибки",
"date": "2018-12-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Рефакторинг"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «рефакторинг Bitrix-проекта». Типичная ситуация выглядит так: задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Изменять старый проект маленькими безопасными шагами.</p>\n<pre><code>Факт -&gt; источник -&gt; гипотеза -&gt; проверка -&gt; решение -&gt; последствия\n\nЕсли пропустить один шаг, текст легко превращается в уверенное мнение.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «рефакторинг Bitrix-проекта» вернётся в следующем релизе под другим именем. Рефакторинг — это управление риском через границы, проверки и короткие циклы обратной связи.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-12-mechanism-legacy-refactoring",
"title": "Разработка. Почему важна тема: Рефакторинг Bitrix-проекта",
"date": "2018-12-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Рефакторинг"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему рефакторинг — это управление риском через границы, проверки и короткие циклы обратной связи — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «рефакторинг Bitrix-проекта». Рефакторинг — это управление риском через границы, проверки и короткие циклы обратной связи. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>Decision: ...\nContext: ...\nAlternatives: ...\nConsequences: ...\nReview date: ...</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-12-practice-legacy-refactoring",
"title": "Разработка. Рефакторинг Bitrix-проекта: рабочая схема",
"date": "2018-12-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"Рефакторинг"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как изменять старый проект маленькими безопасными шагами. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «рефакторинг Bitrix-проекта». Цель заметки — изменять старый проект маленькими безопасными шагами. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Рефакторинг — это управление риском через границы, проверки и короткие циклы обратной связи.</p>\n<pre><code># Контекст\n# Наблюдаемый симптом\n# Гипотеза и проверка\n# Решение и ограничения\n# Как воспроизвести результат</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: изменять старый проект маленькими безопасными шагами.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «рефакторинг Bitrix-проекта» не в сложном синтаксисе, а в неявных предположениях. Задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-11-field-php-integration-tests",
"title": "PHP. Интеграционные проверки: разбор типичной ошибки",
"date": "2018-11-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Кейс о том, как модульные тесты зелёные, а реальная форма ломается после отправки файла. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «интеграционные проверки». Типичная ситуация выглядит так: модульные тесты зелёные, а реальная форма ломается после отправки файла. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Проверить взаимодействие приложения, файловой системы и внешнего HTTP-клиента.</p>\n<pre><code>npx playwright test --trace on-first-retry\nnpx playwright show-report\n# В trace смотрим действия, DOM-снимки и сеть.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «интеграционные проверки» вернётся в следующем релизе под другим именем. Ценность интеграционного теста в том, что он проверяет контракт между частями системы.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-11-mechanism-php-integration-tests",
"title": "PHP. Почему важна тема: Интеграционные проверки",
"date": "2018-11-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Разбираем, почему ценность интеграционного теста в том, что он проверяет контракт между частями системы — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «интеграционные проверки». Ценность интеграционного теста в том, что он проверяет контракт между частями системы. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>use: {\n trace: &#039;on-first-retry&#039;,\n screenshot: &#039;only-on-failure&#039;,\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: модульные тесты зелёные, а реальная форма ломается после отправки файла. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-11-practice-php-integration-tests",
"title": "PHP. Интеграционные проверки: рабочая схема",
"date": "2018-11-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Тестирование"
],
"cover": "/assets/illustrations/tilix-screenshot.svg",
"excerpt": "Практическая заметка о том, как проверить взаимодействие приложения, файловой системы и внешнего HTTP-клиента. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «интеграционные проверки». Цель заметки — проверить взаимодействие приложения, файловой системы и внешнего HTTP-клиента. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Ценность интеграционного теста в том, что он проверяет контракт между частями системы.</p>\n<pre><code>import { expect, test } from &#039;@playwright/test&#039;;\n\ntest(&#039;user saves profile&#039;, async ({ page }) =&gt; {\n await page.goto(&#039;/profile&#039;);\n await page.getByLabel(&#039;Почта&#039;).fill(&#039;user@example.test&#039;);\n await page.getByRole(&#039;button&#039;, { name: &#039;Сохранить&#039; }).click();\n await expect(page.getByRole(&#039;status&#039;)).toHaveText(&#039;Сохранено&#039;);\n});</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: проверить взаимодействие приложения, файловой системы и внешнего HTTP-клиента.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: модульные тесты зелёные, а реальная форма ломается после отправки файла.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «интеграционные проверки» не в сложном синтаксисе, а в неявных предположениях. Модульные тесты зелёные, а реальная форма ломается после отправки файла. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-10-field-image-workflow",
"title": "Bitrix API. Работа с изображениями в форме: разбор типичной ошибки",
"date": "2018-10-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Кейс о том, как картинка успешно выбрана, но после сохранения страницы у сущности остаётся пустое поле. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «работа с изображениями в форме». Типичная ситуация выглядит так: картинка успешно выбрана, но после сохранения страницы у сущности остаётся пустое поле. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Связать UI-редактор, проверку файла и сохранение привязки к сущности.</p>\n<pre><code>$required = [&#039;IBLOCK_ID&#039;, &#039;NAME&#039;];\nforeach ($required as $field) {\n if (empty($fields[$field])) {\n throw new InvalidArgumentException($field . &#039; is required&#039;);\n }\n}</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «работа с изображениями в форме» вернётся в следующем релизе под другим именем. Редактор помогает пользователю, но права, тип файла и жизненный цикл данных остаются на сервере.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "bitrix-api-add-foto-editor",
"title": "Bitrix API. Вставка на страницу встроенного редактора картинок",
"date": "2018-10-19T01:50:29+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "В CMS Bitrix имеется довольно неплохой по функциональным возможностям и дизайну встроенный графический редактор картинок. Поэтому может возникнуть желании использовать его на сайте. Но как это сделать? На самом деле это не так сложно. Класс компонента графичес",
"contentHtml": "<p>В <strong>CMS Bitrix</strong> имеется довольно неплохой по функциональным возможностям и дизайну встроенный графический редактор картинок.<br />\n<figure><img src=\"/assets/illustrations/bitrix-photo-editor-ui.svg\" alt=\"Редактор картинок Bitrix\" /><figcaption>Редактор картинок Bitrix</figcaption></figure></p>\n<p>Поэтому может возникнуть желании использовать его на сайте. <strong>Но как это сделать?</strong> На самом деле это не так сложно. Класс компонента графического редактора является <em>\\Bitrix\\Main\\UI\\FileInput</em> и располагается в файле <em>/bitrix/modules/main/lib/ui/fileinput.php</em></p>\n<p>Для вывода кода компонента необходимо создать экземляр класс с помощью статического метода <em>createInstance</em>. Затем получить код на вывод через метод <em>show</em>. Полный код вызова компонента будет иметь следующий вид:</p>\n\n<div><div><div><div><div><div><div><pre><code>&lt;?=\\Bitrix\\Main\\UI\\FileInput::createInstance([\n\t&quot;name&quot; =&gt; &quot;picture&quot;,\n\t&quot;description&quot; =&gt; true,\n\t&quot;upload&quot; =&gt; true,\n\t&quot;allowUpload&quot; =&gt; &quot;I&quot;,\n\t&quot;medialib&quot; =&gt; true,\n\t&quot;fileDialog&quot; =&gt; true,\n\t&quot;cloud&quot; =&gt; true,\n\t&quot;delete&quot; =&gt; true,\n\t&quot;maxCount&quot; =&gt; 1\n\t])-&gt;show($id);\n?&gt;</code></pre></div></div></div></div></div></div></div>\n\n<p>Переменная <em>$id</em> содержит идентификатор картинки в системе.</p>\n<p>Параметры:</p>\n<ul>\n<li><em>name</em> – задаёт параметр формы, в котором будут переданы данные файла;</li>\n<li><em>description</em> – можно ли задавать описание к файлу;</li>\n<li><em>upload</em> – можно ли загружать файл;</li>\n<li><em>allowUpload</em> – какой тип файлов разрешён к загрузке (F – файлы, I – картинки, A – все типы, по умолчанию);</li>\n<li><em>allowUploadExt</em> – какие типы расширений разрешены к загрузке (*.zip,*.rar,*.doc и пр.);</li>\n<li><em>medialib</em> – можно ли использовать медиа-библиотеку;</li>\n<li><em>fileDialog</em> – разрешён ли файловый диалог для загрузки;</li>\n<li><em>cloud</em> – использовать ли модуль облачного хранилища для загрузки файлов;</li>\n<li><em>delete</em> – можно ли удалять картинку;</li>\n<li><em>edit</em> – можно ли редактировать картинку:</li>\n<li><em>maxCount</em> – максимальное количество файлов;</li>\n<li><em>maxSize</em> – максимальный размер файла (байт).</li>\n</ul>\n<p>Также необходимо учитывать, что некоторые параметры будут работать только при определённых условиях, как, например, <em>medialib</em> и <em>cloud</em>.</p>\n<h2>Завершение интеграции</h2>\n<p>После вывода компонента важно не забыть обработать результат формы: сохранить ID файла, проверить тип загруженного файла и права пользователя. Сам редактор решает задачу интерфейса, но безопасность и привязка к сущности остаются на стороне вашего кода.</p>\n<p>В параметрах компонента отдельно проверьте <em>allowUpload</em>, <em>medialib</em>, <em>fileDialog</em>, <em>cloud</em>, <em>delete</em>, <em>edit</em> и <em>maxCount</em>. Не все параметры будут иметь эффект в любой установке Bitrix: часть зависит от подключённых модулей, прав пользователя и контекста страницы.</p>\n<pre><code>$fileId = (int)$_POST['picture'];\n\nif ($fileId > 0) {\n $file = CFile::GetFileArray($fileId);\n if ($file && str_starts_with($file['CONTENT_TYPE'], 'image/')) {\n // сохраняем ID картинки в своей сущности\n }\n}</code></pre>\n<p>Если редактор не открывается, проверьте подключение модулей <em>main</em> и <em>fileman</em>, права на загрузку, административные JS-расширения и наличие сессии пользователя. В публичной части сайта проблема часто оказывается не в <em>FileInput</em>, а в том, что на странице не подключены нужные скрипты Bitrix.</p>\n<p>Рабочая схема такая: выводим FileInput, разрешаем только картинки, ограничиваем количество, после отправки формы валидируем ID файла и сохраняем его в свою сущность. Тогда встроенный редактор становится нормальной частью формы, а не просто красивой кнопкой загрузки.</p>",
"readingMinutes": 2
},
{
"slug": "editorial-2018-10-mechanism-image-workflow",
"title": "Bitrix API. Почему важна тема: Работа с изображениями в форме",
"date": "2018-10-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Разбираем, почему редактор помогает пользователю, но права, тип файла и жизненный цикл данных остаются на сервере — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «работа с изображениями в форме». Редактор помогает пользователю, но права, тип файла и жизненный цикл данных остаются на сервере. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>$id = $element-&gt;Add($fields);\nif ($id === false) {\n error_log(&#039;Bitrix error: &#039; . $element-&gt;LAST_ERROR);\n return null;\n}\nreturn (int) $id;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: картинка успешно выбрана, но после сохранения страницы у сущности остаётся пустое поле. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-09-field-windows-dev-env",
"title": "Windows. Окружение разработчика: разбор типичной ошибки",
"date": "2018-09-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Windows",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Кейс о том, как у коллеги тот же проект не запускается, хотя код не менялся. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «окружение разработчика». Типичная ситуация выглядит так: у коллеги тот же проект не запускается, хотя код не менялся. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Сделать локальную среду воспроизводимой, а не зависящей от случайных настроек компьютера.</p>\n<pre><code>1. Проверить версию runtime.\n2. Проверить конфигурацию без секретов.\n3. Проверить сетевую доступность зависимости.\n4. Зафиксировать результат в runbook.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «окружение разработчика» вернётся в следующем релизе под другим именем. Версия интерпретатора, расширения, PATH и конфигурация должны быть описаны рядом с проектом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-09-mechanism-windows-dev-env",
"title": "Windows. Почему важна тема: Окружение разработчика",
"date": "2018-09-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Windows",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Разбираем, почему версия интерпретатора, расширения, PATH и конфигурация должны быть описаны рядом с проектом — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «окружение разработчика». Версия интерпретатора, расширения, PATH и конфигурация должны быть описаны рядом с проектом. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>const required = [&#039;DATABASE_URL&#039;, &#039;APP_ENV&#039;];\nfor (const name of required) {\n if (!process.env[name]) throw new Error(name + &#039; is required&#039;);\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: у коллеги тот же проект не запускается, хотя код не менялся. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-09-practice-windows-dev-env",
"title": "Windows. Окружение разработчика: рабочая схема",
"date": "2018-09-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Windows",
"Инструменты"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Практическая заметка о том, как сделать локальную среду воспроизводимой, а не зависящей от случайных настроек компьютера. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «окружение разработчика». Цель заметки — сделать локальную среду воспроизводимой, а не зависящей от случайных настроек компьютера. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Версия интерпретатора, расширения, PATH и конфигурация должны быть описаны рядом с проектом.</p>\n<pre><code>APP_ENV=development\nAPP_DEBUG=0\nDATABASE_URL=...\n\n# Секреты не добавляем в git и не выводим в журнал.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: сделать локальную среду воспроизводимой, а не зависящей от случайных настроек компьютера.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: у коллеги тот же проект не запускается, хотя код не менялся.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «окружение разработчика» не в сложном синтаксисе, а в неявных предположениях. У коллеги тот же проект не запускается, хотя код не менялся. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://docs.docker.com/get-started/\" target=\"_blank\" rel=\"noopener\">Docker documentation</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-08-field-tls-ca",
"title": "PHP. TLS и CA bundle: разбор типичной ошибки",
"date": "2018-08-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как сайт открывается в браузере, но cURL возвращает certificate error. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «TLS и CA bundle». Типичная ситуация выглядит так: сайт открывается в браузере, но cURL возвращает certificate error. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Починить проверку сертификата без отключения проверки SSL.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «TLS и CA bundle» вернётся в следующем релизе под другим именем. Клиент доверяет цепочке сертификатов, а не просто доменному имени в адресной строке.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-08-mechanism-tls-ca",
"title": "PHP. Почему важна тема: TLS и CA bundle",
"date": "2018-08-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему клиент доверяет цепочке сертификатов, а не просто доменному имени в адресной строке — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «TLS и CA bundle». Клиент доверяет цепочке сертификатов, а не просто доменному имени в адресной строке. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: сайт открывается в браузере, но cURL возвращает certificate error. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-08-practice-tls-ca",
"title": "PHP. TLS и CA bundle: рабочая схема",
"date": "2018-08-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"SSL"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как починить проверку сертификата без отключения проверки SSL. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «TLS и CA bundle». Цель заметки — починить проверку сертификата без отключения проверки SSL. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Клиент доверяет цепочке сертификатов, а не просто доменному имени в адресной строке.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: починить проверку сертификата без отключения проверки SSL.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: сайт открывается в браузере, но cURL возвращает certificate error.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «TLS и CA bundle» не в сложном синтаксисе, а в неявных предположениях. Сайт открывается в браузере, но cURL возвращает certificate error. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: HTML5 Security Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-07-field-http-timeouts",
"title": "HTTP. Таймауты в веб-интеграции: разбор типичной ошибки",
"date": "2018-07-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"PHP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Кейс о том, как браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «таймауты в веб-интеграции». Типичная ситуация выглядит так: браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Отличить долгий ответ от зависшего процесса и выбрать корректный срок ожидания.</p>\n<pre><code>client timeout &lt; proxy timeout &lt; worker timeout\n\nДля каждого уровня:\n1. фиксируем значение;\n2. пишем владельца;\n3. проверяем поведение при обрыве.</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «таймауты в веб-интеграции» вернётся в следующем релизе под другим именем. Таймауты клиента, прокси, приложения и базы — это разные уровни одного запроса.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-07-mechanism-http-timeouts",
"title": "HTTP. Почему важна тема: Таймауты в веб-интеграции",
"date": "2018-07-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"PHP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Разбираем, почему таймауты клиента, прокси, приложения и базы — это разные уровни одного запроса — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «таймауты в веб-интеграции». Таймауты клиента, прокси, приложения и базы — это разные уровни одного запроса. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>fetch(url, {\n cache: &#039;no-cache&#039;,\n headers: { &#039;X-Request-ID&#039;: requestId },\n}).then((response) =&gt; {\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-07-practice-http-timeouts",
"title": "HTTP. Таймауты в веб-интеграции: рабочая схема",
"date": "2018-07-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"HTTP",
"PHP"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "Практическая заметка о том, как отличить долгий ответ от зависшего процесса и выбрать корректный срок ожидания. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «таймауты в веб-интеграции». Цель заметки — отличить долгий ответ от зависшего процесса и выбрать корректный срок ожидания. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Таймауты клиента, прокси, приложения и базы — это разные уровни одного запроса.</p>\n<pre><code>curl -I https://example.test/api/items\n\n# Смотрим не только статус:\n# Cache-Control, ETag, Via, Server,\n# время ответа и цепочку прокси.</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: отличить долгий ответ от зависшего процесса и выбрать корректный срок ожидания.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «таймауты в веб-интеграции» не в сложном синтаксисе, а в неявных предположениях. Браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\" target=\"_blank\" rel=\"noopener\">MDN: Fetch API</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching\" target=\"_blank\" rel=\"noopener\">MDN: HTTP caching</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-06-field-webpack-entry",
"title": "Webpack. Точки входа и зависимости: разбор типичной ошибки",
"date": "2018-06-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Webpack"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как после новой сборки bundle неожиданно стал вдвое больше. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «точки входа и зависимости». Типичная ситуация выглядит так: после новой сборки bundle неожиданно стал вдвое больше. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Собрать проект так, чтобы общие библиотеки и код страницы не дублировались.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «точки входа и зависимости» вернётся в следующем релизе под другим именем. Entry-файл задаёт границу загрузки, а граф импортов показывает реальную зависимость модулей.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-06-mechanism-webpack-entry",
"title": "Webpack. Почему важна тема: Точки входа и зависимости",
"date": "2018-06-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Webpack"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему entry-файл задаёт границу загрузки, а граф импортов показывает реальную зависимость модулей — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «точки входа и зависимости». Entry-файл задаёт границу загрузки, а граф импортов показывает реальную зависимость модулей. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после новой сборки bundle неожиданно стал вдвое больше. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-06-practice-webpack-entry",
"title": "Webpack. Точки входа и зависимости: рабочая схема",
"date": "2018-06-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"Webpack"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как собрать проект так, чтобы общие библиотеки и код страницы не дублировались. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «точки входа и зависимости». Цель заметки — собрать проект так, чтобы общие библиотеки и код страницы не дублировались. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Entry-файл задаёт границу загрузки, а граф импортов показывает реальную зависимость модулей.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: собрать проект так, чтобы общие библиотеки и код страницы не дублировались.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после новой сборки bundle неожиданно стал вдвое больше.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «точки входа и зависимости» не в сложном синтаксисе, а в неявных предположениях. После новой сборки bundle неожиданно стал вдвое больше. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-05-field-legacy-jquery",
"title": "JavaScript. Старый код на jQuery: разбор типичной ошибки",
"date": "2018-05-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"jQuery"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Кейс о том, как после точечной правки форма начинает отправляться дважды. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «старый код на jQuery». Типичная ситуация выглядит так: после точечной правки форма начинает отправляться дважды. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Подключить новый код к старому интерфейсу и не получить несколько обработчиков на одну кнопку.</p>\n<pre><code>button.addEventListener(&#039;click&#039;, async () =&gt; {\n button.disabled = true;\n try {\n await save();\n } finally {\n button.disabled = false;\n }\n});</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «старый код на jQuery» вернётся в следующем релизе под другим именем. Граница между глобальным DOM-кодом и модулем должна быть небольшой и наблюдаемой.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-05-mechanism-legacy-jquery",
"title": "JavaScript. Почему важна тема: Старый код на jQuery",
"date": "2018-05-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"jQuery"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Разбираем, почему граница между глобальным DOM-кодом и модулем должна быть небольшой и наблюдаемой — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «старый код на jQuery». Граница между глобальным DOM-кодом и модулем должна быть небольшой и наблюдаемой. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>async function loadData(url) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(String(response.status));\n return response.json();\n}</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: после точечной правки форма начинает отправляться дважды. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-05-practice-legacy-jquery",
"title": "JavaScript. Старый код на jQuery: рабочая схема",
"date": "2018-05-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"JavaScript",
"jQuery"
],
"cover": "/assets/illustrations/jquery-webpack.svg",
"excerpt": "Практическая заметка о том, как подключить новый код к старому интерфейсу и не получить несколько обработчиков на одну кнопку. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «старый код на jQuery». Цель заметки — подключить новый код к старому интерфейсу и не получить несколько обработчиков на одну кнопку. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Граница между глобальным DOM-кодом и модулем должна быть небольшой и наблюдаемой.</p>\n<pre><code>import { mountForm } from &#039;./form.js&#039;;\n\nconst root = document.querySelector(&#039;[data-form]&#039;);\nif (root) {\n mountForm(root);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: подключить новый код к старому интерфейсу и не получить несколько обработчиков на одну кнопку.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: после точечной правки форма начинает отправляться дважды.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «старый код на jQuery» не в сложном синтаксисе, а в неявных предположениях. После точечной правки форма начинает отправляться дважды. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\" target=\"_blank\" rel=\"noopener\">MDN: JavaScript modules</a></li><li><a href=\"https://webpack.js.org/guides/\" target=\"_blank\" rel=\"noopener\">Webpack Guides</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-04-field-bitrix-slugs",
"title": "Bitrix API. Символьные коды и URL: разбор типичной ошибки",
"date": "2018-04-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"SEO"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Кейс о том, как два похожих товара пытаются занять один URL и ломают ссылку из каталога. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «символьные коды и URL». Типичная ситуация выглядит так: два похожих товара пытаются занять один URL и ломают ссылку из каталога. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Получить читаемый и уникальный код элемента из пользовательского названия.</p>\n<pre><code>$required = [&#039;IBLOCK_ID&#039;, &#039;NAME&#039;];\nforeach ($required as $field) {\n if (empty($fields[$field])) {\n throw new InvalidArgumentException($field . &#039; is required&#039;);\n }\n}</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «символьные коды и URL» вернётся в следующем релизе под другим именем. Транслитерация даёт основу, а уникальность и нормализация делают адрес устойчивым.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-04-mechanism-bitrix-slugs",
"title": "Bitrix API. Почему важна тема: Символьные коды и URL",
"date": "2018-04-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"SEO"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Разбираем, почему транслитерация даёт основу, а уникальность и нормализация делают адрес устойчивым — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «символьные коды и URL». Транслитерация даёт основу, а уникальность и нормализация делают адрес устойчивым. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>$id = $element-&gt;Add($fields);\nif ($id === false) {\n error_log(&#039;Bitrix error: &#039; . $element-&gt;LAST_ERROR);\n return null;\n}\nreturn (int) $id;</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: два похожих товара пытаются занять один URL и ломают ссылку из каталога. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-04-practice-bitrix-slugs",
"title": "Bitrix API. Символьные коды и URL: рабочая схема",
"date": "2018-04-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"SEO"
],
"cover": "/assets/illustrations/bitrix-photo-editor.svg",
"excerpt": "Практическая заметка о том, как получить читаемый и уникальный код элемента из пользовательского названия. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «символьные коды и URL». Цель заметки — получить читаемый и уникальный код элемента из пользовательского названия. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Транслитерация даёт основу, а уникальность и нормализация делают адрес устойчивым.</p>\n<pre><code>CModule::IncludeModule(&#039;iblock&#039;);\n$element = new CIBlockElement();\n$id = $element-&gt;Add([\n &#039;IBLOCK_ID&#039; =&gt; 12,\n &#039;NAME&#039; =&gt; $name,\n &#039;ACTIVE&#039; =&gt; &#039;Y&#039;,\n &#039;PROPERTY_VALUES&#039; =&gt; $properties,\n]);\nif (!$id) {\n throw new RuntimeException($element-&gt;LAST_ERROR);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: получить читаемый и уникальный код элемента из пользовательского названия.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: два похожих товара пытаются занять один URL и ломают ссылку из каталога.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «символьные коды и URL» не в сложном синтаксисе, а в неявных предположениях. Два похожих товара пытаются занять один URL и ломают ссылку из каталога. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">Bitrix: CIBlockElement::Add</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-03-field-safe-uploads",
"title": "PHP. Безопасная загрузка файлов: разбор типичной ошибки",
"date": "2018-03-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Безопасность"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Кейс о том, как картинка выглядит безобидно, но сервер не должен доверять расширению файла. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «безопасная загрузка файлов». Типичная ситуация выглядит так: картинка выглядит безобидно, но сервер не должен доверять расширению файла. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Принять изображение от пользователя, не превратив загрузку в вход для чужого кода.</p>\n<pre><code>$context = [\n &#039;request_id&#039; =&gt; $requestId,\n &#039;operation&#039; =&gt; &#039;external_api_call&#039;,\n &#039;status&#039; =&gt; $response-&gt;getStatusCode(),\n];\nerror_log(json_encode($context));</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «безопасная загрузка файлов» вернётся в следующем релизе под другим именем. Имя, MIME-тип, размер, права и место хранения проверяются раздельно.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-03-mechanism-safe-uploads",
"title": "PHP. Почему важна тема: Безопасная загрузка файлов",
"date": "2018-03-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Безопасность"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Разбираем, почему имя, MIME-тип, размер, права и место хранения проверяются раздельно — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «безопасная загрузка файлов». Имя, MIME-тип, размер, права и место хранения проверяются раздельно. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>set_error_handler(function ($severity, $message, $file, $line) {\n error_log(json_encode([&#039;severity&#039; =&gt; $severity, &#039;message&#039; =&gt; $message, &#039;file&#039; =&gt; $file, &#039;line&#039; =&gt; $line]));\n return false;\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: картинка выглядит безобидно, но сервер не должен доверять расширению файла. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-03-practice-safe-uploads",
"title": "PHP. Безопасная загрузка файлов: рабочая схема",
"date": "2018-03-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Безопасность"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Практическая заметка о том, как принять изображение от пользователя, не превратив загрузку в вход для чужого кода. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «безопасная загрузка файлов». Цель заметки — принять изображение от пользователя, не превратив загрузку в вход для чужого кода. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Имя, MIME-тип, размер, права и место хранения проверяются раздельно.</p>\n<pre><code>if (($_FILES[&#039;image&#039;][&#039;error&#039;] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {\n throw new RuntimeException(&#039;Upload failed&#039;);\n}\n$tmp = $_FILES[&#039;image&#039;][&#039;tmp_name&#039;];\n$target = $storage . &#039;/&#039; . bin2hex(random_bytes(12)) . &#039;.jpg&#039;;\nif (!move_uploaded_file($tmp, $target)) {\n throw new RuntimeException(&#039;Cannot move upload&#039;);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: принять изображение от пользователя, не превратив загрузку в вход для чужого кода.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: картинка выглядит безобидно, но сервер не должен доверять расширению файла.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «безопасная загрузка файлов» не в сложном синтаксисе, а в неявных предположениях. Картинка выглядит безобидно, но сервер не должен доверять расширению файла. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/move-uploaded-file\" target=\"_blank\" rel=\"noopener\">PHP: move_uploaded_file</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: File Upload Cheat Sheet</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-02-field-php-diagnostics",
"title": "PHP. Диагностика ошибок интеграции: разбор типичной ошибки",
"date": "2018-02-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Диагностика"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Кейс о том, как интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается. В конце — последовательность проверки и критерий готовности.",
"contentHtml": "<p>На реальном проекте эта история обычно начинается спокойно, а затем всплывает один неприятный крайний случай. Разберём «диагностика ошибок интеграции». Типичная ситуация выглядит так: интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается. В такой момент легко срочно поправить видимый симптом, но полезнее пройти короткое расследование и оставить после него защиту для следующего раза.</p>\n<h2>Последовательность разбора</h2>\n<ol><li>Собрать симптомы до изменения конфигурации или кода.</li><li>Проверить гипотезу самым маленьким безопасным экспериментом.</li><li>Исправить причину, а не только видимый эффект.</li><li>Добавить защиту или наблюдение, чтобы случай не вернулся незаметно.</li></ol>\n<h2>Минимальное доказательство</h2>\n<p>Нам не нужна идеальная модель всей системы. Достаточно такого эксперимента, который отделяет одну гипотезу от другой: повторить запрос, сравнить входы, посмотреть контекст операции или воспроизвести проблему на отдельной записи. Увидеть исходную ошибку, а не только пустой ответ или false.</p>\n<pre><code>$context = [\n &#039;request_id&#039; =&gt; $requestId,\n &#039;operation&#039; =&gt; &#039;external_api_call&#039;,\n &#039;status&#039; =&gt; $response-&gt;getStatusCode(),\n];\nerror_log(json_encode($context));</code></pre>\n<h2>Что меняется после исправления</h2>\n<p>Исправление считается законченным, когда новый путь проверяется автоматически или наблюдается по явному сигналу. Иначе «диагностика ошибок интеграции» вернётся в следующем релизе под другим именем. Ошибка должна иметь контекст: входные данные, идентификатор операции, место возникновения и безопасный журнал.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/manual/en/function.set-error-handler.php\" target=\"_blank\" rel=\"noopener\">PHP: обработка ошибок</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 4
},
{
"slug": "editorial-2018-02-mechanism-php-diagnostics",
"title": "PHP. Почему важна тема: Диагностика ошибок интеграции",
"date": "2018-02-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Диагностика"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Разбираем, почему ошибка должна иметь контекст: входные данные, идентификатор операции, место возникновения и безопасный журнал — и какие ошибки возникают, если этот механизм не учитывать.",
"contentHtml": "<p>Сначала хотелось просто применить готовый рецепт, но без понимания механизма он быстро превращается в набор случайных действий. Поэтому давайте разберём «диагностика ошибок интеграции». Ошибка должна иметь контекст: входные данные, идентификатор операции, место возникновения и безопасный журнал. Когда этот слой остаётся невидимым, команда начинает лечить следствие: добавляет таймаут, глобальную переменную, второй кеш или ещё одну повторную попытку.</p>\n<h2>Модель происходящего</h2>\n<p>Для начала полезно назвать владельца состояния, момент изменения и границу, за которую действие не должно протекать незаметно. Тогда можно отличить нормальную задержку от отказа, локальную оптимизацию от нарушения контракта и временный обход от постоянного решения.</p>\n<pre><code>set_error_handler(function ($severity, $message, $file, $line) {\n error_log(json_encode([&#039;severity&#039; =&gt; $severity, &#039;message&#039; =&gt; $message, &#039;file&#039; =&gt; $file, &#039;line&#039; =&gt; $line]));\n return false;\n});</code></pre>\n<h2>Как проверить модель на практике</h2>\n<ol><li>Назвать границу, на которой действует механизм.</li><li>Зафиксировать, что считается успехом и отказом.</li><li>Проверить, какие данные или ресурсы остаются после ошибки.</li><li>Добавить измерение, которое подтвердит вывод в следующем проекте.</li></ol>\n<h2>Ограничения</h2>\n<p>У этой модели нет магической силы: интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается. Поэтому в рабочем проекте нужно добавлять наблюдение, разумные лимиты и понятный путь отката. Чем дороже ошибка, тем важнее заранее проговорить этот случай.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/manual/en/function.set-error-handler.php\" target=\"_blank\" rel=\"noopener\">PHP: обработка ошибок</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-02-practice-php-diagnostics",
"title": "PHP. Диагностика ошибок интеграции: рабочая схема",
"date": "2018-02-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"Диагностика"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "Практическая заметка о том, как увидеть исходную ошибку, а не только пустой ответ или false. С минимальной схемой, проверкой результата и ограничениями.",
"contentHtml": "<p>Периодически в проекте встречается задача, которая с виду кажется мелкой, а потом съедает полдня. В этот раз разбираюсь с темой «диагностика ошибок интеграции». Цель заметки — увидеть исходную ошибку, а не только пустой ответ или false. Не будем начинать с большой переделки: сначала соберём минимальный сценарий, который можно показать коллеге и повторить на чистой среде.</p>\n<h2>Минимальная рабочая схема</h2>\n<p>Первое правило здесь простое: отделяем входные данные от побочного эффекта. До того как менять состояние системы, проверяем условия, назначаем понятный идентификатор операции и оставляем достаточно контекста для диагностики. Ошибка должна иметь контекст: входные данные, идентификатор операции, место возникновения и безопасный журнал.</p>\n<pre><code>if (($_FILES[&#039;image&#039;][&#039;error&#039;] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {\n throw new RuntimeException(&#039;Upload failed&#039;);\n}\n$tmp = $_FILES[&#039;image&#039;][&#039;tmp_name&#039;];\n$target = $storage . &#039;/&#039; . bin2hex(random_bytes(12)) . &#039;.jpg&#039;;\nif (!move_uploaded_file($tmp, $target)) {\n throw new RuntimeException(&#039;Cannot move upload&#039;);\n}</code></pre>\n<h2>Что проверяем после запуска</h2>\n<ol><li>Сформулировать вход и ожидаемый результат: увидеть исходную ошибку, а не только пустой ответ или false.</li><li>Выполнить минимальный сценарий отдельно от остальной системы.</li><li>Проверить отрицательный путь: интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается.</li><li>Сохранить наблюдаемый результат в тесте, логе или коротком runbook.</li></ol>\n<h2>Где чаще всего ошибаются</h2>\n<p>Опасность темы «диагностика ошибок интеграции» не в сложном синтаксисе, а в неявных предположениях. Интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается. Если решение зависит от версии среды, внешней системы или прав пользователя, это лучше проверить отдельным шагом и записать рядом с кодом.</p>\n<h2>Материалы для проверки</h2><ul><li><a href=\"https://www.php.net/manual/en/function.set-error-handler.php\" target=\"_blank\" rel=\"noopener\">PHP: обработка ошибок</a></li></ul>\n<p>Если держать этот порядок, решение остаётся понятным и через несколько месяцев.</p>",
"readingMinutes": 3
},
{
"slug": "editorial-2018-01-field-bitrix-elements",
"title": "Bitrix API. Элемент есть в админке, но не виден в каталоге",
"date": "2018-01-25T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"Диагностика"
],
"cover": "/assets/editorial/2018/bitrix-visibility-diagnostic.svg",
"excerpt": "Полевой разбор частой ошибки Bitrix: Add вернул ID, админка показывает элемент, но пользователь не видит его в каталоге. Ищем причину по слоям, а не очищаем кеш наугад.",
"contentHtml": "<p>Знакомая картина: скрипт вернул ID, в админке новый товар есть, а на сайте его нет. Первый импульс — «почистить кеш». Иногда это действительно помогает, но чаще кеш просто оказывается первым подозреваемым, потому что его легко назвать. Давайте сначала отделим факт записи от публичной видимости и пройдём путь теми же условиями, которыми живёт каталог.</p>\n<h2>Постановка проблемы</h2>\n<p>Админка и публичный компонент редко показывают одинаковую выборку. Админка может отобразить неактивный элемент, а каталог фильтрует по <code>ACTIVE</code>, датам активности, разделу, правам, цене, наличию и проектным свойствам. Поэтому вопрос «почему элемент не виден?» нельзя решать одной командой. Нужен короткий список слоёв и доказательство на каждом.</p>\n<figure><img src=\"/assets/editorial/2018/bitrix-visibility-diagnostic.svg\" alt=\"Дерево диагностики: от результата Add к условиям публичного каталога\" /><figcaption>Начинаем не с кеша, а с самого раннего условия, которое может исключить элемент из публичной выборки.</figcaption></figure>\n<h2>Проверяем по слоям</h2>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Слой</th><th scope=\"col\">Что проверяем</th><th scope=\"col\">Как получить доказательство</th></tr></thead><tbody><tr><td>Запись</td><td><code>Add</code> вернул ID, <code>LAST_ERROR</code> пуст</td><td>Лог результата и внешний ID операции</td></tr><tr><td>Инфоблок</td><td><code>ACTIVE</code>, даты, символьный код, раздел</td><td>Контрольная выборка с теми же базовыми фильтрами</td></tr><tr><td>Свойства</td><td>Обязательная связь, SKU, картинка, проектные флаги</td><td>Чтение конкретных свойств для созданного ID</td></tr><tr><td>Каталог</td><td>Цена, остаток, доступность — если компонент их требует</td><td>Проверка конфигурации каталога и товарных параметров</td></tr><tr><td>Публичный путь</td><td>Фильтр компонента, права, кеш и индекс</td><td>Повтор сценария от имени нужного пользователя</td></tr></tbody></table></div>\n<h2>Контрольный запрос вместо догадки</h2>\n<p>Документация <code>CIBlockElement::GetList</code> описывает фильтры <code>ACTIVE</code>, <code>ACTIVE_DATE</code> и выбор нужных полей. Ниже не универсальный каталоговый запрос, а диагностическая проба. Она отвечает на первый важный вопрос: проходит ли наш элемент хотя бы базовые условия публичной выдачи. Если нет — проблему надо искать в данных, а не в шаблоне.</p>\n<pre><code>&lt;?php\n\nconst PRODUCT_IBLOCK_ID = 12;\n\n$result = CIBlockElement::GetList(\n [],\n [\n &quot;IBLOCK_ID&quot; =&gt; PRODUCT_IBLOCK_ID,\n &quot;=ID&quot; =&gt; $elementId,\n &quot;ACTIVE&quot; =&gt; &quot;Y&quot;,\n &quot;ACTIVE_DATE&quot; =&gt; &quot;Y&quot;,\n ],\n false,\n [&quot;nTopCount&quot; =&gt; 1],\n [&quot;ID&quot;, &quot;IBLOCK_ID&quot;, &quot;NAME&quot;, &quot;CODE&quot;, &quot;ACTIVE&quot;, &quot;DATE_ACTIVE_FROM&quot;, &quot;DATE_ACTIVE_TO&quot;]\n);\n\n$row = $result-&gt;Fetch();\nif ($row === false) {\n throw new RuntimeException(&quot;Элемент не проходит базовый публичный фильтр&quot;);\n}</code></pre>\n<h2>Где здесь каталог</h2>\n<p>Элемент инфоблока и товарная часть каталога — соседние, но разные уровни. Если публичный компонент требует цену, остаток или связь торгового предложения с товаром, одного <code>CIBlockElement::Add</code> недостаточно. Документация каталога отдельно описывает товарные параметры; в старом коде можно встретить <code>CCatalogProduct::Add</code>, но текущая документация помечает его устаревшим и рекомендует модель <code>\\Bitrix\\Catalog\\Model\\Product</code>. Для исторического проекта это не повод переписывать всё за вечер, а повод явно зафиксировать используемую версию API и не смешивать создание элемента с догадкой о его товарном состоянии.</p>\n<h2>Мини-матрица симптомов</h2>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Самая частая причина</th><th scope=\"col\">Безопасное следующее действие</th></tr></thead><tbody><tr><td>Нет ID</td><td>Ошибка обязательного поля, свойства или прав</td><td>Вывести <code>LAST_ERROR</code> и входной внешний ID</td></tr><tr><td>ID есть, базовый GetList пуст</td><td>ACTIVE, дата, инфоблок или неверный ID</td><td>Сначала читать поля элемента без публичных фильтров</td></tr><tr><td>GetList есть, карточки нет</td><td>Дополнительный фильтр компонента, раздел, права, URL</td><td>Сравнить фильтр и маршрут компонента с контрольной выборкой</td></tr><tr><td>Карточка есть, нельзя купить</td><td>Не настроены параметры каталога, цена или остаток</td><td>Проверить товарный слой отдельно от инфоблока</td></tr><tr><td>После изменения появляется не сразу</td><td>Кеш или индекс</td><td>Подтвердить корректность данных и только затем адресно обновлять кеш/индекс</td></tr></tbody></table></div>\n<h2>Почему не стоит начинать с очистки кеша</h2>\n<p>Потому что очистка кеша скрывает различие между двумя ситуациями: данные корректны, но слой кеширования устарел; или данные с самого начала не удовлетворяют фильтру. В первом случае нужна адресная стратегия инвалидирования. Во втором — очистка не решит проблему, а только добавит шума. Хорошая диагностика оставляет после себя не только исправленный товар, но и понимание, какое условие не было выполнено.</p>\n<h2>Чек-лист перед закрытием задачи</h2>\n<ol><li>Зафиксировать ID созданного элемента и внешний идентификатор операции.</li><li>Считать элемент без публичных ограничений и проверить, что ожидаемые поля и свойства сохранены.</li><li>Повторить контрольную выборку с <code>ACTIVE</code> и <code>ACTIVE_DATE</code>.</li><li>Проверить условия конкретного компонента: раздел, права, проектные фильтры, URL.</li><li>Если это товар — отдельно проверить цену, остаток и доступность, не смешивая этот слой с данными инфоблока.</li><li>Только после этого проверять кеш и индекс; зафиксировать, какое именно действие обновляет их в данном проекте.</li></ol>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">CIBlockElement::Add</a> — контракт метода, обработчики до и после записи, ID и LAST_ERROR</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getlist.php\" target=\"_blank\" rel=\"noopener\">CIBlockElement::GetList</a> — фильтры ACTIVE, ACTIVE_DATE и выборка полей элемента</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/catalog/classes/ccatalogproduct/add.php\" target=\"_blank\" rel=\"noopener\">CCatalogProduct::Add и актуальная модель Catalog</a> — параметры товарного элемента и версия API</li></ul>\n<h2>Итог</h2>\n<p>Фраза «элемент есть в админке» говорит только о том, что одна запись сохранилась. Для каталога этого недостаточно. Если идти от ID к базовой выборке, от неё к товарному слою и только затем к кешу, причина обычно находится быстро. И самое приятное: на следующей похожей задаче уже не нужно вспоминать магическую кнопку очистки — есть нормальный порядок проверки.</p>",
"readingMinutes": 10
},
{
"slug": "editorial-2018-01-mechanism-bitrix-elements",
"title": "Bitrix API. Что на самом деле происходит вокруг CIBlockElement::Add",
"date": "2018-01-15T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"Архитектура"
],
"cover": "/assets/editorial/2018/bitrix-add-lifecycle.svg",
"excerpt": "Разбираем жизненный цикл добавления элемента: кто проверяет поля, где срабатывают события Bitrix, почему глобальный обработчик не заменяет сервис и как тестировать эту границу.",
"contentHtml": "<p>Когда Bitrix-проект разрастается, вокруг простого <code>CIBlockElement::Add</code> появляется невидимый код: обработчики событий, правила символьного кода, импортеры, каталог, поиск и шаблоны. Из-за этого одинаковый вызов сегодня работает из формы, а завтра падает из консольного скрипта. Давайте разложим путь записи по шагам и не будем прятать бизнес-правило в месте, где его трудно обнаружить.</p>\n<h2>Карта жизненного цикла</h2>\n<p>Документация Bitrix говорит важную вещь: перед добавлением вызывается <code>OnBeforeIBlockElementAdd</code>. Обработчик получает поля по ссылке, поэтому способен их изменить; чтобы отменить запись, он должен установить исключение через <code>$APPLICATION-&gt;ThrowException()</code> и вернуть <code>false</code>. После успешной записи срабатывают события после добавления. Это значит, что обработчик — реальная часть контракта метода, а не декоративная «магия в init.php».</p>\n<figure><img src=\"/assets/editorial/2018/bitrix-add-lifecycle.svg\" alt=\"Последовательность от формы до контрольной публичной выборки при создании элемента Bitrix\" /><figcaption>ID возвращается из слоя инфоблока, но качество результата подтверждается уже в пользовательском сценарии.</figcaption></figure>\n<h2>Где живёт каждое правило</h2>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Место</th><th scope=\"col\">Хорошая ответственность</th><th scope=\"col\">Что туда не стоит класть</th></tr></thead><tbody><tr><td>Сервис создания</td><td>Проверка входа, подготовка полей, перевод ошибки в понятный результат</td><td>Глобальные побочные эффекты для любого инфоблока</td></tr><tr><td>OnBeforeIBlockElementAdd</td><td>Последний общий барьер: запрет пустого CODE, аудит общей политики</td><td>Внешние HTTP-вызовы, тяжёлую обработку файлов, правила одного экрана</td></tr><tr><td>После записи</td><td>Отправка события, фоновая реакция, журналирование успешной операции</td><td>Изменение результата, от которого зависит успех текущего Add</td></tr><tr><td>Публичный компонент</td><td>Фильтрация и отображение данных</td><td>Исправление отсутствующих обязательных данных «на лету»</td></tr></tbody></table></div>\n<h2>Минимальный предохранитель в событии</h2>\n<p>Ниже — не замена сервису, а общий барьер для конкретного инфоблока. Он предотвращает запись элемента без символьного кода независимо от того, откуда пришёл вызов: админка, импорт или самописный endpoint. Важно, что код не пытается угадать всё бизнес-правило товара. Он проверяет только инвариант, который действительно должен быть общим.</p>\n<pre><code>&lt;?php\n\nconst PRODUCT_IBLOCK_ID = 12;\n\nAddEventHandler(\n &quot;iblock&quot;,\n &quot;OnBeforeIBlockElementAdd&quot;,\n [&quot;CatalogElementGuard&quot;, &quot;beforeAdd&quot;]\n);\n\nfinal class CatalogElementGuard\n{\n public static function beforeAdd(array &amp;$fields): bool\n {\n if ((int)($fields[&quot;IBLOCK_ID&quot;] ?? 0) !== PRODUCT_IBLOCK_ID) {\n return true;\n }\n\n if (trim((string)($fields[&quot;CODE&quot;] ?? &quot;&quot;)) === &quot;&quot;) {\n global $APPLICATION;\n $APPLICATION-&gt;ThrowException(&quot;Для товара нужен символьный код&quot;);\n return false;\n }\n\n return true;\n }\n}</code></pre>\n<h2>Почему событие не должно быть единственным валидатором</h2>\n<p>Потому что событие не знает намерения конкретной операции. Один экран может создавать черновик без картинки, другой — импортировать поставщика, третий — мигрировать старые записи. Если все проверки спрятать в <code>OnBeforeIBlockElementAdd</code>, получится глобальная функция с десятком условий и неожиданными побочными эффектами. Сервис создания должен объяснять, почему он принимает или отклоняет вход. Событие лишь страхует инвариант, который действует для всех.</p>\n<h2>Сервис остаётся точкой диагностики</h2>\n<pre><code>&lt;?php\n\nfunction addCatalogElement(array $fields): int\n{\n if (!\\Bitrix\\Main\\Loader::includeModule(&quot;iblock&quot;)) {\n throw new RuntimeException(&quot;Модуль iblock не подключён&quot;);\n }\n\n $element = new CIBlockElement();\n $id = $element-&gt;Add($fields);\n\n if ($id === false) {\n $message = $element-&gt;LAST_ERROR ?: &quot;Bitrix не вернул причину ошибки&quot;;\n throw new RuntimeException($message);\n }\n\n return (int)$id;\n}</code></pre>\n<h2>После записи — это уже другой разговор</h2>\n<p>Обработчик после добавления удобен для журналирования, запуска поиска или отправки внутреннего уведомления. Но он не должен молча решать судьбу уже созданного элемента. Если побочный шаг упал после того, как <code>Add</code> вернул ID, повторный вызов <code>Add</code> из обработчика легко создаст дубль, а внешний сервис получит два одинаковых запроса. Поэтому после записи я бы сохранял ID, внешний ключ и понятный статус операции, а ошибку реакции разбирал как отдельную задачу.</p>\n<p>Если синхронизация с внешней системой действительно обязательна для публикации товара, полезно разделить два состояния: «элемент сохранён» и «элемент готов для пользователя». Первый факт подтверждает сервис создания, второй — контрольная проверка после всех зависимых действий. Тогда временный сбой индексации или уведомления не превращается в неясную историю, где никто не понимает, можно ли безопасно повторить импорт.</p>\n<h2>Как тестировать такую связку</h2>\n<p>В 2018-м легко ограничиться ручной проверкой в админке, но здесь полезно хотя бы зафиксировать короткую матрицу. Она не требует сложного тестового фреймворка: часть сценариев можно выполнить на тестовом инфоблоке и сохранить как чек-лист релиза. Главное — проверять и прямой сервис, и поведение глобального события.</p>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Сценарий</th><th scope=\"col\">Ожидание</th><th scope=\"col\">Где искать ошибку при сбое</th></tr></thead><tbody><tr><td>Корректный элемент</td><td>Сервис возвращает ID, элемент читается</td><td>Поля сервиса и конфигурация инфоблока</td></tr><tr><td>Пустой CODE</td><td>Запись отменена, причина понятна вызывающему коду</td><td>Обработчик OnBeforeIBlockElementAdd</td></tr><tr><td>Другой инфоблок</td><td>Охранник не вмешивается</td><td>Слишком широкое условие в обработчике</td></tr><tr><td>Импорт или CLI</td><td>Результат тот же, что из формы</td><td>Скрытая зависимость от HTTP-сессии или интерфейса</td></tr></tbody></table></div>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">CIBlockElement::Add</a> — контракт метода, обработчики до и после записи, ID и LAST_ERROR</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/events/onbeforeiblockelementadd.php\" target=\"_blank\" rel=\"noopener\">OnBeforeIBlockElementAdd</a> — как обработчик может изменить поля или отменить запись</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/setpropertyvaluesex.php\" target=\"_blank\" rel=\"noopener\">CIBlockElement::SetPropertyValuesEx</a> — точечное сохранение свойств и особенности пустых значений</li></ul>\n<h2>Итог</h2>\n<p>События Bitrix полезны, когда их граница ясна. Общий инвариант — в обработчик. Намерение операции, логирование и перевод ошибки — в сервис. Публичная видимость — в отдельную проверку после создания. С такой схемой даже старый проект перестаёт выглядеть набором случайных <code>init.php</code>-заклинаний: у каждого правила появляется место и причина.</p>",
"readingMinutes": 9
},
{
"slug": "editorial-2018-01-practice-bitrix-elements",
"title": "Bitrix API. Создаём элемент инфоблока так, чтобы ошибка не исчезла",
"date": "2018-01-07T10:00:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP",
"Практика"
],
"cover": "/assets/editorial/2018/bitrix-catalog-workflow.png",
"excerpt": "Разбираем создание элемента инфоблока как полноценную операцию: контракт полей, обработка LAST_ERROR, свойства, контрольная выборка и проверка публичного сценария.",
"contentHtml": "<p>Иногда задача формулируется очень просто: «добавь товар через API». Первая версия обычно занимает десять строк — создаём <code>CIBlockElement</code>, вызываем <code>Add</code>, получаем ID. А через день приходит сообщение: товар есть в админке, но карточка пустая, ссылка ведёт не туда или импорт тихо пропустил половину ошибок. Давайте сразу сделаем операцию так, чтобы её можно было проверить, повторить и поддерживать.</p>\n<h2>Ситуация: ID — это ещё не готовый результат</h2>\n<p>Элемент инфоблока — лишь одна часть пользовательского сценария. Для каталога могут быть важны символьный код, раздел, обязательные свойства, активность, картинка, цена и остаток. Метод <code>CIBlockElement::Add</code> действительно возвращает ID при успехе и <code>false</code> при ошибке, а текст причины лежит в <code>LAST_ERROR</code>. Поэтому нормальный критерий готовности состоит из двух вопросов: запись создана и потребитель этой записи видит ожидаемые данные.</p>\n<figure><img src=\"/assets/editorial/2018/bitrix-catalog-workflow.png\" alt=\"Разработчик проверяет путь от формы к карточке товара и фиксирует схему процесса\" /><figcaption>Не начинаем с большого импорта. Сначала рисуем путь данных и называем контрольные точки.</figcaption></figure>\n<h2>Сначала формулируем контракт операции</h2>\n<p>Перед вызовом API полезно выписать, какие поля обязательны именно для нашего инфоблока. Это выглядит занудно до первой ошибки импорта, а после неё экономит часы. Не нужно создавать универсальный валидатор Bitrix: достаточно проверить входные данные, зафиксировать системные значения и вернуть диагностируемую ошибку вызывающему коду.</p>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Участок</th><th scope=\"col\">Что фиксируем</th><th scope=\"col\">Чем доказываем</th></tr></thead><tbody><tr><td>Вход</td><td><code>name</code>, внешний ID, категория, файлы</td><td>Валидация до вызова Bitrix и понятная ошибка для вызывающего кода</td></tr><tr><td>Элемент</td><td><code>IBLOCK_ID</code>, <code>NAME</code>, <code>CODE</code>, <code>ACTIVE</code></td><td>Массив <code>$fields</code> можно залогировать без секретов</td></tr><tr><td>Свойства</td><td>Какие свойства обязательны при первом сохранении</td><td>Они передаются в <code>PROPERTY_VALUES</code> или проверяются отдельно</td></tr><tr><td>Результат</td><td>ID, URL, видимость в нужной выборке</td><td>Контрольный запрос и тест пользовательского сценария</td></tr></tbody></table></div>\n<h2>Рабочий пример</h2>\n<p>Ниже пример для черновика товара. Я намеренно сохраняю элемент неактивным: пока импорт не завершил все обязательные действия, пользователю незачем видеть полуготовую карточку. Конкретные коды свойств и ID инфоблока должны быть вынесены в конфигурацию проекта, а не спрятаны в середине функции.</p>\n<pre><code>&lt;?php\n\nuse Bitrix\\Main\\Loader;\n\nconst PRODUCT_IBLOCK_ID = 12;\n\nfunction createProductDraft(array $input): int\n{\n if (!Loader::includeModule(&quot;iblock&quot;)) {\n throw new RuntimeException(&quot;Модуль iblock не подключён&quot;);\n }\n\n $name = trim((string)($input[&quot;name&quot;] ?? &quot;&quot;));\n $code = trim((string)($input[&quot;code&quot;] ?? &quot;&quot;));\n\n if ($name === &quot;&quot; || $code === &quot;&quot;) {\n throw new InvalidArgumentException(&quot;Нужны NAME и CODE&quot;);\n }\n\n $element = new CIBlockElement();\n $id = $element-&gt;Add([\n &quot;IBLOCK_ID&quot; =&gt; PRODUCT_IBLOCK_ID,\n &quot;NAME&quot; =&gt; $name,\n &quot;CODE&quot; =&gt; $code,\n &quot;ACTIVE&quot; =&gt; &quot;N&quot;,\n &quot;PROPERTY_VALUES&quot; =&gt; [\n &quot;EXTERNAL_ID&quot; =&gt; (string)($input[&quot;externalId&quot;] ?? &quot;&quot;),\n &quot;BRAND&quot; =&gt; (int)($input[&quot;brandId&quot;] ?? 0),\n ],\n ]);\n\n if ($id === false) {\n throw new RuntimeException($element-&gt;LAST_ERROR ?: &quot;Не удалось создать элемент&quot;);\n }\n\n return (int)$id;\n}</code></pre>\n<h2>Почему свойства лучше не «доклеивать» вслепую</h2>\n<p>Для обязательных свойств, без которых объект не имеет смысла, удобнее передавать <code>PROPERTY_VALUES</code> в том же вызове <code>Add</code>. Метод <code>SetPropertyValuesEx</code> полезен, когда нужно сознательно обновить небольшую часть свойств: он не требует передавать полный набор и экономнее по запросам. Но он возвращает <code>null</code>, поэтому его нельзя использовать как удобный индикатор успеха. Если частичное обновление критично, его надо окружить собственным журналированием и контрольным чтением.</p>\n<pre><code>&lt;?php\n\n// Осознанное точечное изменение, а не «попробуем и забудем».\nCIBlockElement::SetPropertyValuesEx(\n $elementId,\n PRODUCT_IBLOCK_ID,\n [&quot;SYNC_STATUS&quot; =&gt; &quot;ready&quot;]\n);\n\n// После важного изменения читаем нужное свойство в контрольном сценарии.</code></pre>\n<h2>Четыре проверки после Add</h2>\n<ol><li>Проверяем, что вернулся положительный ID; при <code>false</code> сохраняем <code>LAST_ERROR</code>, входной внешний идентификатор и контекст операции.</li><li>Читаем элемент в том же инфоблоке и убеждаемся, что поля <code>NAME</code>, <code>CODE</code> и нужные свойства действительно сохранены.</li><li>Проверяем публичную выборку с теми же фильтрами, которые использует компонент каталога: активность, даты, раздел, права, цена и остатки — если они участвуют в сценарии.</li><li>Только после этого включаем элемент или помечаем импортированную запись как готовую.</li></ol>\n<h2>Чего я бы не делал</h2>\n<ul><li>Не игнорировал бы результат <code>Add</code> в надежде, что ошибка «сама попадёт в журнал».</li><li>Не делал бы элемент активным до заполнения зависимых данных.</li><li>Не генерировал бы <code>CODE</code> без правила уникальности: два одинаковых названия неизбежно встретятся.</li><li>Не очищал бы весь кеш первым действием. Сначала нужно доказать, что проблема именно в кеше, а не в данных или фильтре.</li></ul>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y\" target=\"_blank\" rel=\"noopener\">CIBlockElement::Add</a> — контракт метода, обработчики до и после записи, ID и LAST_ERROR</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/setpropertyvaluesex.php\" target=\"_blank\" rel=\"noopener\">CIBlockElement::SetPropertyValuesEx</a> — точечное сохранение свойств и особенности пустых значений</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/getlist.php\" target=\"_blank\" rel=\"noopener\">CIBlockElement::GetList</a> — фильтры ACTIVE, ACTIVE_DATE и выборка полей элемента</li></ul>\n<h2>Итог</h2>\n<p>Сам вызов <code>CIBlockElement::Add</code> несложен. Сложность в том, чтобы не потерять границу между «запись появилась» и «сценарий закончен». Если хранить контракт полей рядом с кодом, проверять <code>LAST_ERROR</code> и делать контрольную выборку, импорт перестаёт быть магией. А дальше уже можно спокойно добавлять цены, остатки и любые проектные правила.</p>",
"readingMinutes": 9
},
{
"slug": "о-tilix-и-d-интервью-с-геральдом-нанном",
"title": "О Tilix и D: интервью с Геральдом Нанном",
"date": "2017-08-25T21:37:08+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Интервью"
],
"cover": "/assets/illustrations/tilix-cover.svg",
"excerpt": "Йоаким — интервьюер-резидент блога о D. Он также брал интервью у членов D-сообщества для This Week in D и ответственен за портирование LDC для Android . Геральд Нанн — разработчик Tilix (ранее называвшийся Terminix). Tilix— продвинутый тайлинговый эмулятор тер",
"contentHtml": "<p>Йоаким — интервьюер-резидент блога о D. Он также брал <a href=\"http://arsdnet.net/this-week-in-d/mar-15.html\">интервью</a> <a href=\"http://arsdnet.net/this-week-in-d/jul-12.html\">у</a> <a href=\"http://arsdnet.net/this-week-in-d/jun-28.html\">членов</a> <a href=\"http://arsdnet.net/this-week-in-d/sep-06.html\">D-сообщества</a> для <em><a href=\"http://arsdnet.net/this-week-in-d/\">This Week in D</a></em> и ответственен за <a href=\"https://github.com/joakim-noah/android/releases\">портирование LDC для Android</a>.</p>\n<p>Геральд Нанн — разработчик Tilix (ранее называвшийся Terminix).</p>\n<p>Tilix— продвинутый тайлинговый эмулятор терминалов с открытым исходным кодом, который является <a href=\"https://github.com/search?l=D&amp;amp;q=stars%3A%3E1&amp;amp;s=stars&amp;amp;type=Repositories\">самым «звёздным» проектом на основеD наGitHub</a>, недавно даже обогнавший стандартный компилятор D, DMD. В этом году на DConf в Берлине он рассказывал о том, как использует D. Имеются <a href=\"http://dconf.org/2017/talks/nunn.html\">слайды</a> и <a href=\"https://www.youtube.com/watch?v=5eUL8Z9AFW0\">видео</a>. В своей повседневной работе, которая не имеет ничего общего с настольными графическими приложениями, он является старшим разработчиком промежуточных решений в Red Hat. <a href=\"http://dblog-ext.info/interviews/gerald-interview.html\">Подробнее об истории Джеральда в [расширенном интервью</a>— Ред.]</p>\n<p><strong>Йоаким:</strong> — Что такое тайлинговый эмулятор терминала?</p>\n<p><strong>Геральд:</strong>— Тайлинговый эмулятор терминала позволяет разделить терминал на несколько частей и распределить их в удобном порядке и месте, что наиболее удобно при работе над конкретной задачей. Люди, которые работают на нескольких терминалах одновременно, как правило, находят такие инструменты наиболее полезными, особенно с постоянно увеличивающимися размерами мониторов и разрешений.</p>\n<p>Несмотря на то, что Tilix очень хорош, основной причиной, по которой я создал его, было то, что мне нужен был эмулятор терминала, который следовал бы концепции Gnome HIG (Human Interface Guidelines — рекомендации по созданию интуитивных, легко изучаемых и логичных интерфейсов взаимодействия с пользователем) и использовал CSD (Client-Side Decorations — отрисовку на стороне клиента). Tilix следует <a href=\"https://developer.gnome.org/hig/stable/\">Gnome HIG, опубликованным здесь</a>, что означает соблюдение интервалов, макетов и других рекомендаций. После HIG важно, чтобы приложение соответствовало всему рабочему столу в целом.</p>\n<p>CSD ссылается на заголовок окна, где не диспетчер дисплея, а пользователь берёт на себя ответственность за него и может заполнять панель заголовка кнопками и другими элементами управления. Это часть концепции Gnome HIG, и большинство приложений Gnome (gedit, файлы, видео) по умолчанию используют этот подход. Единственное исключением является gnome-terminal, который вообще не использует CSD.</p>\n<p><strong>Й.:</strong> — Можете привести несколько примеров того, как вы используете концепцию Gnome HIG?</p>\n<p><strong>Г.:</strong> — Gnome HIG определяет конкретный язык дизайна в отношении того, как приложения, работающие в Gnome, должны выглядеть и показывать себя. Некоторые примеры Tilix, следующие концепции HIG, включают использование CSD и меню приложений по рекомендациям, таким как интервалы, макеты и т.д. Кроме того, разработчики Gnome собрали множество макетов, как они думают, как должны выглядеть различные приложения. Tilix использует макеты, разработанные дизайнерами Gnome для терминала, где это возможно. Например, в Tilix диалог предпочтений и профилей использовался отдельно, однако один из дизайнеров Gnome предложил <a href=\"https://bug722114.bugzilla-attachments.gnome.org/attachment.cgi?id=266172\">этот макет для gnome-терминала</a>. Я пошел вперед и реализовал его в Tilix, гораздо лучше, чем раньше.</p>\n<p>В результате использования CSD и концепции Gnome HIG, надеюсь, что использование Tilix в Gnome более органичено для пользователей.</p>\n<p>Интересная вещь — это напряженность между людьми, которые используют Tilix на Gnome и тех, кто использует его в других дистрибутивах. Хотя я не имею никаких сомнений в том, что разработка под Gnome является моей основной целью, я стараюсь сделать Tilix лучше и в других средах рабочего стола, разрешив пользователю отключить CSD в пользу обычного заголовка, если они того пожелают.</p>\n<p><strong>Й.:</strong> — Вы попали в D из среды Java. Вы все еще пишете код в стиле Java на D? Это было легко, т.е. сколько вам пришлось привыкать, чтобы писать на D?</p>\n<p><strong>Г.:</strong> — Да, чаще всего работаю с Java. Я нашёл это довольно интересным на DConf, когда я спросил, как много людей пришли не из среды C/C++ , только один человек поднял руку.</p>\n<p>Если вы посмотрите на мой код в Tilix, то он очень похож на Java-код. Некоторое из этого связано с моей обычное средой, в которой я работаю, а некоторое из-за того, что <a href=\"https://gtkd.org/\">GtkD</a> является оболочкой классов.</p>\n<p>Я считаю, что переключение между D и Java является довольно плавным по большей части; между ними гораздо меньше когнитивного трения, чем, скажем, между переключением между Java и Python.</p>\n<p>Самая важная идиома D, которую мне пришлось изучить, — это <a href=\"https://wiki.dlang.org/Component_programming_with_ranges\">диапазоны</a>, поскольку они являются основополагающей особенностью D. Однако это не было сильно сложным. <a href=\"https://tour.dlang.org/tour/en/gems/compile-time-function-evaluation-ctfe\">Выполнение функций времени компиляции (CTFE)</a> по-прежнему остаются для меня неестественными. Когда я использую их, мне приходится каждый раз искать про них информацию, и мои текущие попытки использования CTFE с точки зрения кода довольны глупы. Я хотел бы больше использовать CTFE в Tilix, поскольку я получаю всё больший опыт использования D.</p>\n<p>Наконец, мой недостаток опыта работы с C выявляет другую проблему, с которой я немного борюсь — это взаимодействие с кодом на C. Хотя по большей части это довольно просто, но, когда мне приходится расшифровывать что-то сложное, оно становится не таким простым. Поддержка FlatPak (система изолированных контейнеров для графических приложений) в настоящее время есть, так как мне не приходилось так сильно напрягаться по некоторым вопросам, с которыми я сталкивался на C.</p>\n<p>Сказав это, у меня есть некоторый опыт разработки собственного кода, поскольку много лет назад я потратил много времени на разработку кода на Delphi и Object Pascal. Именно на это и приходится большая часть моего опыта работы с графическим интерфейсом.</p>\n<p><strong>Й.:</strong> — И из вашего выступления на DConf вы явное не беспокоились о сборщике мусора (GC). Вам приходилось думать о нём, когда вы разрабатывали Tilix? Имелись ли проблемы задержек с GUI, вызванные GC?</p>\n<p><strong>Г.:</strong> — Придя из Java, GC для меня довольно естественен, и я определенно не считаю его плохим для D. Я думаю, что GC в D получает много плохой прессы на основе опыта Java, но важно помнить, что GC в D сильно отличается от того, что в Java. Самое большое различие для меня в том, что в D есть больше возможностей для его управления, так как <a href=\"http://dlang.org/blog/2017/06/16/life-in-the-fast-lane/\">он хорошо понимает, когда он может начать цикл GC</a>. Я был очень рад видеть, как больше людей поднимают различные темы на reddit и форумах, жалуются на использование GC.</p>\n<p>У меня не было никаких проблем с GC с точки зрения пауз, и ни один пользователь Tilix не сообщал об этом. У меня было несколько проблем, связанных с GC, в основном связанных с утечкой памяти из-за хранения ссылок, но все они были ошибками программного кода, а не проблемой с реализацией GC в D. У меня есть другое приложение на GTK D, Visual Grep, где я столкнулся с ужасающей эффективностью при обработке большого количества совпадений в tight-цикле (цикл, который содержит несколько инструкций и повторяется много раз.). Тем не менее, просто отключив GC для этого раздела кода, ускорилось все.</p>\n<p><strong>Й.:</strong> — Репозиторий github для Tilix замечательно чист, нет открытых запросов «pull request» (PR) и низкий процент проблем, которые все еще открыты. Сколько времени вы еженедельно тратите на Tilix? Является ли Tilix только хобби или он стал чем-то большим?</p>\n<p><strong>Г.:</strong> — Tilix — это просто хобби. Я, вероятно, провожу от 5 до 10 часов в неделю. На данный момент — это зрелое приложение, следовательно, относительно небольшое количество проблем. Я также уделяю приоритетное внимание исправлению ошибок при добавлении новых функций, что помогает сохранить список управляемым.</p>\n<p>Что касается запросов на «pull request» (PR), я твердо убежден в том, что я должен быть отзывчивым, поэтому я, как правило, отвечаю на PR в течение дня или двух. В качестве участника разработок я знаю, что ничего не убивает интерес, как видеть, что ваш PR томится в течение нескольких недель, месяцев или даже лет. Если вы хотите, чтобы люди вносили свой вклад, что я определенно делаю, то я чувствую, что вы обязаны участникам разработки своевременно реагировать на их PR.</p>\n<p>На данный момент Tiltx — это относительно небольшой проект, поэтому мне легко принять этот подход. Я понимаю, почему более крупные проекты могут иметь больше проблем в этой области.</p>\n<p><strong>Й.:</strong> — D имеет много особенностей, насколько хорошо вы их знаете? Вы упомянули, что хотите использовать некоторые из возможностей времени компиляции. Как вы думаете за счёт каких особенностей D и что Tilix выиграет в будущем и как?</p>\n<p><strong>Г.:</strong> — Я не думаю, что знаю это, если честно, помимо основного набора особенностей, которые я использую в Tilix. Я всегда удивляюсь ребятам <a href=\"https://forum.dlang.org/\">на форуме</a>, которые могут утверждать достоинства/недостатки низкоуровневых деталей языка. Это определенно не я. Я бы хотел поправиться, но на самом деле я использую D только как хобби, поэтому я не могу тратить столько же времени, сколько и на Java. Кроме того, моя работа в Red Hat имеет больше компонентов инфраструктуры, чем мои предыдущие рабочие места, поэтому большая часть моего времени расходуется дома на обучение в этом направлении.</p>\n<p>Что касается возможностей, от которых Tilix выиграет, я думаю, что использование CTFE и диапазонов было бы очень полезно для того, чтобы улучшить некоторые моменты в GtkD более идиоматичным способом. У меня есть достаточное количество кода, где он может быть намного более кратким при соответствующем использовании CTFE. В качестве простого примера <a href=\"https://tour.dlang.org/tour/en/basics/ranges\">можно использовать диапазоны для поддержки итераций по различным артефактам с использованием foreach</a>, а не классическим циклом. Тем не менее, я думаю, что некоторые из более сложных вариантов использования, таких как поддержка D-Bus (система межпроцессного взаимодействия, которая позволяет приложениям в операционной системе сообщаться друг с другом) и GObject, будут очень полезны.</p>\n<p>Для тех, кто не знаком с GObject — это объект базового уровня в GTK и является счётчиком ссылок. Возможность легко создавать GObjects в D, как можно и в Python, упростит взаимодействие с некоторыми из API; прямо сейчас это эквивалентно написанию его в сыром C, и это несколько трудоемко. Майк Вей, сопровождающий <a href=\"https://gtkd.org/\">GtkD</a>, начал делать некоторые работы над этим.</p>\n<p><strong>Й.:</strong> — Вы упомянули на DConf, что D имеет быстрый цикл компиляции: как вы используете это, то есть какие IDE, компиляторы, toolchain (набор программ, необходимых для создания других программ) вы используете как для разработки, так и для выпуска релизов?</p>\n<p><strong>Г.:</strong> — Я использую <a href=\"https://code.visualstudio.com/\">MS Visual Studio Code</a> на Linux с отличным плагином code-d, написанным Jan «WebFreak» Jurzitza. Это дает мне все необходимые функции (автозаполнение кода, подсказки, рекомендации и т. Д.). Единственное, чего я не вижу, что есть в Java IDE, — это возможность рефакторинга. Для разработки я использую DMD, поскольку он имеет самое быстрое время компиляции. Сборку версий выполняю с использованием <a href=\"https://wiki.dlang.org/LDC\">LDC, компилятора D с бэкэном LLVM</a>, поскольку он генерирует меньшие и более быстрые двоичные файлы. Мне редко приходится запускать отладчик, но, когда я это делаю, я просто использую GDB из командной строки.</p>\n<p><strong>Й.:</strong> — Какие проблемы у вас были с D? Какие его особенности вам не нравятся?</p>\n<p><strong>Г.:</strong> — Никаких серьёзных проблем с моей точки зрения. Я в целом очень доволен этим языком и считаю, что он несёт правильный баланс между простотой использования и возможностями. Наибольшее неудобство мне доставляла стандартная библиотека <a href=\"http://dlang.org/phobos/index.html\">Phobos</a>, а не сам язык, и эти неудобства напрямую соотносятся с нехваткой времени.</p>\n<p>Никаких серьёзных проблем с Phobos, а скорее кучей раздражителей. Например, нельзя легко использовать immutable для отправки в <a href=\"http://dlang.org/phobos/std_concurrency.html\">std.concurrency</a>, <a href=\"http://dlang.org/phobos/std_experimental_logger.html\">std.experimental.logger</a> являющиеся все ещё экспериментальными, <a href=\"http://dlang.org/phobos/std_json.html\">парсер json</a> имеет проблемы, если в локализации установлена запятая для отделения десятичных знаков чисел и т.д. и т.п. Ни один из данных недочётов по себе не является особо важным, и большинство из скорее всего связаны из-за нехватки трудовых ресурсов. Я на самом деле несколько неохотно жалуюсь на них, потому что я, наверное, мог исправить их сам и отправить PR.</p>\n<p>Меня больше раздражает негативность на форумах в отношении GC. Я чувствую, что иногда люди так поворачиваются в сторону D, что хотят видеть его идеальным системным языком (т.е. без GC, без безопасности памяти и т.д.), упуская из виду, что он очень хороший язык для создания приложений на данный момент. Пока D сравнивают с Rust, в некотором смысле сравнение с Go для меня более интересно. Оба языка основаны на GC и оба начинали как системные языки, однако Go опирается на GC и «удваивается» (от переводчика: скорее всего имеется в виду рост популярности), добиваясь успеха. Один из продуктов Red Hat, который я поддерживаю, OpenShift, использует Kubernetes (проект Google) для управления кластером контейнеров Linux как единой системой, и он написан на Go.</p>\n<p>Я думаю, что как язык D намного превосходит Go, и мне хотелось, что мы бы заявляли об этом громче вместо постоянного отрицательного обсуждения системного программирования. На данный момент надо сказать ради справедливости, что Go имеет крупного корпоративного спонсора, в отличие от D, однако контраст в позиционировании по-прежнему интересен мне.</p>\n<p><strong>Й.:</strong> — Каковы ваши будущие планы в отношении Tilix?</p>\n<p><strong>Г.:</strong> — Две самые большие функции, которые я хотел бы добавить, это поддержка режима управления <a href=\"https://github.com/tmux/tmux\">tmux</a>и добавление возможности отображения боковой панели popout.</p>\n<p>Для тех, кто не знаком с tmux, это терминальный мультиплексор; это, по сути, терминальный разделитель, но внутри самого терминала. Он также поддерживает ряд других функций, но наиболее интересным является сохранение терминальных сеансов, находящихся вне терминала. Поскольку он работает в терминале, он немного ухудшает производительность, а с точки зрения графического интерфейса он не может использовать собственные виджеты, такие как полосы прокрутки. Чтобы смягчить это, он поддерживает так называемый режим управления, который позволяет ему интегрироваться с эмулятором тайлингово терминала для создания новых терминалов, т.е. подтерминалов внутри одного терминала, управляющихся за пределами tmux. Это значительно улучшает его производительность, позволяя пользователям использовать другие функции, поддерживаемые tmux. На данный момент поддерживается только iterm2 на OSX насколько мне известно.</p>\n<p>Боковая панель в Tilix является одним из наиболее противоречивых элементов интерфейса. Я решил не реализовывать интерфейс с вкладками, потому что я счел их бесполезными с точки зрения поиска открытия необходимой вкладки; там просто недостаточно места для вкладок, чтобы отличить их, и переименовывать их вручную — это «боль». Боковая панель — это моя попытка альтернативы, она отображает миниатюру каждого сеанса (известную также как ярлык) на боковой панели, которая может быть убрана по мере необходимости для переключения между сеансами.</p>\n<p><img src=\"/assets/illustrations/tilix-screenshot.svg\" alt=\"Боковая панель Tilix в действии\" /> <em>Боковая панель Tilix в действии</em></p>\n<p><em>Боковая панель Tilix в действии.</em></p>\n<p>У некоторых людей есть сильное предпочтение к тому, что постоянно доступно, но, к сожалению, все, что видно на боковой панели, непросто, так как генерация эскизов занимает значительное количество времени из-за структуры GTK. Существуют потенциальные способы заставить это работать быстрее, но требуется время на то, чтобы попробовать разные варианты, чтобы увидеть, что как отображается, а затем, что наиболее эффективно.</p>\n<p>В мечтах, я бы хотел переключиться на использование эмулятора терминала, написанного изначально в D, а не GTK VTE (Virtual Terminal Emulator), который написан на C, и который я использую сейчас. Для тех, кто не знаком с ним, VTE — это виджет эмуляции терминала, используемый терминалом Gnome и доступен в виде многоразового виджета. Многие эмуляторы терминала в Linux используют этот виджет (gnome-terminal, guake, terminator, tilix и т.д.), поскольку он обеспечивает полностью готовый к работке эмулятор, который прошел через огромное количество тестов.</p>\n<p>Недостатком этого является то, что любые пользовательские функции, которые вы хотите реализовать, которые включают фактический уровень эмуляции терминала, требуют модификации VTE и получения этих изменений вверх по потоку (<em>upstream). У меня есть несколько патчей, которые Tilix поддерживает (для триггеров и значков), но, честно говоря, я провёл плохую работу по внедрению вверх по потоку (</em>upstream). Частично это связано с тем, что VTE написано на C и сделать быстрый и качественный патч на C занимает достаточно много времени.</p>\n<p>Таким образом, наличие эмуляции терминала, написанного на D, сделало бы это намного проще, однако это огромные инвестиции времени, поскольку люди недооценивают объем работы. В эмуляции терминала много сложных случаев, плюс добавление всего материала, чтобы сделать его удобным для пользователей (поиск, клики по ссылкам и т.д.), это много, чтобы взять на себя. У меня просто нет времени, чтобы сделать это реальностью, если только я не выиграю в лотерею. Если кто-то захочет взять на себя это роль и создать виджет эмуляции терминала GTK, который имеет все необходимые функции и поддерживал бы его в течение длительного времени, я был бы рад работать с ним, чтобы интегрировать его с Tilix. <a href=\"https://github.com/adamdruppe/terminal-emulator\">Адам Рапп уже создал один</a>, который работает достаточно хорошо по моим тестам; если кто-то хочет работать над преобразованием его в виджет GTK, добавит необходимые улучшения и согласится его поддерживать, то пусть не стесняется писать и надоедать сообщениями (*ping) мне. ?</p>\n<p>Й.: — Пожалуйста, расскажите из своего опыта, как вы впервые обнаружили D и использовали его для написания Tilix?</p>\n<p>Г.: — На протяжении всей моей карьеры я всегда имел хобби. Некоторые вещи, над которыми я работал, включали популярную надстройку для Delphi под названием <a href=\"http://gexperts.org/\">Gexperts</a>, замену проводника файлов Windows, <a href=\"http://pic.pimg.tw/superhbin/1205243571.png\">Java IDE под названием Gel</a> и популярное приложение для Android под названием OnTrack Diabetes, которое я продал несколько лет назад. Пару лет назад я искал что-то новое для работы в качестве моей программы в качества хобби и остановился на идее создания десктопного приложения для Linux. Я знал, что нужно использовать инструментарий GTK, так как Gnome — моя предпочтительная среда рабочего стола, и особенно с моим прошлым опытом Delphi в графических интерфейсах, которые я мог бы использовать. Я также знал, что меня не интересует разработка на C или C ++, поэтому я посмотрел, какие альтернативы были.</p>\n<p>Я начал с Python, так как у него отличная поддержка GTK, и я немного поработал над программированием Jython в WebLogic, так как я использовал Weblogic Scripting Tool (WLST). Однако большая часть моей предыдущей работы была небольшими сценариями, и я быстро понял, что в больших масштабах Python не для меня. Я вообще предпочитаю статически типизированные языки, и динамический набор текста на Python приводил меня в замешательство, особенно в качестве хобби, где я постоянно нуждался в использование справочных материалов, так как я работал на нем нечасто.</p>\n<p>Я также смотрел в сторону Rust и Go, но в то время ни у одного из них не было полнофункциональных GTK-привязок. Кроме того, в то время у Rust было много позитивной прессы, но у него был плохой материал для убеждения в правильности его подхода, и я был не так сильно убежден в том, что управление безопасностью памяти с помощью контролера заимствований было лучшим подходом, чем GC.</p>\n<p>Я знал о D, поскольку я посматривал на D много лет назад и полюбил этот язык, но экосистема была настолько слабой, что это было не сильно полезно для практической работы. Я взглянул на него снова и обнаружил, что он значительно улучшился и даже удивительно: были доступны полные привязки GtkD. Это также помогло тому, чтобы D и Java были достаточно похожи, и чтобы собирать на D было невероятно легко. Как только я узнал, как работают диапазоны, было легко начать кодить.</p>\n<p>Сначала я создал небольшое приложение под названием <a href=\"https://github.com/gnunn1/vgrep\">Visual Grep</a>, которое переносит grep в графический интерфейс. Когда я консультировался, мне часто приходилось собирать большие базы кода, ищущие конкретные шаблоны, и графический интерфейс, который делал просмотр шаблонов, был абсолютной необходимостью. Это было отличное первое приложение, так как я узнал немало вещей о D. Производительность приложения была изначально плохой с большими наборами результатов, потому что GC постоянно был перегружен при загрузке результатов из-за постоянного распределения. Отключение GC во время этого tight-цикла улучшило производительность неизмеримо. Я также узнал об интеграции GTK с возможностями многопоточности D.</p>\n<p>Я был вдохновлен пользовательским интерфейсом от <a href=\"https://wiki.gnome.org/Apps/Builder\">Gnome Builder IDE</a>, и подумал, что он хорошо подойдёт для эмулятора терминала. Таким образом, Tilix родился. Ну, на самом деле сначала он назывался Terminix, но как только он начал получать популярность, я получил вежливую просьбу на переименование из-за компании Terminix, американской компании по борьбе с вредителями. Будучи канадцем, я не слишком хорошо их знал, поэтому я не много думал об этом имени. Извлеченный урок: потратьте время на выбор хорошего имени, если ваше приложение станет более популярным, чем вы ожидаете.</p>\n<p>Я наслаждаюсь временем, проведённым с D, и это отличный язык для создания настольных приложений в GTK. Во многих отношениях, я чувствую, что D является естественным преемником Vala, который был языком, созданным специально для создания приложений GTK, но постепенно умирающим, в основном из-за его узкой направленности. Если вы не создаёте приложения GTK, вы не используете Vala, что означает, что количество людей, использующих его и работающий над ним, по определению очень мало.</p>\n<p>Я также считаю, что D является естественным преемником Delphi, по крайней мере с GtkD, как мощным инструментом для создания настольных приложений. Благодаря быстрому времени компиляции и легкому изучению языка, он приносит множество лучших атрибутов Delphi в современную эпоху. Плюс я могу ввести {и} вместо Begin и End и увеличить свою эффективность на 75% или около того. ?</p>\n<p>Оригинал (англ.) <a href=\"http://dlang.org/blog/2017/08/11/on-tilix-and-d-an-interview-with-gerald-nunn/\">http://dlang.org/blog/2017/08/11/on-tilix-and-d-an-interview-with-gerald-nunn/</a></p>\n<h2>Что вынести из интервью</h2>\n<p>Главный практический вывод прост: D может быть не только системным языком, но и удобным языком для прикладных desktop-инструментов. Tilix показывает, что связка D, GtkD и аккуратного следования GNOME HIG способна дать зрелое приложение, которым пользуются каждый день.</p>\n<p>В интервью хорошо видно, что выбор языка был не религиозным, а практическим. Автору не хотелось писать GUI на C или C++, Python оказался неудобен для крупного статически проверяемого приложения, а D дал знакомую после Java модель, нативную скорость и нормальные GTK-привязки. Это важный критерий выбора технологии: не «самый модный язык», а язык, на котором конкретный автор может быстро и спокойно делать продукт.</p>\n<p>Если хочется повторить такой путь, начинать стоит не с большого терминала, а с маленькой GTK-утилиты: окно, настройки, несколько действий, сборка через DUB, упаковка и обновления. На таком проекте сразу проявятся реальные вопросы: работа с GtkD, структура проекта, взаимодействие с C-библиотеками, обработка событий и паузы GC.</p>\n<ul>\n<li>Для GUI-кода держите бизнес-логику отдельно от виджетов.</li>\n<li>Не бойтесь GC, но измеряйте горячие участки и большие циклы.</li>\n<li>Если нужен C API, сначала сделайте маленький прототип вызова.</li>\n<li>Сначала исправляйте ошибки, потом добавляйте функции — именно так Tilix сохранил управляемый backlog.</li>\n</ul>\n<p>Именно поэтому интервью полезно не только поклонникам D. Оно показывает нормальный инженерный путь: выбрать задачу, подобрать инструмент, сделать небольшую работающую версию, а затем постепенно доводить приложение до зрелого состояния.</p>",
"readingMinutes": 19
},
{
"slug": "bitrix-api-функция-для-генерации-кода-элемент",
"title": "Bitrix API. Функция для генерации кода элемента из имени через транслит",
"date": "2017-08-15T16:35:54+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP"
],
"cover": "/assets/illustrations/bitrix-translit-api.svg",
"excerpt": "Часто в Bitrix необходимо сгенерировать код элемента из имени. Привожу пример реализации именно такой функции с помощью функции Bitrix API для транслита CUtil::translit :",
"contentHtml": "<p>Часто в Bitrix необходимо сгенерировать код элемента из имени. Привожу пример реализации именно такой функции с помощью функции Bitrix API для транслита CUtil::translit:</p>\n<pre><code>function strToElementCode($str, $maxLength = 100) {\n\t$params = array(\n\t\t\t&quot;max_len&quot; =&gt; $maxLength,\n\t\t\t&quot;change_case&quot; =&gt; &quot;L&quot;,\n\t\t\t&quot;replace_space&quot; =&gt; &quot;_&quot;,\n\t\t\t&quot;replace_other&quot; =&gt; &quot;_&quot;,\n\t\t\t&quot;delete_repeat_replace&quot; =&gt; &quot;true&quot;,\n\t\t\t&quot;use_google&quot; =&gt; &quot;false&quot;,\n\t\t);\n\treturn CUtil::translit($str, &quot;ru&quot;, $params);\n}</code></pre>\n<h2>Как использовать функцию безопасно</h2>\n<p>Транслитерация имени удобна, но код элемента должен оставаться уникальным. Поэтому после генерации проверяйте существующие символьные коды в инфоблоке и при совпадении добавляйте числовой суффикс. Иначе два товара с похожим названием могут получить один URL.</p>\n<pre><code>telefon-samsung\ntelefon-samsung-2\ntelefon-samsung-3</code></pre>\n<p>Также стоит сразу нормализовать результат: привести к нижнему регистру, заменить пробелы на дефис, убрать повторяющиеся дефисы и обрезать дефис в начале или конце строки. Это мелочь, но именно такие мелочи потом портят адреса страниц.</p>\n<pre><code>$code = CUtil::translit($name, &#039;ru&#039;, [\n &#039;replace_space&#039; =&gt; &#039;-&#039;,\n &#039;replace_other&#039; =&gt; &#039;-&#039;,\n &#039;change_case&#039; =&gt; &#039;L&#039;,\n]);\n\n$code = trim(preg_replace(&#039;/-+/&#039;, &#039;-&#039;, $code), &#039;-&#039;);</code></pre>\n<p>Проверку уникальности лучше делать в том же инфоблоке, где будет создан элемент. Если сайт многоязычный или есть разделы с одинаковыми товарами, заранее решите правило: уникальность на весь инфоблок или только внутри раздела. Для SEO обычно проще и надёжнее уникальность на весь инфоблок.</p>\n<p>В итоге функция должна не просто транслитерировать строку, а возвращать готовый символьный код: чистый, нижнего регистра, без мусора и без совпадений с уже существующими элементами.</p>",
"readingMinutes": 1
},
{
"slug": "bitrix-api-создание-добавление-торгового-пре",
"title": "Bitrix API. Создание (добавление) торгового предложения товара",
"date": "2017-08-14T17:02:36+00:00",
"author": "DarkRiDDeR",
"categories": [
"Bitrix",
"PHP"
],
"cover": "/assets/illustrations/bitrix-offer-api.svg",
"excerpt": "Хороший пример создания (добавления) торгового предложения в Bitrix через API :",
"contentHtml": "<p>Хороший пример создания (добавления) торгового предложения в Bitrix через API:</p>\n<pre><code>use \\Bitrix\\Main\\Loader;\n\nif (!Loader::includeModule(&#039;iblock&#039;) || !Loader::includeModule(&#039;catalog&#039;))\n{\n\tdie(&#039;Error loading module iblock or catalog&#039;);\n}\n\n$IBlockOffersCatalogId = 3; // ID инфоблока предложений (должен быть торговым каталогом)\n$productName = &quot;Товар&quot;; // наименование товара\n$offerName = &quot;Торговое предложение&quot;; // наименование торгового предложения\n$offerPrice = 100.50; // Цена торгового предложения\n\n$arCatalog = CCatalog::GetByID($IBlockOffersCatalogId);\n\n$IBlockCatalogId = $arCatalog[&#039;PRODUCT_IBLOCK_ID&#039;]; // ID инфоблока товаров\n$SKUPropertyId = $arCatalog[&#039;SKU_PROPERTY_ID&#039;]; // ID свойства в инфоблоке предложений типа &quot;Привязка к товарам (SKU)&quot;\n\n$obElement = new CIBlockElement();\n$arFields = array(\n &#039;NAME&#039; =&gt; $productName,\n &#039;IBLOCK_ID&#039; =&gt; $IBlockCatalogId,\n &#039;ACTIVE&#039; =&gt; &#039;Y&#039;\n);\n$productId = $obElement-&gt;Add($arFields); // добавили товар, получили ID\n\nif ($productId)\n{\n\t$obElement = new CIBlockElement();\n\t// свойства торгвоого предложения\n\t$arOfferProps = array(\n\t\t$SKUPropertyId =&gt; $productId,\n\t);\n\t$arOfferFields = array(\n\t\t&#039;NAME&#039; =&gt; $offerName,\n\t\t&#039;IBLOCK_ID&#039; =&gt; $IBlockOffersCatalogId,\n\t\t&#039;ACTIVE&#039; =&gt; &#039;Y&#039;,\n\t\t&#039;PROPERTY_VALUES&#039; =&gt; $arOfferProps\n\t);\n\n\t$offerId = $obElement-&gt;Add($arOfferFields); // ID торгового предложения\n\n\tif ($offerId)\n\t{\n\t\t// добавляем как товар и указываем цену\n\t\t$catalogProductAddResult =\tCCatalogProduct::Add(array(\n\t\t\t\t&quot;ID&quot; =&gt; $offersId,\n\t\t\t\t&quot;VAT_INCLUDED&quot; =&gt; &quot;Y&quot;, //НДС входит в стоимость\n\t\t\t));\n\t\tif ($catalogProductAddResult &amp;&amp; !CPrice::SetBasePrice($offerId, $offerPrice, &quot;RUB&quot;))\n\t\t\tthrow new Exception(&quot;Ошибка установки цены торгового предложения \\&quot;{$offerId}\\&quot;&quot;);\n\t\telse\n\t\t\tthrow new Exception(&quot;Ошибка добавления параметров торгового предложения \\&quot;{$offerId}\\&quot; в каталог товаров&quot;);\n\t}\n\telse\n\t{\n\t\tthrow new Exception(&quot;Ошибка добавления торгового предложения: &quot; . $obElement-&gt;LAST_ERROR);\n\t}\n}\nelse\n{\n\tthrow new Exception(&quot;Ошибка добавления товара: &quot; . $obElement-&gt;LAST_ERROR);\n}</code></pre>\n<h2>Что проверить после создания предложения</h2>\n<p>После добавления торгового предложения проверьте три связи: привязку к товару, цены и остатки. В Bitrix предложение может успешно создаться, но не появиться в публичной части, если не заполнены обязательные свойства SKU, не сохранён товарный каталог или не обновлены кеши.</p>\n<ul>\n<li>Убедитесь, что модуль <em>iblock</em> подключён перед созданием элемента.</li>\n<li>Если задаются цена и складской остаток, подключите модуль <em>catalog</em>.</li>\n<li>Проверьте свойство связи с товаром, чаще всего это <em>CML2_LINK</em> или настроенное в конкретном каталоге свойство SKU.</li>\n<li>После записи цены и остатков очистите кеш компонента каталога.</li>\n</ul>\n<p>Минимальная последовательность такая: создать элемент торгового предложения в инфоблоке SKU, записать связь с товаром, сохранить параметры товара, установить цену, установить количество. Если хотя бы один шаг пропущен, предложение может быть видно в админке, но не попадёт в публичный каталог.</p>\n<pre><code>CModule::IncludeModule(&#039;iblock&#039;);\nCModule::IncludeModule(&#039;catalog&#039;);\n\n$offerId = $el-&gt;Add($fields);\nCCatalogProduct::Add([\n &#039;ID&#039; =&gt; $offerId,\n &#039;QUANTITY&#039; =&gt; 10,\n]);\nCPrice::SetBasePrice($offerId, 990, &#039;RUB&#039;);</code></pre>\n<p>На боевом проекте обязательно логируйте результат <em>Add</em> и текст ошибки <em>LAST_ERROR</em>. Bitrix часто молча возвращает <em>false</em>, а настоящая причина находится именно там: обязательное поле, неправильный инфоблок, неверный код свойства или недостаточные права.</p>",
"readingMinutes": 2
},
{
"slug": "firefox-увеличение-ожидания-загрузки-timeout-стр",
"title": "Firefox. Увеличение ожидания загрузки (timeout) страницы.",
"date": "2017-08-04T16:24:11+00:00",
"author": "DarkRiDDeR",
"categories": [
"Firefox",
"Настройки"
],
"cover": "/assets/illustrations/firefox-timeout.svg",
"excerpt": "При выполнении каких-либо ресурсоёмких операций на интернет ресурсах, когда время отклика может переваливать за минуты, а то и больше, браузеры прерывают соединение и сообщают, что сервер не доступен. К таким операция, например, можно отнести выгрузку дампа ба",
"contentHtml": "<p>При выполнении каких-либо ресурсоёмких операций на интернет ресурсах, когда время отклика может переваливать за минуты, а то и больше, браузеры прерывают соединение и сообщают, что сервер не доступен.</p>\n<p>К таким операция, например, можно отнести выгрузку дампа базы данных в phpmyadmin, парсинг больших файлов и прочее.</p>\n<p>Максимальное время ожидания (timeout) в браузерах по умолчанию обычно настроено на несколько минут. В браузере Firefox, если я не ошибаюсь, это значение равно 5 минут.</p>\n<p>Нам, конечно, прерывать соединение не хочется, т.к. проводимая операция может прерваться, и, следовательно, возникнуть дальнейшие проблемы из-за неправильного выполнения операции.</p>\n<p>Благо данную настройку в Firefox можно изменить. Для этого:</p>\n<ol>\n<li>Открываем новую вкладку</li>\n<li>В адресной строку пишем «about:config» и нажимаем Enter</li>\n<li>Соглашаемся с сообщением о риске</li>\n<li>В поле поиска вводим параметр «network.http.response.timeout»</li>\n<li>Двойным нажатием открываем окно настройки данного параметра</li>\n<li>Вводим необходимое время в секундах, например, 1800 (пол часа)</li>\n<li>Нажимаем ОК и закрываем вкладку.</li>\n</ol>\n<p>P.S. Иногда данная опция оказывается даже очень полезной. В небезызвестном браузере Chrome данный параметр для настройки отсутствует.</p>\n<h2>Когда лучше не увеличивать timeout</h2>\n<p>Увеличение ожидания помогает при разовых административных операциях, но не должно маскировать медленный сайт. Если обычная страница требует минуты, правильнее вынести задачу в фон: очередь, cron, прогресс-бар и отдельная проверка статуса.</p>\n<p>Важно не путать разные таймауты. Ожидание HTTP-ответа — это одно, долго выполняющийся JavaScript на странице — другое, а таймауты прокси или веб-сервера — третье. Если Firefox ждёт дольше, но nginx, Apache, PHP-FPM или upstream уже оборвал соединение, настройка браузера не поможет.</p>\n<ul>\n<li>Для долгого ответа сервера проверяйте HTTP timeout в браузере и серверные таймауты.</li>\n<li>Для долгого JavaScript смотрите предупреждения о зависшем скрипте и настройки выполнения JS.</li>\n<li>Для импорта больших файлов лучше использовать фоновую задачу и страницу прогресса.</li>\n<li>Для phpMyAdmin и разовых административных операций увеличение ожидания допустимо.</li>\n</ul>\n<p>Если параметра в <em>about:config</em> нет, его можно создать вручную только тогда, когда вы понимаете, какая версия Firefox и какой именно timeout вам нужен. После завершения работы лучше вернуть настройку к обычному значению, чтобы браузер не висел слишком долго на реально недоступных сайтах.</p>\n<p>Итог: увеличивать timeout можно как временный инструмент администратора. Для пользовательского сценария правильнее исправлять серверную часть, потому что долгий белый экран остаётся плохим интерфейсом даже тогда, когда браузер согласен ждать.</p>",
"readingMinutes": 2
},
{
"slug": "ошибка-php-ssl-certificate-error-unable-to-get-local-issuer-certificate",
"title": "Ошибка PHP. SSL certificate error: unable to get local issuer certificate",
"date": "2017-07-12T23:20:53+00:00",
"author": "DarkRiDDeR",
"categories": [
"PHP",
"SSL"
],
"cover": "/assets/illustrations/php-ssl-cacert.svg",
"excerpt": "В PHP при загрузке или обмене данными с другим сервером через защищённое соединение может возникнуть ошибка: SSL certificate error: unable to get local issuer certificate Ошибка означает, что на сервере не установлен SSL сертификат. Чаще всего она наблюдается,",
"contentHtml": "<p>В PHP при загрузке или обмене данными с другим сервером через защищённое соединение может возникнуть ошибка:</p>\n<p><strong>SSL certificate error: unable to get local issuer certificate</strong></p>\n<p>Ошибка означает, что на сервере не установлен SSL сертификат.</p>\n<p>Чаще всего она наблюдается, когда мы ставим локальные платформы (сервера) быстрого развёртывания для веб-разработки, таких как WAMP, XAMPP и других.</p>\n<p>Для решения проблемы нам необходимо установить SSL сертификат.</p>\n<p>Сертификат, например, можно взять отсюда (чтобы самим не генерировать ;):</p>\n<p><a href=\"https://curl.haxx.se/docs/caextract.html\">https://curl.haxx.se/docs/caextract.html</a></p>\n<ol>\n<li>Скачиваем сертификат помещаем в папку C:\\wamp\\cacert.pem<br />\n(у меня установлен WAMP, рекомендую)</li>\n<li>Включаем в  Apache <b>mod_ssl</b>.</li>\n<li>Добавляем расширение (ну или раскомментируем строчку) в файле php.ini:<br />\nextension=php_openssl.dll</li>\n<li>Добавляем в php.ini также параметры:<br />\ncurl.cainfo=&#187;C:/wamp/cacert.pem&#187;<br />\nopenssl.cafile=&#187;C:/wamp/cacert.pem&#187;</li>\n</ol>\n<p>Перезагружаем сервер. После чего ошибка должна быть решена.</p>\n<h2>Финальная проверка настройки</h2>\n<p>После подключения файла сертификатов перезапустите веб-сервер или PHP-FPM и выполните тестовый HTTPS-запрос из того же окружения, где возникала ошибка. Важно проверять не браузер, а именно PHP: CLI и веб-сервер могут использовать разные <em>php.ini</em>.</p>\n<pre><code>php --ini\nphp -i | grep -E \"curl.cainfo|openssl.cafile\"\nphp -r \"var_dump(file_get_contents('https://example.com') !== false);\"</code></pre>\n<p>Для cURL указывается <em>curl.cainfo</em>, для потоков OpenSSL — <em>openssl.cafile</em>. На Windows путь лучше писать полностью и без относительных директорий. Если используется не глобальный <em>php.ini</em>, а отдельная настройка клиента, можно передать CA bundle прямо в cURL:</p>\n<pre><code>curl_setopt($ch, CURLOPT_CAINFO, 'C:\\php\\extras\\ssl\\cacert.pem');</code></pre>\n<p>Не отключайте проверку SSL через <em>CURLOPT_SSL_VERIFYPEER = false</em> как постоянное решение. Это скрывает проблему и делает соединение уязвимым. Такая настройка допустима только для короткой диагностики в локальной среде, и после проверки её нужно вернуть обратно.</p>\n<p>Если CA bundle указан верно, но ошибка осталась, проверьте цепочку сертификатов на стороне удалённого сервера. Иногда браузер открывает сайт, потому что умеет достраивать цепочку, а PHP/cURL получает неполную цепочку и честно падает. В таком случае исправлять нужно сертификаты сервера, а не PHP-код.</p>\n<p>Правильный итог: актуальный <em>cacert.pem</em>, явно указанные <em>curl.cainfo</em> и <em>openssl.cafile</em>, перезапуск PHP и тест именно из того окружения, где работает приложение.</p>",
"readingMinutes": 2
},
{
"slug": "dconf-2017-под-капотом-мусорщика-ди-дмитрий-ол",
"title": "DConf-2017. Под капотом мусорщика D (Дмитрий Ольшанский)",
"date": "2017-06-24T00:48:13+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"GC"
],
"cover": "/assets/illustrations/dconf-gc-cover.svg",
"excerpt": "DConf-2017. Дмитрий Ольшанский. 2017 июня 14 дня. Оригинал (англ.): http://olshansky.me/gc/runtime/dlang/2017/06/14/inside-d-gc.html Перевод: Глеб Куликов Оригинал перевода: https://yadi.sk/i/dftROrt33KLww6 Небольшие правочки: DarkRiDDeR Во время проходившего ",
"contentHtml": "<p><b>DConf-2017. Дмитрий Ольшанский. 2017 июня 14 дня.</b></p>\n<p><b>Оригинал (англ.):</b> <a href=\"http://olshansky.me/gc/runtime/dlang/2017/06/14/inside-d-gc.html\">http://olshansky.me/gc/runtime/dlang/2017/06/14/inside-d-gc.html</a><br />\n<b>Перевод:</b> Глеб Куликов<br />\n<b>Оригинал перевода:</b> <a href=\"https://yadi.sk/i/dftROrt33KLww6\">https://yadi.sk/i/dftROrt33KLww6</a><br />\n<b>Небольшие правочки:</b> DarkRiDDeR</p>\n<p>Во время проходившего на конференции DConf-2017 хакатона, я самоуверенно возглавил группу из двух человек, хакающих Ди&#8217;шный сборщик мусора (GC). После нескольких часов я уже не мог избавиться от навязчивой мысли &#171;эгей, парень, это надо бы переписать!&#187;. Так что я решил отправиться в квест в поисках лучшего мусорщика для Ди, где первым шагом стал бы более быстрый, клаcсический сборщик, отмечающий достижимые объекты (mark-sweep).</p>\n<p>Для пояснения моих мотивов, я намерен описать внутренности современного сборщика, отмечая промахи архитектуры. В конце концов, надо же понять, &#171;куды тыкать&#187;!</p>\n<h2>Пулы, везде пулы!</h2>\n<p>Игнорируя излишние детали, мусорщик — это просто массив объектов пула. Каждый пул — это кусочек отображённой (mmap) памяти + немного метаданных в куче (таблиц маркирующих бит, освобождающих бит ну и так далее). Выделение памяти происходит внутри пула. Если одиночный пул не в состоянии обслужить выделение памяти, заводится новый пул. Размер пула определяется арифметической прогрессией от числа пулов (или 150% от размера выделения, смотря, что окажется больше).</p>\n<p>Важно, что пулы бывают двух типов, для больших и маленьких объектов. В &#171;маленьких&#187; пулах выделяются объекты размером до 2 Кб, всё остальное обслуживается &#171;большими&#187; пулами. На самом деле, маленькие пулы более интересны, так что давайте на них первым делом и взглянем.</p>\n<p><figure><img src=\"/assets/illustrations/gc-small-pools.svg\" alt=\"Пулы малых объектов в GC D\" /><figcaption>Пулы малых объектов в GC D</figcaption></figure></p>\n<p>Прежде всего, размер памяти для любого маленького выделения округляется до подходящего (по степеням двойки) класса размера &#8212; 16, 32, 64, 128, 256, 512, 1024, 2048. Затем для данного размера проверяется глобальный список свободных блоков и, если такового найти не получается, продолжается поиск маленького пула.</p>\n<p>Маленький пул выделит новую страницу памяти и внесёт её в список свободных блоков данного размера. Вот и &#171;вылезла&#187; первая большая ошибка существующей архитектуры: класс размера назначается на страничной основе, поэтому нам требуется таблица (чтобы жизнь мёдом не казалась, называемая таблицей страниц), ставящая в соответствие каждому классу размера соответствующую страницу. Теперь, чтобы найти начало объекта по внутреннему указателю, мы сперва ищем страницу, к которой он принадлежит, затем ищем класс размера и, наконец, накладываем битовую маску. Более того, метаданные представляют собой кучу простых битовых таблиц, которые теперь должны покрывать страницы разного размера, поэтому на каждые 16 байт требуется примерно 7 бит независимо от размера объекта.</p>\n<p>Чем была обусловлена эта архитектура? У меня на этот счёт есть две гипотезы. Первая связана с нежеланием резервировать память для недоиспользуемых пулов (что, на самом деле, не является проблемой, так как для виртуальной памяти работает ленивая фиксация). Вторая — с опасениями получить слишком много пулов, замедлив выделение и полезную маркировку. Последнее больше похоже на причину, так как во время фазы маркировки, мусорщик действительно довольно часто производит линейное сканирование по пулам и бинарный поиск для каждого предполагаемого указателя (!).</p>\n<p>Вот и вторая ошибка: поиск пула за log(P), где P &#8212; число пулов, и соответственно, отметка за N*log(P). Хэш–таблица могла бы малость сэкономить циклы.</p>\n<p>Завершая наш обзор маленького пула, мы также должны взглянуть на выбор классов размеров. Это третья погрешность (не ошибка, скорее, спорный выбор): размеры, следующие степеням двойки, гарантируют нам внутреннюю фрагментацию, доходящую до 50 %. Современные выделители, подобные jemalloc&#8217;у, обычно предлагают ещё один класс, размер которого лежит между степеням двойки. Деление по модулю на константу, не являющуюся степенью двойки, несколько медленнее одиночного битового, но и вполне приемлемо.</p>\n<p><figure><img src=\"/assets/illustrations/gc-large-pool.svg\" alt=\"Пул больших объектов в GC D\" /><figcaption>Пул больших объектов в GC D</figcaption></figure></p>\n<p>Давайте взглянем на пулы больших объектов. Первое, что нужно заметить, так это гранулярность страницы памяти (4 КБ) как для метаданных, так и собственно выделений памяти. Наборы свободных страниц внесены в один список, который линейно сканируется при каждом запросе на выделение памяти. Это четвёртая ошибка, которая, впрочем, не оказывает влияния на производительность выделения больших объектов. Для поиска начала объекта организована отдельная таблица, в которой для каждой страницы хранится индекс начала объекта, к которой он принадлежит.</p>\n<p>Схема разумна до тех пор, пока не коснётся больших (более 100 МБ) выделений памяти. В этом случае, скорее всего, не удастся перераспределить память &#171;по месту&#187;, в результате чего будет выделен новый пул и огромный объём памяти метаданных будет истрачен на всего один объект.</p>\n<h2>Процесс сбора</h2>\n<p>До сих пор мы наблюдали конвейер выделения памяти, освобождение проходит примерно также. Более интересен автоматический возврат памяти, который и является смыслом сборщика мусора. Прежде всего позвольте мне отметить очевидное: во первых, сборщик мусора в Ди является консервативным, то есть, он не знает, является ли что-то указателем или нет. Во-вторых, он поддерживает финализаторы — действия, которые выполняются на объекте, прежде чем возвратить его память в общий пул. Эти два решения сильно ограничивают архитектуру сборщика.</p>\n<p>С точки зрения высокого уровня, процесс сборки, как ни странно, представляет собой целостный 4-фазный процесс: подготовка &#8212; полная маркировка – подчистка &#8212; возврат памяти.</p>\n<p>Стадия подготовки даёт наибольшие основания для сомнений. В сущности, для предотвращения сканирования свободной памяти, на этой стадии следовало бы скопировать биты–признаки свободных блоков в биты–признаки отметок. Однако всю малину портит то, что требуется вычислить полное свободное место, для чего перебрать списки свободных блоков. Это уже пятая(хм?) ошибка, потому что резкое распутывание бессчётного количества указателей — явно последнее, что нужно сделать во время &#171;приостановки мира&#187;. Лучшей архитектурой было бы переворачивание битов–признаков свободных блоков во время выделения / освобождения памяти, тем более, что список свободных блоков поддерживает указатели на пул для каждого объекта, так что искать нужный пул не потребуется.</p>\n<p>Фаза маркировки сводится, на самом деле, к вызову MarkAll, который просто передаёт подходящие участки памяти в маркировочную функцию, заслуживающую более пристального рассмотрения.</p>\n<ul>\n<li>Прежде всего для каждого указателя в области памяти проверяется, попадает ли он в адресный диапазон кучи сборщика (от наименьшего до наибольшего адреса в пуле).</li>\n<li>Затем для поиска правильного пула для данного указателя, применяется &#171;кошмарный&#187; бинарный поиск.</li>\n<li>Независимо от типа пула, производится поиск в таблице страниц текущего пула для того, чтобы определить его класс размера, либо понять, что это большой объект или даже свободная память, которая вовсе не проверяется. Тут есть небольшая оптимизация: в итоге этого поиска становится ясно, с чем имеем дело: с началом большого объекта или продолжением. У нас есть 3 случая: маленький объект, начало большого объекта, либо продолжение большого объекта. Последние случаи идентичны, за исключением всего одного дополни- тельного поиска в таблице.</li>\n<li>Определяется начало объекта, для этого накладывается правильная битовая маска, а также, в случае продолжения большого объекта, просматривается таблица смещений.</li>\n<li>Заметим, что для случая большого объекта имеется специальный бит — признак &#171;без внутреннего указателя&#187;, что позволяет игнорировать внутренние указатели на объект.</li>\n<li>И наконец, проверяются и устанавливаются биты отметок, которые не были установлены раньше, или &#171;noscan-биты&#187; — биты-признаки &#171;не сканировать&#187; — добавляются в стек памяти сканирования.</li>\n</ul>\n<p>Собственно, это всё, что нужно сказать о функции пометки, не считая забавных манипуляций с ограничением на стек (для того, чтобы избежать переполнения стека всё ещё пытаемся использовать выделение стека).</p>\n<p>Упомянутый ранее поиск пула — далеко не единственный недостаток. Имеющее место смешение несканируемой памяти (без указателей) и нормальной памяти в одном и том же пуле, приводит к необходимости дополнительного просмотра битовой таблицы. А ведь если бы мы разделили пулы по классам размеров, можно было бы с лёгкостью избежать просмотра таблицы страниц. Не стоит даже упоминать сомнительную оптимизацию, касающуюся бита–признака &#171;без внутреннего указателя&#187;, которая мало того, что делает код небезопасным (объект может быть сметён в мусор, хотя на него всё ещё ссылаются), но ещё и вводит несколько дополнительных проверок по критическому пути для всех больших объектов, включая возможный просмотр битовой таблицы.</p>\n<p>Уф, весьма запутанно. Однако, стоит иметь в виду, что фаза отметки — это воистину сердце любого сборщика.</p>\n<p>Перейдём теперь к третье фазе — очистки. Ирония в том, что вопреки ожиданиям, в текущем Ди&#8217;шном сборщике очиститель вовсе не очищает списки свободных блоков. Всё, что его волнует, так это вызвать финализаторы, если таковые имеются, и установить биты–признаки &#171;свободен&#187; и прочие таблицы. Заметьте, что это требует поиска битов–отметок и соответственно, линейного прохода по памяти каждого пула.</p>\n<p>Последняя стадия — возврат освобождённой памяти. На самом деле, на этой стадии перестраиваются списки свободных блоков. и опять это линейный проход по (всем) пулам, но только по маленьким пулам. И опять нужен просмотр таблицы страниц для каждой страницы в пуле. И только для того, чтобы определить её размер. . . определённо, хочется плакать! Основной вопрос без ответа &#8212; зачем? Зачем лишний проход? Я долго пытался представить разумную причину, но так и не смог.</p>\n<p>Возможно, слабое, но вполне возможное объяснение — &#171;для простоты&#187;. По моему мнению, это последняя большая ошибка.</p>\n<h2>Чего же там нет?</h2>\n<p>До сих пор я критиковал то, что бросается в глаза. Пора перейти к тому, что попросту отсутствует.</p>\n<p>Кэш нитей — одно из таких больших упущений. Правда, следует помнить, что Ди&#8217;шный мусорщик родом из 2000–ых, так что это не удивительно. В любом современном распределителе есть хоть какая-то форма кэша нитей, некоторые даже пытаются поддерживать кэш для каждого процессора. Кэш работает для каждого потока, выделяя память скопом и придерживая сделанные выделения памяти на будущее. Такой подход уменьшает стоимость просмотра разделяемых структур данных кучи. Точнее говоря, мелкозернистые блокировки имеются, но не на уровне всего пула.</p>\n<p>Другой пример современного подхода, безусловно ожидаемого в любом мусорщике, это параллельная отметка. Весьма популярны параллельные (или квази–паралелльные) сборщики, в которых отметка и гораздо более редкая очистка и компактификация (операция, которая преобразует топологические пространства в компактные) производятся во время выполнения нитей приложения.</p>\n<h2>Заключительные размышления</h2>\n<p>Это сообщение получилось гораздо длиннее и более подробное, чем бы мне хотелось. Тем не менее, сохраняется его главный посыл: Ди&#8217;шный мусорщик медленный не из-за каких–то фундаментальных ограничений, а просто потому, что почти половина решений в его реализации откровенно плоха. Действуя в том же духе легко можно было бы построить медленный и точный &#171;поколенческий&#187; мусорщик, просто из-за отсутствия хороших методик реализации.</p>\n<p>Подводя итог, что пытается изменить моя первая попытка:</p>\n<ol>\n<li>Разделить маленькие пулы по классам размеров.</li>\n<li>Использовать поиск в пуле с эффективностью O(1).</li>\n<li>Чтобы избавиться от внутренней фрагментации, попробовать использовать больше классов размеров, в том числе, не кратных степеням двойки (а-ля jmalloc).</li>\n<li>Упростить маркировку, разделив все пулы по атрибуту &#171;не сканировать&#187;.</li>\n<li>Ввести третий класс пулов для массивных выделений памяти (более 16 МБ), предназначенных исключительно для одиночных объектов.</li>\n<li>Пулы больших объектов требуют более хитрой работы. Возможно, деревья с ключами на основе длины блока придутся кстати (jemalloc использует красно–чёрные деревья).</li>\n<li>Надо бы убрать атрибут „без внутреннего указателя“. В текущей реализации он предназначен для борьбы с ложными указателями, что характерно для консервативного мусорщика. Однако это принципиально небезопасно и цена слишком высока. В конце концов, это полностью бессмысленно в 64-битных системах, где собственно и живут ваши серверы.</li>\n<li>Не нужно &#171;юродство&#187; с многофазной сборкой в цикле „отметка – очистка“. Он должен отмечать и очищать, только и всего.</li>\n<li>Добиться мелкогранулированной блокировки с самого начала. Я не вижу проблем с блокировками на уровне пула.</li>\n</ol>\n<p>Вторая итерация моих усилий будет сосредоточена на более вкусных вещах: кэш потока, параллельная маркировка и одновременная маркировка, использующая трюк с разветвлением (fork).</p>\n<p>На третьей итерации (если доживём), ожидаем совершенно новый дизайн, вдохновлённый коллегой immix1 — сборщик, основанный на отметки областей.</p>\n<p>На этой оптимистичной ноте я завершаю озвучивание моих планов. Предупреждаю, что собираюсь начать реализацию в Линукс–специфике, медленно доводя её до совместимости с POSIX. Поддержка Windows — отдалённая возможность.</p>\n<p>Дмитрий Ольшанский, исследователь и инженер–программист. Биографию см. в <a href=\"http://olshansky.me/about.html\">http://olshansky.me/about.html</a></p>\n<h2>Практический вывод</h2>\n<p>Статья важна не только для разработчиков компилятора. Она показывает, почему аллокатор и сборщик мусора нельзя оценивать одной фразой «GC медленный». Нужно смотреть на классы размеров, поиск пулов, стоимость маркировки и то, сколько работы выполняется во время остановки мира.</p>\n<p>Для прикладного D-кода вывод такой: держите горячие циклы предсказуемыми, не создавайте лишние временные объекты в tight-loop и измеряйте паузы. Если участок действительно критичен, D позволяет локально управлять GC и заменить поток мелких аллокаций заранее подготовленными буферами.</p>\n<pre><code>import core.memory : GC;\n\nGC.disable();\nscope(exit) GC.enable();\n\n// горячий участок без лишних временных объектов</code></pre>\n<p>Отключать GC нужно аккуратно. Это не волшебная оптимизация, а договор: пока сборщик выключен, программа не должна бесконтрольно накапливать мусор. Поэтому такой приём подходит для короткого измеримого участка, а не для всего приложения.</p>\n<ul>\n<li>Сначала измеряем: время, количество аллокаций, паузы.</li>\n<li>Потом убираем лишние временные объекты и повторные выделения.</li>\n<li>Только затем используем ручное управление GC, если оно действительно помогает.</li>\n<li>После оптимизации оставляем комментарий, почему здесь нельзя писать «просто как обычно».</li>\n</ul>\n<p>Главный смысл доклада в том, что производительность памяти — это архитектура. Если в программе много мелких объектов, непредсказуемых ссылок и больших пауз, проблема редко решается одной настройкой. Нужно менять модель данных, жизненный цикл объектов и место, где выделяется память.</p>",
"readingMinutes": 11
},
{
"slug": "пишем-аналог-функции-php-preg_match_all-на-языке-прог",
"title": "Пишем аналог функции PHP preg_match_all на языке программирования D",
"date": "2017-06-17T18:53:17+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Регулярные выражения"
],
"cover": "/assets/illustrations/preg-match-all-d.svg",
"excerpt": "В PHP есть очень удобная функция для глобального поиска шаблона регулярного выражения в строке preg_match_all . Давайте напишем аналогичный класс статических методов для реализации этой функции с разными флагами на языке программирования D. Описание функции: i",
"contentHtml": "<p>В PHP есть очень удобная функция для глобального поиска шаблона регулярного выражения в строке <em>preg_match_all</em>. Давайте напишем аналогичный класс статических методов для реализации этой функции с разными флагами на языке программирования D.</p>\n<p>Описание функции:</p>\n<p>``` int preg_match_all ( string $pattern , string $subject [, array &amp;amp;$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )</p>\n<pre><code>\nФункция ищет в строке *subject* все совпадения с шаблоном pattern и помещает результат в массив *matches* в порядке, определяемом комбинацией флагов *flags*.\n После нахождения первого соответствия последующие поиски будут осуществляться не с начала строки, а от конца последнего найденного вхождения.\n\nДанная функцию очень удобна и чаще всего она применяется с третьим параметром, чтобы обработать полученный массив совпадений шаблона. В *D* подобной регулярной функции нет. Мне вообще не сильно нравятся, как реализованы регулярные выражения в *D*. Ну да ладно.\n\nПолную документацию по функции можете посмотреть здесь [http://php.net/manual/ru/function.preg-match-all.php](http://php.net/manual/ru/function.preg-match-all.php).\n\nВозможные флаги функции *preg_match_all*:\n\n- *PREG_PATTERN_ORDER* – упорядочивает результаты так, что элемент *$matches[0]* содержит массив полных вхождений шаблона, элемент *$matches[1]* содержит массив вхождений первой подмаски, и так далее;\n- *PREG_SET_ORDER* – упорядочивает результаты так, что элемент *$matches[0]* содержит первый набор вхождений, элемент *$matches[1]* содержит второй набор вхождений, и так далее;\n- *PREG_OFFSET_CAPTURE* – в случае, если этот флаг указан, для каждой найденной подстроки будет указана ее позиция в исходной строке. Необходимо помнить, что этот флаг меняет формат возвращаемого массива matches в массив, каждый элемент которого содержит массив, содержащий в индексе с номером *0* найденную подстроку, а смещение этой подстроки в параметре *subject* — в индексе *1*.\n\nНе будем заморачиваться с реализацией через передачу константы, как в *PHP,* или через шаблоны в *D*. Создадим для каждого флага *PHP*данной функции аналогичные статические методы. Параметр *offset* оставим за бортом. Класс давайте назовём *PReg*. Вырисовывается следующая структура:\n</code></pre>\n<p>class PReg { public static:</p>\n<p>… matchAllPatternOrder (...) {...} … matchAllSetOrder (...){...} … matchAllOffsetCapture (…) {...} }</p>\n<pre><code>\nКак параметры будем передавать строку для поиска (тип *string*), регулярное выражение (тип *???*) и переменную для вывода количества совпадений шаблона регулярного выражения (тип *out int*). Тип регулярного выражения можно посмотреть, чтобы не капаться в модуле, применив такую хитрость:\n</code></pre>\n<p>writeln(typeof(regex(<code>\\d+</code>)).stringof);</p>\n<pre><code>\nТакже как аргумент вы без проблем сможете передавать compile-time регулярное выражение (*ctRegex*). Для данного типа создадим алиас типа, чтобы поудобнее было.\n Функция *matchAllPatternOrde*r и *matchAllSetOrde*r будет возвращать двумерный массив строк, для них тоже создадим алиас типов (мне так кажется, что путаницы меньше). *MatchAllOffsetCapture* пока оставим на закуску.\n\nВ итоге у нас получается:\n</code></pre>\n<p>class PReg { private static alias typeRegex = Regex!char;</p>\n<p>public static: alias typePatternOrder = string[]; alias typeSetOrder = string[];</p>\n<p>typePatternOrder[] matchAllPatternOrder (string subject, typeRegex obRegex, out int count) {..} typeSetOrder[] matchAllSetOrder (string subject, typeRegex obRegex, out int count){...} … matchAllOffsetCapture (string subject, typeRegex obRegex, out int count)\t{...} }</p>\n<pre><code>\nПривожу пример реализованной функции *matchAllPatternOrder* (основные моменты пояснены ниже):\n</code></pre>\n<p>typePatternOrder[] matchAllPatternOrder (string subject, typeRegex obRegex, out int count) { typePatternOrder[] matches; auto stdMatches = matchAll(subject, obRegex);</p>\n<p>while (!stdMatches.empty) { matches.length++; matches[0] ~= stdMatches.front.hit;</p>\n<p>for (int i = 1; i &lt; stdMatches.front.length; i++) { matches.length++; matches[count+1] ~= stdMatches.front[i]; } stdMatches.popFront(); count++; } return matches; }</p>\n<pre><code>- Функция *matchAll* осуществляет глобальный поиск шаблона регулярного выражения obRegex.\n- Цикл *while* файл перебирает найденные совпадения пока они не закончатся (проверка с помомощью *stdMatches.empty*);\n- *stdMatches.front* хранит строку и найденные подстроки;\n- *stdMatches.front.hit* возвращает всё строку входящую в найденный шаблон;\n- *stdMatches.front[i]* возвращает найденные части по позиции маски;\n- *stdMatches.popFront(*) переход к следующей найденной строке.\n\nРеализация функции *matchAllSetOrder* по сути ничем сильно не отличается кроме как позициями найденных строк в массиве (она даже проще). Приведу лишь реализованный вариант:\n</code></pre>\n<p>typeSetOrder[] matchAllSetOrder (string subject, typeRegex obRegex, out int count) { typeSetOrder[] matches; auto stdMatches = matchAll(subject, obRegex);</p>\n<p>while (!stdMatches.empty) { for (int i = 0; i &lt; stdMatches.front.length; i++) { matches.length++; matches[count] ~= stdMatches.front[i]; } stdMatches.popFront(); count++; } return matches; }</p>\n<pre><code>\nРеализация функции *matchAllOffsetCapture* имеет свои особенности. Найденные данные функции будем хранить в двумерном массиве картежа типов *Tuple!(string, «text», int, «position»)*, по которому можно будет получить позицию и найденую строку. Реализация:\n</code></pre>\n<p>alias typeOffsetCapture = Tuple!(string, &quot;text&quot;, int, &quot;position&quot;)[];</p>\n<p>typeOffsetCapture[] matchAllOffsetCapture (string subject, typeRegex obRegex, out int count) { typeOffsetCapture[] matches;</p>\n<p>auto stdMatches = matchAll(subject, obRegex); matches.length = stdMatches.front.length;</p>\n<p>while (!stdMatches.empty) { Tuple!(string, &quot;text&quot;, int, &quot;position&quot;) match; match.text = stdMatches.front.hit; match.position = cast(int)(match.text.ptr - subject.ptr); matches[0].length = count+1; matches[0][count] = match; for (int i = 1; i &lt; stdMatches.front.length; i++) { matches[i].length = matches[0].length; match.text = stdMatches.front[i]; match.position = cast(int)(stdMatches.front[i].ptr - subject.ptr); matches[i][count] = match; } stdMatches.popFront(); count++; } return matches; }</p>\n<pre><code>\nЕдинственное, что здесь важно отметить, как определяются позиции, а именно:\n</code></pre>\n<p>match.position = cast(int)(match.text.ptr - subject.ptr);</p>\n<pre><code>\nПозиция определяется через разность указателей позиций элементов массива. Также здесь выполняется приведение типа к *int*, так как многие процессоры уже использует 64-х битное представление, и указатели будут иметь тип*long*.\n\nПолная реализация модуля с тестами представлена ниже:\n</code></pre>\n<p>module preg;</p>\n<p>import std.string: format; import std.typecons; import std.regex: Regex, regex, ctRegex, matchFirst, matchAll;</p>\n<p>// analog of preg regex in php</p>\n<p>class PReg { private static alias typeRegex = Regex!char;</p>\n<p>public static:</p>\n<p>alias typePatternOrder = string[]; alias typeSetOrder = string[]; alias typeOffsetCapture = Tuple!(string, &quot;text&quot;, int, &quot;position&quot;)[];</p>\n<p>typePatternOrder[] matchAllPatternOrder (string subject, typeRegex obRegex, out int count) { typePatternOrder[] matches; auto stdMatches = matchAll(subject, obRegex);</p>\n<p>while (!stdMatches.empty) { matches.length++; matches[0] ~= stdMatches.front.hit;</p>\n<p>for (int i = 1; i &lt; stdMatches.front.length; i++) { matches.length++; matches[count+1] ~= stdMatches.front[i]; } stdMatches.popFront(); count++; } return matches; }</p>\n<p>typeSetOrder[] matchAllSetOrder (string subject, typeRegex obRegex, out int count) { typeSetOrder[] matches;</p>\n<p>auto stdMatches = matchAll(subject, obRegex);</p>\n<p>while (!stdMatches.empty) { for (int i = 0; i &lt; stdMatches.front.length; i++) { matches.length++; matches[count] ~= stdMatches.front[i]; } stdMatches.popFront(); count++; } return matches; }</p>\n<p>typeOffsetCapture[] matchAllOffsetCapture (string subject, typeRegex obRegex, out int count) { typeOffsetCapture[] matches;</p>\n<p>auto stdMatches = matchAll(subject, obRegex); matches.length = stdMatches.front.length;</p>\n<p>while (!stdMatches.empty) { Tuple!(string, &quot;text&quot;, int, &quot;position&quot;) match; match.text = stdMatches.front.hit; match.position = cast(int)(match.text.ptr - subject.ptr); matches[0].length = count+1; matches[0][count] = match; for (int i = 1; i &lt; stdMatches.front.length; i++) { matches[i].length = matches[0].length; match.text = stdMatches.front[i]; match.position = cast(int)(stdMatches.front[i].ptr - subject.ptr); matches[i][count] = match; } stdMatches.popFront(); count++; } return matches; } }</p>\n<p>unittest { import std.stdio; writeln(&quot;test PReg.matchAll&quot;);</p>\n<p>int count;</p>\n<p>auto matches1 = PReg.matchAllPatternOrder(<code>one two</code>, regex(<code>(\\w)(\\w)\\w+</code>), count); assert(matches1[0][0] == <code>one</code>); assert(matches1[0][1] == <code>two</code>); assert(matches1[1][0] == <code>o</code>); assert(matches1[1][1] == <code>n</code>); assert(matches1[2][0] == <code>t</code>); assert(matches1[2][1] == <code>w</code>); assert(count == 2);</p>\n<p>auto matches2 = PReg.matchAllSetOrder(<code>one two</code>, regex(<code>(\\w)(\\w)\\w+</code>), count); assert(matches2[0][0] == <code>one</code>); assert(matches2[0][1] == <code>o</code>); assert(matches2[0][2] == <code>n</code>); assert(matches2[1][0] == <code>two</code>); assert(matches2[1][1] == <code>t</code>); assert(matches2[1][2] == <code>w</code>); assert(count == 2);</p>\n<p>auto matches3 = PReg.matchAllOffsetCapture(<code>one two</code>, ctRegex!(<code>(\\w)(\\w)\\w+</code>), count); assert(matches3[0][0].text == &quot;one&quot;); assert(matches3[0][0].position == 0); assert(matches3[0][1].text == &quot;two&quot;); assert(matches3[0][1].position == 4);</p>\n<p>assert(matches3[1][0].text == &quot;o&quot;); assert(matches3[1][0].position == 0); assert(matches3[1][1].text == &quot;t&quot;); assert(matches3[1][1].position == 4);</p>\n<p>assert(matches3[2][0].text == &quot;n&quot;); assert(matches3[2][0].position == 1); assert(matches3[2][1].text == &quot;w&quot;); assert(matches3[2][1].position == 5); assert(count == 2); }</p>\n<pre><code>\nНадеюсь, что из этой статьи вы подчеркнули для себя что-то полезное и интересное. Всем спасибо!\n\n## Как довести класс до рабочего состояния\n\nПосле реализации флагов обязательно добавьте тесты на три случая: нет совпадений, одно совпадение с группами и несколько совпадений с позициями. Именно на позициях чаще всего появляются ошибки, потому что PHP возвращает смещения в исходной строке, а не в подстроке после очередного поиска.\n</code></pre>\n<p>assert(matchAll(r&quot;(w+)=(d+)&quot;, &quot;a=1 b=22&quot;).length == 2); assert(matchAll(r&quot;z+&quot;, &quot;abc&quot;).empty);</p>\n<pre><code>\nВ D стоит явно решить, какой формат результата нужен. PHP поддерживает несколько режимов: *PREG_PATTERN_ORDER*, *PREG_SET_ORDER* и вариант с *PREG_OFFSET_CAPTURE*. Если пытаться сделать всё сразу, код быстро станет запутанным. Лучше сначала реализовать простой режим, затем режим группировки по совпадениям, и только после этого добавить позиции.\n</code></pre>\n<p>struct MatchPart { string text; ptrdiff_t offset; }</p>\n<p>alias MatchSet = MatchPart[][];</p>\n<pre><code>\nОтдельно проверьте Unicode. Если шаблон и строка содержат кириллицу, важно понимать, что именно возвращается в offset: позиция в байтах или позиция в символах. PHP для *PREG_OFFSET_CAPTURE* возвращает байтовое смещение. Если в D вы хотите повторить поведение PHP, нужно документировать именно байтовый offset, иначе результаты будут отличаться.\n\nФинальный класс должен иметь маленькую поверхность API: метод без offset, метод с offset и понятный enum для порядка результата. Тогда аналог *preg_match_all* будет не просто копией PHP-функции, а удобным D-инструментом, который предсказуемо работает в тестах.</code></pre>",
"readingMinutes": 7
},
{
"slug": "новый-движок-ctfe",
"title": "Dlang. Новый движок CTFE",
"date": "2017-05-26T11:51:22+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Compile-time"
],
"cover": "/assets/illustrations/ctfe-engine.svg",
"excerpt": "В течение последних 9 месяцев велась работа над проектом под названием NewCTFE, в котором переписываются методы выполнения функций времени компиляции (СTFE) . СTFE считается одной из технологий способных изменить D. Как следует из названия, CTFE позволяет комп",
"contentHtml": "<p>В течение последних 9 месяцев велась работа над проектом под названием NewCTFE, в котором переписываются методы выполнения<a href=\"https://dlang.org/spec/function.html#interpretation\">функций времени компиляции (СTFE)</a>. СTFE считается одной из технологий способных изменить D.</p>\n<p>Как следует из названия, CTFE позволяет компилятору выполнять некоторые функции, когда он компилирует исходный код, в котором реализованы функции. Пока все аргументы функции доступны во время компиляции, а функция чиста (не имеет побочных эффектов), тогда функция квалифицируется как CTFE, и компилятор заменяет вызов функции результатом.</p>\n<p>Поскольку это неотъемлемая часть языка, чистые функции могут быть вычислены везде, где может находиться константа времени компиляции. Простой пример можно найти в стандартном модуле std.uri, где CTFE используется для вычисления таблицы поиска. Это выглядит так:</p>\n<pre><code>private immutable ubyte[128] uri_flags = // indexed by character\n({\nubyte[128] uflags;\n// Compile time initialize\nuflags[&#039;#&#039;] |= URI_Hash;\nforeach (c; &#039;A&#039; .. &#039;Z&#039; + 1)\n{\nuflags[c] |= URI_Alpha;\nuflags[c + 0x20] |= URI_Alpha; // lowercase letters\n}\nforeach (c; &#039;0&#039; .. &#039;9&#039; + 1) uflags[c] |= URI_Digit;\nforeach (c; &quot;;/?:@&amp;amp;=+$,&quot;) uflags[c] |= URI_Reserved;\nforeach (c; &quot;-_.!~*&#039;()&quot;) uflags[c] |= URI_Mark;\nreturn uflags;\n})();</code></pre>\n<p>Вместо заполнения таблицы магическими значениями используется простой экспрессивный литерал функции. Это намного проще понять и отладить, чем некоторые непрозрачные статические массивы. ({ запускает функцию-литерал, а }) закрывает ее. () в конце говорит компилятору немедленно вызвать этот литерал, чтобы uri_flags стал результатом литерала.</p>\n<p>Функции выполняются только во время компиляции, если они необходимы. Uri_flags в приведенном выше фрагменте объявляется в области видимости модуля. Когда переменная области видимости модуля инициализируется таким образом, инициализатор должен быть доступен во время компиляции. В этом случае, поскольку инициализатор является функциональным литералом, будет предпринята попытка выполнить CTFE. Этот конкретный литерал не имеет аргументов и является чистым, поэтому попытка выполнена успешно.</p>\n<p>Более подробное обсуждение CTFE смотрите в статье H. S. Teoh <a href=\"https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time\">D Wiki</a>.</p>\n<p>Конечно, подобный метод может быть применен и к более сложным проблемам. Например, std.regex можно использовать со специализированным автоматом для выполнения регулярного выражения во время компиляции с использованием CTFE. Однако, как только std.regex используется с CTFE для нетривиальных паттернов, то время компиляции может стать чрезвычайно длительным (в D все, что занимает больше секунды при компиляции — избыточность :)). В конце концов, по мере усложнения паттернов, у компилятора появится нехватка памяти, и, возможно, произойдёт крах всей системы.</p>\n<p>Причина этого может крыться в текущей архитектуре интерпретатора CTFE. Это интерпретатор AST (абстрактного синтаксического дерева) — это означает, что он интерпретирует AST во время его обхода. Чтобы представить результат интерпретируемых выражений, он использует классы узлов DMD в AST. Это означает, что на каждое вновь встреченное выражение будет выделено один или несколько узлов AST. В ограниченном цикле интерпретатор может легко создать более 100.000.000 узлов и использовать несколько гигабайт оперативной памяти. Это может быстро израсходовать память.</p>\n<p>В <a href=\"https://issues.dlang.org/show_bug.cgi?id=12844\">issue 12844</a> имеет место проблема, что std.regex занимает более 16 ГБ ОЗУ для одного шаблона. Также есть <a href=\"https://issues.dlang.org/show_bug.cgi?id=6498\">issue 6498</a> , которая показывает, что выполняется простой от 0 до 10.000.000 узлов во время выполнения CTFE и что приводит к критичной нехватки памяти.</p>\n<p>Простое освобождение узлов не устраняет проблему, так мы точно не знаем, какие узлы необходимо освободить, и что cделает весь компилятор очень медленным из-за сборщика мусора. К счастью, есть еще один подход, который не выделяет память для каждого вновь встреченного выражения. Он включает в себя компиляцию функции в виртуальную ISA (архитектуру набора инструкций). Эта виртуальная ISA, также известная как байт-код, затем передается выделенному интерпретатору для этой ISA (в случае, когда виртуальный ISA совпадает с ISA хоста, мы называем его JIT (Just in Time) интерпретатором).</p>\n<p>Проект NewCTFE занимается реализацией такого интерпретатора байт-кода. Написание фактического интерпретатора (эмулятора CPU для виртуального CPU/ISA) достаточно просто. Однако компиляция кода для виртуального ISA выполняется в точности так же, как и при компиляции его в реальном ISA (хотя виртуальная ISA имеет дополнительное преимущество, которое может быть расширено для индивидуальных потребностей, но это затруднит выполнение JIT позже). Вот почему потребовался всего месяц, чтобы пучить первые простые примеры, работающие на новом движке CTFE, и почему немного более сложные из них все еще не работают даже после 9 месяцев разработки. В конце статьи вы найдете примерный график выполненной к настоящему времени работы (см. оригинал).</p>\n<p>Я буду выступать с презентацией на DConf 2017, где я расскажу о своем опыте внедрения движка и объясню некоторые технические детали, особенно относительно компромиссов и архитектурных решений, которые я применил. Текущая оценка состоит в том, что версия 1.0 не будет реализована к тому времени, но я буду заниматься разработкой, пока не закончу данный проект.</p>\n<p>Те, кто хочет отслеживать разработку, могут сделать это на форуме D. В будущем я планирую написать еще одну статью о некоторых технических деталях реализации. К тому времени, я надеюсь, что следующий список поможет пролить свет на то, как много работы по реализации NewCTFE.</p>\n<p><em>Данная статья является переводом статьи Штефана Коха, который является разработчиком sqlite-d, встроенного пакета в D для работы с sqlite, также он внес свой вклад в такие проекты, как SDC (Stupid D Compiler) и vibe.d. Он также был ответственен за 10% -ное повышение производительности в текущей реализации CTFE в D и в настоящее время пишет новый движок CTFE.</em></p>\n<p>Оригинал статьи смотрите по ссылке <a href=\"https://dlang.org/blog/2017/04/10/the-new-ctfe-engine/\">The D Blog/The New CTFE Engine</a>.</p>\n<h2>Как применять выводы на практике</h2>\n<p>CTFE полезен там, где результат можно посчитать один раз во время компиляции: таблицы констант, парсинг небольших DSL, генерация однотипного кода, предварительная подготовка строк и проверка инвариантов. Но не стоит превращать компиляцию в полноценный runtime. Чем проще входные данные и чем меньше побочных эффектов, тем легче будет сопровождать проект.</p>\n<pre><code>enum table = buildLookupTable();\n\nstatic assert(table.length == 256);</code></pre>\n<p>Хороший кандидат для CTFE — функция, которая зависит только от литералов, типов и конфигурации сборки. Плохой кандидат — код, которому нужен ввод-вывод, сеть, текущее время, состояние окружения или большая внешняя база данных. Такой код лучше оставить в runtime или вынести в отдельный генератор, который запускается до сборки.</p>\n<p>Для контроля качества CTFE-кода полезно держать рядом <em>static assert</em>. Он сразу показывает, что вычисление действительно произошло на этапе компиляции и вернуло ожидаемый результат. Если compile-time функция стала слишком сложной, её стоит разбить на маленькие чистые функции: так компилятору проще, и человеку проще понять, что происходит.</p>\n<p>Практический вывод такой: CTFE в D — мощный инструмент, но сила его не в магии, а в переносе предсказуемых вычислений из runtime в compile-time. Используем его для констант, генерации и проверок. Не используем его как замену обычной программе.</p>",
"readingMinutes": 6
},
{
"slug": "особенности-vibe-d",
"title": "Особенности Vibe.d",
"date": "2017-05-23T17:55:03+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"Vibe.d"
],
"cover": "/assets/illustrations/vibe-cover.svg",
"excerpt": "Основная идея vibe.d заключалась в том, чтобы использовать быструю и ресурсоемкую асинхронную модель ввода-вывода (AIO) и сделать ее удобной в использовании. Некоторые другие программные платформы, такие как node.js, напрямую отображают интерфейс AIO с использ",
"contentHtml": "<h2>Простота</h2>\n<h3>Модель программирования псевдоблокировки на основе волокн</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-async.svg\" alt=\"Асинхронная модель vibe.d\" /><figcaption>Асинхронная модель vibe.d</figcaption></figure>Основная идея vibe.d заключалась в том, чтобы использовать быструю и ресурсоемкую асинхронную модель ввода-вывода (AIO) и сделать ее удобной в использовании. Некоторые другие программные платформы, такие как node.js, напрямую отображают интерфейс AIO с использованием событий, используя функции обратного вызова. Хотя это приемлемый подход, он имеет ряд недостатков.</p>\n<p>Прежде всего, в node.js часто становится утомительным и запутанным писать последовательности кода (например, выполнять несколько последовательных запросов к базе данных). На каждом шаге будет представлен новый callback-вызов (обратный) с новой областью, callback-вызовы ошибок часто приходится обрабатывать отдельно. Это часто является причиной того, что возникает соблазн выполнить слабую обработку ошибок.</p>\n<p>Другим следствием асинхронных callback-вызовов является отсутствие значимого стека вызовов. Мало того, что это может усложнить отладку, но такие функции, как исключения, не могут эффективно использоваться в такой среде.</p>\n<p>Подход vibe.d заключается в использовании асинхронного ввода-вывода под оболочкой, но в то же время кажется, что все операции были синхронными и блокирующими, как и обычные операции ввода-вывода.</p>\n<p>Это делает возможным поддержку для так называемых волокон (также часто называемых совместными подпрограммами). Волокна ведут себя как потоки, просто они фактически работают в одном потоке. Как только бегущее волокно вызывает специальную функцию <i>yield()</i>, она возвращает управление функции, которая запустила волокно. Затем волокно может быть возобновлено в точно таком же положении и с тем же состоянием, которое было у него при вызове <i>yield()</i>. Таким образом, волокна могут мультиплексироваться вместе, выполняться квазипараллельно и максимально использовать каждую емкость нитей.</p>\n<p>Все это обычно скрыто за функциями API vibe.d, так что все кажется простой работой с обычными потоками и блокировкой операций. Все блокирующие функции, такие как <i>sleep()</i> или <i>read()</i>, останавливают работу, когда им необходимо ждать, и возобновляют её при возникновении события.</p>\n<p>Используя эти инструменты, основанные на событиях, характер AIO полностью скрыт от пользователя библиотеки. Он также прекрасно сочетается со встроенной поддержкой многопоточности. Все параллельные операции выполняются с использованием так называемых задач. Каждая задача выполняется внутри одного волокна и мультиплексируется вместе с другими задачами, которые выполняются одновременно. Пользователю инструментария обычно нет видимой разницы между задачей и потоком.</p>\n<p>Сказав все это, vibe.d может также использоваться без волокон и без цикла обработки событий, если это необходимо, наподобие стандартной блокирующей модели ввода-вывода.</p>\n<h3>Компактное API</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-api.svg\" alt=\"Короткий API vibe.d\" /><figcaption>Короткий API vibe.d</figcaption></figure>Акцент делается на короткий и простой API. Общие задачи выполняются как можно короче и на высоком уровне, но при этом, при необходимости, допускается низкоуровневый контроль. Используя некоторые из продвинутых функций D, типичные программы на основе vibe.d становятся лаконичными, что обычно могут предложить только скриптовые языки, такие как Ruby.</p>\n<h3>Нулевое время простоя при изменениях</h3>\n<p>Встроенный балансировщик нагрузки, способный динамически создавать, запускать и тестировать новые процессы после того, как код был изменен, прежде чем переключать их в главный поток. Это позволяет осуществлять «бесшовные» и с низким уровнем риска изменения в работающих системах.</p>\n<h3>Обработка ошибок на основе исключений</h3>\n<p>Обычно обработка исключений ограничивается локальными обработчиками ошибок в средах, основанных на событиях, так как невозможно поместить последовательность событий в блок try-catch, потому что каждая операция создает новую область, когда задействованы обратные вызовы. С другой стороны, vibe.d с его волоконно-ориентированным подходом обладает полной поддержкой обработки исключений.</p>\n<p>Волокно ведет себя почти так же, как поток относительно стека: один согласованный стек вызовов существует во всех событиях, которые обрабатываются последовательно. Таким образом, становится возможно естественным образом использовать обработку исключений, и особенно в среде веб-служб, где исключения являются идеальной формой обработки ошибок.</p>\n<p>Исключения имеют чрезвычайно полезную функцию, которую едва можно игнорировать. Таким образом, становится гораздо труднее создать трудноотлавливаемые ошибки и дыры в безопасности, непреднамеренно игнорируя условия ошибки. Любые неперехваченные исключения автоматически генерируют страницу ошибок и записывают всю полезную информацию в случае HTTP-сервисов.</p>\n<h2>Преимущества</h2>\n<h3>Полностью интегрированный веб-фреймворк</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-web.svg\" alt=\"Веб-стек vibe.d\" /><figcaption>Веб-стек vibe.d</figcaption></figure>Библиотека vibe.d содержит полный набор инструментов, необходимых для разработки веб-сайтов и веб-сервисов. В дополнение к HTTP 1.0/1.1 серверу уже интегрированы статические файлы, эффективная система шаблонов, веб-сокеты, сеансы и другие функции. К числу таких функций относятся фильтр markdown, драйверы MongoDB и Redis, криптография, поддержка JSON и BSON и простой SMTP-клиент.</p>\n<p>Есть также средства для автоматической генерации JSON / REST или HTML-интерфейсов на основе форм из классов D, что устраняет много работы и потенциальных ошибок из-за избежания стандартного кода. Генератор интерфейса REST поддерживает генерацию как сервера, так и клиентской части, и как таковой может использоваться как удобный механизм RPC.</p>\n<h3>Встроенные драйверы баз данных</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-db.svg\" alt=\"Драйверы баз данных vibe.d\" /><figcaption>Драйверы баз данных vibe.d</figcaption></figure>Основная библиотека содержит встроенную поддержку баз данных MongoDB и Redis. Эти драйверы по умолчанию обеспечивают быстрое и гибкое хранение данных. Дополнительные драйверы базы данных доступны в реестре пакетов DUB, например, совместимый с vibe.d драйвер MySQL.</p>\n<p>Совместимость существующих драйверов легко сделать благодаря блокирующей природе API vibe.d. Единственное, что нужно сделать в большинстве случаев – заменить вызовы сокетов (<i>send (), recv (), connect ()</i> и т. д.) соответствующими функциями vibe.d. В блоге дается обзор того, что нужно сделать, используя MySQL в качестве примера.</p>\n<h3>&#171;Сырая&#187; сеть и обработка файлов</h3>\n<p>Конечно, «сырые» (необработанные) TCP/UDP и доступ к файлам поддерживаются набором инструментальных средств для включения пользовательских протоколов и форматов файлов. I/O происходит через интерфейс блокирующего потока, который эффективно скрывает тот факт, что базовые операции фактически основаны на событиях.</p>\n<h3>Общие инструменты параллелизма</h3>\n<p>Помимо операций ввода-вывода поддерживаются все обычные инструменты для общих задач программирования:</p>\n<ul>\n<li>Sleep() – приостанавливает выполнение текущих задач в течение определенного времени</li>\n<li>Таймеры позволяют асинхронное планирование обратных вызовов</li>\n<li>Мьютексы и условные переменные позволяют осуществлять совместный доступ к данным между несколькими потоками без вмешательства в цикл обработки событий</li>\n<li>Поддержка передачи сообщений</li>\n<li>Каналы данных для потоковой передачи данных между задачами и потоками</li>\n</ul>\n<h3>Интеграция графического интерфейса пользователя</h3>\n<p>В отличие от большинства других сред, поддерживающих асинхронный ввод-вывод, vibe.d полностью интегрируется с циклом событий пользовательского интерфейса, поэтому его можно использовать для реализации приложений с графическим интерфейсом пользователя.</p>\n<p>Для Windows существует собственная реализация драйвера событий (включается с VibeWin32Driver), которая использует функцию MsgWaitForMultipleObjectsEx для обработки оконных сообщений вместе с событиями ввода/вывода или параллелизма. Для систем, работающих под управлением X11, можно использовать createFileDescriptorEvent для прослушивания связи дисплея вместо использования XNextEvent.</p>\n<h2>Производительность</h2>\n<h3>Асинхронные операции ввода-вывода</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-io.svg\" alt=\"Неблокирующий ввод-вывод\" /><figcaption>Неблокирующий ввод-вывод</figcaption></figure>Вместо использования классического блокирующего ввода-вывода и многопоточности для выполнения параллельных сетевых и файловых операций все операции используют асинхронные API-интерфейсы операционной системы. По умолчанию libevent используется для доступа к этому API операционной системы независимо.</p>\n<p>Использование асинхронного ввода-вывода имеет ряд преимуществ, главным из которых является то, что накладные расходы памяти намного ниже. Это связано с тем, что для обработки произвольного количества одновременных операций необходим только один аппаратный поток. Поток обычно остается в ожидании событий и разбужен операционной системой сразу же после поступления новых данных, установления нового соединения или возникновения ошибки. После каждой инициированной операции блокировки (например, записи данных в сокет) поток вернется в спящий режим и позволит операционной системе выполнить операцию в фоновом режиме.</p>\n<p>Другим важным аспектом для скорости является то, что, поскольку требуется только один или несколько потоков, часто можно сохранить много дорогих контекстных переключателей потока. Однако, если приложение должно выполнять много вычислений помимо операций ввода-вывода, vibe.d имеет полную поддержку многопоточности, чтобы полностью использовать многоядерный процессор системы.</p>\n<p>Поскольку использование модели на основе асинхронных событий часто больше применяется для реализации приложений, волокна используются вместе с планировщиком на основе событий, чтобы создать впечатление классических вызовов блокировки и многопоточности, тем самым скрывая дополнительные сложности и сохраняя при этом эксплуатационные характеристики асинхронности ввода-выводы.</p>\n<h3>Поддержка многопоточности</h3>\n<p>Хотя производительность, как правило, очень высока в однопоточном приложении из-за использования асинхронных операций ввода-вывода и волокон, есть приложения, которые могут извлечь большую пользу из использования нескольких ядер. Vibe.d поддерживает несколько способов использования этой дополнительной вычислительной мощности, оставляя решение по архитектуре потоков программисту.</p>\n<p>Помимо разрешения использовать потоков D низкого уровня, предоставляется пул потоков, который используется для любой задачи, запущенной с помощью runWorkerTask вместо runTask. Это в основном полезно для выполнения вычислительно дорогостоящих операций, таких как декодирование изображений, наряду с обычным потоком программ, но оно также может быть использовано для лучшей производительности тяжелых задач ввода-вывода в некоторых случаях.</p>\n<p>Библиотека очень гибкая в том, что многопоточность может быть использована, и она гарантирует, что не могут возникнуть условия «низкоуровневых гонок», если не используются небезопасные операции, такие как cast () или __gshared переменные. В следующем списке показаны некоторые типичные архитектуры потоков:</p>\n<ul>\n<li>Распределенная обработка входящих соединений</li>\n</ul>\n<p>HTTP-серверу (а также любому другому серверу на базе TCP) можно поручить обработку входящих соединений по рабочим потокам пула потоков, а не по основному потоку. Для приложений, которым не требуется разделять состояние между различными соединениями в этом процессе, это может увеличить линейное число запросов в секунду по количеству ядер в системе. Эта функция активируется с использованием настроек HTTPServerOption.distribute или TCPListenOptions.distribute.</p>\n<ul>\n<li>Использование рабочих задач для вычислений</li>\n</ul>\n<p>Вычислительные дорогостоящие задачи могут быть выгружены из основного потока, выполнив их с помощью runWorkerTask. Любая такая задача будет выполнена в пуле потоков. Этот подход полезен в тех случаях, когда приложение требует выполнения таких дорогостоящих задач, поскольку оно позволяет чистому выполнению операций ввода-вывода оставаться без изменений при большой вычислительной нагрузке. Вероятно, наиболее распространенным примером такой задачи является обработка/декодирование/кодирование изображений.</p>\n<ul>\n<li>Использование простых D-потоков</li>\n</ul>\n<p>Нормальные потоки D также могут использоваться вместе с vibe.d. Это важно, когда используются потоковые примитивы стандартной библиотеки D. Помимо основных потоков D, это std.parallelism и std.concurrency. Обратите внимание, что части стандартной библиотеки D не следует смешивать с функциями ввода-вывода vibe.d, поскольку они блокируют цикл обработки событий. В частности, функции передачи сообщений в std.concurrency_* в настоящее время несовместимы с циклом событий vibe.d и должны быть заменены функциями vibe.core.concurrency.</p>\n<p>Обратите внимание, что vibe.d также включает в себя экспериментальную библиотеку поддерживающую изолированные и ограниченные ссылки. Это позволяет передавать изменяемые данные между потоками без использования мьютексов или аналогичных средств для синхронизации доступа к данным. Это особенно полезно при передаче данных, используемых в рабочих задачах между рабочим потоком и основным потоком.</p>\n<h3>Встроенный компилятор кода</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-dlang.svg\" alt=\"Язык D как основа vibe.d\" /><figcaption>Язык D как основа vibe.d</figcaption></figure>Vibe.d написан на языке программирования D. D – это C-подобный язык, который компилируется с использованием машинного кода с минимальными затратами времени выполнения. Помимо того, что D является быстрым, D предлагает ряд функций, которые позволяют повысить производительность и улучшить читаемость кода, безопасность и удобство использования по сравнению с другими языками, такими как C ++.</p>\n<p>Некоторые заметные отличия перечислены здесь, но есть много дополнительных вещей, которые выходят за рамки этого текста. Смотрите страницу функций D или книгу Programming in D для более тщательного обзора.</p>\n<ul>\n<li><b>Шаблоны, миксины и</b> <strong>compile-time</strong><b> функции</b></li>\n</ul>\n<p>Метопрограммирование в D чрезвычайно эффективно. Шаблоны (сравнимые с шаблонами C ++) поддерживают параметры variadic, а также параметры строк и параметры-псевдонимы (alias) – это функции, которые обеспечивает очень удобные интерфейсы во время компиляции.</p>\n<p>Мощность языка дает возможность выполненять обычные функций во время компиляции, а также возможность компилировать строку, заданную во время компиляции как код D. Эта возможность похожа на функцию eval () в JavaScript: результат статически компилируется в машинный код (и без последствий безопасности eval ()).</p>\n<p>Эти функции вместе с возможностью чтения файлов во время компиляции позволили создать мощный синтаксический анализатор шаблонов Diet.</p>\n<ul>\n<li><b>Сборщик мусора</b></li>\n</ul>\n<p>Среда выполнения D предлагает встроенную сборку мусора, которая предоставляет некоторые интересные возможности, помимо очевидного преимущества отсутствия необходимости ручного управления памятью и связанного с этим риска утечек памяти и опасных потеряных ссылок.</p>\n<p>В частности, он позволяет работать с неизменяемыми данными, такими как строки или объекты, на которые можно безопасно ссылаться и передавать между потоками без каких-либо издержек синхронизации. При передаче данных между потоками статически закрепляется, что данные безопасны для ссылок из нескольких потоков. Это позволяет избежать целого класса трудно воспроизводимых и отслеживаемых ошибок, часто встречающихся в многопоточном коде многих других языков.</p>\n<ul>\n<li><b>Замыкания и лямбда-функции</b></li>\n</ul>\n<p>Функции, также известные из некоторых скриптовых и функциональных языков, а также в C # и некоторых других – замыкания и лямбда-функции. Они позволяют указывать функции обратного вызова очень компактным и читаемым способом. В свою очередь, они, как правило, оказывают сильное влияние на архитектуру API, так как они могут сделать вычислительно эффективные или безопасные API-интерфейсы действительно переносимыми (или даже приятными) в использовании.</p>\n<ul>\n<li><b>Свойства</b></li>\n</ul>\n<p>Свойства – это функции, которые выглядят как поля класса или структуры. Они используются в API для получения логического и интуитивно понятного интерфейса без всякого рода ограничений и дополнительной типизации, необходимой для нормальных вызовов функций.</p>\n<ul>\n<li><b>Модульная система</b><br />\nМодульная система в D похожа на ту, что используется в Java. Среди прочего он семантически прозрачно кодирует зависимости между различными модулями (D-файлами). Используя эту информацию, становится возможным создавать целые приложения, просто указывая корневой файл (app.d) в командной строке. Зависимости можно найти рекурсивно. Инструмент rdmd build, включенный в компилятор D, поддерживаемый менеджером пакетов DUB, работает таким образом.</li>\n<li><b>Массивы и срезы</b></li>\n</ul>\n<p>Массивы поддерживают встроенный функциональные возможности выполнения срезов с очень естественным синтаксисом. Совместно со сборщиком мусора они обеспечивают чрезвычайно быструю реализацию синтаксического анализатора строк. Данные операции безопасны (проверяются границы) и легко читаются.</p>\n<ul>\n<li><b>Краткий и читаемый синтаксис</b></li>\n</ul>\n<p>Синтаксис в целом очень чистый и по смыслу – особенно в сравнение с его, возможно, самым близким родственником, C ++. Но в этом отношении также не нужно бояться современных скриптовых языков, таких как Ruby и Python. Помимо явной необходимости указывать явный типизации типы могут быть приведены автоматически при помощи оператора auto. Вы обнаружите, что использование auto работает предсказуемо, почти без промахов, и что разработка на D очень эффективна.</p>\n<h3>Компилируемый шаблонизатор “Diet”</h3>\n<p><figure><img src=\"/assets/illustrations/vibe-diet.svg\" alt=\"Шаблоны Diet\" /><figcaption>Шаблоны Diet</figcaption></figure>Vibe.d имеет встроенную поддержку HTML-шаблонов Diet на основе синтаксиса шаблона Pug, известного из node.js, который, в свою очередь, основан на Haml. Эти шаблоны устраняют все синтаксические накладные расходы, которые присущи нотации HTML-тегов, а также позволяют вести встроенную спецификацию динамических элементов. Как правило, эти элементы написаны на языке динамических скриптов (JavaScript в случае Pug).<br />\nОднако в vibe.d шаблоны могут содержать встроенный код D. Шаблоны читаются и анализируются, пока приложение компилируется и оптимизируется. Для них создается код на D. Это означает, что накладные расходы времени выполнения для этих шаблонов отсутствуют – не осуществляется доступ к диску, не выполняется синтаксический анализ, и нет необходимости в копировании строк, поскольку все это уже будет сделано до того, как приложение будет запущено.<br />\nВ то же время вы можете программировать на том же языке и остальную часть приложения, что обеспечивает очень последовательную разработку.</p>\n<p>Перевод <a href=\"http://vibed.org/features\">http://vibed.org/features</a></p>\n<h2>Когда выбирать vibe.d</h2>\n<p>vibe.d хорошо подходит для небольших и средних веб-сервисов на D, где важны неблокирующий ввод-вывод, простая маршрутизация и единая экосистема DUB. Начать можно с минимального HTTP-сервера, затем добавить Diet-шаблоны, MongoDB или Redis и вынести повторяющиеся части в отдельные модули.</p>\n<pre><code>shared static this()\n{\n auto settings = new HTTPServerSettings;\n settings.port = 8080;\n listenHTTP(settings, &handleRequest);\n}</code></pre>\n<p>При проектировании сервиса главное не забывать, что удобный синтаксис не отменяет асинхронную модель. Долгие CPU-задачи нельзя выполнять прямо в обработчике запроса, иначе они будут мешать обработке ввода-вывода. Для таких задач лучше использовать отдельные worker-потоки, очередь или заранее подготовленные данные.</p>\n<ul>\n<li>HTTP и WebSocket оставляем в event loop vibe.d.</li>\n<li>Тяжёлые вычисления выносим из обработчика запроса.</li>\n<li>Доступ к базе оборачиваем в небольшой слой, чтобы не размазывать запросы по маршрутам.</li>\n<li>Diet-шаблоны держим простыми: шаблон должен отображать данные, а не выполнять бизнес-логику.</li>\n</ul>\n<p>Если проект требует большого количества готовых интеграций, заранее проверьте наличие пакетов в DUB и состояние нужных драйверов. Если же нужна компактная серверная часть на D, vibe.d остаётся прямым и понятным выбором: один язык, быстрый нативный код, нормальная маршрутизация, шаблоны и асинхронный ввод-вывод.</p>",
"readingMinutes": 13
},
{
"slug": "вирус-самопроизвольный-запуск-брауз",
"title": "Вирус. Самопроизвольный запуск браузера и перенаправление на вредоносные сайты.",
"date": "2017-05-20T00:07:00+00:00",
"author": "DarkRiDDeR",
"categories": [
"Windows",
"Вирусы"
],
"cover": "/assets/illustrations/cover-virus.svg",
"excerpt": "Недавно столкнулся с такой проблемой: браузер через приблизительно одинаковые промежутки времени сам запускался и открывал различные рекламные и вредоносные сайты. «О! Нет! Это вирус!» – воскликнули с ужасом в сердцах. Все мы напуганы зловредами, которые могут",
"contentHtml": "<p>Недавно столкнулся с такой проблемой: браузер через приблизительно одинаковые промежутки времени сам запускался и открывал различные рекламные и вредоносные сайты.</p>\n\n<p>«О! Нет! Это вирус!» – воскликнули с ужасом в сердцах.</p>\n<p>Все мы напуганы зловредами, которые могут нанести ощутимый вред, особенно на фоне недавней атаки вируса шифровальщика WannaCry, который широко разгулялся и нанёс принёс ощутимые убытки большому числу компаний и организаций. Но в нашем случае всё не так страшно.</p>\n<p>Данный вирус практически никакой угрозы не представляет, он даже вирус в кавычках. Основной его вред в том, что по большему счёту лишь надоедает и отвлекает. Сидишь ли делаешь работу, или может отдыхаешь, смотришь любимый сериал, и постоянно запускается браузер с каким-то левым сайтом, на котором находится нелицеприятный контент. Это начинает довольно сильно мешать.</p>\n<p>После чего уже начинаешь задумываться. А какого фига данный вирус попал ко мне на компьютер? Как его удалить? Основная причина попадания таких вирусов – это установка какого-либо пиратского контента. Но бог с ним, как он к нам попал. Теперь его как-то надо удалять.</p>\n<p>Мы сначала запускаем свой любимый установленный на нашем компьютере антивирус. У меня лично стоит <em>ESET</em> NOD32 <em>Smart Security</em> (рекомендую). Но сканирование ничего не даёт. Что же делать? Порывшись в закоулках ОС Windows я нашёл причину этой проблемы. Это планировщик заданий, в котором было указано задание на запуск браузера с последующим переходом на зловредный сайт.</p>\n<p>Чтобы попасть в планировщик заданий Windows запустите командную строку набором клавишь <b>Win+R</b> и введите команду <b>taskschd.msc</b>, которая и запустит его.</p>\n<p><figure><img src=\"/assets/illustrations/taskschd-run.svg\" alt=\"Окно запуска taskschd.msc\" /><figcaption>Окно запуска taskschd.msc</figcaption></figure></p>\n<p>Просмотрев команды планировщика можно легко найти ту, которая запускает наш браузер (у меня Firefox).</p>\n<p><figure><img src=\"/assets/illustrations/taskschd-action.svg\" alt=\"Подозрительное задание в планировщике Windows\" /><figcaption>Подозрительное задание в планировщике Windows</figcaption></figure></p>\n<p>В задании видно, на какой сайт идёт перенаправление.</p>\n<p>Это по сути своей и не вирус – это лишь задание на запуск браузера с последующим перенаправлением, поэтому и антивирус прошёл мимо, не обратив внимания. Хотя добавить в антивирус подобного рода проверку было бы неплохо.</p>\n<p>Нещадно удаляем это задание. После чего проблема с самопроизвольным запуском браузера и последующим перенаправлением на вредоносные сайта решена.</p>\n<p>В данной статье описан один из возможных способов решения данной проблемы. Возможно в вашем случае имеет место та же проблема, и я надеюсь, что помог вам.</p>\n<p>Всего хорошего!</p>\n<h2>Проверяем, что проблема решена</h2>\n<p>После удаления задания перезагрузите компьютер и оставьте систему включённой на тот промежуток времени, через который раньше открывался браузер. Если окно больше не появляется, значит источник найден правильно. Если запуск повторяется, проверяем не только библиотеку планировщика, но и автозагрузку, ключи Run в реестре и расширения браузера.</p>\n<p>В планировщике заданий важно смотреть не только название задания. Название злоумышленник может сделать вполне безобидным: Update Service, Browser Check, Windows Helper. Смысл находится в действии. Если в действии указан путь к браузеру, а после него стоит адрес сайта, то перед нами как раз тот самый случай.</p>\n<ul>\n<li>Откройте свойства подозрительного задания и посмотрите вкладку «Действия».</li>\n<li>Проверьте путь к программе: firefox.exe, chrome.exe, browser.exe или cmd.exe с последующим запуском браузера.</li>\n<li>Проверьте аргументы: если там указан URL, рекламный домен или короткая ссылка, задание можно отключать.</li>\n<li>Сначала отключите задание, перезагрузите компьютер и проверьте поведение. После проверки его уже можно удалить.</li>\n</ul>\n<p>Если задача появляется снова, значит в системе остался установщик или служба, которая её восстанавливает. В таком случае дополнительно смотрим «Приложения и возможности», папки автозагрузки пользователя, ключи <em>HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run</em> и <em>HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run</em>. После очистки нужно обновить антивирусные базы и выполнить полную проверку.</p>\n<p>Итог простой: в этом случае лечится не браузер, а причина его запуска. Находим периодическое задание, проверяем действие, отключаем, убеждаемся, что оно не восстановилось, и только после этого удаляем. Так проблема закрывается полностью, а не до следующей перезагрузки.</p>",
"readingMinutes": 3
},
{
"slug": "компиляция-64-x-разрядных-программ-на-dmd-по",
"title": "Компиляция 64-x разрядных программ на DMD под Windows x64",
"date": "2017-05-04T18:13:42+00:00",
"author": "DarkRiDDeR",
"categories": [
"DLang",
"DMD",
"Windows"
],
"cover": "/assets/illustrations/dmd-win64.svg",
"excerpt": "Существуют проблемы совместимости с 32-x битными файлами DMD в Windows x64, так как они скомпилированы с использованием DMC фоновых программ, которые могут производить только OMF двоичные файлы. Поэтому для того, чтобы избежать проблем с подключением к внешним",
"contentHtml": "<p>Существуют проблемы совместимости с 32-x битными файлами DMD в Windows x64, так как они скомпилированы с использованием DMC фоновых программ, которые могут производить только OMF двоичные файлы. Поэтому для того, чтобы избежать проблем с подключением к внешним скомпилированным библиотекам, гораздо легче придерживаться использования 64-разрядных двоичных файлов. Это можно осуществить использованием Visual Studio компоновщика для DMD, который будет создавать совместимые COFF двоичный файлы. Еще одна проблема в том, что 32-битные DMD двоичные файлы связаны с устаревшими 32-битными библиотека WinAPI, в которых отсутствуют некоторые очень важные функции, в то время как 64-разрядные двоичные файлы должны быть связаны с 64-битными библиотеками, поставляемыми в SDK Windows. Решим эту проблему. </p>\n<p>Для начала нужно установить <a href=\"http://msdn.microsoft.com/en-US/windows//bb980924.aspx\">Microsoft Windows SDK for Windows</a>. Найти различные версии можно по ссылке <a href=\"http://www.microsoft.com/en-us/search/result.aspx?q=sdk%20windows\">http://www.microsoft.com/en-us/search/result.aspx?q=sdk%20windows</a>. Скачиваем для своей версии Windows. Устанавливаем.</p>\n<p>Директория установки Windows SDK в Windows 7 по умолчанию — <i>C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\</i>. Создадим системную переменную VCINSTALLDIR с данным адресом директории.</p>\n<p>Далее нам будет предложено выбрать устанавливаемые средства разработки, нам важны только 64-x разрядные библиотеки и компилятор Visual Studio, выберем данные пункты.</p>\n<p>Далее создадим ещё одну системную переменную DEV_DIR_WINSDK, в которой будет храниться путь к Windows SDK. В моё случае это C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\.</p>\n<p>Теперь необходимо отредактировать файл конфигурации компилятора DMD, он находится по пути dmd2\\windows\\bin\\sc.ini. Файл разбит на разделы, которые отмечены квадратными скобками, на потребуется изменить данные в разделе Environment64:</p>\n<ol>\n<li>Изменим значение параметра LINKCMD на <i>%VCINSTALLDIR%\\bin\\amd64\\link.exe</i></li>\n<li>Раскомментируем строку <i>WindowsSdkDir=</i> и приравняем к <i>%DEV_DIR_WINSDK%</i></li>\n<li>Далее необходимо закомментировать (символ «;») строки с параметром LIB, в которых прописаны пути к Wimdows SDK не для вашей платформы.</li>\n</ol>\n<p>На этом настройка завершена, попробуем скомпилировать проект (запись блога ), открываем командную строку, пишем:</p>\n<p><i>rdmd -m64 C:\\D\\projects\\test\\hello.d</i></p>\n<p>Если всё прошло удачно, будет выведено «Hello, World!». Поздравляю, теперь вы можете компилировать 64-х разрядные приложения на D для Windows.</p>\n<p>Основная информация взята с <a href=\"http://wiki.dlang.org/Installing_DMD_on_64-bit_Windows_7_%28COFF-compatible%29\">http://wiki.dlang.org/Installing_DMD_on_64-bit_Windows_7_%28COFF-compatible%29</a></p>\n<h2>Короткая памятка для современных и старых сборок</h2>\n<p>Главная мысль остаётся той же: нельзя смешивать 32-битные OMF-библиотеки и 64-битную сборку. В старых установках DMD под Windows это часто упиралось в ручную настройку <em>sc.ini</em>, Windows SDK и Visual Studio linker. В более новых установках официальный установщик DMD обычно делает большую часть этой настройки сам, а для нормальной работы достаточно иметь Visual Studio Build Tools или установленную Visual Studio с C++ tooling.</p>\n<p>Для простой 64-битной сборки используем явную архитектуру:</p>\n<pre><code>dmd -m64 main.d -of=app.exe\ndub build --arch=x86_64</code></pre>\n<p>Если используется старая 32-битная сборка, смотрим, какой формат объектных файлов нужен проекту. Старый OMF и MS-COFF несовместимы между собой. Для современных библиотек Windows чаще нужен MS-COFF, а для 64-битной сборки это фактически обычный путь.</p>\n<ul>\n<li>Проверьте, что внешние <em>.lib</em> файлы собраны под ту же архитектуру.</li>\n<li>Запускайте сборку из Developer Command Prompt или x64 Native Tools Command Prompt, если компоновщик не находится.</li>\n<li>Если DMD не видит linker, проверьте переменные окружения и секцию <em>Environment64</em> в <em>sc.ini</em>.</li>\n<li>Если проект собирается через DUB, фиксируйте архитектуру в команде или конфигурации сборки.</li>\n</ul>\n<p>Типичная ошибка выглядит так: код компилируется, а линковка падает на внешней библиотеке. В большинстве случаев виноват не D-код, а то, что рядом лежит библиотека другого формата или другой разрядности. Сначала проверяем toolchain и зависимости, и только потом ищем ошибку в исходниках.</p>",
"readingMinutes": 3
}
]