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/ > Модуль: Тестирование · Уровень: Middle+/Senior ## TL;DR `go test -cover` показывает процент покрытых **строк** (точнее — операторов/блоков), `-coverprofile=cover.out` пишет профиль для `go tool cover -html`/`-func`. Покрытие говорит «этот код **исполнялся** в тестах», но НЕ «он протестирован»: высокий процент при отсутствии ассертов, непокрытые ветки ошибок, неучтённые конкурентные пути — всё это даёт ложную уверенность. `go test -race` включает детектор гонок (ThreadSanitizer): инструментирует доступы к памяти и ловит data race в рантайме на тех путях, что реально исполнились — обязателен в CI для конкурентного кода. Флаки-тесты (недетерминированно падающие) — следствие гонок, зависимости от времени/порядка/сети, общего состояния; их находят повторными прогонами (`-count`, `-race`, `-shuffle`) и устраняют, а не ретраят вслепую. ## Теория ### Измерение покрытия ```bash go test -cover ./... # процент в выводе go test -coverprofile=cover.out ./... # профиль go tool cover -func=cover.out # построчно по функциям + total go tool cover -html=cover.out # визуализация в браузере go test -covermode=atomic -coverprofile=c.out # потокобезопасный режим (нужен с -race) ``` **covermode:** - `set` (по умолчанию без -race): покрыт/не покрыт блок (булево). - `count`: сколько раз исполнен блок. - `atomic`: как count, но потокобезопасно — **обязателен вместе с `-race`** и для конкурентного кода, иначе счётчики сами станут гонкой. **Что считается:** Go меряет покрытие на уровне базовых блоков (операторов), не строк буквально и не веток. `a && b` — частичное покрытие условий не отражается напрямую; обе ветки `if` нужно вызвать отдельно. **Покрытие пакета чужими тестами:** `-coverpkg=./...` учитывает, что тесты пакета A покрывают код пакета B (полезно для интеграционных тестов, меряющих покрытие всей системы). ### Что покрытие НЕ значит - **Исполнено ≠ проверено.** Тест без ассертов даёт 100% покрытия и ничего не гарантирует: ```go func TestParse(t *testing.T) { Parse("input") // вызвали, не проверили результат — 100% покрытия, 0% ценности } ``` - **Покрытие строк ≠ покрытие веток/условий.** `if a || b` может быть «покрыт» при одной комбинации, а баг — в другой. - **Не покрыты пути ошибок.** Часто 80% — это happy path; именно error handling (редкие ветки) ломается в проде и остаётся непокрытым. - **Не отражает конкурентность.** Покрытие не видит interleavings горутин — гонка может быть в «покрытом» коде. - **Не ловит отсутствующую логику.** Покрытие меряет существующий код; пропущенную проверку (которой нет) оно не покажет — для этого нужны mutation testing и продуманные кейсы. - **Цель-метрика деградирует (закон Гудхарта).** Жёсткий порог «90%» провоцирует тесты-пустышки ради цифры. Покрытие — диагностический сигнал (что вообще не тронуто), а не цель. Разумное применение: смотреть на **непокрытые** участки (особенно error-ветки и новый код в diff), а не молиться на общий процент. ### Детектор гонок (-race) ```bash go test -race ./... go run -race ./cmd/app go build -race -o app ./cmd/app ``` - Основан на **ThreadSanitizer**: инструментирует обращения к памяти и синхронизацию, во время выполнения отслеживает happens-before и сообщает о data race — конкурентном доступе к одной памяти, где хотя бы один доступ на запись, без синхронизации. - **Ловит только реально исполнившиеся пути.** Гонка в ветке, которую тест не вызвал, не будет найдена. Поэтому -race + хорошее покрытие конкурентных путей + повторные прогоны. - **Накладные расходы:** ~5-10x по CPU и ~5-10x по памяти. Не для прода; для CI и стресс-тестов. - **Находит data races, не logic races.** Корректная синхронизация (мьютекс) уберёт предупреждение детектора, но логическая гонка (неверный порядок операций под локом, TOCTOU) останется — её детектор не видит. Типичная находка: ```go func TestCounter(t *testing.T) { var c int var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done(); c++ }() // DATA RACE: c++ без синхронизации } wg.Wait() // -race немедленно сообщит о гонке на c } ``` ### -race в CI - Стандартная практика: отдельный прогон `go test -race ./...` в CI (часто с `-count=1` чтобы обойти кэш и с `-shuffle=on`). - Из-за overhead иногда выносят в отдельный job или ночной прогон, но для конкурентного кода — обязателен. - Помните: зелёный -race не доказывает отсутствие гонок — лишь что на исполненных путях их не нашли. Это вероятностный/динамический инструмент. ### Флаки-тесты Тест, который недетерминированно то проходит, то падает без изменений кода. Причины: - **Data race** — наиболее частая; лечится -race + фикс синхронизации. - **Зависимость от времени:** `time.Sleep` для ожидания, таймауты, завязка на `time.Now()`. Лечится `assert.Eventually`/polling, инъекцией Clock, отказом от sleep. - **Зависимость от порядка тестов:** общее глобальное состояние, незачищенные ресурсы. Лечится изоляцией, `t.Cleanup`, `-shuffle=on` для выявления. - **Внешние зависимости:** сеть, реальные сервисы, файловая система, рандомные порты. Лечится testcontainers с wait-стратегиями, моками сети. - **Параллелизм с общим состоянием:** `t.Parallel` на общей мапе/БД. Лечится изоляцией данных. - **Недетерминированный порядок мап** в ассертах. Лечится сортировкой или `ElementsMatch`. Инструменты выявления: ```bash go test -race -count=100 ./pkg/... # повторные прогоны выявляют редкие падения go test -shuffle=on ./... # рандомизирует порядок тестов и пакетов go test -run TestFlaky -count=1000 -race -timeout=5m ``` **Анти-паттерн:** автоматический retry флаки-тестов как «решение». Retry маскирует реальный баг (часто гонку, которая выстрелит и в проде) и эродирует доверие к suite. Флаки нужно диагностировать (systematic debugging, `-race`, `-shuffle`, повтор) и устранять причину. Допустимо карантинить (пометить, исключить из gate) с заведённым тикетом — но не вечно ретраить. ## Подводные камни / gotchas - **Высокое покрытие без ассертов** — иллюзия. Тест, вызвавший код, но не проверивший результат, бесполезен. Покрытие этого не показывает. - **Покрытие строк ≠ ветвей/условий.** `||`/`&&`, ранние `return`, error-ветки требуют отдельных кейсов. - **Порог покрытия как gate** провоцирует тесты-пустышки (Гудхарт). Лучше смотреть покрытие diff/новых строк и непокрытые error-пути. - **`-race` без `-covermode=atomic`** при сборе покрытия даёт гонку в самих счётчиках покрытия. С -race используйте atomic. - **Зелёный -race ≠ нет гонок.** Динамический детектор видит только исполненные interleavings; редкая гонка может не проявиться в прогоне. - **-race overhead** (5-10x CPU/RAM) — нельзя в прод и осторожно с таймаутами в CI (тесты идут медленнее, увеличивайте `-timeout`). - **Кэш тестов** маскирует флаки: `go test` кэширует успешные результаты. `-count=1` отключает кэш, чтобы реально перезапустить. - **Retry флаки вместо фикса** прячет реальные баги (часто продовые гонки) и разрушает доверие к CI. - **`t.Parallel` + общее состояние** — источник и гонок, и флаки. Изолируйте. - **mutation testing** не входит в стандартный тулчейн — высокое покрытие не значит, что тесты ловят регрессии; для этого нужны отдельные инструменты/дисциплина. ## Вопросы на собеседовании **В:** Что именно меряет `go test -cover` и чего оно НЕ значит? **О:** Меряет долю исполненных базовых блоков (операторов) кода во время тестов. Не значит, что код протестирован: тест без ассертов даёт высокое покрытие при нулевой ценности; не отражает покрытие веток/условий, путей ошибок, конкурентных interleavings и отсутствующей логики. Это сигнал «что не тронуто», а не гарантия корректности. **В:** Зачем `-covermode=atomic` и когда он обязателен? **О:** Это потокобезопасный режим счётчиков покрытия. Обязателен вместе с `-race` и для конкурентного кода: иначе инкремент счётчиков покрытия из нескольких горутин сам станет data race и исказит и покрытие, и вывод детектора. **В:** Как работает `-race` и каковы его ограничения? **О:** Это ThreadSanitizer: инструментирует доступы к памяти и синхронизацию, в рантайме строит happens-before и сообщает о data race на реально исполненных путях. Ограничения: ловит только то, что выполнилось (редкие interleavings может пропустить), не видит логических гонок при корректной синхронизации, даёт 5-10x overhead — не для прода. Зелёный прогон не доказывает отсутствие гонок. **В:** Почему 90% покрытия может быть хуже 70%? **О:** Если 90% набраны тестами без ассертов или дублирующими happy path, а 70% — осмысленными тестами с проверкой граничных и error-веток. Покрытие как жёсткая цель (закон Гудхарта) провоцирует тесты-пустышки ради цифры. Важнее качество кейсов и покрытие критичных/ошибочных путей. **В:** Что такое флаки-тест и каковы типичные причины? **О:** Тест, недетерминированно проходящий/падающий без изменений кода. Причины: data race, зависимость от времени (sleep/таймауты), от порядка тестов (общее состояние), внешние зависимости (сеть, ФС), параллелизм с общим состоянием, недетерминированный порядок мап. Корень часто — реальный баг, не «случайность». **В:** Как выявлять флаки-тесты? **О:** Повторными прогонами `go test -count=N` (отключает кэш), под `-race` (ловит гонки), `-shuffle=on` (рандомизирует порядок, вскрывает зависимость от порядка), увеличенным `-timeout`. Затем systematic debugging причины. **В:** Почему retry флаки-теста — плохое решение? **О:** Retry маскирует реальную проблему — чаще всего data race, которая выстрелит и в продакшене, — и разрушает доверие к suite (люди игнорируют падения). Допустим временный карантин с тикетом, но решение — диагностировать и устранить причину, а не ретраить вечно. **В:** Покрывает ли coverage конкурентные баги? **О:** Нет. Покрытие меряет, что блок кода исполнился, но не учитывает порядок чередования горутин (interleavings). Гонка может жить в полностью «покрытом» коде. Для конкурентности нужны `-race`, стресс-прогоны `-count`, продуманные конкурентные сценарии. ## На что копают на senior+ - **Зрелое отношение к метрике:** покрытие как диагностика, а не KPI; внимание к diff-покрытию и error-путям; осознание закона Гудхарта. - **Branch/condition vs line coverage:** понимание, что Go меряет блоки, и почему `&&`/`||` и error-ветки требуют отдельных кейсов; знание про mutation testing как более честную метрику. - **Модель памяти Go:** что такое data race vs logic race, happens-before, почему -race динамический и вероятностный, atomic-режим покрытия. - **CI-инженерия:** -race как отдельный gate, `-count=1` против кэша, `-shuffle`, таймауты под overhead, политика по флаки (карантин+тикет, не вечный retry). - **Диагностика флаки:** systematic debugging, воспроизведение через повтор/shuffle, устранение источников недетерминизма (время, порядок, общее состояние, сеть). - **Тестируемость:** инъекция Clock/рандома, изоляция состояния, `t.Cleanup`, отказ от sleep в пользу polling/Eventually.