import { readFile, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; throw new Error( 'The bootstrap archive generator is retired: it would overwrite reviewed editorial articles. ' + 'Use a reviewed month-specific upgrade and apply the resulting patch instead.', ); const webRoot = join(dirname(fileURLToPath(import.meta.url)), '..'); const articlesPath = join(webRoot, 'data', 'articles.json'); const sources = { bitrix: { label: 'Bitrix: CIBlockElement::Add', url: 'https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/add.php?print=Y', }, phpUpload: { label: 'PHP: move_uploaded_file', url: 'https://www.php.net/move-uploaded-file', }, phpErrors: { label: 'PHP: обработка ошибок', url: 'https://www.php.net/manual/en/function.set-error-handler.php', }, mdnModules: { label: 'MDN: JavaScript modules', url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules', }, mdnFetch: { label: 'MDN: Fetch API', url: 'https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API', }, mdnCache: { label: 'MDN: HTTP caching', url: 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching', }, webpack: { label: 'Webpack Guides', url: 'https://webpack.js.org/guides/', }, docker: { label: 'Docker documentation', url: 'https://docs.docker.com/get-started/', }, postgres: { label: 'PostgreSQL: Indexes', url: 'https://www.postgresql.org/docs/current/indexes.html', }, redis: { label: 'Redis Streams', url: 'https://redis.io/docs/latest/develop/data-types/streams/', }, dGc: { label: 'D: automatic memory management', url: 'https://dlang.org/spec/garbage.html', }, dMemory: { label: 'D: core.memory', url: 'https://dlang.org/library/core/memory.html', }, mdnPerformance: { label: 'MDN: Web performance', url: 'https://developer.mozilla.org/en-US/docs/Web/Performance', }, wcag: { label: 'W3C: WCAG 2.2', url: 'https://www.w3.org/TR/WCAG22/', }, owaspUpload: { label: 'OWASP: File Upload Cheat Sheet', url: 'https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html', }, owaspCsrf: { label: 'OWASP: CSRF Prevention Cheat Sheet', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html', }, owaspHtml: { label: 'OWASP: HTML5 Security Cheat Sheet', url: 'https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html', }, otel: { label: 'OpenTelemetry: signals', url: 'https://opentelemetry.io/docs/concepts/signals/', }, playwright: { label: 'Playwright: best practices', url: 'https://playwright.dev/docs/best-practices', }, kubernetes: { label: 'Kubernetes: Deployments', url: 'https://kubernetes.io/docs/concepts/workloads/controllers/deployment/', }, nistAi: { label: 'NIST: Generative AI Profile', url: 'https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence', }, }; const existingSlots = new Set([ '2018-10-practice', '2019-01-practice', ]); const articleKinds = ['practice', 'mechanism', 'field']; function theme(year, month, id, area, subject, problem, model, field, family, sourceKeys, categories) { return { year, month, id, area, subject, problem, model, field, family, sourceKeys, categories }; } const themes = [ theme(2018, 1, 'bitrix-elements', 'Bitrix API', 'жизненный цикл элемента инфоблока', 'создать элемент так, чтобы обязательные поля, свойства и ошибки не потерялись', 'добавление элемента — это не только вызов Add, но и контракт данных, событий и проверки результата', 'элемент появился в админке, но не стал частью ожидаемого пользовательского сценария', 'bitrix', ['bitrix', 'phpErrors'], ['Bitrix', 'PHP']), theme(2018, 2, 'php-diagnostics', 'PHP', 'диагностика ошибок интеграции', 'увидеть исходную ошибку, а не только пустой ответ или false', 'ошибка должна иметь контекст: входные данные, идентификатор операции, место возникновения и безопасный журнал', 'интеграция падает только у одного клиента и воспроизвести ситуацию сразу не получается', 'php', ['phpErrors'], ['PHP', 'Диагностика']), theme(2018, 3, 'safe-uploads', 'PHP', 'безопасная загрузка файлов', 'принять изображение от пользователя, не превратив загрузку в вход для чужого кода', 'имя, MIME-тип, размер, права и место хранения проверяются раздельно', 'картинка выглядит безобидно, но сервер не должен доверять расширению файла', 'php', ['phpUpload', 'owaspUpload'], ['PHP', 'Безопасность']), theme(2018, 4, 'bitrix-slugs', 'Bitrix API', 'символьные коды и URL', 'получить читаемый и уникальный код элемента из пользовательского названия', 'транслитерация даёт основу, а уникальность и нормализация делают адрес устойчивым', 'два похожих товара пытаются занять один URL и ломают ссылку из каталога', 'bitrix', ['bitrix'], ['Bitrix', 'PHP', 'SEO']), theme(2018, 5, 'legacy-jquery', 'JavaScript', 'старый код на jQuery', 'подключить новый код к старому интерфейсу и не получить несколько обработчиков на одну кнопку', 'граница между глобальным DOM-кодом и модулем должна быть небольшой и наблюдаемой', 'после точечной правки форма начинает отправляться дважды', 'js', ['mdnModules', 'webpack'], ['JavaScript', 'jQuery']), theme(2018, 6, 'webpack-entry', 'Webpack', 'точки входа и зависимости', 'собрать проект так, чтобы общие библиотеки и код страницы не дублировались', 'entry-файл задаёт границу загрузки, а граф импортов показывает реальную зависимость модулей', 'после новой сборки bundle неожиданно стал вдвое больше', 'js', ['webpack', 'mdnModules'], ['JavaScript', 'Webpack']), theme(2018, 7, 'http-timeouts', 'HTTP', 'таймауты в веб-интеграции', 'отличить долгий ответ от зависшего процесса и выбрать корректный срок ожидания', 'таймауты клиента, прокси, приложения и базы — это разные уровни одного запроса', 'браузер ждёт достаточно долго, но сервер всё равно обрывает выгрузку', 'http', ['mdnFetch', 'mdnCache'], ['HTTP', 'PHP']), theme(2018, 8, 'tls-ca', 'PHP', 'TLS и CA bundle', 'починить проверку сертификата без отключения проверки SSL', 'клиент доверяет цепочке сертификатов, а не просто доменному имени в адресной строке', 'сайт открывается в браузере, но cURL возвращает certificate error', 'http', ['phpUpload', 'owaspHtml'], ['PHP', 'SSL']), theme(2018, 9, 'windows-dev-env', 'Windows', 'окружение разработчика', 'сделать локальную среду воспроизводимой, а не зависящей от случайных настроек компьютера', 'версия интерпретатора, расширения, PATH и конфигурация должны быть описаны рядом с проектом', 'у коллеги тот же проект не запускается, хотя код не менялся', 'ops', ['docker'], ['Windows', 'Инструменты']), theme(2018, 10, 'image-workflow', 'Bitrix API', 'работа с изображениями в форме', 'связать UI-редактор, проверку файла и сохранение привязки к сущности', 'редактор помогает пользователю, но права, тип файла и жизненный цикл данных остаются на сервере', 'картинка успешно выбрана, но после сохранения страницы у сущности остаётся пустое поле', 'bitrix', ['bitrix', 'owaspUpload'], ['Bitrix', 'PHP']), theme(2018, 11, 'php-integration-tests', 'PHP', 'интеграционные проверки', 'проверить взаимодействие приложения, файловой системы и внешнего HTTP-клиента', 'ценность интеграционного теста в том, что он проверяет контракт между частями системы', 'модульные тесты зелёные, а реальная форма ломается после отправки файла', 'test', ['phpUpload', 'mdnFetch'], ['PHP', 'Тестирование']), theme(2018, 12, 'legacy-refactoring', 'Разработка', 'рефакторинг Bitrix-проекта', 'изменять старый проект маленькими безопасными шагами', 'рефакторинг — это управление риском через границы, проверки и короткие циклы обратной связи', 'задача кажется простой, пока не выясняется, что функцию вызывают из пяти шаблонов', 'writing', ['bitrix', 'webpack'], ['Bitrix', 'Рефакторинг']), theme(2019, 1, 'jquery-webpack', 'JavaScript', 'jQuery в Webpack', 'сделать зависимость явной и не потерять плагины, ожидающие window.jQuery', 'модульная система и глобальные переменные имеют разные правила видимости', 'после миграции сборки старый inputmask перестаёт видеть jQuery', 'js', ['webpack', 'mdnModules'], ['JavaScript', 'Webpack']), theme(2019, 2, 'es-modules', 'JavaScript', 'ES-модули в браузере', 'разделить код на файлы без хаоса в глобальной области видимости', 'модуль загружается как отдельный ресурс и подчиняется правилам CORS и графа импортов', 'страница работает на сервере, но падает при открытии через file://', 'js', ['mdnModules'], ['JavaScript']), theme(2019, 3, 'event-loop', 'JavaScript', 'event loop и асинхронность', 'понять, почему обработчик интерфейса «подвисает», хотя запросы асинхронные', 'обещание не делает CPU-вычисление параллельным: главный поток всё ещё один', 'кнопка реагирует только после обработки большого массива в then', 'js', ['mdnFetch'], ['JavaScript', 'Асинхронность']), theme(2019, 4, 'forms-validation', 'Frontend', 'валидация форм', 'дать человеку понятную ошибку и всё равно проверить данные на сервере', 'клиентская валидация улучшает UX, но сервер остаётся источником истины', 'поле подсвечено зелёным, а API всё равно отказывается принимать значение', 'frontend', ['wcag', 'owaspHtml'], ['JavaScript', 'UX']), theme(2019, 5, 'http-caching', 'HTTP', 'кеширование HTTP-ответов', 'ускорить повторные запросы, не показывая пользователю устаревшие данные', 'кеш — это договор о свежести, ключе и инвалидировании, а не только заголовок max-age', 'после деплоя клиент продолжает получать старый JavaScript-файл', 'http', ['mdnCache'], ['HTTP', 'Производительность']), theme(2019, 6, 'rest-api', 'API', 'контракт REST API', 'сделать интерфейс предсказуемым для формы, скрипта и внешнего клиента', 'контракт включает не только URL, но и статусы, ошибки, идемпотентность и формат полей', 'одна и та же ошибка то приходит строкой, то пустым массивом', 'http', ['mdnFetch'], ['API', 'HTTP']), theme(2019, 7, 'sql-indexes', 'SQL', 'индексы и планы запросов', 'ускорить нужный запрос, а не поставить индекс на каждую колонку подряд', 'индекс ускоряет поиск ценой записи, места и усложнения плана', 'страница каталога медленно открывается только при большом количестве товаров', 'sql', ['postgres'], ['SQL', 'Производительность']), theme(2019, 8, 'frontend-performance', 'Frontend', 'производительность первой загрузки', 'измерить, что именно мешает странице стать полезной пользователю', 'сеть, парсинг, выполнение JavaScript и рендеринг образуют одну цепочку задержки', 'страница быстро отвечает сервером, но визуально остаётся пустой', 'frontend', ['mdnPerformance'], ['JavaScript', 'Производительность']), theme(2019, 9, 'accessibility-basics', 'Frontend', 'базовая доступность', 'дать интерфейсу нормальную семантику, фокус и текстовые подписи', 'браузер строит не только DOM, но и дерево доступности для вспомогательных технологий', 'красивая кнопка работает мышью, но недоступна с клавиатуры', 'frontend', ['wcag'], ['Доступность', 'Frontend']), theme(2019, 10, 'typescript-migration', 'JavaScript', 'переход на TypeScript', 'добавить типы без переписывания всей кодовой базы', 'типы полезнее всего на границах данных: API, форма, конфигурация и публичная функция', 'старый JavaScript-модуль используют в десятке страниц с разными аргументами', 'js', ['mdnModules', 'webpack'], ['JavaScript', 'TypeScript']), theme(2019, 11, 'reproducible-builds', 'Сборка', 'воспроизводимая сборка', 'получать одинаковый результат на локальной машине и в CI', 'lockfile, версии среды и явная конфигурация превращают сборку из ритуала в процесс', 'после npm install у одного разработчика появляется ошибка, которой нет у остальных', 'ops', ['webpack', 'docker'], ['JavaScript', 'Инструменты']), theme(2019, 12, 'code-ownership', 'Команда', 'ответственность за код', 'сделать передачу модуля понятной, а не держать знания только в голове автора', 'сопровождаемость начинается там, где другой человек способен воспроизвести решение', 'автор старой интеграции ушёл, а никто не понимает, какие поля обязательны', 'writing', ['bitrix', 'webpack'], ['Разработка', 'Команда']), theme(2020, 1, 'docker-local', 'Docker', 'локальная разработка в контейнере', 'запускать приложение с одинаковыми зависимостями у всей команды', 'образ описывает окружение, а контейнер запускает его как изолированный процесс', 'проект требует старую версию PHP, которой нет в системе разработчика', 'docker', ['docker'], ['Docker', 'Инфраструктура']), theme(2020, 2, 'configs-secrets', 'Конфигурация', 'настройки и секреты', 'разделить код, параметры окружения и чувствительные данные', 'конфигурация меняется между средами, а секрет не должен попадать в репозиторий или лог', 'боевой токен случайно оказался в JSON-ответе об ошибке', 'ops', ['docker', 'owaspHtml'], ['Безопасность', 'DevOps']), theme(2020, 3, 'ci-pipeline', 'CI/CD', 'минимальный pipeline', 'превратить проверку кода в повторяемую последовательность шагов', 'CI ценен не скоростью сам по себе, а тем, что одинаково проверяет каждое изменение', 'локально тесты запускались, но после merge приложение не собирается', 'test', ['docker', 'playwright'], ['CI/CD', 'Тестирование']), theme(2020, 4, 'reverse-proxy', 'HTTP', 'reverse proxy перед приложением', 'разобраться, где завершается TLS и кто отвечает за таймауты и заголовки', 'прокси отделяет внешнее HTTP-соединение от процесса приложения и задаёт границу ответственности', 'приложение считает запрос HTTPS, а в логах почему-то видит http', 'http', ['mdnCache', 'docker'], ['HTTP', 'Инфраструктура']), theme(2020, 5, 'background-jobs', 'Backend', 'фоновые задачи', 'вынести долгую работу из пользовательского HTTP-запроса', 'запрос должен принять намерение, а worker — выполнить тяжёлую часть с отдельным состоянием', 'экспорт отчёта занимает минуту и держит браузер пользователя в ожидании', 'redis', ['redis', 'docker'], ['Backend', 'Очереди']), theme(2020, 6, 'retry-idempotency', 'Backend', 'повтор запросов и идемпотентность', 'повторять временные сбои, не создавая второй платёж, заказ или письмо', 'повтор безопасен только тогда, когда операция имеет ключ идемпотентности и понятный результат', 'клиент не получил ответ и отправил ту же команду ещё раз', 'http', ['mdnFetch'], ['API', 'Надёжность']), theme(2020, 7, 'structured-logs', 'Наблюдаемость', 'структурированные логи', 'находить нужный запрос по идентификатору, а не читать тысячи строк вручную', 'лог — это событие с полями, уровнем и контекстом, а не просто текст для консоли', 'в журнале есть ошибка, но непонятно, к какому пользователю и заказу она относится', 'telemetry', ['otel'], ['Наблюдаемость', 'Backend']), theme(2020, 8, 'metrics-basics', 'Наблюдаемость', 'метрики приложения', 'отличать отдельную ошибку от ухудшения поведения всей системы', 'метрика показывает изменение во времени, поэтому важны единицы, лейблы и базовая линия', 'жалобы пользователей есть, а в логах нет явной аварии', 'telemetry', ['otel'], ['Наблюдаемость', 'Производительность']), theme(2020, 9, 'tracing-basics', 'Наблюдаемость', 'трассировка запроса', 'увидеть путь одного запроса через frontend, API и внешнюю систему', 'trace связывает операции общим контекстом и показывает задержки между компонентами', 'один endpoint иногда отвечает десять секунд, но причина меняется от случая к случаю', 'telemetry', ['otel'], ['Наблюдаемость', 'HTTP']), theme(2020, 10, 'backup-recovery', 'Данные', 'резервное копирование и восстановление', 'проверять не наличие бэкапа, а возможность вернуть данные в рабочее состояние', 'копия ценна только после регулярной проверки восстановления и понятного RPO/RTO', 'резервная копия есть, но никто не пробовал поднять из неё тестовую базу', 'sql', ['postgres', 'docker'], ['Данные', 'Надёжность']), theme(2020, 11, 'security-baseline', 'Безопасность', 'минимальная защита веб-приложения', 'закрыть наиболее очевидные дыры в аутентификации, вводе и настройках', 'безопасность — это серия независимых барьеров, а не одна «волшебная» проверка', 'старый административный endpoint доступен без CSRF-защиты', 'security', ['owaspCsrf', 'owaspHtml'], ['Безопасность', 'Web']), theme(2020, 12, 'incident-review', 'Надёжность', 'разбор инцидента', 'извлечь из сбоя изменение процесса, а не найти виноватого', 'хороший разбор связывает симптом, временную шкалу, защитные механизмы и действие', 'сервис восстановили, но через месяц та же ошибка повторилась', 'writing', ['otel', 'docker'], ['Надёжность', 'Команда']), theme(2021, 1, 'transactions', 'PostgreSQL', 'границы транзакции', 'сохранить согласованность нескольких изменений данных', 'транзакция фиксирует набор изменений целиком или откатывает его при ошибке', 'заказ создан, а строка остатка на складе не обновилась', 'sql', ['postgres'], ['PostgreSQL', 'Данные']), theme(2021, 2, 'cache-invalidation', 'Кеширование', 'инвалидация кеша', 'ускорить чтение и не сделать старые данные постоянной правдой', 'кеш всегда требует владельца, ключа, срока жизни и события обновления', 'пользователь поменял профиль, а соседняя страница ещё час показывает старое имя', 'redis', ['redis', 'mdnCache'], ['Кеширование', 'Backend']), theme(2021, 3, 'queues', 'Очереди', 'очереди задач', 'распределить работу между worker-процессами и не потерять задачу при падении', 'очередь отделяет приём работы от её выполнения и требует подтверждения обработки', 'один worker завис, а задачи после него перестали двигаться', 'redis', ['redis'], ['Очереди', 'Backend']), theme(2021, 4, 'data-consistency', 'Архитектура', 'согласованность данных между сервисами', 'не обещать мгновенную синхронность там, где система работает асинхронно', 'между локальной транзакцией и результатом в другом сервисе существует временная граница', 'пользователь видит заказ, но уведомление о нём приходит через несколько минут', 'redis', ['redis', 'postgres'], ['Архитектура', 'Данные']), theme(2021, 5, 'distributed-locks', 'Backend', 'блокировки и конкуренция', 'защитить операцию, которую нельзя выполнить одновременно дважды', 'блокировка имеет область, срок жизни и сценарий потери владельца', 'две вкладки одновременно уменьшают один и тот же остаток', 'redis', ['redis', 'postgres'], ['Backend', 'Конкурентность']), theme(2021, 6, 'event-driven', 'Архитектура', 'событийная интеграция', 'сделать уведомление о факте отдельным от реакции конкретного потребителя', 'событие описывает уже произошедший факт и может быть обработано несколькими независимыми получателями', 'добавление нового письма требует менять основной код оформления заказа', 'redis', ['redis'], ['Архитектура', 'События']), theme(2021, 7, 'search-indexing', 'Поиск', 'индексация и поиск', 'отделить поиск по тексту от транзакционного хранения сущностей', 'индекс оптимизирован под поиск, поэтому обновляется по своему контракту и может отставать', 'новый товар есть в карточке, но не находится через поиск несколько минут', 'sql', ['postgres'], ['Поиск', 'Данные']), theme(2021, 8, 'storage-contracts', 'Данные', 'контракт хранилища', 'выбрать место для файла, записи и временного объекта по их жизненному циклу', 'способ хранения определяется доступом, сроком жизни, размером, резервированием и правами', 'в базе лежат гигабайты изображений, а их нельзя удобно отдать пользователю', 'ops', ['owaspUpload', 'docker'], ['Данные', 'Инфраструктура']), theme(2021, 9, 'api-versioning', 'API', 'версионирование API', 'изменять контракт так, чтобы старые клиенты не ломались внезапно', 'версия — это договор об обратной совместимости, а не только сегмент URL', 'мобильное приложение ещё отправляет поле, которое backend уже переименовал', 'http', ['mdnFetch'], ['API', 'Архитектура']), theme(2021, 10, 'd-runtime-service', 'D', 'runtime D в сервисе', 'использовать сильные стороны D, не игнорируя работу GC и границы ввода-вывода', 'низкоуровневая модель полезна тогда, когда измерена, а не включена из любопытства', 'сервис быстрый в тесте, но под нагрузкой появляются редкие паузы', 'd', ['dGc', 'dMemory'], ['DLang', 'Backend']), theme(2021, 11, 'load-testing', 'Нагрузка', 'нагрузочное тестирование', 'проверять систему по сценарию пользователя, а не по одному красивому RPS', 'нагрузка раскрывает очереди, лимиты соединений, блокировки и узкие места зависимостей', 'на стенде всё быстро, а в день акции время ответа растёт в несколько раз', 'telemetry', ['otel', 'postgres'], ['Нагрузка', 'Надёжность']), theme(2021, 12, 'architecture-review', 'Архитектура', 'архитектурный разбор решения', 'зафиксировать ключевые допущения до того, как они станут скрытым долгом', 'архитектурное решение — это контекст, вариант, последствия и дата пересмотра', 'команда спорит о технологии, но не сформулировала, какую проблему выбирает решать', 'writing', ['kubernetes', 'postgres'], ['Архитектура', 'Команда']), theme(2022, 1, 'ssr-csr', 'Frontend', 'границы SSR и CSR', 'выбрать, что показать пользователю сразу, а что оставить клиентскому коду', 'серверный и клиентский рендер решают разные проблемы времени ответа, интерактивности и кеширования', 'контент важен для первого экрана, но виджет требует браузерного API', 'frontend', ['mdnPerformance', 'mdnModules'], ['Frontend', 'Производительность']), theme(2022, 2, 'performance-budget', 'Frontend', 'бюджет производительности', 'превратить пожелание «быстрее» в проверяемые ограничения для страницы', 'бюджет фиксирует допустимые размеры, задержки и сценарии измерения до регрессии', 'каждая новая библиотека кажется маленькой, а первый экран постепенно тяжелеет', 'frontend', ['mdnPerformance'], ['Производительность', 'Frontend']), theme(2022, 3, 'browser-rendering', 'Frontend', 'как браузер рисует страницу', 'понимать, почему изменение DOM иногда дорого, а правильный порядок ресурсов важен', 'парсинг, стили, layout, paint и JavaScript влияют друг на друга через критический путь', 'простая анимация начинает дёргаться после добавления большого списка', 'frontend', ['mdnPerformance'], ['JavaScript', 'Производительность']), theme(2022, 4, 'accessible-interface', 'Доступность', 'доступный интерфейс', 'проверять фокус, подписи, контраст и сценарий без мыши как обычную часть разработки', 'доступность строится на семантике и ожидаемом поведении, а не на отдельном виджете для проверки', 'форма красивая, но screen reader не связывает подпись с полем', 'frontend', ['wcag'], ['Доступность', 'Frontend']), theme(2022, 5, 'design-system', 'Frontend', 'маленькая дизайн-система', 'снизить число случайных вариантов кнопок, ошибок и полей в продукте', 'компонент становится полезным, когда у него есть контракт поведения, состояний и доступности', 'у трёх форм разные обязательные поля, цвета ошибок и логика disabled', 'frontend', ['wcag'], ['Frontend', 'Дизайн-система']), theme(2022, 6, 'form-errors', 'UX', 'ошибки формы', 'вернуть пользователю действие, а не только факт неудачи', 'хорошая ошибка привязана к полю, объясняет причину и не теряет уже введённые данные', 'после серверной ошибки форма очищается и человек не понимает, что исправлять', 'frontend', ['wcag', 'owaspHtml'], ['UX', 'Frontend']), theme(2022, 7, 'poor-network', 'Web', 'работа при плохой сети', 'сделать интерфейс честным при медленном, нестабильном или отсутствующем соединении', 'сеть может потерять запрос после отправки, поэтому UI должен различать ожидание, ошибку и неизвестный результат', 'кнопка сохранения нажимается дважды, пока пользователь не видит ответа', 'http', ['mdnFetch', 'mdnCache'], ['Web', 'UX']), theme(2022, 8, 'media-performance', 'Frontend', 'изображения и медиа', 'отдавать нужный размер файла и не заставлять браузер скачивать лишнее', 'медиа влияет на сеть, layout и визуальную стабильность страницы', 'карточка товара занимает большую часть трафика, хотя изображение выводится маленьким', 'frontend', ['mdnPerformance', 'owaspUpload'], ['Frontend', 'Производительность']), theme(2022, 9, 'mobile-ux', 'UX', 'мобильный сценарий', 'проектировать действие под палец, маленький экран и краткое внимание', 'мобильный интерфейс ограничен не только размером, но и сетью, вводом и контекстом пользователя', 'десктопная таблица на телефоне превращается в стену мелкого текста', 'frontend', ['wcag', 'mdnPerformance'], ['UX', 'Frontend']), theme(2022, 10, 'ui-tests', 'Тестирование', 'проверки пользовательского сценария', 'автоматизировать путь человека через интерфейс, а не набор технических селекторов', 'устойчивый e2e-тест ждёт наблюдаемое состояние и проверяет результат действия', 'тест кликает по кнопке, но иногда падает из-за случайного тайминга', 'test', ['playwright'], ['Тестирование', 'Frontend']), theme(2022, 11, 'bitrix-performance', 'Bitrix', 'производительность Bitrix-страницы', 'найти запрос, шаблон или кеш, который реально влияет на время ответа', 'быстрый сайт требует измерять цепочку целиком: база, PHP, кеш, HTML и браузер', 'страница каталога тормозит, но разработчики спорят только о размере JavaScript', 'bitrix', ['mdnPerformance', 'postgres'], ['Bitrix', 'Производительность']), theme(2022, 12, 'user-research', 'Продукт', 'исследование пользовательской проблемы', 'связать техническое изменение с наблюдаемым сценарием человека', 'инженерная гипотеза полезна, когда у неё есть пользователь, контекст, сигнал и способ проверки', 'команда хочет переписать экран, но не знает, на каком шаге люди действительно уходят', 'writing', ['mdnPerformance', 'wcag'], ['Продукт', 'UX']), theme(2023, 1, 'threat-model', 'Безопасность', 'модель угроз', 'обсудить, что именно защищаем, от кого и какими границами', 'модель угроз связывает актив, актёра, поверхность атаки и меру защиты', 'команда спорит о шифровании, но не знает, какие данные вообще считаются чувствительными', 'security', ['owaspHtml', 'owaspCsrf'], ['Безопасность']), theme(2023, 2, 'sessions-auth', 'Безопасность', 'аутентификация и сессии', 'не путать подтверждение личности с правом выполнить конкретное действие', 'сессия, cookie, срок жизни и серверная проверка образуют один контур доверия', 'пользователь разлогинился в одной вкладке, а в другой ещё может сохранить данные', 'security', ['owaspCsrf', 'owaspHtml'], ['Безопасность', 'Аутентификация']), theme(2023, 3, 'csrf-cors', 'Безопасность', 'CSRF и CORS', 'не открывать доступ к пользовательской сессии только потому, что API нужен frontend-виджету', 'CORS управляет доступом браузерного кода, а CSRF защищает действие от подделанного запроса', 'сервер отдаёт Access-Control-Allow-Origin: * вместе с cookies', 'security', ['owaspCsrf', 'owaspHtml'], ['Безопасность', 'HTTP']), theme(2023, 4, 'dependency-security', 'Безопасность', 'безопасность зависимостей', 'понимать, какие библиотеки действительно попадают в production и как их обновлять', 'зависимость — это часть цепочки поставки, поэтому версия, источник и обновление имеют значение', 'уязвимость найдена в пакете, который уже не используется в исходном коде напрямую', 'ops', ['webpack', 'docker'], ['Безопасность', 'Зависимости']), theme(2023, 5, 'static-analysis', 'Качество', 'статический анализ', 'ловить повторяемые ошибки до запуска приложения', 'линтер и анализатор полезны там, где правила однозначны и проверяются автоматически', 'одна и та же ошибка с null и необработанным Promise появляется в разных модулях', 'test', ['playwright', 'mdnModules'], ['Качество', 'JavaScript']), theme(2023, 6, 'secrets-supply-chain', 'Безопасность', 'секреты и цепочка поставки', 'не давать токенам попасть в логи, образы, историю git и временные файлы CI', 'секрет безопасен только пока ограничены его распространение, срок жизни и область действия', 'токен тестовой среды случайно сохранился в образе контейнера', 'ops', ['docker', 'owaspHtml'], ['Безопасность', 'DevOps']), theme(2023, 7, 'contract-tests', 'Тестирование', 'контрактные тесты API', 'ловить расхождение producer и consumer до общего релиза', 'контракт фиксирует поля, статусы и примеры, которые обе стороны считают допустимыми', 'backend поменял nullable-поле, а frontend узнал об этом только после выкладки', 'test', ['mdnFetch', 'playwright'], ['Тестирование', 'API']), theme(2023, 8, 'e2e-stability', 'Тестирование', 'стабильные e2e-тесты', 'проверять сценарий без sleep, случайных селекторов и зависимости от чужих данных', 'устойчивость теста строится на изоляции данных, пользовательских локаторах и наблюдаемом результате', 'падение теста невозможно повторить, потому что оно зависит от порядка запуска', 'test', ['playwright'], ['Тестирование', 'Frontend']), theme(2023, 9, 'telemetry-signals', 'Наблюдаемость', 'логи, метрики и трассы', 'использовать три сигнала вместе вместо бесконечного поиска в одном журнале', 'логи объясняют событие, метрики показывают тенденцию, а trace связывает путь запроса', 'график показывает рост ошибок, но не говорит, где именно теряется запрос', 'telemetry', ['otel'], ['Наблюдаемость']), theme(2023, 10, 'sli-slo', 'Надёжность', 'SLI и SLO', 'согласовать, какое качество сервиса действительно важно для пользователя', 'метрика становится целью только после того, как определены окно измерения и допустимый уровень ошибки', 'команда считает доступность по разным формулам и спорит, был ли инцидент', 'telemetry', ['otel'], ['Надёжность', 'Наблюдаемость']), theme(2023, 11, 'postmortem', 'Надёжность', 'postmortem без поиска виноватого', 'сделать из сбоя улучшение системы, а не публичную казнь', 'разбор должен объяснить, почему защита не сработала, и что изменится в коде или процессе', 'в отчёте есть фамилия виноватого, но нет действия, которое предотвратит повтор', 'writing', ['otel', 'playwright'], ['Надёжность', 'Команда']), theme(2023, 12, 'security-audit', 'Безопасность', 'практический аудит веб-проекта', 'пройти по поверхности атаки системно и зафиксировать приоритеты исправления', 'аудит — это инвентаризация, проверка контроля и доказательство результата, а не разовый сканер', 'команда получила длинный список предупреждений и не знает, что исправлять первым', 'security', ['owaspUpload', 'owaspCsrf', 'owaspHtml'], ['Безопасность']), theme(2024, 1, 'legacy-modernization', 'Архитектура', 'модернизация legacy-системы', 'выбрать узкий участок для улучшения, не останавливая весь продукт', 'старую систему меняют через границы и совместимые переходы, а не через одномоментную замену', 'проекту десять лет, и мысль о полном переписывании повторяется каждый квартал', 'writing', ['docker', 'postgres'], ['Рефакторинг', 'Архитектура']), theme(2024, 2, 'modular-monolith', 'Архитектура', 'модульный монолит', 'сделать внутренние границы явными до выделения сервисов', 'модуль определяется владением данными, API и зависимостями, а не папкой в репозитории', 'две команды меняют одну таблицу и случайно ломают друг другу сценарии', 'writing', ['postgres', 'kubernetes'], ['Архитектура', 'Backend']), theme(2024, 3, 'package-boundaries', 'Разработка', 'границы пакетов', 'не дать общему пакету превратиться в свалку случайных утилит', 'публичный API пакета должен быть меньше его внутреннего устройства', 'один helper тянет в frontend половину backend-зависимостей', 'js', ['mdnModules', 'webpack'], ['JavaScript', 'Архитектура']), theme(2024, 4, 'data-migrations', 'Данные', 'безопасная миграция данных', 'изменить схему и данные так, чтобы старая и новая версия приложения могли жить рядом', 'хорошая миграция разделяет добавление, заполнение, переключение чтения и удаление старого', 'новая колонка появилась, но старый сервис ещё не умеет её заполнять', 'sql', ['postgres'], ['Данные', 'Миграции']), theme(2024, 5, 'platform-templates', 'Платформа', 'шаблоны для команд', 'убрать повторяемую настройку без превращения платформы в обязательный лабиринт', 'платформенный шаблон должен давать безопасный старт и оставлять понятные точки расширения', 'каждая новая команда вручную копирует CI, Dockerfile и healthcheck', 'platform', ['docker', 'kubernetes'], ['Платформа', 'DevOps']), theme(2024, 6, 'container-orchestration', 'Kubernetes', 'управление контейнерной нагрузкой', 'отделить описание желаемого состояния сервиса от ручного запуска процессов', 'Deployment поддерживает нужное количество реплик и контролируемо заменяет старые версии', 'после обновления образа часть pod-ов работает на старой версии слишком долго', 'platform', ['kubernetes', 'docker'], ['Kubernetes', 'DevOps']), theme(2024, 7, 'release-engineering', 'Релизы', 'инженерия релиза', 'сделать выпуск версией с проверяемым содержимым и планом отката', 'релиз связывает commit, артефакт, конфигурацию, миграции и наблюдение после выкладки', 'код в main уже другой, чем образ, который фактически работает в production', 'platform', ['docker', 'kubernetes'], ['Релизы', 'DevOps']), theme(2024, 8, 'feature-flags', 'Продукт', 'feature flags', 'отделить выкладку кода от включения поведения для аудитории', 'флаг — это временный контракт с владельцем, условиями включения и датой удаления', 'новая функция работает у сотрудников, но должна быть выключена для всех клиентов', 'ops', ['kubernetes'], ['Продукт', 'DevOps']), theme(2024, 9, 'adr-decisions', 'Архитектура', 'запись инженерного решения', 'сохранить контекст выбора, чтобы через год не спорить с неизвестным прошлым', 'ADR описывает проблему, принятое решение, альтернативы и последствия', 'в коде есть необычный обходной путь, но никто не помнит, какую аварию он предотвращает', 'writing', ['postgres', 'kubernetes'], ['Архитектура', 'Документация']), theme(2024, 10, 'capacity-cost', 'Инфраструктура', 'ёмкость и стоимость', 'измерять потребление ресурсов до того, как оптимизация станет срочной', 'стоимость растёт из нагрузки, хранения, трафика и операционной сложности', 'счёт за инфраструктуру вырос, хотя число пользователей почти не изменилось', 'telemetry', ['otel', 'kubernetes'], ['Инфраструктура', 'Надёжность']), theme(2024, 11, 'deprecation', 'Разработка', 'удаление устаревшего', 'вывести старый API или модуль без внезапного обрыва для потребителей', 'депрекация включает инвентаризацию, предупреждение, миграционный путь и конечную дату', 'старый endpoint никто не поддерживает, но неизвестно, кто им ещё пользуется', 'writing', ['mdnFetch', 'kubernetes'], ['Рефакторинг', 'API']), theme(2024, 12, 'maintenance-retro', 'Сопровождение', 'год сопровождения системы', 'собрать повторяющиеся проблемы в план улучшений вместо списка раздражителей', 'сопровождение — это данные об инцидентах, времени изменений и качестве обратной связи', 'команда много работает, но каждый квартал исправляет одну и ту же категорию ошибок', 'writing', ['otel', 'playwright'], ['Сопровождение', 'Команда']), theme(2025, 1, 'ai-coding-assistant', 'AI и разработка', 'помощник для написания кода', 'использовать генерацию как ускоритель черновика, не передавая ей ответственность за решение', 'модель может предложить форму кода, но требования, тест и проверка остаются задачей инженера', 'сгенерированный фрагмент выглядит убедительно, но не учитывает границы конкретного API', 'ai', ['nistAi', 'playwright'], ['AI', 'Разработка']), theme(2025, 2, 'ai-code-verification', 'AI и разработка', 'проверка сгенерированного кода', 'строить тесты и ревью вокруг риска, а не вокруг доверия к красивому ответу', 'проверка включает контракт, негативные сценарии, безопасность и воспроизводимый запуск', 'ассистент сгенерировал исправление, которое проходит happy path, но ломает авторизацию', 'ai', ['nistAi', 'playwright', 'owaspHtml'], ['AI', 'Качество']), theme(2025, 3, 'knowledge-retrieval', 'Знания', 'поиск по инженерной базе знаний', 'сделать источник и дату важнее уверенного пересказа', 'retrieval полезен только при явном корпусе, ссылке на источник и механизме обновления знаний', 'ответ на вопрос выглядит правдоподобно, но ссылается на устаревший документ', 'ai', ['nistAi'], ['AI', 'Документация']), theme(2025, 4, 'ai-data-privacy', 'AI и безопасность', 'данные и приватность в AI-инструментах', 'до отправки контекста понять, какие данные можно передавать внешнему сервису', 'данные, права доступа, срок хранения и журналирование должны быть частью выбора инструмента', 'в prompt попал фрагмент production-лога с персональными данными', 'ai', ['nistAi', 'owaspHtml'], ['AI', 'Безопасность']), theme(2025, 5, 'engineering-automation', 'Автоматизация', 'автоматизация рутины', 'выбрать процесс, который повторяется достаточно часто и имеет проверяемый результат', 'автоматизация безопасна, когда у неё есть вход, выход, идемпотентность и возможность отмены', 'скрипт быстро закрывает заявки, но иногда удаляет нужное поле из формы', 'ops', ['docker', 'playwright'], ['Автоматизация', 'Разработка']), theme(2025, 6, 'developer-experience', 'Developer Experience', 'удобство внутреннего инструмента', 'измерять путь разработчика от клона репозитория до первого полезного изменения', 'DX складывается из документации, скорости обратной связи, локальной среды и понятных ошибок', 'новичок тратит два дня на запуск проекта, хотя сама задача занимает час', 'writing', ['docker', 'playwright'], ['DX', 'Команда']), theme(2025, 7, 'product-metrics', 'Продукт', 'метрики продукта для инженера', 'использовать метрику как сигнал поведения, а не как повод оптимизировать цифру в вакууме', 'метрика нужна вместе с сегментом, периодом, ограничениями и возможной побочной ценой', 'конверсия выросла, но поддержка получила вдвое больше обращений', 'telemetry', ['otel', 'mdnPerformance'], ['Продукт', 'Аналитика']), theme(2025, 8, 'tool-ux-research', 'Инструменты', 'исследование UX внутреннего инструмента', 'наблюдать реальную работу коллег вместо предположения, где им неудобно', 'внутренний интерфейс тоже продукт: у него есть пользователи, сценарии, ошибки и стоимость времени', 'команда просит «сделать кнопку удобнее», но никто не видел, как они обходят её сейчас', 'writing', ['wcag', 'mdnPerformance'], ['Инструменты', 'UX']), theme(2025, 9, 'engineering-interviews', 'Команда', 'инженерное интервью', 'собирать опыт через конкретный контекст, решение и последствия, а не через абстрактный совет', 'хорошее интервью раскрывает ход мысли и ограничения, поэтому полезно другим инженерам', 'эксперт рассказывает о «лучшем подходе», но не упоминает, при каких условиях он не работает', 'writing', ['dGc', 'otel'], ['Команда', 'Интервью']), theme(2025, 10, 'teaching-engineering', 'Обучение', 'объяснение сложной темы', 'вести читателя от наблюдаемого симптома к модели и самостоятельной проверке', 'обучающая заметка ценна, когда человек может повторить результат и понять, почему он получился', 'после статьи читатель копирует команду, но не знает, что делать при другой ошибке', 'writing', ['playwright', 'mdnPerformance'], ['Обучение', 'Разработка']), theme(2025, 11, 'research-method', 'Исследование', 'проверка источников', 'разделять официальную документацию, наблюдение в проекте и личную гипотезу', 'дата, версия и контекст источника меняют смысл даже технически верного совета', 'популярный ответ из поиска повторяют годами, хотя API давно поменялся', 'writing', ['mdnModules', 'nistAi'], ['Исследование', 'Документация']), theme(2025, 12, 'year-synthesis', 'Развитие', 'синтез инженерного года', 'выделить несколько устойчивых принципов из десятков частных задач', 'синтез не заменяет детали, но показывает повторяющийся механизм за разными инцидентами', 'статьи за год выглядят разрозненными, хотя все они говорят о границах и обратной связи', 'writing', ['otel', 'nistAi'], ['Развитие', 'Автор']), theme(2026, 1, 'platform-api', 'Платформа', 'API платформенной команды', 'сделать внутренний сервис удобным для потребителя и управляемым для владельца', 'платформенный API — это контракт, документация, поддержка изменений и измеряемый уровень сервиса', 'команды начинают обходить внутренний сервис напрямую через базу данных', 'platform', ['kubernetes', 'otel'], ['Платформа', 'API']), theme(2026, 2, 'resilience', 'Надёжность', 'устойчивость к отказам', 'проектировать ожидаемые сбои зависимостей как штатный сценарий', 'timeout, retry, limit и fallback работают только вместе с наблюдением и ограничением нагрузки', 'внешний API отвечает медленно, и очередь запросов начинает валить собственное приложение', 'telemetry', ['otel', 'mdnFetch'], ['Надёжность', 'Backend']), theme(2026, 3, 'data-contracts', 'Данные', 'контракты данных', 'зафиксировать смысл полей, владельца и правила совместимости между системами', 'схема без семантики не защищает от неверной трактовки значения и времени его актуальности', 'поле status одинаково называется в двух сервисах, но означает разные стадии процесса', 'writing', ['postgres', 'mdnFetch'], ['Данные', 'Архитектура']), theme(2026, 4, 'modern-web-security', 'Безопасность', 'современная безопасность веба', 'соединить защиту браузера, API, сессии и цепочки доставки в одну модель', 'угроза проходит через границы: ввод, браузер, сеть, сервис, зависимость и оператор', 'исправление CORS не решило проблему, потому что endpoint всё ещё принимает чужой POST с cookie', 'security', ['owaspCsrf', 'owaspHtml', 'owaspUpload'], ['Безопасность', 'Web']), theme(2026, 5, 'systems-performance', 'Производительность', 'производительность системы', 'искать узкое место по измерению, а не оптимизировать самый знакомый компонент', 'задержка запроса складывается из очереди, сети, CPU, диска, базы и внешних вызовов', 'график CPU низкий, но пользователь всё равно ждёт страницу десять секунд', 'telemetry', ['otel', 'postgres', 'mdnPerformance'], ['Производительность', 'Надёжность']), theme(2026, 6, 'multi-runtime', 'Разработка', 'PHP, JavaScript и D в одном ландшафте', 'выбирать язык по границе задачи, а не пытаться сделать один стек универсальным', 'разные runtime полезны, если контракты, наблюдение и владение системой остаются едиными', 'часть обработки на D работает быстро, но её лог невозможно связать с PHP-запросом', 'd', ['dGc', 'otel', 'mdnModules'], ['PHP', 'JavaScript', 'DLang']), theme(2026, 7, 'migration-playbook', 'Миграции', 'план миграции', 'сделать переход обратимым и контролируемым через этапы и сигналы', 'миграция безопасна, когда старая и новая схема могут сосуществовать, а переключение измеримо', 'перенос авторизации уже начался, но команда не знает, как откатиться при сбое', 'writing', ['kubernetes', 'postgres'], ['Миграции', 'Архитектура']), theme(2026, 8, 'end-to-end-observability', 'Наблюдаемость', 'end-to-end наблюдаемость', 'связать пользовательское действие, браузерный тест, API и инфраструктурный trace', 'сигналы полезны, когда общий идентификатор сохраняется через все границы системы', 'e2e-тест упал, но невозможно быстро найти соответствующий backend-запрос', 'telemetry', ['otel', 'playwright'], ['Наблюдаемость', 'Тестирование']), theme(2026, 9, 'frontend-backend-boundary', 'Архитектура', 'граница frontend и backend', 'разделить представление, контракт и бизнес-правила без дублирования логики', 'граница определяется ответственностью за данные и решение, а не только HTTP-вызовом', 'правило скидки частично живёт в JavaScript, частично в PHP и расходится в редком сценарии', 'http', ['mdnFetch', 'postgres'], ['Frontend', 'Backend']), theme(2026, 10, 'code-review-standard', 'Качество', 'стандарт code review', 'сделать ревью способом снизить риск и передать контекст, а не соревнованием в замечаниях', 'хорошее ревью проверяет цель изменения, границы, тесты, наблюдение и будущую поддержку', 'в PR много комментариев по форматированию, но никто не заметил необратимую миграцию', 'writing', ['playwright', 'otel'], ['Качество', 'Команда']), theme(2026, 11, 'technology-evaluation', 'Архитектура', 'сравнение технологий без хайпа', 'выбирать инструмент по задаче, зрелости команды, ограничениям и стоимости выхода', 'технология не бывает лучшей вообще: у неё есть сильные стороны, риски и цена сопровождения', 'команда хочет заменить рабочую очередь, потому что увидела новый инструмент на конференции', 'writing', ['redis', 'kubernetes', 'dGc'], ['Архитектура', 'Инструменты']), theme(2026, 12, 'portfolio-case', 'Развитие', 'инженерный кейс от проблемы до результата', 'показать не только красивый финал, но и путь измерения, решения и последствий', 'хороший кейс сохраняет исходные условия и критерии успеха, иначе его нельзя перенести в другой контекст', 'в презентации есть новая архитектура, но нет данных, какую проблему она реально решила', 'writing', ['otel', 'mdnPerformance'], ['Развитие', 'Автор']), theme(2027, 1, 'debugging-decade', 'Практика', 'десять лет web-диагностики', 'собрать общий алгоритм поиска причины от симптома до проверяемой гипотезы', 'диагностика движется по слоям: пользовательский эффект, запрос, приложение, зависимость, среда', 'после многих часов правки кода выясняется, что проблема была в конфигурации прокси', 'writing', ['otel', 'mdnFetch'], ['Диагностика', 'Развитие']), theme(2027, 2, 'bitrix-lessons', 'Bitrix', 'уроки Bitrix для legacy-разработки', 'превратить опыт работы с большой CMS в общие принципы сопровождения зрелых систем', 'ценность legacy-системы в её реальных правилах и интеграциях, которые нельзя увидеть по одной папке исходников', 'новая команда хочет всё переписать, не составив карту событий, инфоблоков и пользовательских сценариев', 'bitrix', ['bitrix', 'postgres'], ['Bitrix', 'Legacy']), theme(2027, 3, 'd-lessons', 'D', 'уроки D для прикладного инженера', 'перенести привычку измерять память, типы и время выполнения в любой язык', 'системное мышление полезно даже в высокоуровневом стеке: оно задаёт вопросы о цене абстракции', 'проблема производительности объясняется не языком, а количеством скрытых выделений и копий данных', 'd', ['dGc', 'dMemory'], ['DLang', 'Производительность']), theme(2027, 4, 'build-evolution', 'Сборка', 'эволюция frontend-сборки', 'отделить полезные идеи модульности и кеширования от конкретной моды на инструмент', 'сборка существует, чтобы создать предсказуемый артефакт с понятным графом зависимостей', 'новая сборка быстрее, но команда не умеет диагностировать, почему в bundle попал лишний модуль', 'js', ['webpack', 'mdnModules'], ['JavaScript', 'Сборка']), theme(2027, 5, 'http-tls-guide', 'HTTP', 'полевой справочник HTTP и TLS', 'связать статусы, кеш, таймауты, сертификаты и поведение клиента в одной модели', 'протокол становится понятным, когда каждый симптом сопоставлен с уровнем, который его создаёт', 'ошибку 504 пытаются лечить браузером, хотя причина находится в upstream-сервисе', 'http', ['mdnCache', 'mdnFetch', 'owaspHtml'], ['HTTP', 'SSL']), theme(2027, 6, 'performance-capstone', 'Производительность', 'большой разбор производительности', 'показать путь от жалобы пользователя до ограничения, которое реально изменило время ответа', 'производительность — это последовательность измерений и гипотез, а не коллекция популярных оптимизаций', 'команда уменьшила bundle, а задержка не изменилась из-за медленного SQL-запроса', 'telemetry', ['mdnPerformance', 'postgres', 'otel'], ['Производительность', 'Кейс']), theme(2027, 7, 'reliability-capstone', 'Надёжность', 'большой разбор надёжности', 'собрать timeout, retry, очередь, мониторинг и восстановление в один сценарий', 'надёжность появляется из контролируемого поведения при сбоях, а не из надежды на отсутствие сбоев', 'при недоступности зависимости сервис создаёт лавину повторных запросов и ухудшает ситуацию', 'telemetry', ['otel', 'redis', 'kubernetes'], ['Надёжность', 'Кейс']), theme(2027, 8, 'security-capstone', 'Безопасность', 'большой разбор безопасности', 'показать защиту как последовательность решений от формы до production-окружения', 'безопасность требует слоёв: проверка ввода, права, сессия, журналирование и безопасная доставка', 'одно исправление XSS не помогло, потому что загруженные файлы всё ещё доступны из webroot', 'security', ['owaspUpload', 'owaspCsrf', 'owaspHtml'], ['Безопасность', 'Кейс']), theme(2027, 9, 'mentor-series', 'Наставничество', 'серия для инженера, который растёт', 'дать человеку не список технологий, а способ ставить вопросы и проверять выводы', 'рост инженера ускоряется через малые эксперименты, обратную связь и письменную фиксацию знания', 'junior копирует решения, но не понимает, по каким признакам выбирать следующий шаг', 'writing', ['playwright', 'mdnPerformance'], ['Наставничество', 'Развитие']), theme(2027, 10, 'long-form-interview', 'Интервью', 'длинное техническое интервью', 'раскрыть решение через реальные ограничения, а не через набор победных формулировок', 'история технологии становится полезной, когда в ней видны сомнения, отказы и критерии выбора', 'спикер рассказывает о микросервисах, но интереснее узнать, почему команда не пошла этим путём раньше', 'writing', ['dGc', 'otel'], ['Интервью', 'Разработка']), theme(2027, 11, 'mistakes-revisions', 'Ретроспектива', 'пересмотр старых советов', 'исправлять собственные публикации и показывать, что новое знание меняет практику', 'инженерная зрелость проявляется не в безошибочности, а в способности обновить вывод при новых данных', 'старый совет всё ещё популярен, хотя за годы изменились браузеры, инструменты и угрозы', 'writing', ['mdnModules', 'owaspHtml'], ['Ретроспектива', 'Автор']), theme(2027, 12, 'author-manifesto', 'Автор', 'манифест инженерного письма', 'зафиксировать принципы блога: личный опыт, проверка, ограничения и уважение к читателю', 'техническая статья соединяет факт, модель, действие и честное описание неопределённости', 'после десяти лет публикаций нужно решить, какие темы больше не стоит писать без собственного опыта', 'writing', ['nistAi', 'otel'], ['Автор', 'Развитие']), ]; const covers = { bitrix: '/assets/illustrations/bitrix-photo-editor.svg', php: '/assets/illustrations/php-ssl-cacert.svg', js: '/assets/illustrations/jquery-webpack.svg', http: '/assets/illustrations/firefox-timeout.svg', docker: '/assets/illustrations/vibe-cover.svg', ops: '/assets/illustrations/tilix-cover.svg', sql: '/assets/illustrations/vibe-db.svg', redis: '/assets/illustrations/vibe-async.svg', d: '/assets/illustrations/dconf-gc-cover.svg', frontend: '/assets/illustrations/vibe-web.svg', security: '/assets/illustrations/cover-virus.svg', test: '/assets/illustrations/tilix-screenshot.svg', telemetry: '/assets/illustrations/gc-small-pools.svg', platform: '/assets/illustrations/vibe-api.svg', ai: '/assets/illustrations/ctfe-engine.svg', writing: '/assets/illustrations/tilix-cover.svg', }; function pad(value) { return String(value).padStart(2, '0'); } function escapeHtml(value) { return String(value) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function paragraph(text) { return '
' + text + '
'; } function heading(text) { return '' + escapeHtml(text) + '';
}
function list(items, ordered) {
const tag = ordered ? 'ol' : 'ul';
return '<' + tag + '>' + items.map((item) => '