Production-ready чеклист¶
Пробегай полностью перед тем, как включать prod-трафик на новый сервис (или на крупное изменение существующего: смену схемы трафика, новый публичный endpoint с ожидаемой высокой нагрузкой, новую интеграцию). Список длинный — это осознанная цена работы в prod. Каждый пункт ссылается на источник стандарта; пропустить пункт без явного обоснования в PR-описании — не ok.
Если какой-то пункт не применим к твоему сервису — поставь галочку и напиши «N/A: потому что...». Галочка без пояснения = не проверено.
Код и сборка¶
- Dockerfile multistage. Builder на
golang:1.25-alpine, runtime наalpine:3.20илиgcr.io/distroless/static. См.../how-to/add-new-service.md§5. - Non-root runtime.
USER appс uid 10001 вDockerfile. - Бинарь собран с
CGO_ENABLED=0и-ldflags="-s -w". - Image scanning прошёл без Critical/High (trivy image).
-
govulncheck ./...зелёный в CI. - SBOM сгенерирован (
syft) на CI и прикреплён к release. -
golangci-lintзелёный, без skip'ов.
Конфигурация¶
- Все env-переменные задокументированы в
.env.exampleс placeholder-значениями (change_me,_dev_). См.../conventions/configuration.md. - Required-поля валидируются fail-fast на старте (
required:"true"в envconfig +cfg.Validate()для кросс-полевых проверок). - Секреты из secret manager, не в docker-compose/manifest в явном виде.
- Dual-key rotation поддержан для HMAC/JWT/internal токена:
PRIMARY+PREVIOUS. См.../how-to/rotate-jwt-key.md. -
cfg.Redacted()маскирует секреты в лог на старте.
База данных¶
- Миграции: up + down файлы, advisory lock в начале up.sql
(см.
../how-to/add-migration.md). - Outbox-таблица создана, если сервис публикует события. Схема
совпадает с
watermill-sql/DefaultPostgreSQLSchema(см.../patterns/outbox.md§Schema). - Индексы покрывают hot queries (
EXPLAIN ANALYZEпроверен, нетSeq Scanна больших таблицах). - Partitioning запланирован для таблиц с ожидаемым ростом > 10 млн строк (выкатывается отдельной миграцией до того, как таблица разрастётся).
- Soft-delete через
deleted_atтам, где модель требует истории. - Cleanup outbox через CronJob в infra-репо, retention >= 7
дней (см.
../patterns/outbox.md§Cleanup). - Отдельный DB-user для сервиса, права только на свою схему.
См.
../conventions/security.md.
HTTP¶
-
/healthz— liveness, простой 200 без проверки зависимостей. См.../conventions/shutdown.md. -
/readyz— readiness, проверяет pgxpool.Ping, redis.Ping, kafka.Ping. Возвращает 503, если хотя бы одна зависимость не отвечает, и при shutdown. -
/metrics— Prometheus endpoint, без auth, не роутится gateway'ем наружу. - Timeout middleware на handler'е (30s максимум).
-
http.MaxBytesReaderна body (обычно 64 KiB). - Rate limit на hot endpoints (auth, upload, search).
- Input validation через
validatorна handler boundary. -
mapServiceErrorцентрализованно мапит sentinel-ошибки в HTTP-статусы. -
ReadHeaderTimeoutвыставлен (10s) на*http.Server.
События (Kafka)¶
- Publisher через outbox, не прямой
kafka.Publish. Запись в outbox в той же транзакции с бизнес-write (см.../conventions/events.md,../patterns/outbox.md). - Forwarder запущен как singleton per-service (replicas 1).
- Consumer middleware stack в правильном порядке: CorrelationID → Recoverer → PoisonQueue → Retry → Deduplicator.
- Events зарегистрированы в
../event-catalog.md. - DLQ-топики создаются автоматически при первом сообщении;
алерт на
rate(messages_poisoned_total[5m]) > 0подключён. - Handler идемпотентен (UPSERT / unique constraint /
Deduplicator). См.
../patterns/idempotent-consumer.md. - Partition key выставлен в envelope (
Aggregate-Idчерез forwarder middleware) — гарантирует per-aggregate ordering. - Dedup TTL — 24h для обычных events, 7d для critical.
Observability¶
- Logs: slog JSON → stdout,
LOG_LEVEL=infoпо умолчанию. См.../conventions/logging.md. - Structured fields:
service,request_id,correlation_id,trace_id,user_id(если authenticated),route. - PII masking helpers подключены (
pkg/pii/), email/phone/ token в открытом виде в логи не попадают. - Prometheus metrics: базовый набор (HTTP duration, Go runtime, process) + custom метрики на бизнес-события.
- OTel traces:
otelhttp.NewMiddlewareна сервере,otelhttp.NewTransportна HTTP-клиенте,otelpgx.NewTracerна pgxpool,otelMiddleware.Traceна Kafka router. См.../conventions/observability.md. - Grafana dashboard создан в infra-репо (минимум: HTTP QPS, error rate, p99 latency, consumer lag если применимо).
- SLO определены (минимум baseline: HTTP availability 99.9%, p99 latency < N ms на критичных endpoint'ах).
- Alert rules в infra-репо для всех critical метрик. См.
../how-to/add-metric-and-alert.md. - Runbook-ссылки в alert'ах ведут на актуальные страницы troubleshooting.
Shutdown¶
-
signal.NotifyContextвmain.goна SIGINT/SIGTERM. См.../conventions/shutdown.md. - Порядок shutdown: HTTP → Kafka consumer (router.Close) → background workers → Kafka publisher → DB pool → Redis.
- Shutdown timeout 30s (
context.WithTimeout),os.Exit(1)при превышении. -
/readyzпереключается в 503 при получении сигнала (до начала закрытия HTTP-сервера). -
/healthzостаётся 200 до самого конца. - k8s
terminationGracePeriodSeconds: 45(на 10–15s больше shutdown-таймаута). - Все worker'ы смотрят на
<-ctx.Done()и имеют финальный drain. - Integration-тест shutdown присутствует (in-flight request завершается, readyz=503 сразу).
Тесты¶
- Unit-coverage
internal/service/**>= 70%. См.../conventions/testing.md. - Integration через testcontainers для repository-слоя.
- Handler-tests через
httptestдля всех публичных endpoint'ов. - Event-tests через
gochannelдля всех handler'ов Watermill-router'а. - Race detector зелёный в
make testи в CI. - Load test — провёден 1h sustained на expected QPS, есть отчёт (QPS, p99, error rate, outbox lag, memory).
- Chaos-test (минимум: убить один pod, убить одну реплику БД, изолировать Kafka на 30 секунд) — сервис восстанавливается автоматически.
Безопасность¶
- Auth: сервис валидирует HMAC headers от Gateway (не парсит
JWT сам, кроме user-сервиса). См.
../conventions/security.md. - CORS не настроен на backend (это задача Gateway).
- HTTPS only на входе (TLS terminate на Gateway); внутри кластера — mTLS через service-mesh либо plain HTTP под NetworkPolicy.
-
/internal/*защищёнInternalToken+subtle.ConstantTimeCompare; NetworkPolicy ограничивает доступ до белого списка сервисов. - Rotation plan для HMAC/JWT/internal token
задокументирован — см.
../how-to/rotate-jwt-key.md. - SQL injection невозможен — все запросы через pgx-
плейсхолдеры, никакого
fmt.Sprintfс user input. - Cookies (если применимо) —
SameSite=Strict; Secure; HttpOnly. - Argon2id для паролей (user-сервис); нет bcrypt/scrypt/ MD5/SHA-*.
-
crypto/randдля всех случайностей, неmath/rand.
Операционная готовность¶
- Runbook для типовых инцидентов существует: migration-fails,
consumer-stuck, outbox-lag. См.
../troubleshooting/. - On-call rotation включает этот сервис, владелец
задокументирован в
../onboarding/04-who-owns-what.md. - Paging-система (Grafana OnCall / PagerDuty / аналогичный) настроена и получает alerts.
- Incident-response checklist пройден dry run'ом (тест alert → тест эскалации → тест коммуникации).
- Backup план: pgBackRest PITR для Postgres, mirror для MinIO (если хранит медиа).
- DR drill запланирован в первые 60 дней prod-жизни сервиса — минимум восстановление из backup в staging.
Документация¶
- README сервиса полный: что делает, как запустить, как тестировать, список env.
- API описан (OpenAPI в
api/openapi.yamlили inline- документация). - Сервис зарегистрирован в
../services-catalog.md: имя, owner, порт, публикуемые/потребляемые события, HTTP API. -
../onboarding/04-who-owns-what.mdобновлён с owner и контактами. - Каждый новый паттерн (если есть), не описанный в handbook, —
обсуждён с lead-инженером, follow-up task на внесение в
conventions/илиpatterns/.
Rollout¶
- Feature flag (если возможно) для постепенного включения: 1% → 5% → 25% → 100%. Сервис-к-сервису эта схема работает через Gateway-routing или через env-toggle с реплик-процентом.
- Canary deployment — первая версия идёт в отдельный namespace / deployment и получает subset трафика.
- Rollback plan задокументирован: какой тег откатывает, какой миграции не нужно откатывать (forward-only), критерии отката (error rate > X% на Y минут → rollback).
- Communication plan: команда предупреждена, support-канал знает о запуске, incident commander на дежурстве в первые 24 часа после включения.
Как пользоваться чеклистом¶
Этот список — не формальность, а инструмент: каждый пункт закрывает конкретный класс prod-инцидентов, с которыми мы уже сталкивались в других сервисах.
- N/A — пиши пояснение. «N/A: сервис не публикует события», «N/A: нет публичного API, только internal». Голая галочка без пояснения читается как «забыл».
- Чеклист проходится в PR, не в голове. Скопируй список в описание PR о готовности сервиса к prod, отмечай по ходу. Так ревьюер видит прогресс, ты не забываешь пункты.
- Расхождение = не готов. Один невыполненный пункт без N/A — сервис не готов, даже если «всё остальное работает». Единичные дыры — это как раз то, через что проходит первый prod-инцидент.
- Обновляй чеклист. Если нашли класс проблем, не покрытый списком — PR в handbook, добавь пункт. Следующий сервис не должен наступать на те же грабли.
Первые 72 часа после включения¶
Даже с пройденным чеклистом первые сутки — повышенный риск. Рекомендации:
- Monitoring на экране. Grafana-дашборд сервиса открыт на экране on-call'а первые 24 часа, p99 latency и error rate — в фокусе.
- Feature flag в готовности к rollback. Одна команда
(
kubectl set envили внешний flag manager) отключает новый трафик. - Канал команды предупреждён. Ответственные инженеры на связи, не в отпуске/вне зоны.
- Второй DR drill через 7 дней. Восстановление из backup в staging-среде — подтверждает, что backup реально работает, а не просто хранится.
- Post-launch review через 14 дней. Ретроспектива: что сработало, что сломалось, что добавить в чеклист.
Финальная проверка¶
- Все пункты выше — отмечены (checked или N/A с пояснением).
- PR с новым сервисом прошёл review lead-инженера backend-команды.
- Infra-PR (alerts, dashboards, CronJobs) смёржен.
- Runbook'и открыты на общей странице команды.
- Дата включения зафиксирована; post-launch review назначен на +14 дней.
Связанные разделы¶
../how-to/add-new-service.md— пошаговый bootstrap нового сервиса.../architecture-overview.md— где новый сервис встраивается в архитектуру.../conventions/— все правила, на которые ссылается этот чеклист.../troubleshooting/— runbook'и для первого инцидента.