<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Блог Pokidov.dev</title>
    <link>https://pokidov.dev/blog</link>
    <description>Инженерные заметки, разборы кейсов и продуктовые статьи команды Pokidov.dev.</description>
    <language>ru</language>
    <atom:link xmlns:atom="http://www.w3.org/2005/Atom" href="https://pokidov.dev/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Astro Content Layer на практике: как мы готовим фронтенд к реальному API</title>
      <link>https://pokidov.dev/blog/astro-content-layer-na-praktike</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/astro-content-layer-na-praktike</guid>
      <pubDate>Sat, 15 Aug 2026 06:00:00 GMT</pubDate>
      <description>Разбираем, зачем нужен один свап-поинт между фикстурами и боевым Content API, и почему Content Layer — это не просто ещё один способ читать JSON.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Когда фронтенд собирается статически, а бэкенд ещё не готов, встаёт классический вопрос: <strong>что считать источником правды</strong> во время разработки?</p>
<p>Мы решили эту задачу один раз — на уровне загрузчика коллекций, а не разбрасывая проверки <code>if (isDev)</code> по компонентам.</p>
<h2>Зачем нужен единый свап-поинт</h2>
<p>Идея простая: у каждой коллекции есть <em>ровно одна</em> функция-источник. Сегодня она читает JSON-фикстуру, завтра — дергает Content API Laravel. Компоненты об этом не знают.</p>
<!-- src/content/loaders/content-source.ts --><pre><code>export function contentSource(entity: ContentEntity): Loader {
  if (USE_FIXTURES) {
    return file(`src/content/fixtures/${entity}.json`);
  }
  return contentApiLoader(entity);
}</code></pre>
<blockquote><strong>Почему не просто fetch() в компоненте</strong><br/>Loader выполняется один раз на сборку и результат кешируется Content Layer — на 40 страницах не будет 40 одинаковых запросов.</blockquote>
<h2>Что даёт схема Zod</h2>
<ul><li>Ошибка формата ловится в момент <code>astro build</code>, а не после деплоя.</li><li>IDE знает точные поля коллекции — автодополнение работает как для обычного TypeScript.</li><li>Опциональные поля явно помечены — не нужно гадать, может ли значение быть <code>null</code>.</li></ul>
<table><thead><tr><th>Источник</th><th>Когда используется</th><th>Задержка сборки</th></tr></thead><tbody><tr><td>Фикстуры (JSON)</td><td>Локальная разработка, CI без бэкенда</td><td>~0 мс</td></tr><tr><td>Content API</td><td>Продакшн-сборка, preview-окружения</td><td>зависит от сети</td></tr></tbody></table>
<figure><img src="https://pokidov.dev/_astro/content-1.Bc8jeRcs_2nc9rn.jpeg" alt="Схема потока данных от Content API к статическим страницам Astro" /><figcaption>Один и тот же контракт данных — что в фикстуре, что в ответе API.</figcaption></figure>
<blockquote><p>Лучший рефакторинг — тот, который не потребовался, потому что архитектура заранее оставила для него один шов.</p><cite>из внутренней переписки команды</cite></blockquote>
<hr/>
<h3>Что дальше</h3>
<p>В следующих раундах мы просто меняем <code>USE_FIXTURES</code> на <code>false</code> и реализуем <code>contentApiLoader()</code> — ни одна страница блога, услуг или проектов не потребует правок.</p>
<h2>Ключевые выводы</h2><ul><li>Content Layer в Astro 5+ — универсальный интерфейс загрузчика, а не просто обёртка над файловой системой.</li><li>Один «свап-поинт» между фикстурами и реальным API избавляет страницы от условной логики.</li><li>Схема Zod ловит несовпадения формата данных на этапе сборки, а не в рантайме у пользователя.</li><li>Собственный Loader пишется один раз и переиспользуется для всех коллекций проекта.</li></ul>
<h2>Частые вопросы</h2><h3>Чем Content Layer отличается от старых Content Collections?</h3><p>Старые Content Collections были жёстко привязаны к файлам в src/content. Content Layer вводит интерфейс Loader — источником данных может быть файл, API или база, а схема остаётся общей.</p><h3>Нужно ли переписывать компоненты при смене источника данных?</h3><p>Нет — если контракт (форма данных, описанная схемой) не меняется, компоненты продолжают работать без правок.</p>
<p><strong>Нужен похожий контур для вашего проекта?</strong><br/>Спроектируем фронтенд так, чтобы бэкенд можно было подключить в любой момент без переписывания страниц.<br/><a href="/contacts#form">Обсудить проект</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Laravel 12 + Filament 4: как мы собираем админ-панели без лишнего кода</title>
      <link>https://pokidov.dev/blog/laravel-12-filament-4-admin-panel</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/laravel-12-filament-4-admin-panel</guid>
      <pubDate>Tue, 28 Jul 2026 06:00:00 GMT</pubDate>
      <description>Filament 4 закрывает 80% типовых экранов CRUD из коробки. Показываем, где заканчивается конфигурация и начинается настоящая разработка.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Ещё пять лет назад типовая админка отнимала недели на CRUD-экраны. Filament 4 поверх Laravel 12 сокращает эту работу до конфигурации.</p>
