Senior Go-разработчик отвечает за подсистему или сервис целиком: проектирует, поддерживает, развивает. Влияет на решения за пределами своих задач, помогает расти коллегам. Таких людей на рынке мало, а ошибка при найме обходится дорого — Senior принимает решения, которые живут в системе годами после его ухода.
Поэтому я провожу собеседование на эту роль в два этапа: технический по Go и отдельный архитектурный. Ниже — какие темы покрывать, какие вопросы задавать, по каким критериям оценивать ответы.
Чем Senior отличается от Middle
Зона ответственности
Middle отвечает за реализацию задачи, Senior — за подсистему или сервис целиком: API, схему данных, эксплуатацию, ресурсы, план развития на 6-12 месяцев вперёд. Senior не ждёт, когда ему принесут задачу. Он сам ищет узкие места, в которые подсистема может упереться по производительности, формализует техдолг и берётся за устранение.
Архитектурное мышление
Middle способен спроектировать микросервис с нуля. Senior сначала спрашивает, а нужен ли вообще отдельный сервис, или это модуль в монолите (монолит vs микросервисы). Он мыслит на уровне границ сервисов, контрактов, отказоустойчивости и стоимости владения — а не на уровне пакетов и функций.
Технические компромиссы и принятие решений
Senior формулирует выбор как trade-off с обоснованием через бизнес-метрики, нагрузку и операционные риски. Умеет составить ADR (Architecture Decision Record). Не исходит из позиции «мне кажется, так будет лучше».
Глубина в Go
Senior знает, как Go устроен внутри, а не только как им пользоваться: модель памяти, планировщик, сборщик мусора, escape analysis. Он читал исходники стандартной библиотеки и может объяснить, почему sync.Map быстрее в одном паттерне доступа и медленнее в другом — без подглядывания в документацию.
Лидерство и развитие людей
В роль Senior’а входит и техническое лидерство. Он менторит джунов и миддлов, делает ревью кода так, чтобы команда писала лучше, умеет давать обратную связь — в том числе негативную. Без этих навыков получается не Senior, а «Middle с десятилетним опытом».
Почему собеседование делят на два этапа
С Junior и Middle хватает одного интервью: вы проверяете язык, базовые паттерны и софт-скиллы. С Senior так не сработает по двум причинам.
Разный фокус навыков. Глубокое знание Go и умение проектировать распределённую систему — это разные вещи. Сильный разработчик может слабо ориентироваться в архитектуре сервисов под нагрузкой. Сильный архитектор — писать посредственный Go-код. На Senior-позиции нужно и то и другое, и проверять эти навыки приходится отдельно: иначе вы либо переоцените язык, либо переоцените архитектуру.
Разный формат проверки. Технический этап — это вопросы с конкретными ответами и одна-две небольшие задачи на код. Архитектурный — открытая дискуссия по системе, где у проблем нет единственно правильного ответа, а оценивается ход мысли и зрелость аргументации. Смешав форматы в одном интервью на полтора часа, не успеешь нигде.
Итоговая схема оценки навыков получается примерно такая:
| Этап | Длительность | Формат | Что проверяем |
|---|---|---|---|
| 1. Технический (Go) | 60-75 мин | Вопросы + 1-2 небольшие задачи | Глубина языка, рантайма, экосистемы |
| 2. Архитектурный | 60-90 мин | Открытый дизайн системы по кейсу | Проектирование, компромиссы, операционная зрелость |
Между этапами желательно сделать перерыв в день: и кандидат отдыхает, и вы успеваете обсудить с командой результат первого этапа и решить, идти ли дальше.
Этап 1. Технический (Go)
Темы (кратко)
- Рантайм и модель памяти — планировщик горутин, GMP-модель, GC, escape analysis, модель памяти Go (happens-before, синхронизация).
- Конкурентность на уровне дизайна — паттерны (pipeline, fan-in/fan-out, worker pool, semaphore), управление backpressure, отмена через context, утечки горутин.
- Производительность и низкий уровень — pprof CPU/mem/goroutine/block/mutex, чтение flamegraph, бенчмарки и
benchstat,sync.Pool, аллокации, зачем нуженunsafe. - Стандартная библиотека и внутренности — что внутри
net/http,database/sql,encoding/json, как работаютsync.Mutexиsync.WaitGroup, дженерики. - Дизайн API и библиотек — обратная совместимость, semver, идиоматичные интерфейсы (принимаем интерфейс, возвращаем структуру), функциональные опции,
godoc. - Обработка ошибок и устойчивость — кастомные типы ошибок,
errors.Is/errors.As,context.Cause, retry с jitter, circuit breaker, идемпотентность, graceful degradation. - Тестирование сложных систем — табличные тесты, моки vs стабы vs фейки, интеграционные тесты через testcontainers, тестирование контрактов пакета, fuzzing.
- Безопасность и supply chain —
govulncheck, политика обновлений зависимостей, секреты и их ротация, TLS/mTLS, защита от типовых уязвимостей в HTTP-сервисах (инъекции, CSRF, IDOR и т.п.).
1.1 Рантайм и модель памяти
Что проверяем: Как Go работает под капотом: планировщик, переключения, сборка мусора, escape analysis, модель памяти. Senior должен уметь объяснить наблюдаемое поведение (рост latency, p99, всплески CPU) через свойства рантайма.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | Не отличает горутину от потока, не слышал про GC tuning |
| Проходной минимум | Понимает GMP-модель в общих чертах, знает, что есть escape analysis |
| Хороший senior | Объясняет, что происходит при блокирующем системном вызове, как работают preemption и GOMAXPROCS, читал статью про GC |
| Сильный senior | Связывает рантайм с наблюдаемыми метриками, диагностировал реальные инциденты с применением runtime/* пакетов, понимает компромиссы GOGC/GOMEMLIMIT |
Примеры вопросов:
Опишите GMP-модель. Что произойдёт, если горутина уйдёт в долгий блокирующий syscall? А если в долгий CPU-bound цикл без точек preemption? Что изменилось с Go 1.14 (asynchronous preemption)?
Что такое модель памяти Go? Когда нужно явно синхронизировать доступ, а когда хватает канала или happens-before-гарантий стартующей горутины? Приведите пример кода, который «вроде работает», но фактически содержит data race.
Как сборщик мусора Go (трёхцветный, конкурентный, неперемещающий) влияет на p99 задержки? Когда имеет смысл крутить
GOGC, а когда —GOMEMLIMIT(Go 1.19+)?Что такое escape analysis? Приведите пример кода, где значение «убегает» на кучу — например через
interface{}, замыкание или возврат указателя. Как это проверить (go build -gcflags="-m")?У сервиса периодически растёт CPU на 30% без роста нагрузки. С чего начнёте диагностику? (pprof, runtime/trace, метрики GC через
GODEBUG=gctrace=1).
На что обращать внимание:
- Упоминает
runtime/trace, а не только pprof. - Знает про write barrier и почему GC не «остановит мир» на минуту даже при огромной куче.
- Понимает ли разницу между
GOGCиGOMEMLIMITи причины появления второй переменной. - Умеет ли читать output
gctrace=1.
1.2 Конкурентность на уровне дизайна
Что проверяем: Не «знает каналы», а умеет выбрать паттерн под задачу: pipeline для потоковой обработки, fan-out для распараллеливания, worker pool для ограничения ресурсов, семафор через буферизованный канал. Понимает backpressure и graceful shutdown.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | Каналы и мьютексы как «взаимозаменяемые», игнорирует утечки горутин |
| Проходной минимум | Знает базовые паттерны, использует context для отмены |
| Хороший senior | Выбирает паттерн осознанно, обоснованно использует errgroup, понимает backpressure |
| Сильный senior | Проектирует pipeline под нагрузку, учитывает кардинальность очередей, fairness, диагностирует через runtime/pprof goroutine и runtime/trace |
Примеры вопросов и задач: f
Дизайн: Пишете сервис, который читает события из Kafka, обогащает их вызовом внешнего API (медленный, ~200мс) и пишет в БД. Опишите конкурентную схему. Где здесь backpressure? Что произойдёт, если внешний API лёг?
Worker pool: Реализуйте worker pool с ограничением N горутин, отменой через context и сбором ошибок. Какие компромиссы между передачей задач через канал и через
semaphore + WaitGroup?Утечки: Покажите три способа «утечь» горутиной. Как их находить в проде? (pprof goroutine, метрика
go_goroutines, alerting на рост).Тонкое: Чем
errgroupотличается от ручной координации через канал ошибок? Что нового в Go 1.25 WaitGroup.Go?Подвох: Дан код с
selectиз трёх каналов в цикле. При закрытии одного из каналов цикл крутит CPU на 100%. Почему? Как исправить?
На что обращать внимание:
- Различает ли «параллельность» и «конкурентность».
- Думает ли о fairness и starvation при множественных каналах.
- Упоминает ли graceful shutdown в задачах с пайплайнами.
- Понимает ли разницу между unbuffered, buffered и nil-каналом в
select.
1.3 Производительность и низкий уровень
Что проверяем:
Умение оптимизировать с измерениями. Senior не «оптимизирует на глаз»: профилирует, формулирует гипотезу, измеряет до и после через benchstat. И знает, когда оптимизация принесёт пользу, а когда это over-engineering.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Оптимизирую на глаз», не знает pprof |
| Проходной минимум | Профилирует CPU и память через pprof, пишет бенчмарки |
| Хороший senior | Читает flamegraph, использует benchstat, понимает аллокации, применял sync.Pool |
| Сильный senior | Профилировал прод-сервисы под нагрузкой, делал escape-аналитический рефакторинг, знает runtime/metrics, понимает компромиссы unsafe |
Примеры вопросов:
Сервис тратит 20% CPU на JSON-сериализацию. Ваши шаги? (профилирование → подтверждение → варианты:
encoding/jsonv2,easyjson, code-gen, переход на protobuf; обязательное измерение сbenchstat).Когда
sync.Poolпомогает, а когда вредит? Приведите конкретный сценарий, гдеsync.Poolухудшил производительность.Покажите бенчмарк, в котором компилятор выкинет вашу горячую функцию (dead code elimination). Как защититься? (
b.Loop()с Go 1.24, присвоение результатаruntime.KeepAliveили глобальнойvar sink).Что такое false sharing в Go? Когда вы реально с этим сталкивались?
unsafe: приведите случай, когда он оправдан, и случай, когда это плохая идея. Какие риски?
На что обращать внимание:
- Всегда ли требует измерений до и после.
- Упоминает ли
benchstatи статистическую значимость, а не «было 100, стало 95». - Думает ли о компромиссе «производительность vs читаемость».
- Знает ли про block-, mutex- и allocs-профили, а не только CPU и heap.
1.4 Стандартная библиотека и внутренности
Что проверяем:
Знание стандартной библиотеки на уровне «как это сделано», а не «как этим пользоваться». Senior спокойно ориентируется в net/http, database/sql, encoding/json, sync. Идеально — читал исходники.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | Знает только сигнатуры функций |
| Проходной минимум | Понимает поведение основных пакетов, читает godoc |
| Хороший senior | Заглядывал в исходники, объясняет неочевидные особенности |
| Сильный senior | Может объяснить дизайн-решения авторов, знает историю эволюции пакетов |
Примеры вопросов:
Как
net/http.Serverобрабатывает соединения? Что такоеhttp.Transportи почему важно переиспользование соединений? Что произойдёт, если не читать body ответа?database/sql: как устроен пул соединений? Чемpgxотличается отlib/pq(драйверы PostgreSQL)? Что значитConnMaxLifetimeи зачем оно нужно при работе через pgbouncer?encoding/json: что внутри? Почему он медленный, и какие альтернативы (json/v2,easyjson,sonic)? Какие подводные камни уsonic(случай с багом)?Generics с Go 1.18+: когда они уместны, а когда лучше остаться на интерфейсах? Почему
slices.Sortбыстрее, чемsort.Slice?Что нового в стандартной библиотеке между Go 1.21 и Go 1.25, что вы реально используете? (
slog,cmp,maps/slices,unique,WaitGroup.Go).
На что обращать внимание:
- Когда говорит «я читал, что…» — уточните, читал в блоге или в исходниках.
- Различает ли «никогда не использовал» и «использовал, но не помню деталей».
- Следит ли за релизами и changelog’ами Go.
1.5 Дизайн API и библиотек
Что проверяем: Умение проектировать публичные API: пакеты, интерфейсы, опции, расширяемость, обратная совместимость. Плохо спроектированный публичный API нельзя «откатить» — он застрянет в кодовой базе на годы, и Senior это понимает.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Какая разница, как назвать функцию» |
| Проходной минимум | Знает «принимаем интерфейс, возвращаем структуру» |
| Хороший senior | Использует функциональные опции, продумывает обратную совместимость |
| Сильный senior | Проектирует API под эволюцию, понимает semver на уровне Go-модулей, документирует через примеры |
Примеры вопросов:
Проектируете клиент к внешнему сервису как библиотеку для коллег. Какой будет публичный API? Где интерфейсы, где структуры? Как передать таймауты, retry, метрики? (функциональные опции vs config struct).
Семвер в Go-модулях: что значит
v2, как его правильно выпускать, и почему мажорная версия попадает в путь импорта?Какие изменения в публичном API считаются ломающими в Go? (добавление метода в интерфейс — да, поля в публичную структуру — нет, при условии что её не создают литералом без имён полей).
Когда оправдан паттерн «context.Context первым аргументом», а когда нет?
Дан godoc плохой библиотеки — что бы вы исправили? (примеры, doc.go, идиоматичные имена, отсутствие глобального состояния).
На что обращать внимание:
- Думает ли о пользователях библиотеки, а не только о её авторе.
- Упоминает ли doc-комментарии, runnable examples, тесты как часть API.
- Понимает ли, что «удобный» API часто противоречит «расширяемому».
1.6 Обработка ошибок и устойчивость
Что проверяем: Идиоматичная обработка ошибок на уровне дизайна сервиса: классификация (retryable/non-retryable), wrapping, sentinel’ы vs типы, паттерны устойчивости (retry с jitter, circuit breaker, идемпотентность).
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | if err != nil { return err } без контекста |
| Проходной минимум | Использует %w, errors.Is/errors.As |
| Хороший senior | Классифицирует ошибки, реализовал retry с backoff и jitter |
| Сильный senior | Проектирует error-таксономию, понимает идемпотентность на уровне системы, реализовал circuit breaker и graceful degradation |
Примеры вопросов:
Сервис вызывает 5 внешних API. Как спроектируете обработку ошибок? Что считать retryable, что нет? Где границы транзакции?
Retry: почему нужен jitter? Что произойдёт без него при отказе downstream-сервиса? (thundering herd, синхронизация retry, повторный обвал).
Идемпотентность: как реализуете идемпотентный POST? (ключ идемпотентности, дедупликация, окно хранения).
Когда оправдан circuit breaker, когда — bulkhead, когда достаточно таймаута? (три разных паттерна, разные цели).
context.Causeс Go 1.20: зачем добавили, какие проблемы он решает?
На что обращать внимание:
- Различает ли ошибки клиента и ошибки инфраструктуры.
- Думает ли о том, что ошибка — это сигнал для observability, а не только для return.
- Знает ли, что
panicчерез границу горутины не ловитсяrecoverснаружи.
1.7 Тестирование сложных систем
Что проверяем: Отношение к тестам: не «100% покрытия любой ценой», а понимание, где какой тест уместен. Senior умеет писать integration-тесты с testcontainers, contract-тесты для межсервисных API, fuzzing для парсеров.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Тесты пишет QA» |
| Проходной минимум | Юнит-тесты, табличные тесты, моки |
| Хороший senior | Integration через testcontainers, понимает пирамиду тестирования |
| Сильный senior | Contract testing, fuzzing критичных мест, осознанный выбор «когда не тестировать» |
Примеры вопросов:
У вас 3 микросервиса: A → B → C. Какие тесты на каких границах? (юнит внутри, contract на границах, e2e только для критичных сценариев).
Когда мок вреден? (когда тест становится тестом мока, а не кода; когда мок расходится с реальным поведением — классический «прод сломался, тесты зелёные»).
Fuzzing: какие места в коде стоит фаззить? Покажите пример находки фаззингом (реальный случай с sonic).
Как тестировать код с временем? (
clockинтерфейс, фейковые часы, не использоватьtime.Sleepв тестах).Flaky-тест в CI. Стратегия? (не «retry в CI», а найти причину: гонка, зависимость от порядка, внешний ресурс).
На что обращать внимание:
- Понимает ли, что покрытие — не метрика качества тестов.
- Различает ли «тестируемый код» и «код с тестами».
- Упоминает ли testcontainers, а не «давайте поднимем БД в Docker Compose для тестов».
1.8 Безопасность и supply chain
Что проверяем: Senior отвечает за безопасность своего кода и зависимостей. «Security — это другая команда» — не позиция Senior’а. Базовое понимание угроз обязательно: уязвимости в зависимостях, секреты, TLS, классические дыры в HTTP-сервисах.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «За безопасность отвечает security-команда» |
| Проходной минимум | Знает про SQL injection, параметризованные запросы, секреты не в git |
| Хороший senior | Использует govulncheck, понимает supply chain risk, настраивает TLS |
| Сильный senior | Понимает OWASP, проводит threat modeling, ротирует секреты, ограничивает blast radius |
Примеры вопросов:
Стратегия обновления зависимостей: dependabot/renovate, политика мажорных обновлений,
go.sumи проверка checksum. Что делать с CVE в транзитивной зависимости?govulncheck: чем отличается от обычного сканера CVE? (анализирует, реально ли вызывается уязвимый код, а не «есть ли он вgo.sum»).Хранение секретов: что плохого в
.env? Как ротировать? Где хранить (Vault, secrets manager облака)?TLS в Go: верифицируете ли цепочку, что такое pinning, когда нужна mTLS?
HTTP-сервис: SSRF, открытые редиректы, CSRF — что из этого относится к API на Go и как защищаться?
На что обращать внимание:
- Не путает ли «security» и «compliance».
- Понимает ли blast radius и принцип least privilege.
- Знает ли, что в Go ошибки парсинга URL/email не дают гарантий безопасности — нужны явные allow-list проверки.
Этап 2. Архитектурный
Это открытое интервью на 60-90 минут вокруг одного кейса. Я не использую заготовленный список вопросов: даю задачу и веду диалог. Проверяю не «знает ли он Kafka», а как он думает. Задаёт ли уточняющие вопросы. Формулирует ли требования. Рассматривает ли альтернативы. Обосновывает ли выбор.
Темы (кратко)
Сбор требований — функциональные и нефункциональные, нагрузка, SLA, бюджет, ограничения.
Декомпозиция системы — границы сервисов, ownership данных, контракты, синхронные vs асинхронные взаимодействия.
Хранилища данных — SQL vs NoSQL, репликация, шардирование, кэширование, выбор по требованиям.
Распределённые свойства — consistency, CAP, exactly-once vs at-least-once, sagas, идемпотентность на уровне системы.
Очереди и event-driven — Kafka vs RabbitMQ vs NATS, ordering, partitioning, ретраи и dead letter.
Надёжность и операционная зрелость — SLO/SLI, observability (logs/metrics/traces), incident response, deploy-стратегии.
Безопасность и compliance — авторизация, аудит, персональные данные, защита границы сети, мульти-тенант изоляция.
Эволюция и стоимость — план миграции, обратная совместимость, capacity planning, cost-aware design.
2.1 Сбор требований
Что проверяем: Может ли кандидат не бросаться рисовать диаграмму по первой формулировке, а сначала выяснить, что на самом деле нужно. Применяет ли «5 почему», различает ли функциональные и нефункциональные требования.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | Сразу начинает рисовать диаграмму |
| Проходной минимум | Уточняет, что должна делать система |
| Хороший senior | Спрашивает про нагрузку, SLA, бюджет, команду |
| Сильный senior | Выясняет реальную проблему за постановкой, проверяет ограничения, обсуждает что не нужно делать |
Пример кейса:
«Спроектируйте сервис, который принимает события от мобильных приложений и складывает их для аналитики».
Сильный senior за первые 10 минут задаст вопросы вроде:
- Сколько событий в секунду на пике? На сколько лет планируем хранить?
- Кто читатель — BI-аналитики раз в день, или real-time дашборд?
- Допустимы ли потери? Допустимо ли дублирование?
- Какой бюджет, какая команда поддерживает?
- А зачем строить своё, а не использовать готовое (ClickHouse + Vector, или managed-решение)?
На что обращать внимание:
- Думает ли про «зачем» — может, задача не в инженерии, а в build vs buy.
- Спрашивает ли про SLA и SLO, а не «нужна высокая доступность».
- Уточняет ли неявные ограничения (compliance, регион, on-premise vs cloud).
2.2 Декомпозиция системы
Что проверяем: Где провести границы сервисов, что выделять в отдельный сервис, а что оставить модулем. Когда синхронный вызов, когда событие. Кто владеет какими данными.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Микросервисы потому что модно» или «всё в монолите потому что просто» |
| Проходной минимум | Разделяет по функциональным областям |
| Хороший senior | Аргументирует границы через ownership данных и autonomy команд |
| Сильный senior | Применяет DDD-bounded contexts, обсуждает компромисс монолит/микросервисы через размер команды и cognitive load |
Вопросы в дискуссии:
Почему именно эти границы? Что произойдёт, если объединить сервисы A и B?
Где у вас синхронные вызовы, где асинхронные? Что произойдёт, если очередь упадёт?
Кто владеет данными о пользователе? Что делать, если две команды хотят добавить поле?
Как менять контракт между сервисами без downtime?
На что обращать внимание:
- Не плодит ли сервисы без причины.
- Думает ли про команды (Conway’s Law) и cognitive load, а не только про техническую красоту.
- Понимает ли «модульный монолит» как валидную опцию.
2.3 Хранилища данных
Что проверяем: Выбор хранилища под требования, а не «потому что мы любим Postgres». Понимание репликации, шардирования, индексов, паттернов доступа, бэкапов и восстановления.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «PostgreSQL подходит для всего» или «NoSQL быстрее» |
| Проходной минимум | Различает SQL и NoSQL, базово понимает индексы |
| Хороший senior | Выбирает под паттерн доступа, понимает репликацию, шардирование |
| Сильный senior | Проектирует с учётом точки роста, делал миграции хранилищ под нагрузкой, понимает RPO/RTO |
Вопросы:
Как выбираете между PostgreSQL, ClickHouse, ScyllaDB/Cassandra, S3 для разных частей системы?
Когда нужно шардирование? Какие стратегии шардирования и компромиссы (consistent hashing, range, hash, geo)?
Чтение из реплики: какие гарантии вы теряете? Как с этим жить (read-your-writes consistency)?
Что такое RPO и RTO? Как они влияют на стратегию бэкапов?
Кэширование: write-through, write-back, write-around, look-aside. Где какая стратегия и какие проблемы (thundering herd при инвалидации, stampede)?
На что обращать внимание:
- Различает ли OLTP и OLAP нагрузку.
- Думает ли про схему миграции и rollback.
- Понимает ли, что выбор хранилища — это решение на 5+ лет.
2.4 Распределённые свойства
Что проверяем: Понимание сложности распределённых систем: consistency, ordering, exactly-once как иллюзия, сетевые партиции, частичные отказы. Где система реально гарантирует то, что обещает, а где гарантии — на бумаге.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Сеть надёжна», «exactly-once есть» |
| Проходной минимум | Знает CAP, понимает at-least-once |
| Хороший senior | Проектирует идемпотентность, понимает разницу strong/eventual consistency |
| Сильный senior | Применяет saga, outbox, change data capture, понимает компромисс consistency и latency |
Вопросы:
Транзакция «перевести деньги между двумя сервисами»: как? (saga, two-phase commit и его проблемы, outbox).
Гарантии Kafka: что значит «at-least-once», что нужно от потребителя для идемпотентности?
Если в очереди порядок важен — как обеспечить? Что с этим теряете?
Distributed transactions vs eventual consistency vs orchestration vs choreography — компромиссы.
Сетевая партиция между датацентрами на 5 минут. Что произойдёт с вашей системой? Что вы выбираете — доступность или консистентность — и для какой части системы?
На что обращать внимание:
- Не верит ли в «exactly-once» как в свойство транспорта.
- Понимает ли, что «consistency» в CAP — это linearizability, а не «целостность данных».
- Различает ли уровни изоляции в БД и distributed consistency.
2.5 Очереди и event-driven
Что проверяем: Выбор брокера под задачу, проектирование топологии, обработка отказов: ретраи, DLQ, ordering. Подход не «возьмём Kafka», а «вот требования, поэтому Kafka, и вот цена этого выбора».
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Берём Kafka» без обоснования |
| Проходной минимум | Выбирает между Kafka и RabbitMQ по задаче |
| Хороший senior | Проектирует партиционирование, DLQ, ретраи, exactly-once-обработку через идемпотентность |
| Сильный senior | Учитывает hot partitions, ребалансировку, схему эволюции событий, backfill |
Вопросы:
Kafka vs RabbitMQ vs NATS — для каких задач каждый?
Партиционирование: как выбрать ключ? Что произойдёт, если один ключ начнёт давать 50% трафика (hot partition)?
Эволюция схемы событий: добавление поля, удаление, переименование. Как? (schema registry, обратная совместимость на 2 версии).
Потребитель упал, обработав сообщение, но не закоммитив offset. Что произойдёт? Как защититься?
DLQ: когда отправлять, как разбирать, кто отвечает?
На что обращать внимание:
- Понимает ли, что Kafka — это лог, а не очередь.
- Думает ли про operational cost (Kafka — это команда, поддержка, ZK/KRaft, мониторинг).
- Знает ли, что «гарантированный порядок» в Kafka — только в рамках партиции.
2.6 Надёжность и операционная зрелость
Что проверяем: Системное мышление об операциях: SLO/SLI, observability, deploy-стратегии, incident response, on-call. Senior проектирует систему вместе с её эксплуатацией, а не отдельно от неё.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Передадим в эксплуатацию» |
| Проходной минимум | Знает про логи и метрики |
| Хороший senior | Формулирует SLO, проектирует observability, разбирал реальные инциденты |
| Сильный senior | Применяет error budgets, проводит post-mortems, проектирует под graceful degradation и blast radius |
Вопросы:
Сформулируйте 3 ключевых SLI для вашей системы. Какие SLO? Что делать при превышении error budget?
Observability: какие метрики, логи, трейсы вы соберёте на старте? Какие добавите после первого инцидента?
Deploy-стратегии: rolling, blue-green, canary. Когда какая? Как откатиться?
Кейс: 3 часа ночи, p99 latency вырос с 100мс до 5с. Ваши действия? (триаж, runbook, постановка бекапа, не «давайте сразу деплоить фикс»).
Что такое blameless post-mortem? Зачем он?
На что обращать внимание:
- Не путает ли SLA, SLO, SLI.
- Думает ли о «runbook должен быть готов до прода», а не «допишем потом».
- Понимает ли, что доступность — это бюджет, который тратят на изменения.
2.7 Безопасность и compliance
Что проверяем: В дизайне сразу заложены аутентификация, авторизация, аудит, изоляция данных. Senior понимает compliance-ограничения (152-ФЗ, GDPR, PCI DSS, если применимо), даже если их реализуют не он.
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «За это отвечает другая команда» |
| Проходной минимум | Понимает аутентификацию и авторизацию |
| Хороший senior | Проектирует с учётом RBAC/ABAC, аудита, изоляции tenant’ов |
| Сильный senior | Применяет threat modeling, понимает 152-ФЗ/GDPR-ограничения, защищает по принципу defense in depth |
Вопросы:
Авторизация в мульти-тенантной системе: где проверка, как избежать «забыл проверить tenant_id»?
Аудит: что логируем, как защищаем логи от изменения, сколько храним?
Персональные данные: как хранить, как удалять (right to be forgotten), как анонимизировать для аналитики?
Если один из ваших сервисов скомпрометирован, что получит атакующий? Как ограничить blast radius?
Threat modeling: проведите для одной из подсистем. Что считаете рисками, что митигациями?
На что обращать внимание:
- Думает ли про blast radius и least privilege.
- Не считает ли «у нас в VPN, значит безопасно».
- Понимает ли, что compliance — это не только бумажки, а реальные технические ограничения.
2.8 Эволюция и стоимость
Что проверяем: Система живёт годами: миграции, обратная совместимость, capacity planning, cost-aware design. Senior умеет оценить TCO решения и спланировать эволюцию — не только «сделать работающее на старте».
Критерии оценки:
| Уровень | Ожидания |
|---|---|
| Не прошёл | «Главное чтобы запустилось» |
| Проходной минимум | Думает про обратную совместимость API |
| Хороший senior | Планирует миграции, понимает стоимость инфраструктуры |
| Сильный senior | Проектирует под 10x роста, делал реальные миграции на проде, считает unit economics |
Вопросы:
Как мигрировать схему БД на 1 Тб без downtime? (expand-contract, обратная совместимость кода и схемы, фоновая миграция данных).
Сейчас 1000 RPS, через год прогноз 10000. Что вы спроектируете иначе?
Стоимость: сколько стоит ваша система в облаке в месяц? Где основные статьи (compute, storage, egress)? Где можно оптимизировать?
Когда переписывание оправдано, а когда — это «второй системный синдром»? Стратегия рефакторинга legacy без остановки фич.
Sunset плана: как закрыть сервис, который больше не нужен?
На что обращать внимание:
- Думает ли про деньги, а не только про техническую красоту.
- Различает ли «масштаб сейчас» и «масштаб через 2 года».
- Не предлагает ли «переписать всё на Rust».
Что проверяем поверх двух этапов: лидерство
Эту тему я не выношу в отдельный этап: её сигналы проступают сквозь оба интервью и через несколько дополнительных вопросов в конце:
- Менторинг: как вы развиваете Middle в Senior? Расскажите случай, когда ваш менти заметно вырос.
- Ревью кода: что вы делаете на ревью, чего намеренно не делаете? Как ведёте сложный спор в комментариях PR?
- Обратная связь: расскажите случай, когда вы давали тяжёлую обратную связь коллеге. Как готовились, как прошло?
- Принятие решений: расскажите про ADR, который вы писали. Какие альтернативы рассматривали? Что в итоге решили и почему?
- Влияние без полномочий: как вы протаскивали изменение, которое не нравилось команде? Что сработало, что нет?
Senior, который силён технически, но не работает с людьми, на команду из 5+ человек не подойдёт. Он станет узким горлом: вместо того чтобы развивать команду, будет всё делать сам.
Итог
Senior Go-разработчик — это глубина в языке плюс широта в системном мышлении. Поэтому я делю собеседование на два этапа: технический (рантайм, конкурентность, производительность, экосистема) и архитектурный (требования, декомпозиция, хранилища, распределённые свойства, эксплуатация, стоимость). Между этапами сверяюсь с командой. Если технический прошёл с натяжкой, на архитектурный звать не стоит — без глубины в Go позицию не закрыть.
Главное на обоих этапах — слушать, как кандидат думает, а не только что отвечает. Задаёт ли уточняющие вопросы. Формулирует ли компромиссы. Ссылается ли на собственный опыт. Готов ли сказать «не знаю». Senior с честным «не знаю, но вот как бы я подходил» сильнее Senior с уверенным неправильным ответом.
Теги: