Перейти к основному содержимому

Local Environment Parity

Цель

Проект обязан обеспечивать воспроизводимость и идентичность поведения во всех средах выполнения:

  • локальная разработка
  • CI
  • staging
  • production

Любое расхождение между локальной средой разработчика и CI считается дефектом конфигурации проекта.


Общие принципы

MUST

  • Среда выполнения приложения должна быть детерминированной.
  • Код, конфигурация и зависимости должны вести себя одинаково во всех окружениях.
  • CI pipeline является эталонной средой выполнения проекта.

Идентичность runtime-окружения (Node.js)

MUST

  • Версия Node.js строго зафиксирована и используется:
    • локально
    • в CI
    • на staging
    • на production

Допустимые способы фиксации

  • .tool-versions (asdf)
  • package.json -> engines.node

✅ Пример: .tool-versions

nodejs 20.11.1
pnpm 9.12.0

✅ Пример: package.json -> engines

{
"engines": {
"node": ">=20.11.0 <21"
}
}

❌ Плохо (Node версии расходятся)

  • локально: Node 18
  • CI: Node 20

Результат: разные версии зависимостей/полифиллов/ESM поведения, build может "работать у одного и падать у другого".


Идентичность package manager

MUST

  • В проекте используется один package manager.
  • Он зафиксирован в package.json -> packageManager.

✅ Пример: фиксация package manager

{
"packageManager": "[email protected]"
}

❌ Запрещено

  • Один разработчик ставит зависимости через pnpm, другой - через yarn.
  • В репозитории появляются два lockfile (pnpm-lock.yaml и yarn.lock) или установка даёт разные версии пакетов.

Консистентность scripts

MUST

  • Все команды, используемые в CI, обязаны выполняться локально через scripts в package.json.

FORBIDDEN

  • Запускать сборку напрямую вне scripts:
    • vite build, next build, tsc и т.п.
    • Использовать локальные shell-скрипты, отсутствующие в репозитории.

✅ Пример: корректные scripts

{
"scripts": {
"build": "next build",
"lint": "eslint .",
"test": "vitest run"
}
}

✅ Пример: CI вызывает только scripts

# concept example
steps:
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test
- run: pnpm build

❌ Плохо (CI вызывает команды напрямую)

steps:
- run: next build # обход scripts
- run: eslint . # обход scripts

Управление переменными окружения

MUST

  • Все env-переменные задокументированы:
    • в .env.example
    • в README.md (только имена, без значений)
    • Использование недокументированных env-переменных запрещено.

Env-переменные используются только для runtime-конфигурации

Допустимо

  • URLs и endpoints сервисов
  • параметры инфраструктуры
  • определение окружения (development, staging, production, testing)

FORBIDDEN

  • Env-переменные не должны:
    • включать/отключать пользовательскую функциональность

✅ Пример: .env.example

APP_ENV=development
API_BASE_URL=https://api.example.com
ASSETS_HOST=https://cdn.example.com

❌ Плохо (env управляет бизнес-логикой)

if (process.env.ENABLE_NEW_CHECKOUT === 'true') {
// ❌ бизнес-функциональность включается через env
showNewCheckout();
}

Feature Flags

MUST

  • Feature Flags - это не env и не .env.* файлы.
  • Feature Flags — отдельный механизм управления функциональностью.

Допускается

  • rollout
  • A/B тесты
  • временное управление доступностью фич

Ограничение

Feature Flags могут влиять на бизнес-логику, но только в рамках явно определённого модуля (например shared/feature-flags).

✅ Пример: централизованный модуль feature flags

// shared/feature-flags/types.ts
export type FeatureFlagName = 'newCheckout' | 'newOnboarding';

export interface FeatureFlags {
isEnabled(name: FeatureFlagName): boolean;
}

// shared/feature-flags/provider.ts
export const flags: FeatureFlags = {
isEnabled: (name) => {
return false;
}
};

Запрещенные практики

.env.local, .env.development и аналоги - FORBIDDEN для обхода проверок

Запрещено использовать для:

  • отключения линтеров/тестов/форматтера
  • отключения git hooks
  • создания условий, когда код проходит проверки только у одного разработчика

.env.production, .env.staging - FORBIDDEN для публичных client env

Запрещено хранить/подставлять публичные client variables, включая:

  • публичные ключи Google Ads
  • GTM
  • аналитика/трекинг/сбор статистики

CI как источник правды

MUST

CI pipeline является эталонной и единственно корректной средой выполнения проекта.

Если код:

  • работает локально
  • но падает в CI то это трактуется как ошибка локальной конфигурации, а не дефект CI.

Типовые примеры

  • build падает в CI при успешном локальном build
  • разные версии runtime/инструментов/зависимостей
  • разные env или флаги сборки

Приоритет: исправление расхождений - highest priority.


Допустимые исключения

MUST

Исключения допускаются только:

  • при техническом обосновании
  • после согласования с техлидом и PM, а также QA

И обязательно документируются в README.md:

  • что именно исключено:
    1. почему
    2. на какой срок
    3. как проверить/воспроизвести

Docker (локальная разработка)

Docker может потребоваться, если

  • есть native зависимости/бинарники, зависящие от архитектуры ОС (x86_64 vs ARM)
  • в репозитории есть backend (монорепа)
  • e2e/интеграционные тесты требуют окружения (DB/Redis и т.п.)

Docker допускается, но не обязателен, если:

  • проект frontend-only (SPA)
  • Node полностью контролирует runtime
  • зафиксированы Node + package manager
  • нет локальных инфраструктурных зависимостей

Требования к Docker-конфигурации

MUST

  • Docker не является “второй средой”.
  • Docker-контейнер обязан повторять CI/runtime окружение.
  • Dockerfile не должен отличаться от CI-образа.

Пример: Плохо (Dockerfile отличается от CI) CI использует Node 20 + pnpm, а Dockerfile — Node 18 + npm Это считается дефектом конфигурации.


Процедуры контроля (Code Review)

  • Node версия зафиксирована (.tool-versions или engines.node) и совпадает с CI
  • package.json.packageManager задан и используется в CI
  • CI выполняет команды только через scripts
  • Все env задокументированы в .env.example и README
  • Feature Flags - отдельный централизованный механизм, не .env.*
  • Docker (если используется) повторяет CI окружение

TODO

  1. Infrastructure Configuration Branches