Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - Встраивание структур и интерфейсов (Embedding): https://go.vbloher.org/docs/01-core-go/embedding/ - Ошибки в Go: error, wrapping, errors.Is/As/Join: https://go.vbloher.org/docs/01-core-go/errors/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Сборщик мусора Go: concurrent tri-color mark-sweep: https://go.vbloher.org/docs/03-runtime-memory/gc/ - Тюнинг GC: GOGC и GOMEMLIMIT: https://go.vbloher.org/docs/03-runtime-memory/gogc-gomemlimit/ - GOMAXPROCS: параллелизм планировщика и проблема контейнеров: https://go.vbloher.org/docs/03-runtime-memory/gomaxprocs/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - pprof: профилирование CPU, памяти и блокировок в Go: https://go.vbloher.org/docs/03-runtime-memory/pprof/ - Execution Tracer и runtime/trace: тайминги вместо агрегатов: https://go.vbloher.org/docs/03-runtime-memory/runtime-tracing/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - Backend: https://go.vbloher.org/docs/05-backend/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - DNS: записи, резолвинг, кэширование, DNS в Go: https://go.vbloher.org/docs/06-networking/dns/ - Версии HTTP: 1.1, 2, 3: https://go.vbloher.org/docs/06-networking/http-versions/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Observability: https://go.vbloher.org/docs/09-observability/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - Notification Service: https://go.vbloher.org/docs/10-system-design/notification-service/ - Order Service: https://go.vbloher.org/docs/10-system-design/order-service/ - Payment Service: https://go.vbloher.org/docs/10-system-design/payment-service/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - DevOps: https://go.vbloher.org/docs/11-devops/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ > Модуль: Backend · Уровень: Middle+/Senior ## TL;DR - **OpenAPI** — машиночитаемая спецификация (контракт) HTTP/REST API в YAML/JSON. До версии 3.0 называлась **Swagger** (2.0 == "Swagger 2.0"). Swagger сейчас — это набор инструментов (Swagger UI, Swagger Editor, Codegen) от SmartBear, а сам формат — OpenAPI Specification (OAS) под управлением OpenAPI Initiative (Linux Foundation). - **Версии:** 2.0 (2014, Swagger) → 3.0 (2017, переработана структура: `components`, `requestBody`, `servers`) → 3.1 (2021, **полная совместимость с JSON Schema Draft 2020-12**). - **Два подхода:** **contract-first (design-first)** — сначала пишем спеку, из неё генерим типы/сервер/клиент (`oapi-codegen`); **code-first** — пишем Go-код с аннотациями, из него генерим спеку (`swaggo/swag`). Для контракта между командами/фронтом промышленный стандарт — contract-first. - **Спека = source of truth.** Контракт между бэкендом, фронтом, мобайлом, внешними потребителями. Версионируется в git, линтуется в CI (`spectral`, `vacuum`), на breaking changes проверяется (`oasdiff`). - **Валидация:** статическая (линт спеки в CI) + рантайм (middleware на базе `kin-openapi` валидирует request/response против схемы). --- ## Теория ### Что такое OpenAPI OpenAPI Specification (OAS) — формальное, языконезависимое описание HTTP API. Описывает: эндпоинты, методы, параметры, тела запросов/ответов, схемы данных, аутентификацию, примеры. Главная ценность — **единый машиночитаемый контракт**, из которого можно: - генерировать серверные стабы и клиентов (десятки языков); - генерировать документацию (Swagger UI, Redoc, Stoplight); - валидировать трафик в рантайме; - проверять обратную совместимость; - использовать в mock-серверах (Prism) для параллельной разработки фронта и бэка. **История названий:** | Эра | Название формата | Кто ведёт | |-----|------------------|-----------| | 2010–2015 | Swagger Specification | SmartBear | | 2016+ | OpenAPI Specification (OAS) | OpenAPI Initiative (Linux Foundation) | «Swagger 2.0» и «OpenAPI 2.0» — синонимы. С 3.0 используют только «OpenAPI». ### Версии: 2.0 vs 3.0 vs 3.1 | Аспект | OpenAPI 2.0 (Swagger) | OpenAPI 3.0 | OpenAPI 3.1 | |--------|----------------------|-------------|-------------| | Корневой ключ | `swagger: "2.0"` | `openapi: 3.0.x` | `openapi: 3.1.x` | | Серверы | `host` + `basePath` + `schemes` | `servers: []` (URL-шаблоны, переменные) | то же | | Тело запроса | `parameters` с `in: body` | отдельный `requestBody` | то же | | Переиспользуемые объекты | `definitions`, `parameters`, `responses` | единый `components` | то же | | Несколько content-type | один на operation | `content: {application/json: ..., ...}` | то же | | JSON Schema | подмножество Draft 4 (свои отклонения) | расширенное подмножество Draft 5/«Wright» | **полный JSON Schema 2020-12** | | `nullable` | нет (хаки через `x-nullable`) | `nullable: true` | нет `nullable`; вместо него `type: [string, "null"]` (массив типов) | | `type` как массив | нет | нет | **да** (`type: ["string","null"]`) | | Webhooks | нет | нет | да (`webhooks`) | | `examples` (множ.) | нет | да | да | | Произвольный `$schema`/`$id` | нет | ограниченно | да | **Ключевой сдвиг в 3.1** — это переход на JSON Schema 2020-12. Это значит, что схемы из OpenAPI 3.1 можно переиспользовать в обычных JSON Schema валидаторах и наоборот. Но это создаёт боль в тулинге: многие генераторы и валидаторы заточены под 3.0 и не понимают `type: ["string","null"]` или `nullable`-в-3.1-нет. На 2026 год часть production-инструментов всё ещё лучше работает с 3.0.x, поэтому версию выбирают исходя из зрелости тулчейна команды. ### Структура спецификации Корневые объекты OpenAPI 3.x: | Поле | Назначение | |------|------------| | `openapi` | версия спеки (строка, напр. `3.0.3`) | | `info` | метаданные: `title`, `version` (версия **API**, не спеки), `description`, `contact`, `license` | | `servers` | список базовых URL (с переменными окружения) | | `paths` | объект «путь → операции» (`get`, `post`, `put`, `patch`, `delete`, ...) | | `components` | переиспользуемые объекты: `schemas`, `parameters`, `requestBodies`, `responses`, `securitySchemes`, `headers`, `examples` | | `security` | глобальные требования безопасности (ссылки на `securitySchemes`) | | `tags` | группировка операций для документации | Внутри **operation**: `operationId` (уникальный, критичен для кодогенерации — из него генерится имя метода), `summary`, `parameters` (path/query/header/cookie), `requestBody`, `responses`, `security`, `tags`. `parameters` — каждый имеет `name`, `in` (`path`|`query`|`header`|`cookie`), `required`, `schema`. Для `in: path` всегда `required: true`. `requestBody` — `content` с разбивкой по media-type, каждый со своей `schema`. `responses` — ключи это HTTP-коды (`200`, `404`, `default`), внутри `description` (обязателен!) + `content` + `headers`. `securitySchemes` — типы: `apiKey`, `http` (basic/bearer), `oauth2`, `openIdConnect`, `mutualTLS` (3.1). ### Пример YAML-спеки для CRUD-эндпоинта ```yaml openapi: 3.0.3 info: title: Users API version: 1.2.0 description: CRUD over users servers: - url: https://api.example.com/v1 description: production - url: http://localhost:8080/v1 description: local security: - bearerAuth: [] paths: /users: get: operationId: listUsers tags: [users] summary: List users parameters: - name: limit in: query required: false schema: { type: integer, minimum: 1, maximum: 100, default: 20 } - name: cursor in: query schema: { type: string } responses: '200': description: page of users content: application/json: schema: $ref: '#/components/schemas/UserPage' '401': $ref: '#/components/responses/Unauthorized' post: operationId: createUser tags: [users] summary: Create user requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: created headers: Location: schema: { type: string, format: uri } content: application/json: schema: { $ref: '#/components/schemas/User' } '400': $ref: '#/components/responses/BadRequest' '409': description: email already exists /users/{id}: parameters: - name: id in: path required: true schema: { type: string, format: uuid } get: operationId: getUser tags: [users] responses: '200': description: user content: application/json: schema: { $ref: '#/components/schemas/User' } '404': $ref: '#/components/responses/NotFound' patch: operationId: updateUser tags: [users] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UpdateUserRequest' } responses: '200': description: updated content: application/json: schema: { $ref: '#/components/schemas/User' } '404': { $ref: '#/components/responses/NotFound' } delete: operationId: deleteUser tags: [users] responses: '204': description: deleted '404': { $ref: '#/components/responses/NotFound' } components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT responses: BadRequest: description: validation error content: application/json: schema: { $ref: '#/components/schemas/Error' } Unauthorized: description: missing/invalid token content: application/json: schema: { $ref: '#/components/schemas/Error' } NotFound: description: resource not found content: application/json: schema: { $ref: '#/components/schemas/Error' } schemas: User: type: object required: [id, email, createdAt] properties: id: { type: string, format: uuid } email: { type: string, format: email } name: { type: string, maxLength: 255 } createdAt: { type: string, format: date-time } CreateUserRequest: type: object required: [email] properties: email: { type: string, format: email } name: { type: string, maxLength: 255 } UpdateUserRequest: type: object properties: name: { type: string, maxLength: 255 } UserPage: type: object required: [items] properties: items: type: array items: { $ref: '#/components/schemas/User' } nextCursor: { type: string } Error: type: object required: [code, message] properties: code: { type: string } message: { type: string } details: type: array items: { type: string } ``` ### Contract-first vs code-first **Contract-first (design-first):** спека — артефакт, который пишут (часто фронт + бэк вместе) до кода. Из неё генерируют типы, server interface, клиентов. Спека ревьюится в PR как любой код. **Code-first:** истина — Go-код с аннотациями (`swaggo/swag`) или со структурными тегами; спека генерируется из кода как побочный продукт. | Критерий | Contract-first | Code-first | |----------|---------------|------------| | Source of truth | YAML-спека | Go-код / аннотации | | Параллельная работа фронт/бэк | отлично (mock из спеки сразу) | плохо (нужен код) | | Риск дрейфа кода и спеки | низкий (код генерится из спеки) | высокий (аннотации забывают обновить) | | Гарантия валидности спеки | высокая | спека может не валидироваться, генерится «как есть» | | Барьер входа | выше (надо знать OAS) | ниже (пишешь привычный Go) | | Контроль над сгенерированным кодом | средний | полный (код твой) | | Кросс-командный контракт | сильная сторона | слабая | | Документация | первична | вторична, легко отстаёт | | Типичный инструмент в Go | `oapi-codegen` | `swaggo/swag` | Senior-выбор: для публичного/межкомандного API — **contract-first**. Для внутреннего сервиса, который никто кроме тебя не дёргает, и где спека нужна лишь для доки — допустим code-first, но осознавая риск дрейфа. ### Кодогенерация в Go #### oapi-codegen (contract-first) `oapi-codegen` (форк/наследник `deepmap/oapi-codegen`, сейчас под `oapi-codegen/oapi-codegen`) генерирует из OpenAPI 3.x: - **типы** (Go-структуры из `components/schemas`); - **server interface** под нужный роутер: `chi`, `echo`, `gin`, `net/http` (`std-http`), `fiber`, `gorilla`; - **HTTP-клиент** (типизированные методы); - **встроенную спеку** (`embedded-spec`) для рантайм-валидации; - **strict server** — обёртка, где хендлеры принимают типизированный request-объект и возвращают типизированный response (роутер сам парсит/сериализует). Конфиг (`oapi-codegen.yaml`): ```yaml # oapi-codegen.yaml package: api output: internal/api/api.gen.go generate: models: true chi-server: true # сгенерировать ServerInterface под chi strict-server: true # строгий типизированный слой поверх embedded-spec: true output-options: skip-prune: false user-templates: {} # можно подменять шаблоны ``` Запуск через `go generate`: ```go //go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=oapi-codegen.yaml openapi.yaml ``` Сгенерированный `ServerInterface` (упрощённо, для chi): ```go // api.gen.go (фрагмент) type ServerInterface interface { // (GET /users) ListUsers(w http.ResponseWriter, r *http.Request, params ListUsersParams) // (POST /users) CreateUser(w http.ResponseWriter, r *http.Request) // (GET /users/{id}) GetUser(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) // (PATCH /users/{id}) UpdateUser(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) // (DELETE /users/{id}) DeleteUser(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) } type ListUsersParams struct { Limit *int `form:"limit,omitempty"` Cursor *string `form:"cursor,omitempty"` } // Регистрирует роуты в chi.Router и вешает обработчики на ServerInterface. func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { /* ... */ } ``` Со `strict-server` интерфейс становится чище — без `http.ResponseWriter`: ```go type StrictServerInterface interface { ListUsers(ctx context.Context, request ListUsersRequestObject) (ListUsersResponseObject, error) CreateUser(ctx context.Context, request CreateUserRequestObject) (CreateUserResponseObject, error) GetUser(ctx context.Context, request GetUserRequestObject) (GetUserResponseObject, error) // ... } // Имплементация: func (s *Service) CreateUser(ctx context.Context, req CreateUserRequestObject) (CreateUserResponseObject, error) { u, err := s.users.Create(ctx, req.Body.Email, deref(req.Body.Name)) if err != nil { if errors.Is(err, ErrEmailTaken) { return CreateUser409JSONResponse{Error{Code: "conflict", Message: "email exists"}}, nil } return nil, err // 500 } return CreateUser201JSONResponse(toAPIUser(u)), nil } ``` Тонкости `oapi-codegen`: - `operationId` обязателен и должен быть уникальным — иначе имена методов конфликтуют/мусорные. - Управление маппингом типов: `x-go-type`, `x-go-type-import` для подмены сгенерированного типа на свой (напр. кастомный `Money`, `decimal.Decimal`). - `nullable` vs `required` определяет, генерится поле как `*T` или `T`. Отсутствие `required` → указатель/`omitempty`. - `additionalProperties: true` → `map[string]interface{}` (или генерится спец-тип с `AdditionalProperties`). - `oneOf`/`anyOf` генерируются в union-тип с методами `As...`/`Merge...` — работать с ними неудобно, на senior это частый разговор о моделировании. #### swaggo/swag (code-first) Генерирует Swagger **2.0** (не 3.x!) из аннотаций в комментариях Go. Это его принципиальное ограничение — для 3.x придётся конвертировать. ```go // @Summary Create user // @Tags users // @Accept json // @Produce json // @Param body body CreateUserRequest true "payload" // @Success 201 {object} User // @Failure 400 {object} Error // @Router /users [post] func (h *Handler) CreateUser(c *gin.Context) { /* ... */ } ``` `swag init` парсит AST и комментарии → `docs/swagger.json` + `swagger.yaml` + Go-пакет `docs`. Минусы: спека вторична, аннотации легко рассинхронизировать с реальным поведением, ограничены Swagger 2.0, нет строгой типобезопасности контракта. #### Сравнение генераторов | Инструмент | Подход | OAS-версия | Что генерит | |-----------|--------|-----------|-------------| | `oapi-codegen` | contract-first | 3.0/3.1 | типы, server iface, client, strict-server, embedded spec | | `swaggo/swag` | code-first | 2.0 | спека из аннотаций + Swagger UI | | `ogen` | contract-first | 3.0/3.1 | типы + сервер + клиент, без рефлексии, встроенная валидация, OTEL | | `go-swagger` | оба | 2.0 | сервер/клиент (тяжёлый, legacy на 2.0) | `deepmap/oapi-codegen` — старое имя; проект переехал в org `oapi-codegen`, импорт-путь `github.com/oapi-codegen/oapi-codegen/v2`. На собеседовании упоминание «deepmap» — маркер того, что человек давно с ним работает. ### Валидация Два уровня: **1. Статическая (CI, линт спеки):** - **Spectral** (Stoplight) — линтер OpenAPI/AsyncAPI/JSON Schema с настраиваемыми правилами (`.spectral.yaml`): обязательные `operationId`, описания, запрет `additionalProperties` без схемы, нейминг-конвенции и т.д. - **vacuum** — быстрый (Go) линтер, совместим с правилами Spectral, удобен для больших спек в CI. ```yaml # .spectral.yaml extends: ["spectral:oas"] rules: operation-operationId: error operation-description: warn oas3-unused-component: warn ``` **2. Рантайм (request/response validation middleware):** `kin-openapi` (`getkin/kin-openapi`) — Go-библиотека: парсит спеку (`openapi3.Loader`), строит роутер (`gorillamux`/`legacy`), валидирует входящие запросы и (опционально) исходящие ответы против схемы. ```go loader := openapi3.NewLoader() doc, _ := loader.LoadFromFile("openapi.yaml") _ = doc.Validate(loader.Context) // проверка самой спеки router, _ := gorillamux.NewRouter(doc) func ValidatorMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { route, pathParams, err := router.FindRoute(r) if err != nil { http.Error(w, "not found in spec", 404); return } reqInput := &openapi3filter.RequestValidationInput{ Request: r, PathParams: pathParams, Route: route, } if err := openapi3filter.ValidateRequest(r.Context(), reqInput); err != nil { http.Error(w, err.Error(), http.StatusBadRequest); return } next.ServeHTTP(w, r) }) } ``` `oapi-codegen` интегрируется с `kin-openapi`: генерируемый `embedded-spec` + middleware `oapimiddleware.OapiRequestValidator(swagger)` даёт авто-валидацию запросов против встроенной спеки. **Response-валидация** обычно включают только в тестах/staging (на проде дорого и рискованно ронять валидный для клиента ответ из-за расхождения схемы). ### Source of truth и кросс-командное взаимодействие Спека — **контракт**. Практики senior-уровня: - Спека живёт в git (отдельный репо `api-specs` или рядом с сервисом), ревьюится в PR. - Из неё генерится: серверный код бэка, типизированные клиенты (фронт через `openapi-typescript`/`orval`, другие сервисы через `oapi-codegen`). - Mock-сервер (Prism, `stoplight/prism`) поднимается из спеки — фронт пилит UI, не дожидаясь бэка. - Документация (Redoc/Swagger UI) генерится автоматически — всегда актуальна. - **Single source of truth** убирает класс багов «фронт ждёт одно, бэк отдаёт другое». ### Версионирование и breaking changes **Версионирование API** (не путать с версией спеки `openapi:`): - В пути: `/v1/users`, `/v2/users` — самый явный, грубый. - В заголовке: `Accept: application/vnd.api+json; version=2` или кастомный `X-API-Version`. - Поле `info.version` (semver) — версия документа/API. **Breaking vs non-breaking:** | Non-breaking (backward compatible) | Breaking | |-----------------------------------|----------| | добавление нового optional-поля в ответ | удаление поля из ответа | | новый эндпоинт | удаление/переименование эндпоинта | | новый optional query-параметр | новый **required** параметр | | расширение enum в ответе (спорно для клиентов) | сужение enum, смена типа поля | | ослабление валидации входа | ужесточение валидации входа, новый `required` в body | **oasdiff** — инструмент для diff двух версий спеки и детекта breaking changes; ставят в CI как gate на PR: ```bash oasdiff breaking old.yaml new.yaml --fail-on ERR # выводит классифицированные изменения; ненулевой код → блокируем merge ``` Так контракт защищён: нельзя случайно сломать потребителей. Для намеренных breaking changes — бамп мажорной версии API и план миграции (deprecation через `deprecated: true` + `Sunset`/`Deprecation` заголовки). --- ## Подводные камни / gotchas - **3.1 ≠ просто «новее»**: переход на JSON Schema 2020-12 ломает совместимость с тулингом, заточенным под 3.0. `nullable` в 3.1 **нет** — вместо него `type: ["string","null"]`. Многие генераторы/UI на 2026 ещё неполно поддерживают 3.1. - **`swaggo/swag` отдаёт Swagger 2.0**, а не OpenAPI 3.x. Если в требованиях «OpenAPI 3», то либо `oapi-codegen`/`ogen` (contract-first), либо конвертация 2.0→3.0 (теряются нюансы). - **Дрейф code-first**: аннотации забывают обновить — спека и код расходятся, документация врёт. В contract-first код **генерится**, дрейф невозможен (но можно забыть перегенерить — добавляют CI-проверку «generated up to date»). - **`operationId` коллизии/отсутствие** → у `oapi-codegen` мусорные/конфликтующие имена методов. Делать его обязательным правилом Spectral. - **`required` ≠ nullable.** `required` говорит «поле присутствует», nullable — «значение может быть null». В Go: `required && !nullable` → `T`; иначе → `*T`/`omitempty`. Путаница ведёт к багам сериализации (отправка `null` vs отсутствие ключа). - **`additionalProperties`**: по умолчанию в JSON Schema разрешены любые доп. поля. Если хочешь strict — явно `additionalProperties: false`. Иначе валидация пропустит мусор; в Go генерится `map`, теряется типобезопасность. - **`oneOf`/`anyOf`/`allOf`** — мощно в схеме, но в Go генерируются в неудобные union-типы. `allOf` часто (неправильно) используют как «наследование/композицию»; для дискриминируемых union нужен `discriminator`. - **Response-валидация в рантайме на проде** может уронить корректный для клиента ответ из-за расхождения схемы. Держать в тестах/staging. - **`format`** (`email`, `uuid`, `date-time`) — это аннотация; не все валидаторы его проверяют, и не все генераторы мапят в нужный Go-тип. Проверять, что выбранный тулчейн действительно валидирует format. - **`$ref` и циклические ссылки**: глубокие/циклические `$ref` ломают часть генераторов; внешние `$ref` (на другие файлы/URL) поддерживаются неравномерно — часто бандлят в один файл (`redocly bundle`). - **Версия спеки vs версия API**: `openapi: 3.0.3` — версия формата; `info.version` — версия твоего API. Их регулярно путают. - **embedded-spec и расхождение**: если валидируешь по встроенной (на момент сборки) спеке, а деплоят другую — поведение разойдётся; держать единый источник. --- ## Вопросы на собеседовании **В:** В чём разница между Swagger и OpenAPI? **О:** Swagger — историческое название формата (версия 2.0 == «Swagger 2.0») и набор инструментов SmartBear (Swagger UI/Editor/Codegen). С версии 3.0 формат называется OpenAPI Specification и ведётся OpenAPI Initiative под Linux Foundation. То есть OpenAPI — это спецификация, Swagger сегодня — тулинг вокруг неё. **В:** Что принципиально нового в OpenAPI 3.1 по сравнению с 3.0? **О:** Полная совместимость с JSON Schema Draft 2020-12. Исчез `nullable` (теперь `type: ["string","null"]`), `type` может быть массивом, появились `webhooks`, поддержка `$schema`/`$id`, `mutualTLS`. Минус — часть тулинга ещё не догнала 3.1, поэтому переход не бесплатный. **В:** Contract-first vs code-first — что выберешь для API между несколькими командами и почему? **О:** Contract-first. Спека — единый source of truth, ревьюится отдельно, из неё генерится сервер и типизированные клиенты для всех потребителей, можно сразу поднять mock-сервер для параллельной разработки фронта. Code-first (swaggo) удобен для внутреннего сервиса, но спека вторична и склонна к дрейфу с кодом. **В:** Что генерирует oapi-codegen и чем strict-server отличается от обычного? **О:** Генерирует Go-типы из `components/schemas`, `ServerInterface` под выбранный роутер (chi/echo/gin/std-http), HTTP-клиент, опционально embedded-spec. Обычный server interface даёт методы с `http.ResponseWriter`/`*http.Request` — парсинг/сериализация на тебе. Strict-server генерирует слой, где хендлер принимает типизированный RequestObject и возвращает типизированный ResponseObject (напр. `CreateUser201JSONResponse`), а инфраструктурный код сам парсит вход и сериализует выход — меньше boilerplate и ошибок. **В:** Почему свежий проект на «OpenAPI 3» нельзя делать на swaggo/swag? **О:** `swaggo/swag` генерирует Swagger 2.0, а не OpenAPI 3.x. Для 3.x нужен contract-first генератор (`oapi-codegen`, `ogen`) либо конвертация 2.0→3.0 с потерей нюансов. Плюс swaggo — code-first, спека вторична и дрейфует. **В:** Как валидировать запросы против OpenAPI-спеки в рантайме в Go? **О:** Через `kin-openapi`: загрузить и провалидировать спеку (`openapi3.Loader`, `doc.Validate`), построить роутер (`gorillamux`), в middleware найти маршрут (`FindRoute`) и вызвать `openapi3filter.ValidateRequest`. С `oapi-codegen` это упрощается через embedded-spec и `OapiRequestValidator`. Response-валидацию обычно держат в тестах/staging, не на проде. **В:** Чем отличаются `required` и `nullable` и как это влияет на сгенерированный Go-код? **О:** `required` — поле обязано присутствовать в объекте; `nullable` — значение может быть `null`. Это ортогонально. В Go: `required` и не `nullable` → значение по значению (`T`); иначе → указатель (`*T`) с `omitempty`. Путаница приводит к багам: отправка `null` против отсутствия ключа, неверная (де)сериализация. **В:** Как в CI не дать сломать обратную совместимость API? **О:** Линт спеки (`spectral`/`vacuum`) на качество и конвенции + diff против предыдущей версии (`oasdiff breaking old new --fail-on ERR`), который классифицирует изменения и блокирует merge при breaking. Breaking-изменения требуют бампа мажорной версии API и плана миграции (`deprecated: true`, `Sunset`/`Deprecation` заголовки). **В:** Что считается breaking change в REST API? **О:** Удаление/переименование эндпоинта или поля ответа, добавление нового required-параметра/поля в запрос, ужесточение валидации входа, смена типа поля, сужение enum. Non-breaking: новый эндпоинт, новое optional-поле в ответе, новый optional-параметр, ослабление валидации. **В:** Как подменить сгенерированный oapi-codegen тип на свой (например, decimal вместо float)? **О:** Расширениями `x-go-type` и `x-go-type-import` прямо в схеме, либо через секцию маппинга типов в конфиге. Это позволяет отдать в код, скажем, `decimal.Decimal` вместо `float64` для денежных сумм, сохранив контракт в спеке. --- ## На что копают на senior+ - **Моделирование union/полиморфизма**: `oneOf`/`anyOf`/`allOf` + `discriminator`, как это ложится на Go (генерируемые union-типы, `As/Merge` методы), когда лучше денормализовать схему ради удобства кода. - **Эволюция контракта**: стратегия версионирования (path vs header vs media-type), deprecation policy, `Sunset` header (RFC 8594), как катить breaking changes без даунтайма потребителей. - **CI-гейтинг контракта**: связка spectral/vacuum + oasdiff + проверка «generated code up to date» (перегенерить и `git diff --exit-code`), запрет merge при дрейфе/breaking. - **Где валидировать**: статика vs рантайм vs только-тесты; стоимость response-валидации на проде; что делать при расхождении embedded-spec и задеплоенной спеки. - **Тулчейн-зрелость 3.0 vs 3.1**: осознанный выбор версии под возможности генераторов/валидаторов команды, а не «берём новее». - **Организация спек в масштабе**: монорепо vs отдельный `api-specs` репо, бандлинг `$ref` (redocly bundle), переиспользование общих схем (error-модель, pagination) между сервисами. - **Контракт как граница команд**: mock-сервера (Prism) для параллельной разработки, генерация клиентов фронта (`openapi-typescript`/`orval`), consumer-driven contract testing (Pact) как дополнение к схеме. - **Безопасность в спеке**: корректное описание `securitySchemes` (oauth2 flows, scopes), что спека описывает контракт, но не заменяет реальную проверку прав; не утечь в публичную доку внутренние эндпоинты. - **Производительность валидации**: стоимость рантайм-валидации тяжёлых тел, кэширование скомпилированных схем, выбор `ogen` (без рефлексии) для горячих путей.