<h2>Anatomy ресурса</h2>
<p>Один класс <code>Resource</code> описывает форму, таблицу и — через <code>Schemas</code> — сложные составные поля вроде блочного редактора статей.</p>
<!-- app/Filament/Resources/Posts/PostResource.php --><pre><code>class PostResource extends Resource
{
    protected static ?string $model = Post::class;

    public static function form(Schema $schema): Schema
    {
        return $schema-&gt;components([
            PostForm::make(),
        ]);
    }
}</code></pre>
<ol><li>Модель и миграции — как в обычном Laravel-проекте.</li><li>Policy определяет, кто видит ресурс и какие действия доступны.</li><li>Schema/Table классы описывают форму и таблицу декларативно.</li><li>Actions добавляют кастомную логику там, где конфигурации не хватает.</li></ol>
<blockquote><strong>Авторизация — не декорация</strong><br/>Policy проверяется и в UI, и при прямом обращении к модели — скрыть кнопку недостаточно, если политика не защищает сам метод.</blockquote>
<table><thead><tr><th>Задача</th><th>Решение из коробки</th><th>Нужен кастом</th></tr></thead><tbody><tr><td>Сортировка таблицы</td><td>да</td><td>нет</td></tr><tr><td>Блочный редактор контента</td><td>частично (Builder)</td><td>да, доменные блоки</td></tr><tr><td>2FA</td><td>да (App Authentication)</td><td>нет</td></tr></tbody></table>
<hr/>
<p>Итог: Filament экономит время именно там, где раньше терялись недели — на однотипных CRUD-экранах. Домен-специфичную логику всё равно пишете вы.</p>
<h2>Ключевые выводы</h2><ul><li>Filament 4 Resource — это декларативное описание формы, таблицы и политики доступа в одном классе.</li><li>Policy-классы Laravel и авторизация Filament работают вместе без дублирования правил.</li><li>Кастомные Actions нужны реже, чем кажется — сначала стоит проверить встроенные.</li><li>Тесты на уровне Resource дешевле, чем ручная проверка каждого релиза.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Индексы PostgreSQL на практике: где они помогают, а где вредят</title>
      <link>https://pokidov.dev/blog/postgres-indeksy-na-praktike</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/postgres-indeksy-na-praktike</guid>
      <pubDate>Thu, 09 Jul 2026 06:00:00 GMT</pubDate>
      <description>Не каждое поле в WHERE нужно индексировать. Разбираем частые ошибки на реальных запросах из наших проектов.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Индекс — это не бесплатное ускорение. Каждая запись в таблицу теперь пишет ещё и в индекс, а на диске индекс занимает место, сопоставимое с самой таблицей.</p>
<h2>Когда индекс действительно нужен</h2>
<ul><li>Поле часто участвует в <code>WHERE</code>, <code>JOIN</code> или <code>ORDER BY</code>.</li><li>Таблица достаточно большая, чтобы последовательное сканирование было заметно дороже.</li><li>Селективность высокая — индекс по полю <code>status</code> с тремя значениями почти бесполезен.</li></ul>
<!-- explain.sql --><pre><code>EXPLAIN ANALYZE
SELECT * FROM posts
WHERE status = 'published'
  AND published_at &lt;= now()
