Я соло-фаундер и единственный разработчик нишевого B2B-продукта. Лендинги для него я переделывал не раз: новая гипотеза — новая посадочная, под кампанию — ещё одна, плюс блог. Каждый раз повторялась одна и та же возня: не забыть мета-теги и Open Graph, прикрутить аналитику, не сломать индексацию, выкатить, проверить. Claude Code заметно ускорял это, но каждый запуск был «с нуля» — качество зависело от того, насколько удачно я в этот раз сформулировал промпт.
В какой-то момент я понял, что повторяю один и тот же процесс, и его можно зафиксировать. Так получилась Landforge — небольшая система Claude Code skills, которая доводит лендинг от брифа до задеплоенной измеримой страницы. Я обезличил её, отвязал от своего продукта и выложил в open-source (MIT). В статье — как она устроена и какие решения за этим стоят. Ссылка на репозиторий в конце; сначала — польза.
Коротко: что такое Claude Code skills
Skill — это папка с файлом SKILL.md (инструкция + when-to-use) и, опционально, вспомогательными файлами: справочниками, шаблонами, скриптами. Когда вы вызываете /skill-name, ассистент подхватывает эти инструкции и выполняет задачу по ним. Это способ превратить разовый удачный промпт в воспроизводимый процесс: не «как повезёт сегодня», а по контракту, с самопроверкой.
Landforge — это пять таких скиллов:
|
Skill |
Что делает |
|---|---|
|
|
лендинг из брифа: контракт + скелет + валидатор |
|
|
A/B-тест на одном URL (inline-сплит) |
|
|
аудит-журнал изменений (память «что и когда меняли») |
|
|
материалы под Директ/Google Ads (UTM, тексты, ключи) |
|
|
развернуть всю систему в другой проект |
Решение №1: лендинг — это статический HTML, а не SPA
Первое и главное решение, на котором держится всё остальное. Раньше у меня лендинг был на React, который собирался в браузере. Выглядел отлично — но поисковые роботы и превью-боты соцсетей видели вот это:
<div id="root"></div>
Пустой контейнер. Контент дорисовывал JavaScript уже после загрузки — а краулер его не дожидался. Для страницы, вся суть которой — приходить из поиска и красиво превьюиться при шеринге, это провал.
Поэтому в Landforge зафиксирован жёсткий инвариант: лендинг — самодостаточный статический HTML-файл, контент в разметке, ноль JS на критическом пути. CSS и минимальный vanilla-JS — инлайн. Никакого клиентского рендеринга контента, никаких внешних CSS/JS-блокировщиков.
Что это даёт:
-
контент виден без JavaScript → индексируется и корректно превьюится;
-
страница грузится мгновенно (нет фреймворка на критпути);
-
деплой тривиален — что в репозитории, то и на проде, без шага сборки.
Этот инвариант — не пожелание, а то, что нельзя нарушать ни одному скиллу. Он вынесен в отдельную запись решения (ADR) и проверяется автоматически (см. ниже).
Решение №2: контракт + валидатор вместо «повезёт с промптом»
Главная проблема генерации через LLM — недетерминированность. Сегодня модель добавит canonical и JSON-LD, завтра забудет. Лечится это не «более длинным промптом», а контрактом и проверкой.
В скилле new-landing три части:
-
landing-contract.md— что обязан содержать любой лендинг:<title>иmeta descriptionв разумных пределах,canonical, Open Graph + Twitter Card, JSON-LD, ровно один<h1>, guarded-аналитика, и — никаких внешних CSS/JS на критпути. -
landing-skeleton.html— нейтральный скелет, уже соответствующий контракту (системные шрифты, инлайн-стили, полный SEO-блок). Модель наполняет его контентом, а не выдумывает структуру с нуля. -
check_landing.py— валидатор. Прогоняется по готовому файлу и возвращает ненулевой код, если контракт нарушен.
Кусок проверки инварианта — буквально несколько строк, но именно они ловят самую коварную ошибку:
# никаких внешних CSS/JS на критическом пути — иначе ломается смысл статикиext_scripts = re.findall(r'<script\b[^>]*\bsrc=', html, re.I)ext_css = re.findall(r'<link\b[^>]*rel=["\']stylesheet["\']', html, re.I)if ext_scripts: err("внешние <script src> на критпути — инвариант: JS инлайн")if ext_css: err("внешний <link rel=stylesheet> — инвариант: CSS инлайн")
Забавный момент: когда я прогнал валидатор по своему же старому лендингу, он сразу нашёл нарушение — там грузились Google Fonts через render-blocking <link>. Инструмент окупился на первом же запуске, поймав то, что я делал руками годами.
Вход у генератора один — бриф (единый формат). Чтобы его было удобно собирать даже не-разработчику, в комплекте есть промпт-интервьюер: вставляете его в любую нейросеть, она проводит интервью и отдаёт готовый бриф. Один формат — два входа (промпт снаружи или сам skill спрашивает).
Решение №3: ветка = окружение, ветка = превью
Деплой я сделал branch-routed через GitHub Actions. Логика максимально простая:
|
Ветка |
Куда уезжает |
Индексация |
|---|---|---|
|
|
продакшн |
индексируется |
|
|
staging |
|
|
любая другая |
превью на |
|
То есть любая ветка автоматически поднимает превью по своему URL — удобно показать заказчику или обкатать гипотезу, не трогая прод. Под капотом — Traefik перед двумя nginx-контейнерами; превью-папка чистится отдельным workflow, когда ветку удаляют.
Отдельно важна индексация: в поиске должен быть только продакшн. Staging и превью отдают X-Robots-Tag: noindex на уровне nginx — это надёжнее, чем мета-тег, и закрывает разом и staging, и все превью (они в одном контейнере). Иначе вы сами создаёте себе дубли в выдаче.
A/B на одном URL — без клоакинга
A/B-тесты лендингов часто делают на разных URL, и это легко превращается в клоакинг (роботу один контент, людям — другой), за что поисковики наказывают. В Landforge тест идёт на одном URL: вариант A (control) лежит в HTML, вариант B применяется поверх лёгким скриптом по cookie-сплиту, а сам вариант прокидывается в аналитику как параметр визита. Контент реальный и доступен роботу — это не клоакинг. Один активный тест за раз, и есть «трафик-гейт»: всерьёз делать выводы можно, когда трафика достаточно, иначе тест просто не наберёт значимости.
Память об изменениях: аудит-журнал
Когда крутишь гипотезы, легко забыть, что и когда менял и чем закончилось. Поэтому в систему встроен аудит-журнал (landing-journal): каждое значимое действие — создание лендинга, добавление варианта, запуск теста, решение, раскат — пишется append-only в journal.jsonl, плюс git как нижний слой (хеш коммита в каждой записи). История неизменяемая: ошиблись — добавляем корректирующую запись, старые не переписываем. По сути — «чёрный ящик» проекта.
Переносимость: развернуть систему в чужой проект
Раз система обезличена, логично уметь ставить её куда угодно. Скилл init-landing-system берёт снимок всей системы и разворачивает в существующий проект, подставляя его домены/контейнеры на место плейсхолдеров. Важная деталь: он подставляет только инфраструктурные плейсхолдеры (домены, имена контейнеров), а контентные плейсхолдеры скелета ({{TITLE}} и т.п.) не трогает — их заполнит new-landing при генерации. И он merge-aware: не перезаписывает существующие файлы без явного флага.
Честно про ограничения
Чтобы не выглядело рекламой — где у системы края:
-
Серверная часть — ручная. Контейнеры nginx, маршруты Traefik, DNS и GitHub-секреты вы заводите сами; скрипт лишь печатает чеклист. Автоматизировать это «из dev-time» честно нельзя.
-
Аналитика-сниппеты — вендорный boilerplate. В скелете лежат стандартные сниппеты Метрики/GA под guard (не грузятся без ID). Это пример интеграции, не часть «моего» кода.
-
Это не движок-генератор. Пока новый лендинг — это скелет + наполнение моделью, а не сборка из данных. Превратить паттерн в полноценный генератор (контент-модель, библиотека блоков) — в дорожной карте.
-
Демо. На момент публикации в репозитории текстовая схема потока, а не GIF — записать живое демо ещё предстоит.
Итог
Главный вывод для меня оказался не про лендинги, а про формат. Claude Code skills — это удобный способ кристаллизовать повторяющийся рабочий процесс: вынести правила в контракт, добавить самопроверку, и перестать зависеть от того, как сегодня сформулирован промпт. Лендинги — просто первый домен, на котором я это попробовал.
Landforge лежит в открытом доступе под MIT. Там пять скиллов, CI, nginx-шаблоны, ADR и contributing-гайд. Если делаете лендинги или просто присматриваетесь к skills — забирайте, форкайте, заводите issue. Буду рад обратной связи и звёздам; если соберётся комьюнити — у проекта для этого уже всё готово.
Расскажите в комментариях: как вы решаете задачу быстрых SEO-лендингов — статика, конструкторы, SSG? И пробовали ли упаковывать свои рабочие процессы в Claude Code skills?
ссылка на оригинал статьи https://habr.com/ru/articles/1044516/