Go Middle 2 Roadmap — путь до Senior (2025-2026)
Цель: уверенный Middle Go-разработчик уровня Middle 2 (M2 / Middle+), готовый идти на собеседования в Yandex (G16/G17), Avito, Ozon, VK, Tinkoff, Sber на позиции “Middle 2 / Senior Go Developer”. Документ — шпаргалка + roadmap.
Источники: данные собраны из веб-поисков (Habr, Medium, dev.to, оф. блог Go, levels.fyi, Glassdoor, Pyroscope/Grafana docs, Kubernetes docs, Хабр).
Грейды и зарплаты в РФ/СНГ (2025-2026)
Заголовок раздела «Грейды и зарплаты в РФ/СНГ (2025-2026)»Yandex (по levels.fyi):
- G14: ~1.84M ₽/год — Junior
- G15: ~2.5–3M ₽/год — Junior+/Middle 1
- G16: ~3.5–4.5M ₽/год — Middle / Middle 1
- G17 (Middle 2): медиана ~5.87M ₽/год; диапазон ~3.15M–7.46M ₽
- G18: ~6–9M ₽/год — Senior
- G19+: ~9–12M+ ₽/год — Senior+/Staff
Tinkoff (Software Engineer):
- SDE I (Junior): ~2.2M ₽/год
- SDE III (Middle 2): ~3.4M ₽/год (медиана)
- SDE V (Senior): ~4.2M+ ₽/год
VK Senior SWE: медиана ~5.47M ₽/год (диапазон 4–6.77M+)
Avito / Ozon: Middle 2 Go — 300–450 тыс. ₽/мес (3.6–5.4M ₽/год TC); Senior — от 450 тыс. ₽/мес и выше. На M2 уже стабильно дают офферы.
Источник: Levels.fyi Yandex G17, Tinkoff Bank salaries, ENIGMA AI зарплата Go
Что отличает Middle 2 от Middle 1:
- Не просто “пишу код по тикетам” — умею самостоятельно дизайнить сервис среднего масштаба
- Знаю Go на уровне runtime/internals, не только синтаксис
- Понимаю distributed systems (CAP, идемпотентность, Saga, Outbox)
- Могу профилировать продакшен и оптимизировать (pprof, flame graphs, Pyroscope)
- Веду техлид-функции в рамках команды: code review, наставничество Junior-ов
- На собесе ожидают system design (URL shortener, чат, лента) уровня LLD + базовый HLD
1. Глубокое понимание Go internals
Заголовок раздела «1. Глубокое понимание Go internals»1.1 Scheduler (GMP-модель)
Заголовок раздела «1.1 Scheduler (GMP-модель)»G (Goroutine) — пользовательская корутина (~2KB стек).
M (Machine) — OS-поток.
P (Processor) — логический процессор, держит локальную run-очередь.
GOMAXPROCS = N→ N штук P.- Каждый P держит локальную LRQ (Local Run Queue, ~256 goroutines).
- Есть GRQ (Global Run Queue) — куда идут “пересыпы”.
Work stealing:
- Goroutine кончается на P → сначала идём за работой в GRQ (раз в ~61 такт также).
- Если GRQ пуст — steal half из другого P (с хвоста очереди другого P).
- Если совсем нечего делать — M паркуется (futex), пока не разбудят.
Network poller (netpoll):
- Когда goroutine делает blocking I/O (read/write socket), она уходит в netpoller.
- Реализация:
epoll(Linux) /kqueue(macOS/BSD) /iocp(Windows). - M освобождается и берёт следующую goroutine из LRQ.
- Когда I/O готов → netpoller возвращает goroutine в run-очередь.
- Это позволяет тысячам соединений жить на десятке M.
Syscalls:
- Когда G делает блокирующий syscall (не netpoll), M “отлепляется” от P.
- P подхватывается другой M (берётся из пула holdoff-M или создаётся новая).
- Когда syscall завершается, исходный M пытается заново захватить P, иначе кладёт G в GRQ и засыпает.
Asynchronous preemption (Go 1.14+):
- До Go 1.14 — кооперативная преемпция (точки вызова функций).
- С Go 1.14 — сигнально-асинхронная: runtime отправляет
SIGURGпотоку, обработчик прерывает goroutine на безопасной точке. - Решает проблему “tight loop без вызовов функций” — раньше такая горутина блокировала весь P.
Parking:
- При
chan recvна пустом канале → goroutine паркуется (g.status = waiting), удаляется из LRQ. - При sending → wake up через
runtime.goready().
Источники: bytesizego scheduler 2025, container-aware GOMAXPROCS
1.2 Memory allocator: mcache, mcentral, mheap
Заголовок раздела «1.2 Memory allocator: mcache, mcentral, mheap»Иерархия (от частного к общему):
goroutine → P.mcache → mcentral[size_class] → mheap → OS (mmap)- mcache — per-P (нет лока). Держит спаны размерных классов.
- mcentral — глобальный пул для одного size class. 136 mcentral (по числу spanClass).
- mheap — глобальная куча, выделяет арены у OS.
Размерные классы (~67-70 классов small + huge):
- Малые объекты (≤16B) — tiny allocator (комбинирует в один блок).
- Малые (16B–32KB) — round up до ближайшего size class (16, 32, 48, …, 32K).
- Большие (>32KB) — выделяются напрямую из mheap (large object).
Аллокация:
- Размер → size class
- Идём в
mcache.alloc[spanClass]— находим свободный slot в bitmap span’а - Если span полон →
mcentral.cacheSpan()— берём новый span (с локом mcentral) - Если mcentral пуст →
mheap.alloc()— у OS просим арену (64MB)
span (mspan): диапазон страниц (8KB каждая), разбитый на объекты одного size class.
Источник: Memory Allocation in Go (2025)
Go 1.24 — Swiss Tables maps: новая хеш-таблица в runtime, ~2-3% быстрее в среднем по бенчмаркам.
1.3 GC и pacer
Заголовок раздела «1.3 GC и pacer»Go использует concurrent, tri-color, mark-sweep GC (без compaction).
Цикл GC:
- STW start (~10µs) — barrier turn on, корни (стеки) подготовлены
- Concurrent mark — goroutines продолжают работать, GC помечает живые объекты
- STW mark termination (~10µs) — финал маркинга
- Concurrent sweep — освобождает память, лениво
Write barrier: при записи указателя в маркинг-фазе runtime интерсептит запись, чтобы не потерять живой объект.
Pacer (как выбирает момент старта):
- Цель — закончить GC до того, как куча вырастет до target heap size.
target = live_heap × (1 + GOGC/100)— приGOGC=100куча может удвоиться.- Pacer считает CPU assist: если приложение аллоцирует быстрее, чем GC чистит, аллоцирующие goroutines помогают GC (тратят свои CPU-такты на маркинг).
- С Go 1.18 — новый pacer: учитывает не только heap size, но и goal CPU usage (~25% макс на GC).
GOMEMLIMIT (с Go 1.19):
- Soft limit на общее потребление памяти Go runtime.
- Когда близко к лимиту — pacer триггерит GC чаще, даже если GOGC говорит “ещё не время”.
- Полезно в контейнерах с memory limit:
GOMEMLIMIT=900MiBпри контейнере 1Gi. GOGC=off+GOMEMLIMIT=900MiB— GC запускается только когда близко к лимиту (batch jobs).
Источники: Go GC Guide, goperf.dev GC
1.4 Stack growing / shrinking
Заголовок раздела «1.4 Stack growing / shrinking»- Каждая goroutine стартует с 2KB стеком (с Go 1.4).
- Стек сегментированный (раньше был “split stacks”; теперь contiguous stacks).
- Если функция требует больше места → runtime выделяет новый, в 2x больший стек, копирует фреймы туда (move).
- При копировании — переписываются указатели в стек.
- Поэтому указатели на стек могут менять адрес — нельзя кэшить их извне.
- GC может shrinking стек: если меньше 1/4 используется на следующем GC, стек уменьшается.
- Максимум стека: 1GB (по умолчанию, можно
runtime/debug.SetMaxStack).
1.5 Defer внутри: open-coded defers
Заголовок раздела «1.5 Defer внутри: open-coded defers»С Go 1.14 появились open-coded defers (proposal Russ Cox).
Три типа defer:
- Heap-allocated (старый) — медленный, ~50ns, аллокация
_deferструктуры. - Stack-allocated (с Go 1.13) — ~35ns, без аллокации.
- Open-coded (с Go 1.14) — ~6ns, инлайн-код, сравнимо с прямым вызовом (~4.4ns).
Когда работает open-coded:
- ≤ 8 defer’ов в функции
- defer не в цикле
- Все аргументы известны
- Нет
recover()без defer
При panic’е — компилятор хранит метаданные (funcdata), чтобы корректно развернуть стек.
Источник: Go defer benchmark
1.6 Panic / recover internals
Заголовок раздела «1.6 Panic / recover internals»panic — структура _panic в стеке goroutine, образует связный список (panic во время panic’а).
Что делает panic:
- Останавливает выполнение функции.
- Раскручивает стек вверх, выполняя все defer’ы.
- Если в defer вызван
recover()— panic “снимается”, выполнение продолжается из defer’а. - Если recover не было — goroutine падает, runtime печатает stack trace и kills весь процесс.
Важно: panic в одной goroutine, не обработанный, убивает всю программу, не только эту goroutine.
recover возвращает значение из panic только если вызван прямо внутри defer’ed функции.
1.7 Compiler optimizations
Заголовок раздела «1.7 Compiler optimizations»- Inlining — функции до ~80 nodes (с Go 1.17), управляется
-gcflags=-m=2. - Devirtualization — если компилятор видит конкретный тип интерфейса, заменяет виртуальный вызов прямым.
- Bounds check elimination (BCE) — компилятор доказывает, что индекс в массиве в пределах, удаляет проверку.
- Escape analysis — определяет, “сбегает” ли переменная в heap. Не сбежавшие — на стеке.
- PGO (Profile-Guided Optimization) — с Go 1.21: подкладываете
default.pgoв корень проекта, компилятор оптимизирует hot paths. Прирост ~5-15% (улучшение inlining + devirt).
Просмотр escape analysis: go build -gcflags="-m -m" → видно “escapes to heap”.
1.8 Build tags и условная компиляция
Заголовок раздела «1.8 Build tags и условная компиляция»//go:build linux && amd64// +build linux,amd64
package mypkg- Файлы
_linux.go,_darwin.go,_amd64.go— автотеги. //go:build(новый стиль с Go 1.17) поддерживает выражения с&&,||,!.- Полезно для:
- Платформо-зависимого кода
- Разделения unit/integration тестов:
//go:build integration+go test -tags=integration - Feature flags на уровне сборки
1.9 CGO — подводные камни
Заголовок раздела «1.9 CGO — подводные камни»Когда нельзя избежать:
- Системные библиотеки без Go bindings (SQLite —
mattn/go-sqlite3, ImageMagick) - Аппаратное ускорение (CUDA, FFmpeg, OpenSSL)
Подводные камни:
- Стоимость вызова CGO ~ 200ns — это в 1000 раз дороже обычного Go вызова. Не вызывайте в hot loop.
- Goroutine с CGO-вызовом блокирует M (OS thread) на время вызова — нужно больше M.
- GC не видит указатели в C-памяти, и наоборот.
- Cross-compilation ломается — нужны cross-toolchains.
- CGO + race detector — медленный.
- Запрещено передавать Go pointers, содержащие другие Go pointers, в C (runtime check
cgocheck).
С Go 1.24 появились #cgo noescape funcName и #cgo nocallback funcName — снижают overhead.
1.10 Assembly: go:noescape, go:linkname
Заголовок раздела «1.10 Assembly: go:noescape, go:linkname»//go:noescape— обещание компилятору: эта функция не “сбегает” указателями в heap. Применяется к assembly-функциям и stubs.//go:linkname localName remotePackage.remoteName— линкер делает алиас на символ. Используется stdlib (например,time.nowалиаситruntime.nanotime).//go:nosplit— функция не должна вызывать stack-growth check (для critical-path).//go:noinline— запрет инлайнить (нужно для бенчмарков).
С Go 1.23+ запрещены новые использования //go:linkname для внутренних символов stdlib (только legacy).
2. Concurrency — продвинутый уровень
Заголовок раздела «2. Concurrency — продвинутый уровень»2.1 Lock-free структуры
Заголовок раздела «2.1 Lock-free структуры»Go умеет atomics (sync/atomic): LoadInt64, StoreInt64, CompareAndSwapInt64, AddInt64.
С Go 1.19 — типы atomic.Int64, atomic.Pointer[T], atomic.Bool (удобнее).
Lock-free паттерны:
- Atomic counter —
atomic.AddInt64(&counter, 1) - Atomic config swap —
atomic.Pointer[Config]:cfg.Store(newCfg),cfg.Load() - Lock-free queue — Michael-Scott queue (есть реализации в
go.uber.org/atomic) - RCU (Read-Copy-Update) — копируем структуру, меняем, atomically swap указатель
Главное правило: lock-free дороже Mutex’а только при низкой контеншн. При высокой контеншн atomics проигрывают (cache line ping-pong).
2.2 sync.Map vs RWMutex + map
Заголовок раздела «2.2 sync.Map vs RWMutex + map»sync.Map оптимизирован под 2 кейса:
- Write-once, read-many (например, кэш конфигов).
- Disjoint key sets — разные goroutines пишут в разные ключи.
Внутри: двойной слой — read-only readMap (atomic-pointer) + dirtyMap (mutex). Чтения идут без лока, пока ключ есть в readMap.
RWMutex + map:
- Лучше для write-heavy или mixed нагрузки.
- Лучше для range — sync.Map iterate медленный.
- Меньше памяти.
Бенчмарки (грубо):
- 90% read / 10% write на 1M ключей: sync.Map ~2-3x быстрее.
- 50/50: RWMutex+map ~1.5x быстрее.
- 100% write: RWMutex+map существенно быстрее.
Источник: dev.to sync.Map
2.3 sync.Pool — глубже
Заголовок раздела «2.3 sync.Pool — глубже»var bufPool = sync.Pool{ New: func() any { return new(bytes.Buffer) },}
buf := bufPool.Get().(*bytes.Buffer)defer func() { buf.Reset() bufPool.Put(buf)}()Что класть:
- Большие, дорого-инициализируемые объекты (bytes.Buffer, JSON-decoder, X.509 cert)
- Объекты, которые переиспользуются часто в hot path
Что НЕ класть:
- Маленькие объекты (<100B) — overhead pool’а больше выгоды
- Объекты с указателями на внешние данные (могут утечь)
- Объекты с финализаторами
Race с GC:
- Каждый GC цикл опустошает sync.Pool (до Go 1.13 — полностью; с 1.13 — двухуровневый, более щадящий).
- Поэтому Pool не подходит для долгоживущего кэша — используйте
freelist-структуру.
Бонус — per-P sharded: sync.Pool внутри использует per-P pools (no contention).
Источник: Leapcell sync.Pool
2.4 errgroup, semaphore, singleflight
Заголовок раздела «2.4 errgroup, semaphore, singleflight»golang.org/x/sync/errgroup:
g, ctx := errgroup.WithContext(ctx)for _, url := range urls { url := url g.Go(func() error { return fetch(ctx, url) })}if err := g.Wait(); err != nil { return err }- Первая ошибка →
ctx.Cancel()для всех остальных. g.SetLimit(N)(с Go 1.21) — ограничение concurrency.
semaphore.Weighted:
sem := semaphore.NewWeighted(10) // 10 одновременноif err := sem.Acquire(ctx, 1); err != nil { ... }defer sem.Release(1)- Weighted = можно взять K единиц за раз (для heavy запросов).
singleflight:
var g singleflight.Groupval, err, _ := g.Do("user:123", func() (any, error) { return loadUserFromDB(123)})- 100 параллельных запросов с тем же ключом → DB query будет один, остальные ждут результата.
- Стандартное решение thundering herd на кэш-мисс.
2.5 Backpressure паттерны
Заголовок раздела «2.5 Backpressure паттерны»Bounded channel — простой backpressure:
jobs := make(chan Job, 100) // буфер 100; если полон — producer ждётDrop-old / drop-new:
select {case jobs <- j:default: // drop, log "queue full"}Token bucket:
import "golang.org/x/time/rate"lim := rate.NewLimiter(rate.Limit(100), 200) // 100 RPS, burst 200if err := lim.Wait(ctx); err != nil { return err }2.6 Rate limiting
Заголовок раздела «2.6 Rate limiting»Token bucket (golang.org/x/time/rate) — Limit(rps), burst.
Leaky bucket — фиксированный rate; нет burst.
Sliding window — точнее, но дороже (хранит timestamps).
Для distributed — Redis с Lua-скриптом или сервис envoy/ratelimit, или Uber’s ratelimit library.
2.7 Структурированная concurrency
Заголовок раздела «2.7 Структурированная concurrency»Принципы (вдохновлены Trio / Kotlin coroutines):
- Никогда не запускайте goroutine “в никуда” без owner’а.
- Все дочерние goroutines должны завершаться до возврата родительской функции.
- Ошибки распространяются вверх.
- Cancellation распространяется вниз.
В Go это errgroup.WithContext + defer g.Wait(). Никаких “fire and forget” в hot path.
2.8 Тестирование concurrent кода
Заголовок раздела «2.8 Тестирование concurrent кода»go test -race— всегда в CI.testing/synctest(экспериментальный, Go 1.24): виртуальное время, детерминированные тесты concurrency.go.uber.org/goleak—defer goleak.VerifyNone(t)в каждом тесте, ловит утечки goroutines.- Stress-тестирование —
go test -count=1000 -race.
2.9 Memory model и happens-before
Заголовок раздела «2.9 Memory model и happens-before»Go Memory Model (обновлён в Go 1.19): атомики sequentially consistent.
Гарантии:
- Channel: N-я успешная отправка happens-before N-го успешного приёма. Закрытие канала happens-before приёма zero-value.
- Mutex: N-й
Unlockhappens-before (N+1)-гоLock. - Once:
Do(f)— единственный вызовfhappens-before любого возврата изDo. - Atomic: все атомарные операции sequentially consistent (с Go 1.19).
- WaitGroup:
Donehappens-before возврата изWait.
Без синхронизации — нет гарантий! Чтение переменной без синхронизации в Go = data race = неопределённое поведение (компилятор может переставить чтения).
Источник: Go Memory Model
3. Производительность и оптимизация
Заголовок раздела «3. Производительность и оптимизация»3.1 Профилирование в production
Заголовок раздела «3.1 Профилирование в production»Стандартный pprof:
import _ "net/http/pprof"go func() { log.Println(http.ListenAndServe("localhost:6060", nil))}()Endpoints: /debug/pprof/heap, /profile, /goroutine, /block, /mutex, /allocs, /trace.
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30(pprof) top10(pprof) list FunctionName(pprof) web # flame graph в браузереContinuous profiling — Pyroscope / Grafana Cloud Profiles:
import "github.com/grafana/pyroscope-go"
pyroscope.Start(pyroscope.Config{ ApplicationName: "my-service", ServerAddress: "http://pyroscope:4040", Tags: map[string]string{"region": "eu"}, ProfileTypes: []pyroscope.ProfileType{ pyroscope.ProfileCPU, pyroscope.ProfileAllocObjects, pyroscope.ProfileAllocSpace, pyroscope.ProfileInuseObjects, pyroscope.ProfileInuseSpace, },})Overhead: ~2-5% CPU.
Plus: можно сравнивать профили до/после деплоя — gold standard для перформанс-работы.
Источник: Pyroscope Go docs
3.2 Flame graphs
Заголовок раздела «3.2 Flame graphs»- X-ось — относительное время в функции (НЕ хронология).
- Y-ось — стек вызовов (root внизу).
- Ширина — сколько CPU/allocations.
- Ищите широкие плато на вершине — это место для оптимизации.
pprof -http=:8080 profile.pb.gz→ встроенный flame в браузере.
3.3 eBPF для Go
Заголовок раздела «3.3 eBPF для Go»Parca / Pixie:
- eBPF-агент на ноде, samples stack traces ~19-100Hz.
- Не требует инструментации кода — работает с любым Go процессом.
- Overhead <1%.
- Pixie умеет dynamic logging для Go: указываете функцию, eBPF-пробник собирает аргументы и возврат.
Полезно когда:
- pprof endpoint выключен / нельзя добавить
- Нужно профилировать всю ноду (включая сторонние процессы)
Источник: Pixie eBPF docs
3.4 Zero-allocation patterns
Заголовок раздела «3.4 Zero-allocation patterns»// ПЛОХО: каждая итерация — аллокацияvar result stringfor _, s := range parts { result += s}
// ХОРОШОvar b strings.Builderb.Grow(estimatedSize) // pre-allocatefor _, s := range parts { b.WriteString(s)}return b.String()
// []byte vs stringfunc process(b []byte) { s := string(b) // АЛЛОКАЦИЯ (копия) s := unsafe.String(...) // zero-copy (с Go 1.20)}
// Pre-allocate slicesxs := make([]int, 0, 1000) // знаем capacity заранееЭскейп анализ:
go build -gcflags="-m" .# main.go:10:13: &x escapes to heapПравила:
- Возврат указателя из функции → escape to heap.
- Передача в
interface{}→ escape (interface boxing). - Передача в goroutine → может escape.
- Большой объект (>10MB) → всегда heap (Go runtime).
3.5 Bottleneck анализ
Заголовок раздела «3.5 Bottleneck анализ»| Симптом | Тип | Где смотреть |
|---|---|---|
| CPU 100%, latency растёт | CPU-bound | pprof profile, flame graph |
| CPU низкое, latency растёт | I/O-bound | trace (go tool trace), network metrics |
| GC pauses частые, p99 латенси скачет | GC pressure | pprof heap, GODEBUG=gctrace=1 |
| Goroutines растут | Goroutine leak | pprof goroutine, look for “chan receive” |
| Mutex contention | Lock contention | pprof mutex, pprof block |
GODEBUG=gctrace=1 ./app — в stderr каждый GC цикл.
3.6 benchstat
Заголовок раздела «3.6 benchstat»go test -bench=. -benchmem -count=10 > old.txt# меняем кодgo test -bench=. -benchmem -count=10 > new.txtbenchstat old.txt new.txtПоказывает статистически значимые изменения с p-value.
3.7 Tuning GOGC, GOMEMLIMIT, GOMAXPROCS
Заголовок раздела «3.7 Tuning GOGC, GOMEMLIMIT, GOMAXPROCS»В Kubernetes:
До Go 1.25:
import _ "go.uber.org/automaxprocs" // в mainЭто поправит GOMAXPROCS под cgroup CPU limit.
С Go 1.25 — GOMAXPROCS container-aware by default (проверяет cgroup limit каждые 10 секунд и пересчитывает).
Рекомендуемые env vars:
env: - name: GOMAXPROCS # с Go 1.25 можно опустить valueFrom: ... # обычно автоматом - name: GOMEMLIMIT value: "900MiB" # 90% от memory limit пода - name: GOGC value: "100" # default, можно 50 для low-latency, 200 для batchAnti-pattern: GOMAXPROCS=8 в поде с CPU limit 0.5 → cgroup throttling, p99 latency взрывается. Improvement: ~25x p99.
Источник: Container-aware GOMAXPROCS, VictoriaMetrics k8s CPU
4. Сетевые приложения и протоколы
Заголовок раздела «4. Сетевые приложения и протоколы»4.1 HTTP/1.1, HTTP/2, HTTP/3
Заголовок раздела «4.1 HTTP/1.1, HTTP/2, HTTP/3»| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Транспорт | TCP | TCP | QUIC (UDP) |
| Мультиплексирование | Pipelining (плохо) | Streams (отлично) | Streams (отлично, без HOL blocking) |
| Header compression | — | HPACK | QPACK |
| TLS | Optional | Effectively required | Required (TLS 1.3) |
| Server push | — | Да (deprecated) | Нет |
В Go:
net/http— HTTP/1.1 + HTTP/2 (transparent с HTTPS).- HTTP/3:
github.com/quic-go/quic-goилиgithub.com/quic-go/quic-go/http3.
Head-of-line blocking: HTTP/2 → один TCP stream, packet loss блокирует всех. HTTP/3 → независимые QUIC streams.
4.2 WebSockets
Заголовок раздела «4.2 WebSockets»github.com/gorilla/websocket— самый популярный, стабильный.github.com/coder/websocket(бывшийnhooyr/websocket) — современнее, используетcontext, проще API.
Паттерны:
- Одна goroutine для чтения, одна для записи (обязательно).
- Heartbeat (ping/pong) каждые 30 сек.
- Backpressure через bounded channel перед
WriteJSON.
4.3 gRPC — глубоко
Заголовок раздела «4.3 gRPC — глубоко»Типы вызовов:
- Unary — request/response
- Server streaming — client отправляет 1, server возвращает stream
- Client streaming — client стримит, server отвечает 1
- Bidirectional streaming — оба стримят
Interceptors:
func loggingInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { start := time.Now() resp, err := handler(ctx, req) log.Printf("%s took %v err=%v", info.FullMethod, time.Since(start), err) return resp, err}Deadlines:
ctx, cancel := context.WithTimeout(parent, 2*time.Second)defer cancel()resp, err := client.GetUser(ctx, req)Deadline пропагируется в server и дальше во все downstream вызовы.
Retries:
import "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
conn, _ := grpc.Dial(addr, grpc.WithUnaryInterceptor( retry.UnaryClientInterceptor( retry.WithMax(3), retry.WithBackoff(retry.BackoffExponential(100*time.Millisecond)), retry.WithCodes(codes.Unavailable, codes.DeadlineExceeded), ),))ВАЖНО: Retry ТОЛЬКО для идемпотентных RPC. Иначе двойные платежи и т.п.
4.4 Protocol Buffers продвинуто
Заголовок раздела «4.4 Protocol Buffers продвинуто»- Well-known types:
Timestamp,Duration,Empty,Struct,Any,FieldMask. - Custom options:
extend google.protobuf.FieldOptions {bool sensitive = 50001;}message User {string email = 1 [(sensitive) = true];}
- Versioning:
- Не меняйте номера полей.
- Не переиспользуйте удалённые номера —
reserved 5, 7;. - Новые поля — optional (proto3 — все optional).
- Удаление поля — НЕ удаляйте номер, добавьте reserved.
4.5 Connect-Go (новый стандарт)
Заголовок раздела «4.5 Connect-Go (новый стандарт)»connectrpc.com — Buf Build.
- Работает над HTTP/1.1, HTTP/2 (HTTP/3 на подходе).
- Совместим с gRPC (можно поднять Connect сервер, обслуживать и Connect, и gRPC clients).
- Проще debug: можно
curlв endpoint, если JSON. - Идиоматичный Go API (без unnatural patterns gRPC).
4.6 TLS, mTLS
Заголовок раздела «4.6 TLS, mTLS»cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")clientCAs := x509.NewCertPool()clientCAs.AppendCertsFromPEM(caBundle)
tlsCfg := &tls.Config{ Certificates: []tls.Certificate{cert}, ClientCAs: clientCAs, ClientAuth: tls.RequireAndVerifyClientCert, // mTLS! MinVersion: tls.VersionTLS13,}mTLS = клиент тоже предъявляет сертификат, сервер проверяет. Стандарт для service-to-service inside cluster.
5. Базы данных — продвинуто
Заголовок раздела «5. Базы данных — продвинуто»5.1 PostgreSQL
Заголовок раздела «5.1 PostgreSQL»EXPLAIN ANALYZE:
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)SELECT * FROM orders WHERE user_id = 42 AND created_at > '2025-01-01';Что читать:
- Seq Scan — плохо на больших таблицах
- Index Scan / Bitmap Index Scan — хорошо
- Hash Join / Merge Join / Nested Loop — выбор зависит от размера
actual time=X..Y rows=N loops=M— actual time × loops = реальное общее время- Rows estimate vs actual — расхождение в 10x → нужен
ANALYZE table
Индексы:
- B-tree (default) — equality + range
- Hash — только equality (редко нужен)
- GIN — массивы, JSONB, full-text search
- GiST — геометрия, диапазоны, full-text
- BRIN — большие отсортированные таблицы (логи)
- Partial index —
CREATE INDEX ... WHERE deleted_at IS NULL; - Covering index (INCLUDE) — индекс хранит дополнительные колонки, чтобы index-only scan
MVCC:
- Каждая строка хранит
xmin, xmax(creating/deleting transaction IDs). - Транзакция видит только строки с
xmin ≤ snapshot_xidи (xmax > snapshot_xidили NULL). - Update не меняет строку — создаёт новую версию, помечает старую как dead tuple.
VACUUMчистит dead tuples (но не возвращает место OS — для этогоVACUUM FULL, который lockает таблицу).- Autovacuum — фоновой процесс, запускается при
n_dead_tup > autovacuum_vacuum_threshold + scale_factor * n_live_tup.
Bloat — таблица + индексы растут из-за dead tuples. Мониторить через pg_stat_user_tables.
Источник: PostgreSQL MVCC
Connection pooling:
pgx/pgxpool(Go) — пул на уровне приложения.- PgBouncer — внешний пулер, режим transaction pooling (1 backend на транзакцию).
- Pgcat — современный (rust), supports read-replicas routing.
5.2 Distributed transactions / Saga
Заголовок раздела «5.2 Distributed transactions / Saga»Two-Phase Commit (2PC) — не используйте в микросервисах. Блокирующий, single point of coordinator failure.
Saga pattern:
- Orchestration — координатор знает все шаги, дёргает каждый сервис, при ошибке вызывает компенсации.
- Choreography — каждый сервис публикует событие, другие реагируют.
Choreography пример (бронирование):
OrderCreated → PaymentService (charges) → PaymentSucceeded → InventoryService (reserve) → InventoryReserved → ShippingService (book) → ShippingFailed!!! → InventoryService (compensate: release) → PaymentService (compensate: refund)Правила:
- Каждый шаг и каждая компенсация идемпотентны (вызовут несколько раз — норм).
- Не теряем события (см. Outbox).
- Логируем sagaID в каждом событии.
Источник: Saga in Go
5.3 Outbox pattern
Заголовок раздела «5.3 Outbox pattern»Проблема: атомарно записать в БД + опубликовать в Kafka — нельзя (две системы).
Решение:
-- В той же транзакции:INSERT INTO orders ...;INSERT INTO outbox (event_type, payload, created_at) VALUES (...);COMMIT;Фоновой outbox-relay читает outbox, публикует в Kafka, удаляет/помечает sent.
Современный подход — CDC (Change Data Capture):
Debeziumчитает PostgreSQL WAL → публикует изменения в Kafka.- Не нужно отдельной outbox-таблицы, но требует setup.
5.4 Event Sourcing основы
Заголовок раздела «5.4 Event Sourcing основы»- Состояние = последовательность событий.
- Event Store = append-only лог.
- Текущее состояние = свёртка всех событий (left fold).
- Snapshots — периодически сохраняем “промежуточное” состояние для скорости.
Pros: аудит, time travel, отлаживаемость.
Cons: schema evolution событий — сложно, нужны upcasters.
5.5 CQRS базово
Заголовок раздела «5.5 CQRS базово»Command Query Responsibility Segregation:
- Write model (Commands) — normalized, optimized for consistency.
- Read model (Queries) — denormalized, optimized for queries (materialized views).
- Write → publish event → projection обновляет read model (eventually consistent).
Подходит, когда чтение сильно отличается от записи (например, отчёты vs OLTP).
5.6 Read replicas, шардирование, partitioning
Заголовок раздела «5.6 Read replicas, шардирование, partitioning»- Read replicas — readonly slaves для масштабирования чтения. Async repl → eventual consistency, read-your-writes проблема.
- Sharding (горизонтальное) — данные разделены по ключу (user_id mod N). Сложно с cross-shard JOIN.
- Partitioning (PostgreSQL declarative) — одна логическая таблица, физически разбита (по дате/range/hash). Полезно для большой
eventsтаблицы.
5.7 NoSQL: когда что
Заголовок раздела «5.7 NoSQL: когда что»| Use case | Не подходит для | |
|---|---|---|
| MongoDB | Документы с гибкой схемой, прототипы, real-time analytics на доках | Сложные JOIN, аналитика на BIG data |
| Cassandra | Высокий write throughput, time-series, гео-распределение | Сложные queries, JOIN, ad-hoc analytics |
| ClickHouse | OLAP, аналитика, agg-queries по миллиардам строк | OLTP, частые UPDATE/DELETE |
| DynamoDB | KV/document с предсказуемой latency на AWS | Сложные queries без знания шардинга |
| Redis | Кэш, sessions, pub/sub, locks, queues | Persistent storage главного бизнеса |
| Elasticsearch | Full-text search, логи, поиск с фасетами | OLTP, ACID транзакции |
5.8 Elasticsearch / OpenSearch базово
Заголовок раздела «5.8 Elasticsearch / OpenSearch базово»- Inverted index — для каждого токена хранится список doc ID.
- Analyzer = tokenizer + filters (lowercase, stop words, stemming).
- Mapping — схема (определять заранее, не auto!).
- Шарды — primary + replicas; реплики читают, primary пишет.
- Bulk API — массовая индексация.
- Refresh interval — стандарт 1 сек (eventually searchable), для high-throughput ставят 30 сек.
Go клиенты: github.com/elastic/go-elasticsearch/v8 или github.com/opensearch-project/opensearch-go.
6. Распределённые системы
Заголовок раздела «6. Распределённые системы»6.1 CAP theorem практически
Заголовок раздела «6.1 CAP theorem практически»В сетевом разделении (P) выбираем:
- CP — consistency over availability (PostgreSQL master-replica, etcd, ZooKeeper)
- AP — availability over consistency (Cassandra, DynamoDB eventual)
В реальности нет ни одной системы 100% CP или AP — это спектр. PACELC уточняет: при partition выбираем PA или PC; otherwise (else) — латентность L или консистентность C.
6.2 Идемпотентность
Заголовок раздела «6.2 Идемпотентность»API должно быть безопасно вызвать N раз с одним и тем же результатом.
Способы:
- Idempotency-Key header (Stripe-style): клиент генерирует UUID, server хранит результаты ключей.
- Natural idempotency:
PUT /resource/{id}идемпотентен;POST /resources— нет. - Conditional updates в БД:
UPDATE ... WHERE version = ? AND status = 'pending'.
6.3 Eventual consistency
Заголовок раздела «6.3 Eventual consistency»- Read replicas, кэши, шардинг → eventually consistent.
- Read-your-writes — если пользователь только что написал, должен видеть свою запись. Решение: после write читать с master в течение N секунд.
- Monotonic reads — нельзя “откатить” в чтении. Решение: sticky session к одной реплике.
6.4 Распределённые блокировки
Заголовок раздела «6.4 Распределённые блокировки»Redis Redlock:
- Локает на N (5) независимых Redis. Замок считается взятым, если acquired на majority (3/5).
- Лучше использовать готовое:
github.com/go-redsync/redsync. - Caveat (Martin Kleppmann): Redlock небезопасен при clock drift. Для критичных задач — fencing tokens или etcd/ZK.
etcd lock:
session, _ := concurrency.NewSession(etcdClient)mu := concurrency.NewMutex(session, "/locks/myresource")mu.Lock(ctx); defer mu.Unlock(ctx)Linearizable (Raft consensus), но дороже.
Когда что:
- Redlock — кэширование, де-дупликация, “best effort”
- etcd/ZK — финансовые транзакции, единственный лидер
Источник: How to Implement Redlock in Go
6.5 Service discovery
Заголовок раздела «6.5 Service discovery»- Consul — health checks, KV store, multi-DC.
- etcd — strongly consistent KV, основа Kubernetes.
- Kubernetes Service / DNS — наиболее распространённый сейчас (через kube-dns / CoreDNS).
6.6 Circuit breaker, retries, timeouts, bulkhead
Заголовок раздела «6.6 Circuit breaker, retries, timeouts, bulkhead»Circuit Breaker:
- 3 состояния: Closed (норма) → Open (ошибки выше threshold, не пускаем) → Half-Open (пробуем 1 запрос).
- Library:
github.com/sony/gobreakerилиgithub.com/afex/hystrix-go.
Retries with backoff + jitter:
backoff := time.Secondfor i := 0; i < maxRetries; i++ { if err := call(); err == nil { return nil } time.Sleep(backoff + jitter()) backoff *= 2}Library: github.com/avast/retry-go или cenkalti/backoff.
Timeouts — всегда. На HTTP, на DB query, на channel send.
Bulkhead — изоляция ресурсов:
- Отдельные goroutine pools / pools соединений для разных downstream.
- Если один deps медленный, не съест все слоты.
6.7 Distributed tracing
Заголовок раздела «6.7 Distributed tracing»OpenTelemetry (стандарт 2025):
import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" sdktrace "go.opentelemetry.io/otel/sdk/trace")
exporter, _ := otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint("collector:4317"))tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(resource.NewWithAttributes("service.name", "my-svc")),)otel.SetTracerProvider(tp)Backend: Jaeger / Tempo / Grafana Cloud. Все через OTLP (OpenTelemetry Protocol).
Trace propagation — traceparent HTTP header (W3C standard).
Sampling: не семплируйте 100% в проде — дорого. Tail-sampling (Tempo) или head-sampling 1-10%.
Источник: OTel Go docs
6.8 API gateway, BFF
Заголовок раздела «6.8 API gateway, BFF»- API Gateway — единая точка входа, auth/rate-limit/routing. Kong, Traefik, Envoy, Tyk.
- BFF (Backend For Frontend) — отдельный backend для каждого клиента (mobile, web), агрегирует данные из multiple microservices.
7. Architecture & Design
Заголовок раздела «7. Architecture & Design»7.1 Микросервисы vs модульный монолит
Заголовок раздела «7.1 Микросервисы vs модульный монолит»Микросервисы:
- Pros: независимый деплой, разные команды, разные стеки, scaling per-service.
- Cons: distributed system complexity (сеть, eventually consistent, traceability), overhead на тривиальной фиче.
Модульный монолит:
- Один артефакт, чёткие границы внутри (Hexagonal/DDD).
- Деплоится за секунды, transactional consistency by default.
- Start here. Разделять на сервисы — когда реально нужно.
7.2 Event-driven architecture
Заголовок раздела «7.2 Event-driven architecture»- Сервисы общаются через события (через Kafka/NATS), не через RPC.
- Pros: loose coupling, replay, audit, scalability.
- Cons: eventual consistency, debug сложнее (trace через events).
7.3 Outbox, Inbox, Saga, CQRS, ES — детально
Заголовок раздела «7.3 Outbox, Inbox, Saga, CQRS, ES — детально»(Outbox и Saga см. раздел 5.)
Inbox pattern:
- При получении события из Kafka — пишем в
inbox(по unique idempotency key) + бизнес-таблицу в одной транзакции. - Если событие уже было — skip. Защита от дублей.
7.4 DDD (Domain-Driven Design)
Заголовок раздела «7.4 DDD (Domain-Driven Design)»Aggregate — кластер entities + value objects, транзакционная граница + invariant’ы.
Пример: Order (root) содержит OrderItem[], ShippingAddress (VO). Любая модификация items идёт через Order.
Bounded Context — модель валидна только в своём контексте. “Customer” в Sales BC ≠ “Customer” в Billing BC.
Ubiquitous Language — единый словарь для команды + домена. Должен быть в коде, БД, доке.
Value Object — immutable, без identity (Money, Address, Email).
Domain Service — операция, не относящаяся к одному агрегату (например, перевод денег между счетами).
Источник: DDD in Go (2025)
7.5 Hexagonal architecture в Go
Заголовок раздела «7.5 Hexagonal architecture в Go»Слои:
- Domain (entities, VO, domain services) — НЕТ зависимостей.
- Application (use cases, ports) — зависит от Domain.
- Adapters (HTTP handlers, DB repositories, Kafka consumers) — зависит от Application через интерфейсы.
Структура:
internal/ domain/ # сущности, VO, бизнес-правила order/ order.go events.go application/ # use cases (Service'ы) order/ create_order.go # OrderService.Create() ports.go # OrderRepository interface adapter/ http/ # gin/chi/echo handlers postgres/ # OrderRepository implementation kafka/ # event consumers/producers grpc/ cmd/ server/main.go # wire it all togetherЗависимости идут ВНУТРЬ: adapter знает application, application знает domain. Никогда не наоборот.
7.6 Project structure для больших Go-проектов
Заголовок раздела «7.6 Project structure для больших Go-проектов»Стандарт сообщества — golang-standards/project-layout (с оговорками — это не official).
/cmd/<binary>/main.go — entry points/internal/ — приватный код проекта (нельзя импортить извне)/pkg/ — публичные библиотеки (если open-source)/api/ — OpenAPI/proto файлы/configs/ — конфиги (yaml, default values)/deployments/ — k8s manifests, helm/migrations/ — SQL миграции (goose/golang-migrate)/scripts/ — bash утилиты/test/ — integration тестыgo.mod, Makefile, Dockerfile, .golangci.yamlИсточник: Go Project Structure (2025)
7.7 Конфигурация
Заголовок раздела «7.7 Конфигурация»spf13/viper— самый популярный, поддерживает YAML/JSON/TOML/env/etcd/Consul.knadh/koanf— современная альтернатива (легковеснее, чище).envconfig(kelseyhightower) — только env-vars, очень простой.go-playground/validator— валидация структуры конфига.
12-Factor — конфиг через env vars (контейнеры).
7.8 Feature flags
Заголовок раздела «7.8 Feature flags»- Local — env var + sync.RWMutex map.
- Centralized — Unleash (open-source), LaunchDarkly, ConfigCat, GO Feature Flag.
- Use cases: gradual rollout, A/B, kill switch, canary by user_id mod.
8. Очереди сообщений (production-уровень)
Заголовок раздела «8. Очереди сообщений (production-уровень)»8.1 Kafka
Заголовок раздела «8.1 Kafka»Базовое:
- Topic → разделён на partitions.
- Каждая partition — лог, упорядочена.
- Consumer group — N consumers разделяют партиции (1 partition = 1 consumer внутри группы).
- Offset — текущая позиция consumer’а в partition.
Retention: время / размер. Стандарт 7 дней. Compacted topics — последнее значение по ключу.
Exactly-once (только внутри Kafka):
- Producer:
enable.idempotence=true(дедуп по producer ID). - Consumer-Producer pattern: transactional API (
transactional.id), commit offset + write в одной транзакции. - При записи в external system — не работает. Решения: idempotent upserts (UPSERT по key), outbox.
Балансировка:
- Старый: Eager Rebalance → STW для всей группы.
- Новый (default с Kafka 2.4): Cooperative Sticky — incremental, минимальный disrupt.
Go клиенты:
github.com/segmentio/kafka-go— pure Go, простой API.github.com/confluentinc/confluent-kafka-go— обёртка над librdkafka (C), самый фичастый.github.com/twmb/franz-go— pure Go, современный, очень performant.
8.2 NATS / JetStream
Заголовок раздела «8.2 NATS / JetStream»- NATS Core — at-most-once, pub/sub, request/reply, fan-out.
- JetStream — persistent, at-least-once, exactly-once delivery, replay.
- Производительность — миллионы msg/sec, low latency (<1ms).
- Простой Go-native client:
github.com/nats-io/nats.go.
8.3 RabbitMQ
Заголовок раздела «8.3 RabbitMQ»- AMQP 0.9.1 (есть и 1.0).
- Exchanges (direct, topic, fanout, headers) → routing keys → queues.
- DLQ, TTL, priority queues, delayed messages — встроено.
- Хорош для per-message ACK, complex routing между сервисами.
8.4 Когда что выбрать
Заголовок раздела «8.4 Когда что выбрать»| Use case | Tool |
|---|---|
| Event log, replay, audit, аналитика | Kafka |
| Микросервисы request/reply, низкая латенси | NATS |
| Task queue, сложный routing, ACK per message | RabbitMQ |
| Простой fire-and-forget, embedded | NATS Core |
| ETL, streaming, analytics | Kafka + Kafka Connect |
Источник: Kafka vs NATS vs RabbitMQ (2025)
9. Тестирование — продвинуто
Заголовок раздела «9. Тестирование — продвинуто»9.1 Integration tests с testcontainers
Заголовок раздела «9.1 Integration tests с testcontainers»import "github.com/testcontainers/testcontainers-go/modules/postgres"
container, err := postgres.Run(ctx, "postgres:16", postgres.WithDatabase("test"), postgres.WithUsername("user"), postgres.WithPassword("pass"), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready").WithOccurrence(2)),)defer container.Terminate(ctx)
dsn, _ := container.ConnectionString(ctx)// прогоняем миграции, тестируемПоддерживает PG, MySQL, Mongo, Redis, Kafka, Localstack (AWS), Selenium и др.
9.2 Contract tests (Pact)
Заголовок раздела «9.2 Contract tests (Pact)»Consumer side:
- Описывает ожидания: “при GET /users/1 верни {name: …}”
- Генерирует pact файл (JSON).
- Публикует в Pact Broker.
Provider side:
- Скачивает pact’ы из Broker.
- Запускает provider в тестовом режиме.
- Прогоняет ожидания: реально ли так отвечает.
Library: github.com/pact-foundation/pact-go.
9.3 Property-based testing
Заголовок раздела «9.3 Property-based testing»github.com/leanovate/gopter или pgregory.net/rapid.
rapid.Check(t, func(t *rapid.T) { xs := rapid.SliceOf(rapid.Int()).Draw(t, "xs") sorted := Sort(xs) require.True(t, sort.IntsAreSorted(sorted)) require.ElementsMatch(t, xs, sorted)})Генерит рандомные входы, ищет минимальный пример, где свойство нарушается.
9.4 Mutation testing
Заголовок раздела «9.4 Mutation testing»github.com/go-gremlins/gremlins — мутирует код (меняет > на <=, etc), запускает тесты, считает coverage поведенческий. Если тесты прошли при мутации — слабые тесты.
9.5 Load testing
Заголовок раздела «9.5 Load testing»k6 — JavaScript-сценарии, можно из Go вызывать.
import http from 'k6/http';export const options = { vus: 100, duration: '30s' };export default () => { http.get('http://localhost:8080/api'); };Vegeta — pure Go, постоянная RPS:
echo "GET http://localhost:8080/api" | vegeta attack -rate=1000 -duration=30s | vegeta report9.6 Chaos testing основы
Заголовок раздела «9.6 Chaos testing основы»- Chaos Mesh (Kubernetes) — pod kill, network delay, DNS chaos.
- Toxiproxy — TCP-прокси с искусственной задержкой/loss.
gremlin/chaos-toolkit.
Принципы Netflix’s Chaos Monkey: убиваем prod нестабильные сервисы регулярно, чтобы команды строили резистентность.
10. Observability — production
Заголовок раздела «10. Observability — production»10.1 OpenTelemetry полностью
Заголовок раздела «10.1 OpenTelemetry полностью»- Traces (стабильно), Metrics (стабильно), Logs (beta в Go).
- OTLP exporter → OTel Collector → Tempo/Jaeger/Loki/Prometheus.
- Auto-instrumentation для
net/http,database/sql, gRPC, Kafka, etc.
10.2 Prometheus + Grafana
Заголовок раздела «10.2 Prometheus + Grafana»- Pull model — Prometheus сам ходит на
/metricsendpoint. - TSDB — time series database.
- PromQL — query language.
rate(http_requests_total[1m]),histogram_quantile(0.99, ...).
import "github.com/prometheus/client_golang/prometheus/promhttp"http.Handle("/metrics", promhttp.Handler())Стандартные метрики (RED):
- Rate — RPS
- Errors — error rate
- Duration — latency (histogram!)
USE для ресурсов: Utilization, Saturation, Errors.
10.3 ELK / Loki
Заголовок раздела «10.3 ELK / Loki»- ELK (Elasticsearch + Logstash + Kibana) — мощный full-text, но дорог.
- Loki (Grafana) — индексирует только labels, blob storage для логов. Дешевле в 10x.
Структурированные логи (JSON):
import "log/slog" // Go 1.21+slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))slog.Info("user logged in", "user_id", 42, "trace_id", traceID)Альтернативы: zerolog, zap (Uber).
10.4 Health checks
Заголовок раздела «10.4 Health checks»/livez— жив ли процесс. Просто200 OK./readyz— готов ли принимать трафик. Проверяет downstream (DB ping, kafka ready)./startupz— пока приложение инициализируется (миграции, прогрев кэша).
10.5 SLO / SLA / SLI
Заголовок раздела «10.5 SLO / SLA / SLI»- SLI — что меряем (например, latency p99 < 100ms).
- SLO — целевое значение (99.9% запросов за 100ms).
- SLA — контракт с клиентом, юридические последствия.
Error budget = 100% - SLO. Если 99.9% SLO, бюджет 0.1% (43 мин/мес). Превысили → стоп фич, фикс надёжности.
Sloth (github.com/slok/sloth) — генератор Prometheus rules для SLO.
Источник: Uptrace SLO 2025
11. Kubernetes для Go-разработчика Middle 2
Заголовок раздела «11. Kubernetes для Go-разработчика Middle 2»11.1 Базовые сущности
Заголовок раздела «11.1 Базовые сущности»- Pod — атомарная единица, 1+ контейнеров с общим network/volume.
- Deployment — управляет ReplicaSet’ом, rolling update, rollback.
- Service — стабильный endpoint для группы pod’ов (ClusterIP / NodePort / LoadBalancer).
- ConfigMap / Secret — конфиг и секреты.
- Ingress / Gateway API — HTTP роутинг снаружи.
- Job / CronJob — одноразовые / периодические.
11.2 Helm charts базово
Заголовок раздела «11.2 Helm charts базово»- Templates с Go templating (sprig functions).
values.yaml— параметры.helm install,helm upgrade,helm rollback.- Альтернативы: Kustomize (overlays без templating), Cue/Timoni.
11.3 Health checks правильно
Заголовок раздела «11.3 Health checks правильно»livenessProbe: httpGet: { path: /livez, port: 8080 } initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3readinessProbe: httpGet: { path: /readyz, port: 8080 } periodSeconds: 5 failureThreshold: 2startupProbe: # пока initContainer/миграции httpGet: { path: /readyz, port: 8080 } failureThreshold: 30 periodSeconds: 10Graceful shutdown:
sig := make(chan os.Signal, 1)signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)<-sig
// 1. Mark not ready (failing readiness)ready.Store(false)// 2. Wait for LB to updatetime.Sleep(5 * time.Second)// 3. Shutdown server with timeoutctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)defer cancel()server.Shutdown(ctx)preStop hook — sleep 10, чтобы LB обновился до того, как контейнер примет SIGTERM.
11.4 Resource limits и Go runtime
Заголовок раздела «11.4 Resource limits и Go runtime»resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 2GiВлияние на Go:
- До Go 1.25 / без
automaxprocs— GOMAXPROCS = host CPU count → CPU throttling, p99 latency взрывается. - Memory limit без GOMEMLIMIT → OOM kill, потому что Go runtime не знает о лимите.
Совет: GOMEMLIMIT = 80-90% от memory limit, GOGC = 100 (default) или 50 (low-latency).
11.5 HPA, VPA
Заголовок раздела «11.5 HPA, VPA»- HPA (Horizontal Pod Autoscaler) — scale по CPU/memory/custom metrics (через Prometheus Adapter).
- VPA (Vertical Pod Autoscaler) — изменяет request/limit под нагрузку.
- KEDA — event-driven scaling (Kafka lag, queue size).
12. CI/CD
Заголовок раздела «12. CI/CD»12.1 GitHub Actions / GitLab CI для Go
Заголовок раздела «12.1 GitHub Actions / GitLab CI для Go»name: CIon: [push, pull_request]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: { go-version: '1.25' } - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - run: go test -race -coverprofile=cover.out ./... - run: go vet ./... - uses: golangci/golangci-lint-action@v612.2 Линтеры и security scanners
Заголовок раздела «12.2 Линтеры и security scanners»golangci-lint— мета-линтер (govet, staticcheck, errcheck, gosec, gocritic, revive, etc).gosec— security (включается в golangci-lint).govulncheck— известные CVE в зависимостях.trivy— Docker image scan (vulnerabilities в base image).syft— SBOM генерация.
В CI:
- run: govulncheck ./...- run: trivy image myapp:latest12.3 Build/test cache
Заголовок раздела «12.3 Build/test cache»~/.cache/go-build— кэш скомпилированных пакетов.~/go/pkg/mod— кэш модулей.- В GitHub Actions:
actions/cache@v4.
12.4 Release automation (goreleaser)
Заголовок раздела «12.4 Release automation (goreleaser)»goreleaser.yaml:
builds: - env: [CGO_ENABLED=0] goos: [linux, darwin] goarch: [amd64, arm64]archives: - format: tar.gzdockers: - image_templates: ["ghcr.io/me/app:{{.Version}}"]goreleaser release → собирает бинарники под все ОС/arch, делает Docker images, GitHub release, changelog.
13. Безопасность
Заголовок раздела «13. Безопасность»13.1 OWASP Top 10 (2025)
Заголовок раздела «13.1 OWASP Top 10 (2025)»- Broken Access Control — checks роли на каждый ресурс.
- Cryptographic Failures — не самописная крипта.
- Injection (SQL, command, etc).
- Insecure Design.
- Security Misconfiguration — default passwords, debug в проде.
- Vulnerable Components — govulncheck.
- Authentication Failures — слабые пароли, rate-limit на login.
- Software/Data Integrity Failures — supply chain attacks.
- Logging/Monitoring Failures.
- SSRF — server-side request forgery.
13.2 Authentication
Заголовок раздела «13.2 Authentication»JWT:
- Claims:
iss, sub, aud, exp, iat, nbf, jti. - Алгоритмы: RS256/ES256 (asymmetric, лучше для distributed). HS256 — только если secret один.
- Never trust
alg: none. Library должна валидировать алгоритм. - Хранить access token ~15 мин, refresh — 7-30 дней.
- HttpOnly + Secure cookies для refresh, чтобы JS не доступен.
OAuth 2.0 / OIDC:
- Authorization Code + PKCE — стандарт 2025 (PKCE мандатно для всех flows в OAuth 2.1).
state— против CSRF;nonce— против replay (OIDC).- Access Token → API; ID Token → клиент.
- DPoP / mTLS — sender-constrained tokens (предотвращают stolen-token attacks).
Источник: OAuth 2.0 best practices (2025)
13.3 Authorization
Заголовок раздела «13.3 Authorization»- RBAC — пользователю даём роли, роли = набор permissions.
- ABAC — атрибутный, например “owner_id == request.user_id AND time < 18:00”.
- Casbin — Go библиотека, поддерживает ACL, RBAC, ABAC, ReBAC.
- OpenFGA / Zanzibar — для relationship-based auth (Google Drive-like sharing).
13.4 Password hashing
Заголовок раздела «13.4 Password hashing»- bcrypt — стандарт, медленный (12 rounds).
- argon2id — современный, лучше для GPU resistance.
golang.org/x/crypto/argon2. - scrypt — есть в stdlib.
Никогда:
- MD5, SHA1, SHA256 без соли.
13.5 Защита от инъекций
Заголовок раздела «13.5 Защита от инъекций»SQL:
// ВСЕГДА placeholdersdb.QueryRow("SELECT ... WHERE id = $1", id)// НИКОГДА конкатенация// db.QueryRow("SELECT ... WHERE id = " + id) // SQL INJECTION!XSS: html/template (НЕ text/template) для HTML — auto-escape.
CSRF: для cookies-auth — gorilla/csrf middleware (double-submit cookie). Для JWT в header — обычно не страшно.
13.6 Govulncheck в CI
Заголовок раздела «13.6 Govulncheck в CI»govulncheck ./...# илиgo install golang.org/x/vuln/cmd/govulncheck@latestИспользует static analysis — репортит только если уязвимый код достижим из ваших entrypoints (меньше шума).
14. System Design на собеседовании Middle 2
Заголовок раздела «14. System Design на собеседовании Middle 2»14.1 Что ожидается
Заголовок раздела «14.1 Что ожидается»В РФ на Middle 2 — обычно LLD (low-level design) + базовый HLD (high-level). Senior — глубокий HLD.
Структура ответа (45-60 мин):
- Requirements clarification (5 мин) — функциональные, нефункциональные. Цифры (DAU, QPS, storage).
- High-level design (10 мин) — компоненты на доске.
- Deep dive (20 мин) — выбранный компонент в детали.
- Scaling, bottlenecks, trade-offs (10 мин).
- Failure modes, observability (5 мин).
14.2 Типичные задачи
Заголовок раздела «14.2 Типичные задачи»URL shortener (Bit.ly):
- Хеширование: base62(MD5(url + salt))[:7] vs counter base62.
- Storage: KV (Redis для hot, PG для cold).
- Read:Write ~1000:1 → агрессивный кэш.
- Custom URLs → проверка коллизий.
- Аналитика — отдельный async pipeline (Kafka → ClickHouse).
Chat (WhatsApp / Telegram):
- WebSocket connection manager (sharded by user_id).
- Message store: Cassandra (write-heavy, partition by chat_id, sort by timestamp).
- Presence service — Redis + pub/sub.
- Push notifications для offline.
Лента (Twitter / Instagram):
- Fan-out on write — при post автор пишет в feed подписчиков (хорошо для read, плохо для celeb).
- Fan-out on read — query при заходе пользователя (хорошо для write).
- Hybrid — fan-out для регулярных юзеров, fan-out on read для celeb.
- Cache: Redis с feed_id → [post_id1, post_id2].
Такси (Uber):
- Геопоиск — Redis GEO commands или PostgreSQL PostGIS.
- Matching driver-rider — отдельный сервис, hot data в Redis.
- Real-time location updates через WebSocket.
14.3 Throughput / latency / availability
Заголовок раздела «14.3 Throughput / latency / availability»- Latency: p50, p95, p99, p999. Меряем worst case, не average.
- Throughput: RPS / TPS. Little’s law: L = λ × W (concurrent users = rate × wait time).
- Availability: 99.9% = 8.76h downtime/year; 99.99% = 52 мин; 99.999% = 5 мин.
14.4 Capacity planning
Заголовок раздела «14.4 Capacity planning»DAU = 100MЗапросов на юзера = 50Total = 5 × 10^9 / day = ~60K RPS average; peak ×3 = 180K RPS
Storage: 1KB per record, 5B records/day = 5TB/day = 150TB/month3 replicas → 450TB/month
Pod на 1K RPS → 60 pods × 3 (peak buffer) = 180 pods14.5 Кэширование на разных уровнях
Заголовок раздела «14.5 Кэширование на разных уровнях»- Client-side — Cache-Control headers, ETags.
- CDN — статика (CloudFront, Cloudflare).
- API Gateway / Reverse proxy — Varnish, Nginx.
- Application-level — in-memory (
groupcache,bigcache,freecache). - Distributed — Redis, Memcached.
- Database query cache — PostgreSQL prepared statements, materialized views.
Cache patterns:
- Cache-aside (lazy) — read: cache miss → DB → cache fill. Write: invalidate.
- Write-through — пишем сразу в cache + DB.
- Write-back — пишем в cache, async в DB (риск потери).
- TTL — у всего, чтобы не залипало.
Thundering herd — миллион запросов на cache miss одновременно → DB перегружена. Решение: singleflight + распределённый lock + jitter в TTL.
15. Типичные вопросы на собеседовании Middle 2 Go (100+ вопросов)
Заголовок раздела «15. Типичные вопросы на собеседовании Middle 2 Go (100+ вопросов)»15.1 Internals — runtime, GC, scheduler
Заголовок раздела «15.1 Internals — runtime, GC, scheduler»1. Опишите GMP модель.
G — goroutine, M — OS thread, P — logical processor. P держит LRQ, GOMAXPROCS = число P.
2. Что такое work stealing?
Когда LRQ P пуст, забирает половину goroutines из LRQ другого P. Уменьшает contention.
3. Как работает netpoller?
Блокирующие network I/O вызовы регистрируются в epoll/kqueue. M освобождается, goroutine паркуется. Когда I/O готов — goroutine кладётся в runqueue.
4. Чем отличается syscall от netpoll?
Network I/O идёт через netpoller (M свободен). Обычный blocking syscall — M залипает; P подбирается другим M.
5. Что такое preemption в Go? Как изменилось в 1.14?
До 1.14 — кооперативная (только на функциональных точках). С 1.14 — асинхронная через SIGURG, прерывает в любой safe point.
6. Сколько стека у новой goroutine?
2KB. Растёт удвоением, при overflow runtime копирует фреймы в больший стек.
7. Что происходит с указателями при stack copy?
Runtime обновляет все указатели на стек. Указатели НА стек, хранимые в heap, не безопасны — но они должны быть на heap escape’нуты.
8. Опишите memory allocator (mcache → mcentral → mheap).
Per-P mcache без лока → mcentral (с локом, на size class) → mheap (с локом, у OS просит арены).
9. Что такое size class? Сколько их?
~67-70 классов до 32KB. Объект округляется до ближайшего класса. >32KB — large object, напрямую mheap.
10. Tiny allocator — для чего?
Объекты ≤16B группируются в один блок 16B. Снижает overhead на крошечных аллокациях.
11. Как работает Go GC?
Concurrent mark-sweep, tri-color, write barrier. STW только на mark start/end (~10µs каждый).
12. Что такое write barrier?
Когда в маркинг-фазе goroutine пишет указатель в объект, runtime интерсептит запись, чтобы не “потерять” живой объект.
13. Что такое pacer? Что он делает?
Pacer регулирует, когда стартовать следующий GC цикл. Цель — закончить mark раньше, чем heap дойдёт до target_size.
14. GOGC — что значит 100?
Target heap = live_heap × 2 (т.е. удвоение). GOGC=50 → target = 1.5× live, чаще GC.
15. GOMEMLIMIT — для чего?
Soft limit на всю память Go runtime. Когда близко — pacer триггерит GC агрессивнее. Полезно в контейнерах.
16. Как настроить Go под контейнер с лимитами?
GOMAXPROCS=cpu_limit (с 1.25 — авто; или uber-go/automaxprocs). GOMEMLIMIT=80-90% от memory limit. GOGC=100 или 50.
17. Open-coded defers — что это?
С Go 1.14 — компилятор инлайнит defer-код прямо в функцию, если ≤8 defer’ов и не в цикле. ~6ns vs 35ns старого.
18. panic и recover — как работают?
panic раскручивает стек, выполняя defer’ы. Если в defer recover() — panic снимается, выполнение продолжается. Иначе процесс падает.
19. Что произойдёт, если panic в одной goroutine не обработан?
Падает весь процесс.
20. Escape analysis — что это?
Компилятор определяет, можно ли переменную на стек, или она “сбегает” в heap. Возврат указателя, interface boxing → heap.
15.2 Concurrency продвинуто
Заголовок раздела «15.2 Concurrency продвинуто»21. В чём разница между make(chan int) и make(chan int, 10)?
Unbuffered блокирует sender пока reader не возьмёт. Buffered ёмкостью 10 — sender блокируется когда буфер полный.
22. Что вернёт чтение из закрытого канала?
Zero-value немедленно, ok=false.
23. Что произойдёт при send в закрытый канал?
Panic: “send on closed channel”.
24. Что произойдёт при close уже закрытого канала?
Panic: “close of closed channel”.
25. Кто должен закрывать канал?
Sender. Если несколько senders → нужен координатор / sync.Once.
26. select с пустым case — что значит default?
default выполняется немедленно, если ни один case не готов. Делает select non-blocking.
27. Расскажите про happens-before гарантии каналов.
N-я отправка happens-before N-го приёма. Закрытие — before приёма zero-value.
28. sync.Mutex vs sync.RWMutex — когда что?
RWMutex — много чтений, мало записей. Иначе Mutex (RWMutex дороже из-за двух счётчиков и contention в write path).
29. Как обнаружить deadlock в Go?
runtime panic “fatal error: all goroutines are asleep - deadlock!” если все горутины спят. Для частичных — pprof goroutine, ищем “chan receive” / “sync.Mutex.Lock” застрявшие.
30. Что такое goroutine leak? Как найти?
Goroutine, которая никогда не завершится (заблокирована на чтении канала, который никто не закроет). Найти: pprof /debug/pprof/goroutine, считать растущее количество.
31. Как написать worker pool на N горутинах?
Создать buffered channel jobs, запустить N goroutines, каждая делает for j := range jobs { process(j) }. close(jobs) для shutdown. WaitGroup.
32. errgroup vs sync.WaitGroup?
errgroup отменяет ctx при первой ошибке, собирает первую ошибку. WaitGroup — просто ждёт всех.
33. singleflight — для чего?
Дедупликация одинаковых параллельных запросов. Один реально выполняется, остальные ждут результата.
34. sync.Pool — race с GC?
Каждый GC цикл частично опустошает Pool (с Go 1.13 — двухуровневый, не полностью).
35. Какие гарантии у sync.Once?
Doсалось гарантированно один раз. Все вызывающие видят результат после возврата (happens-before).
36. Что такое context.Background() и context.TODO()?
Background — корень, никогда не cancel. TODO — placeholder, “пока не знаю, какой контекст нужен”. Семантически идентичны.
37. Как правильно использовать context.WithCancel?
defer cancel() ВСЕГДА. Иначе leak горутин/timer’ов.
38. Что произойдёт, если context.Cancel вызвать дважды?
Ничего — идемпотентен.
39. Зачем нужен sync/atomic, если есть Mutex?
Lock-free, быстрее на низкой contention. Для простых счётчиков, флагов, указателей.
40. Что такое sequential consistency?
Все атомарные операции в программе кажутся выполненными в одном глобальном порядке. С Go 1.19 атомики SC.
41. Race condition vs data race?
Data race — concurrent access к памяти без синхронизации. Race condition — логически неправильный результат из-за порядка операций. Race detector ловит data races; race conditions может только тесты.
42. go test -race — как работает?
Инструментирует доступы к памяти, отслеживает happens-before граф. Overhead ~5-10x CPU, 5-10x memory. No false positives.
43. Когда использовать sync.Map?
Read-heavy (90%+ read), либо disjoint key sets. Иначе RWMutex+map.
44. Что такое spinning в sync.Mutex?
Перед паркингом goroutine крутится несколько раз (active wait). Помогает в коротких lock-ах.
45. Объясните паттерн fan-in / fan-out.
Fan-out: один input → N workers. Fan-in: N channels → один result channel (через select).
15.3 БД + распределёнка
Заголовок раздела «15.3 БД + распределёнка»46. MVCC в PostgreSQL — что это?
Каждая строка имеет xmin/xmax. Транзакция видит снапшот по своему transaction ID. UPDATE = новая строка + dead tuple.
47. Чем UPDATE отличается от DELETE+INSERT в Postgres?
Логически почти то же — старая строка помечается dead, новая создаётся. UPDATE сохраняет primary key.
48. Что делает VACUUM?
Чистит dead tuples, обновляет статистику. AUTOVACUUM делает это автоматически.
49. VACUUM FULL vs VACUUM?
FULL — переписывает таблицу целиком, EXCLUSIVE LOCK, возвращает место OS. Регулярный VACUUM — concurrent, освобождает место для переиспользования.
50. Что такое index bloat?
Индекс растёт из-за dead tuples, не сжимается автоматически. Решение: REINDEX CONCURRENTLY.
51. Когда B-tree индекс не помогает?
LIKE ‘value%’ — помогает; LIKE ‘%value’ — нет. Не помогает на функциях колонок (если не functional index).
52. Что такое covering index?
INCLUDE дополнительные колонки в листья B-tree → index-only scan без обращения к heap.
53. Объясните EXPLAIN ANALYZE.
Реально выполняет query, показывает план и реальные времена. Cost — оценка планировщика. Rows estimate vs actual — если расходятся → ANALYZE.
54. Что такое query plan? Seq Scan vs Index Scan?
План — как Postgres планирует исполнить. Seq Scan — full table scan. Index Scan — через индекс. Index Only Scan — без обращения к heap. Bitmap Index Scan — для большого набора rows.
55. Connection pooling — почему важен?
Установка соединения в Postgres дорогая (auth + backend fork). Pool переиспользует.
56. PgBouncer modes?
Session (1 backend = 1 client connection), Transaction (1 backend = 1 transaction; нельзя prepared statements), Statement (1 backend = 1 statement).
57. CAP теорема?
Consistency, Availability, Partition tolerance. При partition — выбираем C или A. PACELC уточняет: Else — L (latency) или C.
58. Что такое eventually consistent? Read-your-writes?
Eventually — со временем все реплики согласованы. Read-your-writes — пользователь видит свою запись сразу (через sticky session или read from master).
59. Saga vs 2PC?
2PC блокирующий, single point of failure (coordinator). Saga — sequence of local transactions + compensations, eventually consistent, без локов.
60. Outbox pattern — зачем?
Атомарно записать в БД + опубликовать в Kafka нельзя напрямую. Outbox: пишем в outbox таблицу в той же транзакции, relay читает и публикует.
61. Что такое idempotency key?
Уникальный ID запроса, сервер сохраняет результат → повтор с тем же ключом возвращает тот же результат, не выполняет ещё раз.
62. Как реализовать distributed lock на Redis?
Redlock: SETNX с TTL на N (5) Redis-нод, нужен majority. Используем go-redsync. Caveat: clock drift; для критичного — etcd.
63. Чем отличаются Kafka partition / consumer group?
Partition — упорядоченный лог. Consumer group — N consumers разделяют partitions (1 partition → 1 consumer внутри group).
64. Exactly-once в Kafka — возможно?
Внутри Kafka — да (transactional API). При записи в external — практически нет; используют idempotent upserts.
65. Когда Kafka, когда RabbitMQ, когда NATS?
Kafka — event log, аналитика. RabbitMQ — per-message ACK, complex routing. NATS — low-latency RPC pub/sub.
15.4 Архитектура + микросервисы
Заголовок раздела «15.4 Архитектура + микросервисы»66. Микросервисы vs монолит — когда что?
Start with монолит, разделять когда: разные команды, разные scaling needs, разные SLA. Не для каждого CRUD.
67. Что такое DDD aggregate?
Кластер entities + value objects под единой transactional границей. Inv’ы валидируются внутри.
68. Bounded Context — пример?
”Customer” в Sales BC (имя, email, история покупок) ≠ “Customer” в Billing BC (имя, счёт, способ оплаты). Разные модели одного концепта.
69. Hexagonal architecture — зачем?
Изолировать бизнес-логику от инфраструктуры. Замена БД / транспорта → меняем только адаптер.
70. Что такое CQRS?
Разделение моделей чтения и записи. Write — нормализованная, для consistency. Read — денормализованная, для performance.
71. Когда CQRS оправдан?
Когда read запросы сильно отличаются от write — отчёты, аналитика, сложные join’ы для UI.
72. Event Sourcing — pros/cons?
Pros: audit, replay, time travel. Cons: сложная schema evolution, debug сложнее.
73. Circuit breaker — состояния?
Closed (норма) → Open (порог ошибок превышен, fail-fast) → Half-Open (пробуем 1 запрос).
74. Bulkhead?
Изоляция ресурсов (thread pools, connection pools) per-downstream — если один deps медленный, не съест всё.
75. Retry с какой стратегией?
Exponential backoff с jitter. Только для idempotent operations. С circuit breaker.
15.5 System design
Заголовок раздела «15.5 System design»76. Спроектируйте URL shortener (Bit.ly).
См. раздел 14. Counter→base62 vs hash, KV storage + PG, агрессивный кэш, async аналитика.
77. Спроектируйте чат на 100M юзеров.
WebSocket conn manager (sharded), Cassandra для messages, Redis для presence, push для offline, federation между регионами.
78. Лента (как Twitter)?
Fan-out on write для regular, fan-out on read для celeb. Redis для feed cache. Async pipeline для генерации.
79. Rate limiter для API на 1M RPS?
Token bucket per user, in-memory + Redis sync. Sticky sessions к одному инстансу для consistency. Sliding window для точности.
80. Как масштабировать БД на 100K writes/sec?
Sharding по user_id, async repl на read replicas, write batching, queue absorbtion (Kafka), CDC pipeline для аналитики.
15.6 Observability + тестирование
Заголовок раздела «15.6 Observability + тестирование»81. Какие метрики собирать (RED method)?
Rate (RPS), Errors (rate), Duration (histogram). По каждому endpoint/operation.
82. Что такое SLO error budget?
100% - SLO. Если 99.9% → 0.1% (~43 мин/мес). Сжигаем фичами, восстанавливаем reliability.
83. p50 vs p99 — почему p99 важен?
Average и медиана скрывают tail latency. p99 показывает worst 1% — это user-facing pain.
84. Distributed tracing — как работает?
Каждый запрос → trace_id, каждая операция → span. Контекст пропагируется через HTTP headers (traceparent). Бэкенд (Jaeger/Tempo) строит дерево.
85. Как тестировать concurrent код?
go test -race + stress (count=1000). go.uber.org/goleak для goroutine leaks. testing/synctest (Go 1.24) для детерминизма.
86. Что такое property-based testing?
Генерим рандомные входы, проверяем инвариант. Library: rapid, gopter.
87. testcontainers — для чего?
Поднимать реальные сервисы (PG, Redis, Kafka) в Docker во время integration tests.
88. Pact / contract testing?
Consumer описывает ожидания, генерит pact-файл. Provider читает pact, проверяет что реально так отвечает.
89. k6 vs Vegeta?
k6 — JavaScript scenarios, мощнее. Vegeta — pure Go, простой constant RPS. Vegeta точнее для precision-rate tests.
90. Что такое chaos engineering?
Намеренно ломаем системы (kill pods, network delay) в проде, чтобы команды строили резистентность. Tools: Chaos Mesh, Toxiproxy.
15.7 Go-specific traps
Заголовок раздела «15.7 Go-specific traps»91. Что выведет?
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }()}time.Sleep(time.Second)В Go ≤1.21 — обычно “3 3 3” (захват по ссылке). В Go ≥1.22 — “0 1 2” (loop variable scoped per iteration).
92. nil interface vs nil concrete pointer?
var p *MyError = nilvar err error = perr != nil // TRUE! Interface не nil, concrete value nil.93. Что выведет?
m := map[string]int{}m["k"]++ // OK?OK — возвращает zero value 0, инкрементит до 1.
94. range по map — порядок?
Намеренно randomized. Не полагаться на порядок.
95. Когда defer вычисляется vs выполняется?
Аргументы вычисляются при defer. Тело — при возврате.
i := 10defer fmt.Println(i) // выведет 10i = 2096. Чем отличается make от new?
*new(T) — zero value, возвращает T. make — только для slice/map/chan, инициализирует внутреннюю структуру.
97. Slice — append может вернуть тот же slice?
Если cap позволяет — да. Если переполнен → новый underlying array, старый slice указывает на старое.
98. Что такое slice header?
{ptr to array, len, cap}. Передача slice копирует header (но не data).
99. Зачем нужны errors.Is и errors.As?
errors.Is — проверка identity (включая wrapped). errors.As — type assertion через цепочку wrap.
100. Что такое fmt.Errorf("...: %w", err)?
Wrapping (Go 1.13+). Внутренний err доступен через errors.Unwrap, errors.Is, errors.As.
101. Когда использовать pointer receiver vs value receiver?
Pointer — если метод модифицирует структуру, большая структура, или у других методов pointer (для consistency). Value — небольшие immutable VO.
102. Что такое embedding (vs наследование)?
Структура содержит другую без имени поля. Методы внутренней доступны “промоутед”. Это композиция, не наследование.
103. init() — когда вызывается? Порядок?
При import пакета, до main. Один пакет — несколько init в разных файлах, порядок по алфавиту имени файла. Цепочка: импорты → init импортов → init текущего.
104. Generic constraints (Go 1.18+)?
type Number interface { ~int | ~float64 }func Sum[T Number](xs []T) T { ... }~ — также type aliases (“underlying type”). | — union.
105. any vs interface{}?
Идентичны. any — alias с Go 1.18.
16. Pet-проекты для портфолио Middle 2
Заголовок раздела «16. Pet-проекты для портфолио Middle 2»Project 1: Distributed URL Shortener with Analytics
Заголовок раздела «Project 1: Distributed URL Shortener with Analytics»Stack:
- Go (HTTP server: chi or echo)
- PostgreSQL (URL mappings, шардинг по hash)
- Redis (hot cache for shortcode → URL)
- Kafka (clicks → analytics)
- ClickHouse (analytics queries)
- gRPC между сервисами
Что демонстрирует: distributed system, sharding, кэширование, event streaming, OLAP vs OLTP.
Бонус-фичи: custom URLs, expiration TTLs, QR-code generator, rate limiting per API key.
Project 2: Real-time Chat / Notification Hub
Заголовок раздела «Project 2: Real-time Chat / Notification Hub»Stack:
- Go (WebSocket: gorilla/websocket, coder/websocket)
- Redis (pub/sub + presence)
- PostgreSQL (chat history)
- Kafka (события для downstream)
- OpenTelemetry traces
Что демонстрирует: WebSocket с тысячами одновременных соединений, sharding connection manager, presence, message ordering, offline delivery.
Project 3: E-commerce backend with Outbox / Saga
Заголовок раздела «Project 3: E-commerce backend with Outbox / Saga»Stack:
- Go monorepo (3-4 микросервиса: Order, Payment, Inventory, Notification)
- PostgreSQL per service (no shared DB)
- Kafka для events + Outbox pattern
- Saga orchestrator (или choreography)
- Идемпотентные API через Idempotency-Key
Что демонстрирует: DDD aggregates, Saga, Outbox, eventual consistency, distributed transactions, observability с tracing.
Project 4: Search Engine (Elasticsearch-style на Go)
Заголовок раздела «Project 4: Search Engine (Elasticsearch-style на Go)»Stack:
- Go (без сторонних поиск-движков)
- Свой inverted index (BoltDB / BadgerDB для persistent)
- TF-IDF / BM25 ranking
- HTTP API
- gRPC для cluster nodes
- Replication через Raft (
hashicorp/raft)
Что демонстрирует: алгоритмы (inverted index, ranking), distributed consensus (Raft), архитектура хранения.
Project 5: Job Scheduler (Like cron/Sidekiq for Go)
Заголовок раздела «Project 5: Job Scheduler (Like cron/Sidekiq for Go)»Stack:
- Go core scheduler
- PostgreSQL для job store (skip-locked для distributed lock)
- Redis для leadership election (distributed lock) или etcd
- HTTP/gRPC API для job submission
- Web UI (можно простой htmx)
- Prometheus metrics
Что демонстрирует: distributed leadership, retries with backoff, dead-letter, idempotency, observability.
17. Ресурсы для обучения
Заголовок раздела «17. Ресурсы для обучения»17.1 Книги
Заголовок раздела «17.1 Книги»Обязательное:
- Designing Data-Intensive Applications (Martin Kleppmann) — 2nd edition (2025). База для distributed systems.
- The Go Programming Language (Donovan, Kernighan) — стандарт Go.
- 100 Go Mistakes and How to Avoid Them (Teiva Harsanyi) — для middle++ обязательно.
- Concurrency in Go (Katherine Cox-Buday) — patterns и подходы.
- Cloud Native Go (Matthew A. Titmus) — облачные приложения, k8s.
Дополнительно:
- System Design Interview Vol 1+2 (Alex Xu).
- Domain-Driven Design with Golang (Matthew Boyle).
- Distributed Services with Go (Travis Jeffery).
- Let’s Go / Let’s Go Further (Alex Edwards) — практический web-dev на Go.
- Practical Go (Dave Cheney).
- Database Internals (Alex Petrov).
17.2 Курсы
Заголовок раздела «17.2 Курсы»- Ardan Labs Ultimate Go (Bill Kennedy) — глубокий runtime/internals.
- JetBrains Academy — Go разделы.
- Уроки Дмитрия Шевелева на YouTube (RU).
- Yandex Practicum — Go разработчик (RU).
17.3 Каналы и блоги
Заголовок раздела «17.3 Каналы и блоги»EN:
- Dave Cheney — practical Go, perf, internals.
- Russ Cox blog — глубокая теория, Go internals.
- Ardan Labs Blog (Bill Kennedy) — scheduler, memory.
- The Go Blog — official.
- YouTube: GopherCon, Bill Kennedy, Dave Cheney talks.
- Awesome Go.
RU:
- tproger Go.
- Habr Go-хаб.
- YouTube: каналы “Артём Кашканов”, “GoToBrno” (русскоязычные митапы), Yandex Cloud Meetups.
17.4 Подкасты
Заголовок раздела «17.4 Подкасты»- Go Time (Changelog) — еженедельный, must.
- Cup o’ Go (Shay Nehmad, Jonathan Hall).
- Backend & Беседы (RU).
17.5 Конференции
Заголовок раздела «17.5 Конференции»- GopherCon (US, EU, Singapore, India, Africa).
- GoLab (Italy).
- Go West (UK).
- GopherCon Brazil.
- RU: GolangConf (Москва, осень).
17.6 Сообщества
Заголовок раздела «17.6 Сообщества»- r/golang (Reddit) — 200K+ subscribers.
- Gopher Slack (gophers.slack.com) — invite на invite.gophers.io.
- Telegram: GolangRu, Go в продакшен, Pro.Go.
18. План перехода Middle 1 → Middle 2 (6-12 месяцев)
Заголовок раздела «18. План перехода Middle 1 → Middle 2 (6-12 месяцев)»Реалистичный график. Если уже что-то знаешь — пропусти соответствующие недели.
Месяц 1-2: Go internals + продвинутая concurrency
Заголовок раздела «Месяц 1-2: Go internals + продвинутая concurrency»Неделя 1-2:
- Прочитать раздел про scheduler (Ardan Labs blog series).
- Изучить GMP, work stealing, netpoller.
- Поэкспериментировать с
GODEBUG=schedtrace=1000на своём проекте.
Неделя 3-4:
- Memory allocator (mcache/mcentral/mheap).
- GC pacer, write barriers.
- Прочитать Go GC Guide от корки до корки.
Неделя 5-6:
- Concurrency продвинуто: sync.Pool, sync.Map, errgroup, semaphore, singleflight.
- Race detector, goleak, testing/synctest.
- Прочитать Go Memory Model.
Неделя 7-8:
- Написать простой in-memory кэш с TTL, поддержкой LRU (для тренировки concurrency).
- Прогнать race + бенчмарки.
Месяц 3-4: Профилирование, оптимизации, БД
Заголовок раздела «Месяц 3-4: Профилирование, оптимизации, БД»Неделя 9-10:
- pprof endpoints, flame graphs.
- benchstat, escape analysis.
- Развернуть Pyroscope локально, попрофилировать свой проект.
Неделя 11-12:
- PostgreSQL deep: MVCC, EXPLAIN ANALYZE, индексы (B-tree, GIN, partial, covering).
- Vacuum, autovacuum tuning.
- Шардирование, partitioning.
Неделя 13-14:
- Distributed transactions: Outbox, Saga (orchestration + choreography).
- Идемпотентность, Idempotency-Key pattern.
- Распределённые блокировки (Redlock, etcd).
Неделя 15-16:
- Pet-проект 1: URL shortener с шардингом и кэшем (см. раздел 16).
Месяц 5-6: Microservices, gRPC, observability
Заголовок раздела «Месяц 5-6: Microservices, gRPC, observability»Неделя 17-18:
- gRPC глубоко: интерсепторы, streaming, deadlines, retries.
- Protobuf продвинуто: well-known types, custom options.
- Connect-Go.
Неделя 19-20:
- OpenTelemetry полностью: traces, metrics, logs (slog).
- Prometheus + Grafana, развернуть локально.
- SLO/SLI/SLA — настроить для своего проекта (Sloth).
Неделя 21-22:
- Kafka deep: partitions, consumer groups, exactly-once, Cooperative Sticky.
- NATS JetStream базово.
- Outbox + Debezium CDC локально.
Неделя 23-24:
- Pet-проект 2: чат на WebSocket с presence (см. раздел 16).
Месяц 7-8: Architecture + System Design
Заголовок раздела «Месяц 7-8: Architecture + System Design»Неделя 25-26:
- DDD: agregates, bounded context, ubiquitous language.
- Hexagonal architecture в Go (прочитать, переписать pet-проект).
- CQRS, Event Sourcing основы.
Неделя 27-28:
- System Design Interview (Alex Xu) Vol 1 — пройти 5 задач.
- Решить: URL shortener, лента, чат, такси, search.
Неделя 29-30:
- DDIA Ch. 1-9 (replication, partitioning, transactions, distributed consensus).
Неделя 31-32:
- Pet-проект 3: e-commerce с Saga + Outbox (см. раздел 16).
Месяц 9-10: Безопасность, K8s, CI/CD
Заголовок раздела «Месяц 9-10: Безопасность, K8s, CI/CD»Неделя 33-34:
- JWT, OAuth 2.0, PKCE.
- Casbin (RBAC, ABAC).
- bcrypt, argon2.
Неделя 35-36:
- Kubernetes: Pod, Deployment, Service, ConfigMap, Secret.
- Helm charts.
- HPA с custom metrics через Prometheus.
- Развернуть свой pet-проект в k8s (kind/minikube).
Неделя 37-38:
- CI/CD: GitHub Actions для Go.
- golangci-lint, gosec, govulncheck, trivy.
- goreleaser.
Неделя 39-40:
- Резерв / закрытие пробелов.
Месяц 11-12: Подготовка к собеседованиям
Заголовок раздела «Месяц 11-12: Подготовка к собеседованиям»Неделя 41-44:
- Решать LeetCode Medium (Go): минимум 100 задач.
- Concentrate on: graphs, DP, two pointers, sliding window, hash maps, heaps.
- LeetCode Top Interview 150.
Неделя 45-46:
- Прорешать список из раздела 15 (100+ вопросов), записать свои ответы.
- Mock interview: с другом/коллегой/Pramp/Interviewing.io.
Неделя 47-48:
- 5 system design задач за неделю — по 1 час каждая, рисуя на доске/Excalidraw.
- Отрепетировать на камеру.
Финал: податься в Yandex G17, Avito Senior, Ozon Middle 2, Tinkoff SDE III, VK Senior.
Чеклист “Готов к собесу Middle 2”
Заголовок раздела «Чеклист “Готов к собесу Middle 2”»- Знаю GMP scheduler, work stealing, netpoller изнутри
- Знаю memory allocator (mcache/mcentral/mheap) и GC pacer
- Могу объяснить open-coded defers, panic/recover
- Умею профилировать с pprof + Pyroscope, читать flame graphs
- Знаю escape analysis, могу писать zero-alloc код
- Tuning GOGC, GOMEMLIMIT, GOMAXPROCS в k8s (или automaxprocs)
- PostgreSQL MVCC, EXPLAIN ANALYZE, индексы (включая partial/covering)
- Понимаю CAP, eventual consistency, idempotency
- Реализовал Saga + Outbox в своём pet-проекте
- gRPC: streaming, interceptors, deadlines, retries
- OpenTelemetry: traces, metrics, logs (slog)
- SLO/SLI с error budget
- Kafka: partitions, consumer groups, exactly-once
- DDD: aggregates, bounded context
- Hexagonal architecture в Go
- JWT, OAuth 2.0 / PKCE, Casbin RBAC/ABAC
- Kubernetes: probes, graceful shutdown, HPA
- CI: golangci-lint, gosec, govulncheck, trivy
- Решил 100+ LeetCode Medium на Go
- 5+ system design задач (URL, чат, лента, такси, search)
- 3+ pet-проекта в портфолио, выложены на GitHub
Источники
Заголовок раздела «Источники»Главные использованные источники (всё 2025-2026):
- Go-разработка в 2026: путь middle-разработчика
- Зарплата Go разработчик 2026 — ENIGMA AI
- Реальные задачи с собеседований в Яндекс, VK, Ozon, Сбер (Habr)
- goavengers/go-interview
- Yandex levels.fyi G17
- Go Scheduler Deep Dive 2025 — bytesizego
- Memory Allocation in Go (2025)
- A Guide to the Go Garbage Collector
- Go Memory Efficiency — goperf.dev
- Container-aware GOMAXPROCS — Go Blog
- The Go Memory Model
- Pyroscope Go push docs — Grafana
- Saga Pattern in Go (2025)
- OpenTelemetry Go docs
- Kafka vs RabbitMQ vs NATS (2026)
- Собеседование Go-разработчика — Yandex Practicum
- Go 1.25 Upgrade Guide — Leapcell
- Connect RPC introduction
- OAuth 2.0 / OIDC best practices 2025
- Detecting goroutine leaks (Antonz)
- Hexagonal Architecture in Go (Glukhov)
- Apache Casbin
- Pixie eBPF docs
- DDD in Go (Geison)
- PostgreSQL MVCC internals (Walia)