ORDER BY published_at DESC
LIMIT 12;</code></pre>
<blockquote><p>Не добавляйте индекс, потому что «так надёжнее». Добавляйте, потому что EXPLAIN это подтвердил.</p><cite>внутренний код-ревью гайд</cite></blockquote>
<table><thead><tr><th>Тип индекса</th><th>Хорош для</th><th>Плох для</th></tr></thead><tbody><tr><td>B-tree</td><td>равенство, диапазоны, сортировка</td><td>полнотекстовый поиск</td></tr><tr><td>GIN</td><td>JSONB, массивы, полнотекст</td><td>частые обновления строки</td></tr><tr><td>Частичный</td><td>узкий фильтр (например, status)</td><td>запросы без этого условия</td></tr></tbody></table>
<hr/>
<blockquote><strong>Частая ошибка</strong><br/>Композитный индекс (a, b) не используется для запросов только по b — порядок колонок в индексе имеет значение.</blockquote>
<p>Итог простой: сначала <code>EXPLAIN ANALYZE</code>, потом индекс, потом снова <code>EXPLAIN ANALYZE</code> — чтобы убедиться, что план запроса действительно изменился.</p>
<h2>Ключевые выводы</h2><ul><li>Индекс ускоряет чтение и замедляет запись — добавлять его «на всякий случай» нельзя.</li><li>B-tree подходит для равенства и диапазонов, GIN — для JSONB и полнотекстового поиска.</li><li>EXPLAIN ANALYZE — обязательный шаг перед добавлением индекса, а не после.</li><li>Частичные индексы экономят место, если запросы всегда фильтруют по одному условию.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>SEO-чек-лист для B2B-сайтов: что реально влияет на трафик</title>
      <link>https://pokidov.dev/blog/seo-checklist-dlya-b2b-saitov</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/seo-checklist-dlya-b2b-saitov</guid>
      <pubDate>Sat, 20 Jun 2026 06:00:00 GMT</pubDate>
      <description>Большая часть SEO-советов для B2B — это карго-культ. Собрали шорт-лист того, что действительно двигает позиции в нашей практике.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>B2B-сайты редко выигрывают SEO объёмом контента — у них просто нет тысяч страниц, как у маркетплейса. Зато у них есть экспертность, которую можно правильно показать поисковику.</p>
<h2>Техническая база</h2>
<ol><li>Чистые URL без параметров для основных страниц.</li><li>Canonical на каждой странице, включая отфильтрованные состояния.</li><li><code>BreadcrumbList</code> и <code>FAQPage</code> JSON-LD там, где это уместно.</li><li>Изображения — только через современные форматы (AVIF/WebP) с явным <code>width</code>/<code>height</code>.</li></ol>
<blockquote><strong>Про Core Web Vitals</strong><br/>LCP, INP и CLS — не абстрактные метрики для отчётов, а прямой фактор ранжирования с 2021 года.</blockquote>
<h2>Контент, который решает</h2>
<p>Одна статья, отвечающая на реальный вопрос клиента <strong>полно и честно</strong>, приносит больше трафика, чем десять статей «ниочём» под ключевые слова.</p>
<hr/>
<p>Дальше — вопросы, которые нам задают чаще всего.</p>
<h2>Ключевые выводы</h2><ul><li>Структурированные данные (JSON-LD) не двигают позиции напрямую, но повышают CTR через rich-сниппеты.</li><li>Скорость первой отрисовки важнее количества ключевых слов на странице.</li><li>Внутренняя перелинковка — самый недооценённый инструмент B2B SEO.</li><li>Уникальный контент побеждает объём: 5 сильных статей лучше 50 сгенерированных.</li></ul>
<h2>Частые вопросы</h2><h3>Нужен ли отдельный SEO-специалист на старте проекта?</h3><p>Нет — на старте важнее правильная техническая база (структура URL, скорость, семантическая разметка); специалист нужен для контентной стратегии позже.</p><h3>Сколько времени занимает эффект от SEO-правок?</h3><p>В B2B — от 2 до 6 месяцев, в зависимости от конкурентности ниши и текущего технического состояния сайта.</p><h3>Влияет ли дизайн на SEO?</h3><p>Косвенно — через поведенческие метрики (время на сайте, отказы) и Core Web Vitals, которые дизайн и вёрстка формируют напрямую.</p>]]></content:encoded>
    </item>
    <item>
      <title>Дизайн-система с нуля: с чего начать, чтобы не переделывать через полгода</title>
      <link>https://pokidov.dev/blog/dizain-sistema-s-nulya</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/dizain-sistema-s-nulya</guid>
      <pubDate>Sat, 30 May 2026 06:00:00 GMT</pubDate>
      <description>Токены, компоненты, документация — в каком порядке их строить, чтобы дизайн-система не развалилась при первом крупном редизайне.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Самая частая ошибка при запуске дизайн-системы — начать с компонентов, а не с токенов. Тогда через полгода у вас три оттенка «основного синего» в разных местах.</p>
