Десять лет в бюджетных организациях из них три года работы с гособоронзаказом научили меня одному: сложность ГОЗ не в законах, а в интерфейсах и людях. Я собрал консалтинговую платформу «Страна ГОЗ» с нуля — на современном стеке с полным CI/CD pipeline, мониторингом и production-grade архитектурой.
Почему именно ГОЗ
Несколько лет участия в проектах ГОЗ показали: мир оборонных заказов крутится вокруг регламентов, писем и «согласовать до 15:00». Я сопровождал исполнителей и заказчиков, видел, как трудно бизнесу пройти путь от заявки до отчётности, и понял: сложность не в нормативке — она в инструментах. Любая операция должна быть задокументирована, любая копейка должна быть подтверждена - иначе всё упирается в проблемы с надземными органами. Были случае в практике, когда пришлось обосновать почему ручка стоила 1 рубль, хотя в "Интернетах ваших" есть за 0,99 рублей: "Вы украли из бюджета - 300 рублей, немедленно верните".
Но люди боятся самой аббревиатуры. ГОЗ кажется чем-то закрытым и бюрократичным, хотя на деле — это просто процесс с чёткими правилами. Нужен был сайт, который объяснит это простым языком. Да, ГОЗ это не только военное оборудование и снаряжение, это еще и куча проектов, которые смежные.
С чего началось
Первый лендинг собрал в конструкторе — чтобы проверить гипотезу. Сделал все максимально простым и читаемым, без сложных аббревиатур, локальных постановлений и сокращений. Хотелось сделать продукт дружелюбным, моя целевая аудитория были люди, которые занимаются производством или разработкой, которые готовы развиваться и проводить исследования, но при этом которые находятся за пределами уже поднаторелых товарищей. Заказчикам я должен проводил консультации, так как с большим опытом автоматизации я настраивал их процессы. Вы не поверите, но бумажные документы и отсутствие знаний банальных формул в экселе - приводило к тому, что процессы которые занимают простейшей формулой ВПР за пару минут - люди проводят в ручную сутками.
Изображения генерировал в GPT под стать названию своего проекта. Решил обыграть страну ОЗ и поэтому изображения генерировались в подобном ключе.
Гипотеза оказалась рабочей: компании начали обращаться за консультациями. Тогда стало ясно — пора строить настоящий продукт.


