Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Встраивание структур и интерфейсов (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/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Замыкания и переменная цикла (Go 1.22): https://go.vbloher.org/docs/01-core-go/closures-loopvar/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - encoding/json и теги структур: https://go.vbloher.org/docs/01-core-go/json-encoding/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Модули, версионирование и зависимости: https://go.vbloher.org/docs/01-core-go/go-modules/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - sync.Map и sync.Pool: https://go.vbloher.org/docs/02-concurrency/sync-map-pool/ - errgroup: группа горутин с ошибкой и отменой: https://go.vbloher.org/docs/02-concurrency/errgroup/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Сборщик мусора 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/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - 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/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Backend: https://go.vbloher.org/docs/05-backend/ - Структура Go-проекта: https://go.vbloher.org/docs/05-backend/project-layout/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - Чистая архитектура и внедрение зависимостей в Go: https://go.vbloher.org/docs/05-backend/clean-architecture/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - 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/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Observability: https://go.vbloher.org/docs/09-observability/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - 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/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - DevOps: https://go.vbloher.org/docs/11-devops/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ # Структура Go-проекта > Модуль: Backend · Уровень: Middle+/Senior ## TL;DR В Go нет официального стандарта раскладки каталогов; популярный `golang-standards/project-layout` — не документ команды Go и подходит не всем. Единственное, что закреплено в языке, — каталог **`internal/`**: его содержимое компилятор запрещает импортировать извне модуля. Пакет в Go — это **единица инкапсуляции и единица зависимости**, поэтому раскладка определяется не эстетикой, а графом импортов: циклы между пакетами запрещены компилятором, и это главное ограничение дизайна. Здоровый подход — плоская структура для маленького сервиса, деление **по предметной области** (а не по техническим слоям) для крупного, `cmd/` для точек входа и отсутствие пакетов-помоек `utils`, `common`, `models`. ## Простыми словами Первый вопрос новичка в Go: «где официальная структура проекта?» Ответа нет — команда Go её не публиковала, а популярный репозиторий `project-layout`, который выпадает в поиске, к официальным не относится и справедливо критикуется за избыточность. Что действительно закреплено в языке — это `internal/`. Всё, что лежит внутри такого каталога, невозможно импортировать снаружи модуля: не по соглашению, а по запрету компилятора. Это единственный настоящий механизм «приватности» уровня модуля, и им стоит пользоваться. Дальше начинается то, что определяет структуру на самом деле: **в Go запрещены циклические импорты**. Пакет A импортирует B, B импортирует A — код не соберётся. Поэтому раскладка каталогов — это не вопрос вкуса, а вопрос направления зависимостей, и продумывать её приходится заранее. Практическое правило простое: маленький сервис живёт одним-двумя пакетами и не нуждается ни в какой иерархии. Растущий сервис делят **по предметной области** (`order`, `payment`, `user`), а не по техническим слоям (`models`, `services`, `repositories`) — иначе любое изменение одной фичи расползается по четырём каталогам. **Если ты с другого языка.** Привычная по Java и C# раскладка по слоям здесь работает плохо: там пакет — просто пространство имён, а в Go это ещё и единица зависимости. И имена вроде `utils`, `helpers`, `common` в Go считаются запахом: пакет должен называться по тому, что он делает, потому что его имя становится частью каждого вызова (`order.New`, а не `models.NewOrder`). **Словарь этой статьи:** - **`internal/`** — каталог, импорт из которого запрещён вне модуля; - **граф импортов** — кто кого импортирует; в Go он обязан быть ациклическим; - **`cmd/`** — соглашение для точек входа (`main`-пакетов); - **пакет-помойка** — `utils`/`common`, куда складывают всё подряд. ## Теория ### Пакет как единица дизайна В Go пакет решает три задачи сразу: 1. **Инкапсуляция.** Экспортируется то, что с большой буквы; остальное недоступно снаружи пакета. Не класс, не модуль — именно пакет является границей видимости. 2. **Единица зависимости.** Импортируется пакет целиком, частично взять нельзя. 3. **Часть имени.** Вызов выглядит как `пакет.Функция`, поэтому `strings.Split` читается хорошо, а `utils.StringSplit` — плохо. Отсюда рекомендация не дублировать имя пакета в именах функций. Главное ограничение: **циклы запрещены**. Если `order` импортирует `payment`, а `payment` — `order`, сборка падает. Обойти это можно только одним способом — выделить общий интерфейс или тип в третий пакет, от которого зависят оба. Это ограничение больно бьёт по привычным из ООП-языков схемам с двусторонними ссылками между сущностями, зато не даёт архитектуре превратиться в клубок. ### internal: единственная гарантия от компилятора ``` myapp/ ├── go.mod // module github.com/user/myapp ├── internal/ │ └── storage/ // импортируется только внутри github.com/user/myapp └── pkg/ └── client/ // импортируется кем угодно ``` Правило: пакет из каталога `internal` доступен только коду, чей путь начинается с родителя этого `internal`. Попытка импортировать снаружи — ошибка компиляции, а не замечание линтера. Практический вывод: **по умолчанию клади код в `internal/`**. Публичным делай только то, что действительно является API библиотеки. Иначе любая внутренняя структура превращается в обязательство перед чужими проектами. Каталог `pkg/` — это уже просто соглашение, никакой магии в нём нет. Многие считают его лишним: если код не в `internal`, он и так публичен, а лишний уровень вложенности удлиняет пути импорта. Разумный компромисс — заводить `pkg/` только когда в репозитории действительно есть код, предназначенный для внешних потребителей. ### cmd: точки входа ``` cmd/ ├── api/ │ └── main.go // HTTP-сервер ├── worker/ │ └── main.go // фоновый обработчик └── migrate/ └── main.go // утилита миграций ``` Соглашение `cmd/<имя>/main.go` даёт две вещи: имя каталога становится именем бинарника при `go build ./cmd/api`, и в одном модуле спокойно уживаются несколько исполняемых файлов, разделяя общий код. Главное правило для `main`: он должен быть **тонким**. Прочитать конфигурацию, собрать зависимости, запустить, дождаться сигнала, корректно остановиться. Бизнес-логики в `main` быть не должно — её невозможно тестировать. ### Три рабочие раскладки **1. Плоская — для маленького сервиса** ``` myservice/ ├── go.mod ├── main.go ├── handler.go ├── store.go ├── store_test.go └── config.go ``` Всё в одном пакете. Никаких проблем с циклами, всё видит всё, тесты имеют доступ к внутренностям. Для сервиса на пару тысяч строк это лучший вариант, и переусложнять его «на вырост» не надо — разложить по пакетам всегда можно позже, когда границы станут очевидны. **2. По предметной области — для растущего сервиса** ``` myservice/ ├── cmd/api/main.go └── internal/ ├── order/ // всё про заказы: модель, логика, хранилище, хендлеры │ ├── order.go │ ├── service.go │ ├── postgres.go │ └── http.go ├── payment/ ├── user/ └── platform/ // общая инфраструктура ├── database/ └── httpx/ ``` Каждый пакет — законченный кусок предметной области. Изменение в логике заказов затрагивает один каталог. Границы совпадают с границами бизнеса, а значит и с границами возможного будущего разделения на сервисы. **3. По слоям — для сложной логики** ``` internal/ ├── domain/ // сущности и правила, без зависимостей ├── usecase/ // сценарии, зависят только от domain ├── adapter/ // http, grpc, postgres — знают про usecase └── infra/ // конфиг, логгер, подключения ``` Оправдано, когда бизнес-логика действительно сложная и её важно изолировать от способа доставки и хранения. Подробнее — в статье про чистую архитектуру. Для CRUD-сервиса такая раскладка даёт больше церемоний, чем пользы. ### Чего делать не стоит **Пакеты-помойки.** `utils`, `common`, `helpers`, `misc` — это пакеты без предметной области. Они растут бесконечно, их импортируют все, и они мгновенно становятся источником циклов. Функция должна лежать рядом с тем, к чему относится: работа со строками — в `strings`-подобном пакете, а не «в общем». **Раскладка «по типу сущности».** `models/`, `interfaces/`, `structs/` — техническая классификация вместо смысловой. Пакет `models` с сорока структурами импортируется отовсюду и связывает весь проект в один узел. **Преждевременное деление.** Восемь пакетов в проекте на тысячу строк не улучшают ничего, зато немедленно создают проблемы с циклами и заставляют экспортировать то, что могло остаться приватным. **Дублирование имени.** `order/order.go` с типом `order.Order` — читается как заикание. Часто лучше `order.Service`, `order.Repository`, `order.New`. ### Куда класть остальное Устоявшиеся соглашения, которые встречаются в большинстве репозиториев: ``` api/ — контракты: .proto, openapi.yaml migrations/ — SQL-миграции deployments/ — Dockerfile, k8s-манифесты, helm scripts/ — вспомогательные скрипты testdata/ — данные для тестов (имя понимает сам go tool: каталог игнорируется при сборке) docs/ — документация tools/ — инструменты сборки, зафиксированные через tools.go ``` `testdata` — единственный из них, который имеет специальное значение для тулчейна: каталоги с таким именем игнорируются при сборке. ### Тесты и границы пакета ```go package order // внутренний тест: видит неэкспортируемое package order_test // внешний тест: видит только публичный API ``` Второй вариант полезнее, чем кажется: он заставляет писать тест так, как этим пакетом будет пользоваться настоящий код, и сразу показывает, удобен ли API. Оба файла могут лежать в одном каталоге. ## Подводные камни / gotchas - **Циклический импорт.** Компилятор откажется собирать. Лечится выделением общего интерфейса в отдельный пакет либо инверсией зависимости — интерфейс объявляется у потребителя. - **`internal` работает от родителя.** `a/internal/x` доступен всему внутри `a/`, а не только соседям. Уровень вложенности `internal` задаёт область видимости. - **`pkg/` не даёт ничего**, кроме лишнего уровня в пути. Это соглашение, а не механизм. - **Слишком ранние абстракции.** Интерфейсы «на будущее», которые имеют ровно одну реализацию, — чистый минус к читаемости. - **`main` с бизнес-логикой** невозможно протестировать. - **Имя пакета — часть API.** Переименование каталога ломает импорты у всех потребителей. - **Подкаталоги — отдельные пакеты.** Вложенность не даёт доступа к неэкспортируемым идентификаторам родителя; в Go нет «подпакетов» в java-смысле. ## Вопросы на собеседовании **В:** Есть ли в Go официальный стандарт структуры проекта? **О:** Нет. Репозиторий `golang-standards/project-layout` — не документ команды Go и не является стандартом. Единственное, что закреплено в языке, — семантика каталога `internal`. **В:** Что делает `internal/`? **О:** Запрещает импорт своего содержимого извне поддерева, в котором он лежит. Это проверяет компилятор, поэтому механизм надёжный. Обычно весь код сервиса кладут именно туда, оставляя публичным только то, что действительно предназначено для внешних потребителей. **В:** Как решить циклический импорт? **О:** Либо выделить общую часть (типы, интерфейсы) в третий пакет, от которого зависят оба, либо инвертировать зависимость: объявить интерфейс на стороне потребителя, а реализацию передавать снаружи. Второй способ идиоматичнее. **В:** Почему деление по предметной области предпочтительнее деления по слоям? **О:** Потому что изменения приходят по бизнес-фичам, а не по слоям. При раскладке по областям правка одной фичи затрагивает один пакет, при раскладке по слоям — четыре. Плюс границы областей естественно совпадают с возможными границами будущих сервисов. **В:** Чем плох пакет `utils`? **О:** У него нет предметной области, поэтому он растёт бесконечно и импортируется отовсюду, становясь узлом связности и источником циклов. Кроме того, имя пакета участвует в каждом вызове, и `utils.DoSomething` ничего не сообщает читателю. **В:** В чём разница между пакетами `foo` и `foo_test` в тестах? **О:** Первый видит неэкспортируемые идентификаторы, второй — только публичный API. Внешний тестовый пакет полезен как проверка удобства API и защита от тестирования деталей реализации. **В:** Как организовать несколько бинарников в одном модуле? **О:** Через `cmd/<имя>/main.go` для каждого. Общий код лежит в `internal/` и переиспользуется; сборка идёт командой `go build ./cmd/<имя>`. ## На что копают на senior+ - Понимание, что структура определяется графом импортов и запретом циклов, а не эстетикой каталогов. - Умение обосновать выбор раскладки под размер и срок жизни проекта, включая честное «здесь достаточно плоской структуры». - Осознанное использование `internal` как механизма контроля публичного API модуля. - Способность распознать преждевременную абстракцию и объяснить, чем она вредна. - Follow-up: «Как вы делите монорепозиторий на модули?», «Куда класть общие для нескольких сервисов типы?», «Как организовать пакет так, чтобы его было удобно тестировать без моков?».