<h2>Порядок слоёв</h2>
<ol><li>Токены: цвет, типографика, отступы, радиусы, тайминги анимаций.</li><li>Примитивы: кнопка, поле ввода, чип, карточка.</li><li>Композиты: форма, карточка проекта, модальное окно.</li><li>Паттерны страниц: как композиты складываются в реальные экраны.</li></ol>
<div><img src="https://pokidov.dev/_astro/content-2.GIxjhAcD_ZeunFh.jpeg" alt="Палитра токенов дизайн-системы — тёмная тема" /><img src="https://pokidov.dev/_astro/content-3.C530pwJV_ZeQOU.jpeg" alt="Палитра токенов дизайн-системы — светлая тема" /><img src="https://pokidov.dev/_astro/content-4.mwzbpzTf_2upnDa.jpeg" alt="Сетка компонентов: кнопки, чипы, поля ввода" /></div>
<blockquote><strong>Контраст — не опция</strong><br/>Каждая пара «текст на фоне» в токенах должна проходить минимум AA (4.5:1) — иначе доступность придётся чинить точечно на каждой странице.</blockquote>
<figure><img src="https://pokidov.dev/_astro/content-5.WrespwZ7_i1EFn.jpeg" alt="Пример карточки, собранной из примитивов дизайн-системы" /><figcaption>Карточка — это композиция из уже существующих токенов и примитивов, не новый набор стилей.</figcaption></figure>
<h3>Документация</h3>
<p>Живой пример с переключателем состояний экономит часы созвонов «а как это должно выглядеть при ошибке».</p>
<h2>Ключевые выводы</h2><ul><li>Дизайн-токены — это первый слой, а не последний штрих: без них компоненты негде переиспользовать согласованно.</li><li>Начинать стоит с 5–7 самых частых компонентов, а не пытаться описать всё сразу.</li><li>Доступность (контраст, фокус-состояния) дешевле заложить в токены, чем чинить постфактум.</li><li>Документация с живыми примерами используется чаще, чем статичный PDF-гайдлайн.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>UX-исследования перед релизом: минимальный набор, который окупается</title>
      <link>https://pokidov.dev/blog/ux-issledovaniya-pered-relizom</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/ux-issledovaniya-pered-relizom</guid>
      <pubDate>Mon, 11 May 2026 06:00:00 GMT</pubDate>
      <description>Не каждый проект может себе позволить полноценную исследовательскую лабораторию. Что делать, если бюджет — три дня и пять респондентов.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>«У нас нет бюджета на исследования» — самая частая причина, по которой в продакшн уходят интерфейсы, которые никто не тестировал на живых людях.</p>