Технический стек и архитектура
Frontend: React 18.3 + TypeScript 5.8 (strict mode)
Сборка: Vite 5.4 — HMR за ~200ms, production bundle ~360 KB
UI: Tailwind CSS 3.4 + shadcn/ui (50+ готовых компонентов на Radix UI)
Анимации: Framer Motion для плавных переходов
Иконки: Lucide React (tree-shakeable)
Качество: ESLint 9 с ~40 правилами + pre-commit hooks
Тести��ование: Vitest + React Testing Library (coverage 80%+)
src/ ├── components/ # 13 компонентов (~730 строк кода) │ ├── ui/ # 50+ shadcn/ui компонентов │ ├── Hero.tsx # Главный экран с CTR-оптимизацией │ ├── Services.tsx # Сервисы для заказчиков/исполнителей │ ├── Process.tsx # Визуализация процесса работы │ └── *.test.tsx # Unit-тесты для критичных компонентов ├── hooks/ # 4 кастомных хука ├── lib/ │ ├── analytics.ts # Unified API для GA4 + YM │ ├── sentry.ts # Error monitoring + Session Replay │ └── utils.ts # Type-safe утилиты ├── pages/ # Роутинг через React Router └── types/ # TypeScript типы и интерфейсы
Почему shadcn/ui, а не MUI или Ant Design?
Компоненты копируются в проект — полный контроль
Нет runtime overhead от библиотеки
Tailwind-first подход = предсказуемые стили
Radix UI в основе = accessibility из коробки
Пример кода: унифицированная аналитика
// lib/analytics.ts import ReactGA from "react-ga4"; interface AnalyticsConfig { googleAnalyticsId?: string; yandexMetricaId?: string; } class Analytics { private initialized = false; private config: AnalyticsConfig = {}; // Unified API для обеих систем event(category: string, action: string, label?: string): void { if (!this.initialized) return; // Google Analytics 4 if (this.config.googleAnalyticsId) { ReactGA.event({ category, action, label }); } // Яндекс.Метрика if (this.config.yandexMetricaId) { window.ym?.(this.config.yandexMetricaId, 'reachGoal', action, { category, label }); } } // Удобные обёртки для типовых событий trackButtonClick(buttonName: string): void { this.event('Button', 'click', buttonName); } trackFormSubmit(formName: string): void { this.event('Form', 'submit', formName); } } export const analytics = new Analytics();
Зачем две системы аналитики?
GA4 — глубокая аналитика и когортный анализ
Метрика — тепловые карты, вебвизор, Российская юрисдикция
CI/CD Pipeline: от коммита до production
Используется GitHub Actions с 4-этапным пайплайном:
# .github/workflows/ci.yml jobs: quality: # TypeScript strict + ESLint 40 правил - run: npm run typecheck - run: npm run lint test: # Vitest + покрытие 80%+ - run: npm run test:run - run: npm run test:coverage # Опционально: отправка в Codecov build: # Production сборка с минификацией - run: npm run build # Артефакты сохраняются на 7 дней deploy: # Только для main ветки - if: github.ref == 'refs/heads/main' - run: rsync dist/ ${{ secrets.SERVER }}
Результат: Zero-downtime deployment за ~3-5 минут от push до live.
Что проверяется автоматически:
✅ Типизация (strict mode — никаких
any)✅ Code style (40+ ESLint правил)
✅ Unit-тесты критичных компонентов
✅ Coverage thresholds (80% lines/functions/branches)
✅ Production сборка без ошибок
Мониторинг: Sentry с Session Replay
// lib/sentry.ts import * as Sentry from "@sentry/react"; export const initSentry = (config: SentryConfig): void => { Sentry.init({ dsn: config.dsn, integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration({ maskAllText: true, // GDPR compliance blockAllMedia: true, }), ], tracesSampleRate: 0.1, // 10% транзакций replaysSessionSampleRate: 0.1, // 10% сессий replaysOnErrorSampleRate: 1.0, // 100% ошибок с видео beforeSend(event, hint) { // Фильтруем dev-ошибки if (import.meta.env.DEV) return null; return event; }, }); };
Session Replay = дебаг на стероидах:
Видео воспроизведение действий пользователя до ошибки
Логи консоли и network requests
Breadcrumbs (навигация, клики, формы)
Real case: Пользователь жаловался на «сломанную форму». Session Replay показал, что он нажимал Enter вместо кнопки отправки. Добавил обработчик — проблема решена за 5 минут.
Тестирование: Vitest + Coverage 80%+
// components/Hero.test.tsx import { describe, it, expect, vi } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import { Hero } from "./Hero"; describe("Hero Component", () => { it("tracks analytics on CTA click", () => { const trackSpy = vi.spyOn(analytics, 'trackButtonClick'); render(<Hero />); fireEvent.click(screen.getByText("Получить консультацию")); expect(trackSpy).toHaveBeenCalledWith("hero_cta"); }); it("renders correct heading", () => { render(<Hero />); expect(screen.getByRole("heading")) .toHaveTextContent(/Ваш надёжный партнёр/i); }); });
Настройки покрытия:
# .github/workflows/ci.yml jobs: quality: # TypeScript strict + ESLint 40 правил - run: npm run typecheck - run: npm run lint test: # Vitest + покрытие 80%+ - run: npm run test:run - run: npm run test:coverage # Опционально: отправка в Codecov build: # Production сборка с минификацией - run: npm run build # Артефакты сохраняются на 7 дней deploy: # Только для main ветки - if: github.ref == 'refs/heads/main' - run: rsync dist/ ${{ secrets.SERVER }}
CI падает, если coverage < 80% — это жёсткое требование для production.
Производительность: цифры и оптимизации
Lighthouse Scores (Mobile):
Performance: 94
Accessibility: 100
Best Practices: 100
SEO: 100
Bundle Size:
dist/assets/ ├── index-abc123.js 360 KB (gzip: 114 KB) ├── index-xyz789.css 66 KB (gzip: 12 KB) └── vendor-def456.js 280 KB (gzip: 88 KB) ← React + libs
Оптимизации:
✅ Vite code splitting — автоматическое разделение чанков
✅ Tree shaking — Lucide React (только используемые иконки)
✅ Route-based splitting через React.lazy()
✅ Image optimization — WebP с fallback
✅ Critical CSS inline, остальное async
Что не делал (пока):
Service Workers / PWA — нет смысла для лендинга
Server-Side Rendering — контент статичный
Prerendering — пока избыточно
Что внутри: UX для сложной ниши
Сайт решает две задачи:
Для заказчиков ГОЗ:
Адаптация внутренних процессов под 275-ФЗ
Расчёт расходов на контролёра качества (РКМ)
Подготовка документов для военного представительства
Для исполнителей:
Участие в тендерах на ЕИС
Подготовка жалоб в ФАС
Ценообразование и отчётность
Принцип работы с текстами:
❌ «В соответствии с требованиями законодательства...»
✅ «Поможем пройти проверку военпреда за 2 недели»
Конверсионная воронка:
Hero → проблема + решение (CTR ~8%)
Services → кейсы для разных аудиторий
Process → прозрачность работы (снижает барьер)
CTA → простая форма (имя + телефон)
Почему всё сделал сам
Четыре причины:
Контроль качества
Внешние подрядчики часто делают «работает» вместо «работает правильно». Хотелось production-grade с первого дня.Скорость итераций
Нужно A/B тестировать тексты/формы/CTA - без зависимости от подрядчика.Знание предметной области
Я понимаю боли клиентов ГОЗ - это даёт преимущество в UX.Проект дополнительный и за свой счёт)
Что не стал делать сам:
Дизайн - использовал готовые компоненты shadcn/ui
Хостинг - взял VPS с Nginx (не поднимал с нуля)
Мониторинг - готовые решения (Sentry, GA4, YM)
Выводы и планы развития
Что получилось:
Production-ready продукт за ~1 месяц (вечера/выходные)
CI/CD с автотестами и zero-downtime deploy
Мониторинг с Session Replay для быстрого дебага
Lighthouse 90+ без костылей
Технологии-победители:
React 18 + TypeScript strict - надёжность и предсказуемость
Vite - сборка в 10 раз быстрее Webpack
shadcn/ui - красиво + контроль + accessibility
Vitest - быстрые тесты (в 2-3 раза быстрее Jest)
Что дальше:
Личный кабинет - история обращений, документы;
Калькуляторы - расчёт РКМ, себестоимости, сроков;
CRM-система - автоматизация процесса от заявки до отчёта;
Интеграция с ЕИС - почти все заказчики станционные, а если даже нет, все равно 99% закупочных процедур публикуются на закрытых площадках, поэтому это будет в качестве тренировки;
Форма заявки и согласие на обработку - намеренно обошел сбор информации на сайте, чтобы не обременять себя кучей регистрацией в надзорном органе.
Технический долг (пока минимален):
3 TODO в коде (некритичные);
Coverage ~82% (хочу 90%+);
Нет E2E тестов (планирую Playwright);
Контакты:
Telegram: @Amonoc
Проект: stranagoz.ru
