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

Конверсионная воронка:

  1. Hero → проблема + решение (CTR ~8%)

  2. Services → кейсы для разных аудиторий

  3. Process → прозрачность работы (снижает барьер)

  4. CTA → простая форма (имя + телефон)


Почему всё сделал сам

Четыре причины:

  1. Контроль качества
    Внешние подрядчики часто делают «работает» вместо «работает правильно». Хотелось production-grade с первого дня.

  2. Скорость итераций
    Нужно A/B тестировать тексты/формы/CTA - без зависимости от подрядчика.

  3. Знание предметной области
    Я понимаю боли клиентов ГОЗ - это даёт преимущество в UX.

  4. Проект дополнительный и за свой счёт)

Что не стал делать сам:

  • Дизайн - использовал готовые компоненты 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)

Что дальше:

  1. Личный кабинет - история обращений, документы;

  2. Калькуляторы - расчёт РКМ, себестоимости, сроков;

  3. CRM-система - автоматизация процесса от заявки до отчёта;

  4. Интеграция с ЕИС - почти все заказчики станционные, а если даже нет, все равно 99% закупочных процедур публикуются на закрытых площадках, поэтому это будет в качестве тренировки;

  5. Форма заявки и согласие на обработку - намеренно обошел сбор информации на сайте, чтобы не обременять себя кучей регистрацией в надзорном органе.

Технический долг (пока минимален):

  • 3 TODO в коде (некритичные);

  • Coverage ~82% (хочу 90%+);

  • Нет E2E тестов (планирую Playwright);


Контакты:

Комментарии (0)