<h2>Минимальный набор</h2>
<ul><li>5 респондентов из целевой аудитории — не коллеги и не друзья.</li><li>Сценарий из 3–5 задач, которые действительно решает продукт.</li><li>Кликабельный прототип, а не финальная вёрстка.</li></ul>
<blockquote><p>Пользователь не обязан понимать вашу ментальную модель интерфейса. Ваша задача — понять его.</p><cite>из внутреннего гайда по исследованиям</cite></blockquote>
<p><a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">Видео: https://www.youtube.com/watch?v=dQw4w9WgXcQ</a></p>
<h3>Что делать с результатами</h3>
<p>Каждая найденная проблема получает приоритет по формуле <em>частота × серьёзность</em> — это отсекает соблазн чинить редкую, но заметную мелочь раньше системной проблемы.</p>
<h2>Ключевые выводы</h2><ul><li>Пять пользователей находят около 85% проблем юзабилити — дальше эффект от каждого нового теста падает.</li><li>Модерируемое тестирование дороже, но даёт понимание причины проблемы, а не только её факта.</li><li>Тестировать нужно на прототипе, а не после релиза — иначе исправление обходится в разы дороже.</li><li>Запись сессии важнее финального отчёта — команда должна увидеть проблему своими глазами.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как мы оцениваем проекты: от брифа до вилки в смете</title>
      <link>https://pokidov.dev/blog/kak-my-otsenivaem-proekty</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/kak-my-otsenivaem-proekty</guid>
      <pubDate>Wed, 22 Apr 2026 06:00:00 GMT</pubDate>
      <description>Оценка «на глаз» почти всегда ошибается в 1.5–2 раза. Показываем, как мы декомпозируем задачу перед тем, как называть сроки и бюджет.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Клиент почти всегда спрашивает точную цифру в первую неделю знакомства с проектом — и почти всегда это худший момент, чтобы её называть.</p>
<h2>Как устроена декомпозиция</h2>
<table><thead><tr><th>Этап</th><th>Что делаем</th><th>Результат</th></tr></thead><tbody><tr><td>Бриф</td><td>Собираем цели, ограничения, интеграции</td><td>Список открытых вопросов</td></tr><tr><td>Декомпозиция</td><td>Разбиваем на модули и экраны</td><td>Список задач с диапазоном часов</td></tr><tr><td>Вилка сметы</td><td>Суммируем диапазоны, добавляем риск</td><td>Итоговая вилка бюджета и сроков</td></tr></tbody></table>
<ol><li>Фиксируем цели проекта в терминах бизнеса, не технологий.</li><li>Раскладываем по модулям: авторизация, каталог, оплата и так далее.</li><li>На каждый модуль — диапазон часов, а не единственное число.</li><li>Отдельно считаем риск интеграций с чужими системами.</li></ol>
<blockquote><strong>Почему вилка, а не число</strong><br/>Диапазон в 20–30% честнее отражает неопределённость технического задания на старте, чем ложно точная цифра.</blockquote>
<h2>Ключевые выводы</h2><ul><li>Оценка без декомпозиции на модули — это гадание, а не расчёт.</li><li>Вилка в смете (а не точное число) честнее отражает неопределённость на старте.</li><li>Технический риск (интеграции, легаси) закладывается отдельной строкой, а не «про запас».</li><li>Оценка пересматривается после первого спринта, когда неопределённость уже ниже.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Кейс: CRM для сети автосервисов — от заявки до записи на ремонт за 90 секунд</title>
      <link>https://pokidov.dev/blog/keis-crm-dlya-seti-avtoservisov</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/keis-crm-dlya-seti-avtoservisov</guid>
      <pubDate>Mon, 30 Mar 2026 06:00:00 GMT</pubDate>
      <description>Разбираем, как мы спроектировали CRM, которая сократила время обработки заявки с 8 минут до полутора и снизила количество потерянных клиентов.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Сеть из 12 автосервисов принимала заявки в четырёх разных каналах: сайт, телефон, WhatsApp и офлайн-визиты. У каждого сервиса — своя тетрадь записи.</p>
