Десять лет в бюджетных организациях из них три года работы с гособоронзаказом научили меня одному: сложность ГОЗ не в законах, а в интерфейсах и людях. Я собрал консалтинговую платформу «Страна ГОЗ» с нуля — на современном стеке с полным 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