<h2>Задача</h2>
<p>Собрать все заявки в одном месте, автоматически распределять их между сервисами по загрузке и не терять ни одного обращения.</p>
<figure><img src="https://pokidov.dev/_astro/content-6.BGqICa0P_gclYV.jpeg" alt="Экран очереди заявок в CRM для сети автосервисов" /><figcaption>Все входящие заявки — в одной очереди, независимо от канала.</figcaption></figure>
<div><img src="https://pokidov.dev/_astro/content-7.BerjyUQV_ZcDgoX.jpeg" alt="Карточка заявки с историей коммуникации по клиенту" /><img src="https://pokidov.dev/_astro/content-8.C0km00V4_1jwdM9.jpeg" alt="Панель загрузки мастеров по сервисам сети" /></div>
<h2>Решение</h2>
<ul><li>Единая очередь заявок с автораспределением по загрузке сервиса.</li><li>Синхронизация с 1С по расписанию (каждые 15 минут) вместо вебхуков.</li><li>Уведомления мастеру и клиенту через один и тот же шаблон в разных каналах.</li></ul>
<table><thead><tr><th>Метрика</th><th>До</th><th>После</th></tr></thead><tbody><tr><td>Время обработки заявки</td><td>8 минут</td><td>1.5 минуты</td></tr><tr><td>Потерянные заявки в месяц</td><td>~40</td><td>~3</td></tr><tr><td>Время до первого ответа</td><td>25 минут</td><td>4 минуты</td></tr></tbody></table>
<blockquote><p>Мы перестали терять клиентов между сервисами — теперь заявка не «зависает» ни в одной тетради.</p><cite>операционный директор сети</cite></blockquote>
<h2>Ключевые выводы</h2><ul><li>Единая очередь заявок из всех каналов (сайт, телефония, мессенджеры) убрала «потерю» лидов между менеджерами.</li><li>Автоматическое распределение по загрузке мастеров сократило время до первого ответа клиенту.</li><li>Интеграция с 1С по расписанию, а не в реальном времени, оказалась надёжнее и дешевле в поддержке.</li><li>Отчёты для владельца сети стали ежедневными, а не «раз в квартал в Excel».</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Vue-острова и бюджет гидратации: сколько JS можно позволить на статичном сайте</title>
      <link>https://pokidov.dev/blog/vue-islands-i-byudzhet-gidratatsii</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/vue-islands-i-byudzhet-gidratatsii</guid>
      <pubDate>Thu, 05 Mar 2026 06:00:00 GMT</pubDate>
      <description>Каждый Vue-компонент на странице — это килобайты рантайма. Разбираем, как мы выбираем директиву гидратации и считаем бюджет вручную.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Архитектура «острова» — это компромисс: HTML рендерится статически, а интерактивность подключается точечно, только там, где она нужна.</p>
<h2>Директивы гидратации</h2>
<!-- ProjectsPage.astro --><pre><code>&lt;ProjectsGrid client:load categories={categories}&gt;
  &lt;!-- статическая разметка карточек, доступна без JS --&gt;
&lt;/ProjectsGrid&gt;
&lt;CaseModal client:idle /&gt;</code></pre>
<ul><li><code>client:load</code> — гидратация сразу, для критичного интерактива.</li><li><code>client:idle</code> — когда браузер свободен, для второстепенного интерактива.</li><li><code>client:visible</code> — при появлении в вьюпорте, лучший вариант по умолчанию.</li><li><code>client:media</code> — только при совпадении медиа-запроса (например, мобильное меню).</li></ul>
<blockquote><strong>Частая ошибка</strong><br/><code>client:load</code> на каждом острове «на всякий случай» — самый быстрый способ выйти за бюджет JS на первой загрузке.</blockquote>
<hr/>
<p>Мы проверяем бюджет вручную после каждого раунда: суммарный gzip JS первой загрузки не должен превышать ~70 КБ, из них ~35 КБ — рантайм Vue.</p>
<h2>Ключевые выводы</h2><ul><li>client:visible откладывает гидратацию до появления в вьюпорте — почти всегда правильный выбор по умолчанию.</li><li>client:load оправдан только для того, что обязано работать сразу же (мобильное меню, критичная форма).</li><li>Общий бюджет первой загрузки стоит проверять руками на каждой новой странице, а не раз в квартал.</li><li>Статическая разметка передаётся в слот компонента — так контент остаётся доступным без JS вообще.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Доступность веб-приложений: чек-лист, который проверяем перед каждым релизом</title>
      <link>https://pokidov.dev/blog/dostupnost-veb-prilozhenii-checklist</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/dostupnost-veb-prilozhenii-checklist</guid>
      <pubDate>Tue, 20 Jan 2026 06:00:00 GMT</pubDate>
      <description>Доступность — не разовая доработка «для галочки», а список проверок, который выполняется на каждом релизе. Делимся своим чек-листом.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>Доступность легко превращается в разовую задачу «прогнать аудит перед сдачей». Через два релиза регрессии возвращаются — потому что процесс её не удерживает.</p>
<h2>Чек-лист на каждый релиз</h2>
<ol><li>Все интерактивные элементы доступны с клавиатуры (Tab/Shift+Tab, Enter/Space).</li><li>Фокус видим и не теряется при открытии/закрытии модалок.</li><li>Контраст текста — минимум AA (4.5:1 для обычного текста).</li><li>Изображения имеют осмысленный <code>alt</code> или помечены декоративными.</li><li>Формы связывают лейбл, поле и текст ошибки через <code>aria-describedby</code>.</li></ol>
<blockquote><strong>Модалки — самое частое место регрессий</strong><br/>Фокус-трап, закрытие по Esc и возврат фокуса на источник — три вещи, которые чаще всего забывают при добавлении нового модального окна.</blockquote>
<hr/>
<p>Этот чек-лист живёт в репозитории рядом с кодом, а не в отдельном документе — иначе про него забывают уже на втором проекте.</p>
<h2>Ключевые выводы</h2><ul><li>Фокус-состояния должны быть видны на каждом интерактивном элементе, без исключений.</li><li>Модальные окна обязаны ловить фокус и возвращать его на элемент-триггер при закрытии.</li><li>Контраст текста проверяется автоматически в CI, а не вручную перед релизом.</li><li>Скринридер-тестирование хотя бы одного ключевого сценария снимает 90% проблем, незаметных глазами.</li></ul>
<h2>Частые вопросы</h2><h3>С чего начать, если аудита доступности никогда не было?</h3><p>С автоматической проверки (axe-core) на ключевых страницах — это находит примерно треть проблем за один прогон.</p><h3>Нужен ли скринридер-тест для каждого релиза?</h3><p>Нет — достаточно для крупных изменений интерфейса; мелкие правки покрывает автоматика и ручная проверка фокуса/контраста.</p>
<p><strong>Хотите бесплатный экспресс-аудит доступности?</strong><br/>Проверим ключевые сценарии вашего продукта по этому чек-листу и пришлём список находок.<br/><a href="/contacts#form">Запросить аудит</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Дизайн API: чем REST-как-получится хуже осознанной конвенции</title>
      <link>https://pokidov.dev/blog/api-dizain-rest-vs-json-api</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/api-dizain-rest-vs-json-api</guid>
      <pubDate>Tue, 18 Nov 2025 06:00:00 GMT</pubDate>
      <description>Большинство «REST API» в реальных проектах — это набор эндпоинтов без единой конвенции. Показываем, как мы стандартизируем контракт.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>«REST API» без осознанной конвенции — это на практике набор эндпоинтов, где каждый называется и пагинируется по-своему. Фронтенду приходится помнить исключения для каждого ресурса.</p>
<h2>Что мы фиксируем заранее</h2>
<table><thead><tr><th>Аспект</th><th>Конвенция</th></tr></thead><tbody><tr><td>Именование ресурсов</td><td>множественное число, kebab-case в URL</td></tr><tr><td>Пагинация</td><td>единый формат meta.page/meta.total на всех списках</td></tr><tr><td>Ошибки валидации</td><td>errors.{field} — как отдаёт Laravel Form Request</td></tr><tr><td>Даты</td><td>ISO 8601 в UTC, без исключений</td></tr></tbody></table>
<!-- types/content-api.ts --><pre><code>export interface PostResource {
  slug: string;
  category: { slug: string; title: string };
  author: { name: string; position: string; same_as: string[] };
  published_at: string; // ISO 8601
}</code></pre>
<blockquote><strong>Зачем типизировать ответ API на фронтенде</strong><br/>TypeScript-интерфейс, синхронизированный с Resource-классом бэкенда, ловит несовпадение полей на этапе компиляции, а не в проде.</blockquote>
<h2>Ключевые выводы</h2><ul><li>Единая конвенция именования и пагинации экономит часы на онбординге фронтенд-разработчика.</li><li>Ресурсные классы (Laravel API Resources) — естественная точка контроля версии ответа.</li><li>Явные коды ошибок с машинно-читаемым полем важнее «человеческого» текста сообщения.</li><li>Один и тот же формат ответа для фикстур и боевого API избавляет фронтенд от адаптеров.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Микросервисы или модульный монолит: что мы выбираем для проектов уровня B2B</title>
      <link>https://pokidov.dev/blog/mikroservisy-ili-modulnyi-monolit</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/mikroservisy-ili-modulnyi-monolit</guid>
      <pubDate>Tue, 02 Sep 2025 06:00:00 GMT</pubDate>
      <description>Микросервисы решают проблему масштаба команды, а не проблему производительности. Разбираем, когда монолит — осознанный выбор, а не компромисс.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>«У нас будут микросервисы» — фраза, которую мы слышим на старте почти каждого второго проекта, даже когда команда — три разработчика.</p>
<h2>Какую проблему решают микросервисы</h2>
<ul><li>Независимый деплой разных частей системы разными командами.</li><li>Изоляция отказа одного сервиса от остальной системы.</li><li>Возможность использовать разные технологии под разные задачи.</li></ul>
<blockquote><p>Микросервисы — это решение проблемы Конвея, а не проблемы производительности.</p><cite>парафраз известного тезиса в архитектурных обсуждениях</cite></blockquote>
<blockquote><strong>Модульный монолит</strong><br/>Чёткие границы между доменами внутри одного деплоя дают большую часть изоляции микросервисов без сетевых издержек и отдельной инфраструктуры на сервис.</blockquote>
<h2>Ключевые выводы</h2><ul><li>Микросервисы решают организационную проблему (много команд), а не техническую (нагрузка).</li><li>Модульный монолит с чёткими границами между доменами даёт почти все плюсы микросервисов без сетевых издержек.</li><li>Разделение на сервисы имеет смысл, когда команды физически не могут деплоиться независимо иначе.</li><li>Миграция монолит → сервисы дешевле, чем обратная — начинать с монолита почти всегда безопаснее.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как читать продуктовую аналитику, не обманывая самого себя</title>
      <link>https://pokidov.dev/blog/kak-chitat-analitiku-produkta</link>
      <guid isPermaLink="true">https://pokidov.dev/blog/kak-chitat-analitiku-produkta</guid>
      <pubDate>Sat, 14 Jun 2025 06:00:00 GMT</pubDate>
      <description>Рост конверсии на 2% может быть шумом, а не результатом. Разбираем, как мы проверяем значимость изменений перед тем, как праздновать.</description>
      <dc:creator>Александр Покидов</dc:creator>
      <content:encoded><![CDATA[<p>«Конверсия выросла на 2% после редизайна» — фраза, которая ничего не значит без размера выборки и доверительного интервала.</p>
<h2>Минимальный набор проверок</h2>
<table><thead><tr><th>Вопрос</th><th>Зачем спрашивать</th></tr></thead><tbody><tr><td>Какой размер выборки?</td><td>Малая выборка — почти любое изменение «значимо» на глаз, но не статистически</td></tr><tr><td>Что ещё менялось в этот период?</td><td>Сезонность и параллельные изменения искажают атрибуцию</td></tr><tr><td>Одинаково ли распределены сегменты?</td><td>Разный трафик по каналам может сам объяснить разницу метрик</td></tr></tbody></table>
<ol><li>Зафиксировать гипотезу и метрику успеха до запуска изменения.</li><li>Посчитать минимальный размер выборки заранее.</li><li>Смотреть на доверительный интервал, а не только на точечное значение.</li></ol>
<h2>Ключевые выводы</h2><ul><li>Изменение метрики без проверки статистической значимости — это чаще шум, чем эффект.</li><li>Минимальный размер выборки нужно считать до теста, а не после, глядя на результат.</li><li>Сегментация метрики (по устройству, каналу) часто важнее общего среднего значения.</li><li>Дашборд без контекста (что менялось и когда) провоцирует неверные выводы.</li></ul>]]></content:encoded>
    </item>
  </channel>
</rss>
