Roadmap: Go-разработчик уровня Junior (0 → Junior), 2025-2026
Подготовка к собеседованиям в РФ/СНГ. Версия Go: 1.22+ (актуально для 1.24/1.25). Зарплатная вилка Junior в РФ: 100-160k₽ (Москва/СПб до 160k₽, регионы 80-130k₽).
1. Базовый синтаксис и язык
Заголовок раздела «1. Базовый синтаксис и язык»1.1 Типы данных
Заголовок раздела «1.1 Типы данных»Примитивы:
- Целые:
int,int8/16/32/64,uint,uint8/16/32/64,uintptr,byte(=uint8),rune(=int32) - Вещественные:
float32,float64 - Комплексные:
complex64,complex128 - Логический:
bool(true/false) - Строки:
string(immutable, набор байт UTF-8) intзависит от платформы — 32 или 64 бита
Составные:
- Массивы (
[5]int) — фиксированной длины, размер часть типа - Слайсы (
[]int) — динамические, header (ptr, len, cap) - Карты (
map[K]V) — хеш-таблицы - Структуры (
struct) - Каналы (
chan T) - Интерфейсы (
interface{}/anyначиная с 1.18) - Функции как тип
- Указатели (
*T)
Zero values (значения по умолчанию):
int → 0float64 → 0.0bool → falsestring → "" (пустая строка)pointer → nilslice → nil (len=0, cap=0)map → nilchannel → nilinterface→ nilstruct → все поля в zero values1.2 Переменные, константы, iota
Заголовок раздела «1.2 Переменные, константы, iota»// Объявление переменныхvar x int = 10var y = 10 // тип выводитсяz := 10 // short declaration, только внутри функцийvar a, b int = 1, 2
// Константыconst Pi = 3.14const ( StatusOK = 200 StatusBadRequest = 400)
// iota — генератор последовательных значенийconst ( Sunday = iota // 0 Monday // 1 Tuesday // 2 Wednesday // 3)
// iota с битовыми флагамиconst ( Read = 1 << iota // 1 Write // 2 Execute // 4)Важно: := создаёт новую переменную, = присваивает. Минимум одна новая переменная должна быть слева от :=.
1.3 Операторы, условия, циклы
Заголовок раздела «1.3 Операторы, условия, циклы»- Арифметические:
+,-,*,/,% - Сравнения:
==,!=,<,>,<=,>= - Логические:
&&,||,! - Битовые:
&,|,^,<<,>>,&^(AND NOT)
Циклы (только for!):
// Классическийfor i := 0; i < 10; i++ { }
// whilefor x < 10 { x++ }
// бесконечныйfor { break }
// rangefor i, v := range slice { }for k, v := range myMap { }for i, c := range "hello" { } // c — rune, i — байтовый индекс
// Go 1.22+: range по числуfor i := range 10 { } // 0..9Условия:
if err := doSomething(); err != nil { return err}
switch x {case 1, 2, 3: fmt.Println("small")case 4, 5: fmt.Println("medium")default: fmt.Println("other")}
// switch без переменнойswitch {case x < 0: ...case x == 0: ...}
// Type switchswitch v := i.(type) {case int: ...case string: ...}В Go нет while, do-while, тернарного оператора (?:).
1.4 Функции (variadic, defer, named return)
Заголовок раздела «1.4 Функции (variadic, defer, named return)»// Обычная функцияfunc Add(a, b int) int { return a + b }
// Множественный возвратfunc Divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("div by zero") } return a / b, nil}
// Named return valuesfunc Split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return // "naked return"}
// Variadic — переменное число аргументовfunc Sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}Sum(1, 2, 3)slice := []int{1, 2, 3}Sum(slice...) // распаковка
// defer — выполнится при выходе из функции (LIFO)func main() { defer fmt.Println("3") defer fmt.Println("2") defer fmt.Println("1") // выведет: 1, 2, 3 → но в обратном порядке!}
// Функция как значениеadd := func(a, b int) int { return a + b }Подвох: аргументы defer вычисляются сразу, а функция выполняется при возврате:
i := 1defer fmt.Println(i) // выведет 1, не 2i = 21.5 Пакеты, импорты, видимость
Заголовок раздела «1.5 Пакеты, импорты, видимость»- Пакет — каталог с
.goфайлами с одинаковой директивойpackage package main+func main()— исполняемая программа- Видимость: идентификаторы с заглавной буквы (PascalCase) экспортируются, со строчной (camelCase) — приватные для пакета
- Импорт:
import "fmt", групповой импорт через скобки - Алиасы:
import f "fmt", blank import:import _ "package"(для side effects) init()— выполняется при загрузке пакета
package main
import ( "fmt" "net/http" json "encoding/json" // алиас _ "github.com/lib/pq" // только side effects)1.6 Структуры, методы
Заголовок раздела «1.6 Структуры, методы»type User struct { ID int Name string Email string}
// Созданиеu1 := User{1, "Alice", "a@e.com"} // позиционно (не рекомендуется)u2 := User{ID: 1, Name: "Alice"} // именованно (рекомендуется)u3 := &User{Name: "Bob"} // указательu4 := new(User) // указатель с zero values
// Методыfunc (u User) Greet() string { // value receiver return "Hello, " + u.Name}
func (u *User) Rename(name string) { // pointer receiver — может изменить u.Name = name}
// Встраивание (composition)type Admin struct { User // встроенный тип Permissions []string}
a := Admin{User: User{Name: "Eve"}}a.Greet() // методы User промотированы1.7 Указатели
Заголовок раздела «1.7 Указатели»&x— взять адрес переменной*p— разыменовать указатель (получить значение)*T— тип “указатель на T”nil— нулевое значение указателя
x := 10p := &xfmt.Println(*p) // 10*p = 20fmt.Println(x) // 20Когда использовать указатели:
- Нужно мутировать значение (
func (u *User) SetName(...)) - Большая структура (избежать копирования)
- Чтобы отличить “значение не задано” (nil) от zero value
- Реализовать nil-safe методы
Когда НЕ использовать:
- Маленькие immutable значения (int, bool, string)
- Когда мутация нежелательна
- Map, slice, chan уже содержат внутренние указатели
1.8 Массивы и слайсы
Заголовок раздела «1.8 Массивы и слайсы»Массив — фиксированный размер, размер часть типа, передаётся по значению (копируется):
var a [3]int = [3]int{1, 2, 3}b := a // полная копия!Слайс — структура из 3 слов: (ptr, len, cap). Передаётся по значению (копируется header), но указывает на тот же базовый массив.
s := []int{1, 2, 3} // len=3, cap=3s = append(s, 4) // len=4, cap=6 (рост в 2 раза)s2 := s[1:3] // len=2, cap=5 (share underlying array!)s3 := make([]int, 5, 10) // len=5, cap=10Рост capacity при append:
- До 256 элементов — рост ×2
- После 256 — рост постепенно снижается до ×1.25
- Если cap превышен — создаётся новый базовый массив
Подвохи слайсов (ОБЯЗАТЕЛЬНО ЗНАТЬ ДЛЯ СОБЕСА):
// 1. Share underlying arraya := []int{1, 2, 3, 4, 5}b := a[1:3]b[0] = 99// a теперь [1, 99, 3, 4, 5]
// 2. append может или не может изменить оригиналfunc add(s []int) { s = append(s, 999) }x := []int{1, 2, 3}add(x)// x остался [1, 2, 3]! header скопирован, append вернул новый slice
// 3. nil slice vs empty slicevar s1 []int // nil, len=0, cap=0s2 := []int{} // не nil, len=0, cap=0s3 := make([]int,0) // не nil, len=0, cap=0// append работает с nil!
// 4. Memory leak: маленький slice держит большой массивbig := make([]int, 1_000_000)small := big[:3] // small держит весь массив!// решение: small = append([]int(nil), big[:3]...)copy():
src := []int{1, 2, 3}dst := make([]int, 3)n := copy(dst, src) // вернёт min(len(dst), len(src))1.9 Maps
Заголовок раздела «1.9 Maps»m := make(map[string]int)m["a"] = 1v, ok := m["b"] // v=0, ok=false (idiomatic check)delete(m, "a")len(m)
// Литералm2 := map[string]int{"a": 1, "b": 2}
// Итерация (порядок СЛУЧАЙНЫЙ!)for k, v := range m2 { }Внутреннее устройство (ОБЯЗАТЕЛЬНО ЗНАТЬ):
- Хеш-таблица с separate chaining через overflow buckets
- Каждый bucket хранит 8 пар key-value
- При load factor > 6.5 — растёт ×2
- Использует AES-хеш на современных CPU
- Порядок итерации намеренно рандомизирован (предотвращает зависимости от порядка)
- В Go 1.24+ — реализация на Swiss Tables (быстрее, меньше памяти)
Нюансы:
- nil map: чтение возвращает zero value, запись → panic
- Запись/чтение в карту НЕ потокобезопасны — нужен
sync.Mutexилиsync.Map - Нельзя взять адрес значения в карте:
&m["key"]— ошибка компиляции - Ключом может быть любой comparable тип (нельзя slice, map, function)
1.10 Strings, runes, bytes
Заголовок раздела «1.10 Strings, runes, bytes»string— readonly слайс байт в UTF-8byte=uint8(один байт)rune=int32(один Unicode code point)len(s)возвращает кол-во байт, не символов!
s := "Привет"fmt.Println(len(s)) // 12 (в кириллице 2 байта на букву)fmt.Println(utf8.RuneCountInString(s)) // 6
// Итерация по байтамfor i := 0; i < len(s); i++ { fmt.Printf("%x ", s[i]) }
// Итерация по руническим символамfor i, r := range s { fmt.Printf("%d:%c\n", i, r)}
// Конвертацииb := []byte(s) // в байтыr := []rune(s) // в руныs2 := string(r) // обратно
// Конкатенацияresult := strings.Builder{}result.WriteString("hello")result.WriteString(" world")final := result.String()Подвох: s[0] — это байт, не символ. Для кириллицы s[0] будет половиной буквы.
1.11 Error handling
Заголовок раздела «1.11 Error handling»Error — это интерфейс:
type error interface { Error() string}Идиоматический подход:
func ReadFile(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read %s: %w", path, err) // wrap! } return data, nil}Создание ошибок:
errors.New("not found")fmt.Errorf("user %d not found", id)fmt.Errorf("read failed: %w", err) // wrapping (Go 1.13+)Sentinel errors:
var ErrNotFound = errors.New("not found")// в вызывающем коде:if errors.Is(err, ErrNotFound) { ... }Custom error types:
type ValidationError struct { Field string Msg string}func (e *ValidationError) Error() string { return fmt.Sprintf("%s: %s", e.Field, e.Msg)}
// Проверка:var ve *ValidationErrorif errors.As(err, &ve) { fmt.Println(ve.Field)}errors.Is vs errors.As:
errors.Is(err, target)— проверка на конкретное значение (sentinel)errors.As(err, &target)— проверка типа + извлечение
panic/recover:
defer func() { if r := recover(); r != nil { log.Printf("recovered: %v", r) }}()panic("something bad")Использовать panic только в неисправимых ситуациях — обычные ошибки возвращать через error.
2. Базовая concurrency
Заголовок раздела «2. Базовая concurrency»2.1 Goroutines
Заголовок раздела «2.1 Goroutines»- Лёгкие потоки (~2-4 KB стек, растёт динамически)
- Управляются рантаймом Go, не ОС
- Запуск:
go funcCall() - Модель: G (goroutine), M (machine = OS thread), P (processor = scheduler context)
- При блокирующем системном вызове M отвязывается от P, рантайм создаёт/берёт нового M
go func() { fmt.Println("hello from goroutine")}()Отличие от потоков:
| Goroutine | Thread (OS) |
|---|---|
| ~2 KB старт | ~1-8 MB |
| Стек растёт | Фикс. размер |
| Управляется Go | Управляется ОС |
| Контекст ~ns | Контекст ~µs |
| Можно запустить млн | Тысячи max |
2.2 Channels
Заголовок раздела «2.2 Channels»ch := make(chan int) // небуферизованный (синхронный)ch := make(chan int, 10) // буферизованный (асинхронный до cap)
ch <- 42 // отправкаv := <-ch // приёмv, ok := <-ch // ok=false если канал закрыт и пустclose(ch) // закрытиеПоведение каналов (ВАЖНО):
| Операция | nil chan | open chan (empty) | open chan (full) | closed chan |
|---|---|---|---|---|
| send | блокируется навсегда | пишет | блокируется | panic! |
| receive | блокируется навсегда | блокируется | читает | возвращает zero, ok=false |
| close | panic! | OK | OK | panic! |
Принципы:
- Закрывать должен отправитель, не получатель
- Не закрывать канал из нескольких горутин (паника)
- Чтение из закрытого канала всегда возвращает zero value
2.3 select
Заголовок раздела «2.3 select»select {case v := <-ch1: fmt.Println("from ch1:", v)case ch2 <- 42: fmt.Println("sent to ch2")case <-time.After(time.Second): fmt.Println("timeout")default: fmt.Println("non-blocking")}- Выбирает готовую операцию случайно (если несколько готовы)
default— делает select неблокирующим- Без
defaultи без готовых веток — блокируется - Без веток (
select {}) — deadlock навсегда
2.4 sync.WaitGroup
Заголовок раздела «2.4 sync.WaitGroup»var wg sync.WaitGroupfor i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() // работа }(i)}wg.Wait()Подвох: wg.Add(1) должен быть до go, не внутри горутины!
2.5 sync.Mutex / RWMutex
Заголовок раздела «2.5 sync.Mutex / RWMutex»var mu sync.Mutexmu.Lock()defer mu.Unlock()// критическая секция
var rw sync.RWMutexrw.RLock() // много читателей одновременноv := data[key]rw.RUnlock()
rw.Lock() // один писательdata[key] = vrw.Unlock()- Не копировать! Mutex нельзя копировать после использования (go vet ругается).
- Не лочить дважды (deadlock).
- Используется по значению как поле структуры — но передавать структуру через указатель.
2.6 sync.Once
Заголовок раздела «2.6 sync.Once»var once sync.Onceonce.Do(func() { // выполнится ровно один раз во всех горутинах})2.7 Базовые паттерны concurrency
Заголовок раздела «2.7 Базовые паттерны concurrency»Fan-out — один писатель → N воркеров:
jobs := make(chan int, 100)for w := 1; w <= 3; w++ { go worker(w, jobs)}for j := 1; j <= 5; j++ { jobs <- j }close(jobs)Fan-in — N писателей → один консьюмер (через merge):
func merge(cs ...<-chan int) <-chan int { var wg sync.WaitGroup out := make(chan int) output := func(c <-chan int) { defer wg.Done() for v := range c { out <- v } } wg.Add(len(cs)) for _, c := range cs { go output(c) } go func() { wg.Wait(); close(out) }() return out}Worker pool:
type Job struct { ID int }type Result struct { ID, Output int }
func worker(jobs <-chan Job, results chan<- Result) { for j := range jobs { results <- Result{ID: j.ID, Output: j.ID * 2} }}Pipeline:
func gen(nums ...int) <-chan int { out := make(chan int) go func() { defer close(out) for _, n := range nums { out <- n } }() return out}
func square(in <-chan int) <-chan int { out := make(chan int) go func() { defer close(out) for n := range in { out <- n * n } }() return out}
// Использование: square(gen(1,2,3,4))2.8 Race conditions
Заголовок раздела «2.8 Race conditions»Race возникает, когда минимум 2 горутины обращаются к одной переменной, и хотя бы одна пишет.
Детектор:
go run -race main.gogo test -race ./...go build -raceРешения: sync.Mutex, каналы, sync/atomic.
3. Стандартная библиотека
Заголовок раздела «3. Стандартная библиотека»3.1 fmt
Заголовок раздела «3.1 fmt»fmt.Println("hello", x)fmt.Printf("name=%s age=%d\n", name, age)fmt.Sprintf("%v", x) // format в stringfmt.Errorf("wrap: %w", err)
// Глаголы:%v // значение по умолчанию%+v // структуры с именами полей%#v // Go-синтаксис%T // тип%d // int%s // string%q // строка в кавычках%x // hex%f // float%t // bool%p // указатель%w // wrap error (только в fmt.Errorf)3.2 strings, strconv
Заголовок раздела «3.2 strings, strconv»strings.Contains("hello", "ell")strings.Split("a,b,c", ",")strings.Join([]string{"a","b"}, "-")strings.ReplaceAll(s, "old", "new")strings.ToLower(s), strings.ToUpper(s)strings.TrimSpace(s)strings.HasPrefix(s, "go")
strconv.Itoa(42) // "42"strconv.Atoi("42") // 42, nilstrconv.ParseFloat("3.14", 64)strconv.FormatFloat(3.14, 'f', 2, 64)3.3 time
Заголовок раздела «3.3 time»now := time.Now()later := now.Add(5 * time.Minute)diff := later.Sub(now) // Durationtime.Sleep(100 * time.Millisecond)
now.Format("2006-01-02 15:04:05") // эталонная дата!time.Parse("2006-01-02", "2025-05-21")
// Таймерыt := time.NewTimer(time.Second)<-t.C
// Тикерticker := time.NewTicker(time.Second)defer ticker.Stop()for range ticker.C { ... }3.4 os, io, bufio
Заголовок раздела «3.4 os, io, bufio»data, err := os.ReadFile("file.txt")err = os.WriteFile("out.txt", data, 0644)
f, err := os.Open("file.txt")defer f.Close()
// bufio для построчного чтенияsc := bufio.NewScanner(f)for sc.Scan() { line := sc.Text()}
// os.Argsfmt.Println(os.Args) // [program, arg1, arg2]os.Getenv("HOME")os.Setenv("KEY", "val")os.Exit(1)3.5 encoding/json
Заголовок раздела «3.5 encoding/json»type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email,omitempty"` // не сериализуется если пустой pw string `json:"-"` // лоукерс + неэкспорт = ignore Pass string `json:"-"` // явный ignore}
// Marshal: struct → JSONdata, err := json.Marshal(user)data, err := json.MarshalIndent(user, "", " ") // с отступами
// Unmarshal: JSON → structvar u Usererr := json.Unmarshal(data, &u)
// В map для неизвестной структурыvar m map[string]interface{}json.Unmarshal(data, &m)
// Encoder/Decoder для потоковenc := json.NewEncoder(w)enc.Encode(user)
dec := json.NewDecoder(r)dec.Decode(&user)Подвох: только экспортируемые (PascalCase) поля попадают в JSON.
3.6 net/http
Заголовок раздела «3.6 net/http»Сервер:
func main() { http.HandleFunc("/", helloHandler) http.HandleFunc("/api/users", usersHandler) log.Fatal(http.ListenAndServe(":8080", nil))}
func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, world!")}
// Go 1.22+ — паттерны с методами и wildcardsmux := http.NewServeMux()mux.HandleFunc("GET /users/{id}", getUserHandler)mux.HandleFunc("POST /users", createUserHandler)
func getUserHandler(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") // ...}Middleware:
func logMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start)) })}Клиент:
resp, err := http.Get("https://api.example.com/users")defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)
// С таймаутомclient := &http.Client{Timeout: 5 * time.Second}req, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody))req.Header.Set("Content-Type", "application/json")resp, err := client.Do(req)3.7 context
Заголовок раздела «3.7 context»Используется для cancellation, timeout, и передачи request-scoped значений.
// Базовыеctx := context.Background() // корневойctx = context.TODO() // если не уверен
// Cancellationctx, cancel := context.WithCancel(context.Background())defer cancel()go worker(ctx)cancel() // отменит ctx и всех потомков
// Timeoutctx, cancel := context.WithTimeout(ctx, 5*time.Second)defer cancel()
// Deadlinectx, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Second))
// Values (использовать только для request-scoped данных, не для параметров!)ctx = context.WithValue(ctx, userIDKey, 42)id := ctx.Value(userIDKey).(int)
// Использованиеfunc work(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() // context.Canceled или context.DeadlineExceeded case <-time.After(time.Second): return nil }}Правила:
ctx— всегда первый параметр функции- НЕ хранить в структуре (передавать явно)
- ВСЕГДА вызывать
cancel()(через defer) - Передавать в БД-запросы, HTTP-запросы, etc.
3.8 testing
Заголовок раздела «3.8 testing»// файл math_test.gopackage math
import "testing"
func TestAdd(t *testing.T) { got := Add(2, 3) want := 5 if got != want { t.Errorf("Add(2,3) = %d; want %d", got, want) }}
// Table-driven test (стандарт идиоматический)func TestAdd(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positive", 2, 3, 5}, {"zero", 0, 0, 0}, {"negative", -1, 1, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Add(tt.a, tt.b); got != tt.want { t.Errorf("got %d, want %d", got, tt.want) } }) }}
// Benchmarkfunc BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add(2, 3) }}Запуск:
go test ./... # все тестыgo test -v ./... # verbosego test -run TestAdd ./... # конкретныйgo test -race ./... # race detectorgo test -cover ./... # coveragego test -bench=. ./... # бенчмаркиtestify (популярная сторонняя библиотека):
import "github.com/stretchr/testify/assert"assert.Equal(t, expected, actual)assert.NoError(t, err)assert.Nil(t, obj)4. Инструменты
Заголовок раздела «4. Инструменты»4.1 Go toolchain
Заголовок раздела «4.1 Go toolchain»go mod init github.com/user/project # создать модульgo mod tidy # подчистить зависимостиgo mod download # скачатьgo get github.com/foo/bar@v1.2.3 # добавить/обновитьgo get -u ./... # обновить всё
go run main.go # запускgo build # сборкаgo build -o myapp ./cmd/server # с именемgo install # build + поместить в $GOPATH/bin
go test ./... # тестыgo test -v -race -cover ./...
go vet ./... # статанализgofmt -w . # форматированиеgo fmt ./...goimports -w . # + сортировка импортов
go doc fmt.Println # документацияgo env # переменные окруженияgo.mod пример:
module github.com/user/myapp
go 1.22
require ( github.com/gin-gonic/gin v1.10.0 github.com/stretchr/testify v1.9.0)go.sum — хеши версий, не редактировать руками.
4.2 IDE
Заголовок раздела «4.2 IDE»GoLand (JetBrains, платный):
- Лучшая поддержка Go
- Встроенный отладчик, рефакторинг
- Студенческая лицензия бесплатно
VS Code + Go extension (бесплатно):
- Расширение
golang.go - Использует
gopls,delve - Меньше “из коробки”, но настраивается
4.3 Линтеры
Заголовок раздела «4.3 Линтеры»golangci-lint v2 (актуальный стандарт):
brew install golangci-lintgolangci-lint run ./...golangci-lint fmt # форматер v2Конфиг .golangci.yml:
version: "2"linters: default: none enable: - errcheck - staticcheck - govet - ineffassign - gosimple - revive - gosec - misspell - unused4.4 Отладчик
Заголовок раздела «4.4 Отладчик»Delve (dlv):
go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug ./cmd/server # запуск(dlv) break main.main # точка останова(dlv) continue(dlv) next # next line(dlv) step # step in(dlv) print x # значение переменной(dlv) goroutines(dlv) bt # стекУдобнее всего отлаживать в GoLand или VS Code (через дебаггер).
4.5 Прочие полезности
Заголовок раздела «4.5 Прочие полезности»pprof— профайлинг CPU/памятьair— hot reload для разработкиgodoc— документацияsqlc— генерация Go из SQLmockery/mockgen— генерация моков
5. Базовые знания вне Go
Заголовок раздела «5. Базовые знания вне Go»5.1 Git
Заголовок раздела «5.1 Git»Must-know команды:
git init / git clone <url>git statusgit add . / git add file.gogit commit -m "feat: add user endpoint"git push origin maingit pull --rebase
# Веткиgit branchgit checkout -b feature/auth # или git switch -cgit switch main
# Merge / rebasegit merge feature/authgit rebase main
# Конфликты — разрешать в редакторе, потомgit add <file>git rebase --continue
# Историяgit log --oneline --graphgit diffgit diff --stagedgit stashgit stash pop
# Откатитьgit reset HEAD~1 # отменить коммит, сохранить файлыgit reset --hard HEAD~1 # ОПАСНО: уничтожить измененияgit revert <commit> # создать обратный коммитЗнать концептуально:
- HEAD, branches, tags
- Fast-forward vs merge commit
- Rebase vs merge (когда что)
- Что такое pull request / merge request
- Conventional Commits (
feat:,fix:,docs:,chore:)
5.2 SQL (базовый)
Заголовок раздела «5.2 SQL (базовый)»-- SELECTSELECT id, name FROM users WHERE age > 18 ORDER BY name LIMIT 10;
-- JOINSELECT u.name, o.amountFROM users uINNER JOIN orders o ON o.user_id = u.idWHERE o.created_at > '2025-01-01';
-- LEFT JOIN, RIGHT JOIN, INNER JOIN, FULL OUTER JOIN — понимать разницу
-- AggregationSELECT user_id, COUNT(*), SUM(amount), AVG(amount)FROM ordersGROUP BY user_idHAVING SUM(amount) > 1000;
-- DMLINSERT INTO users (name, email) VALUES ('Alice', 'a@e.com');UPDATE users SET name = 'Bob' WHERE id = 1;DELETE FROM users WHERE id = 1;
-- DDLCREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE);
-- ИндексыCREATE INDEX idx_users_email ON users(email);Должен знать:
- INNER vs LEFT/RIGHT/OUTER JOIN
- Что такое индекс, когда помогает (поиск, JOIN), когда тормозит (вставки)
- Транзакции (BEGIN, COMMIT, ROLLBACK)
- Уровни изоляции (READ COMMITTED, REPEATABLE READ, SERIALIZABLE) — базово
- Primary key, foreign key, unique constraint
- N+1 проблема
- EXPLAIN / EXPLAIN ANALYZE — базовое понимание
5.3 HTTP / REST
Заголовок раздела «5.3 HTTP / REST»- Методы: GET (получить), POST (создать), PUT (заменить), PATCH (обновить), DELETE (удалить)
- Статус-коды:
- 2xx: 200 OK, 201 Created, 204 No Content
- 3xx: 301, 302, 304
- 4xx: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Entity, 429 Too Many Requests
- 5xx: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable
- Headers:
Content-Type,Authorization,Accept,User-Agent - Идемпотентность (GET/PUT/DELETE — да, POST — нет)
- REST принципы: ресурсы, stateless, единообразный интерфейс
- JSON, query params (
?name=alice&age=20), path params (/users/{id}), request body - Cookies, JWT (что это, как используется)
5.4 JSON
Заголовок раздела «5.4 JSON»{ "id": 1, "name": "Alice", "tags": ["admin", "user"], "address": {"city": "Moscow"}, "active": true, "score": null}Знать: типы (string, number, boolean, null, object, array), синтаксис, отличия от YAML/XML.
5.5 Linux базовый
Заголовок раздела «5.5 Linux базовый»# Навигацияpwd, cd, ls, ls -la, cd ..
# Файлыcat file, less file, head, tail, tail -f log.txtcp, mv, rm, rm -rftouch, mkdir, mkdir -p
# Поискfind . -name "*.go"grep "pattern" file.txtgrep -r "TODO" .
# Праваchmod +x script.shchown user:group file
# Процессыps auxtop, htopkill -9 <pid>nohup ./app &
# Сетьcurl, wgetping, netstat -tulpn, ss -tulpn
# Pipes & redirectscmd1 | cmd2cmd > filecmd >> filecmd 2>&1cmd < file
# vim/nano (базовый редактор)# ssh, scpssh user@hostscp file.txt user@host:/path6. Типичные вопросы на собеседовании Junior Go (50+ вопросов с ответами)
Заголовок раздела «6. Типичные вопросы на собеседовании Junior Go (50+ вопросов с ответами)»Базовый синтаксис
Заголовок раздела «Базовый синтаксис»1. Что такое zero value и какие zero values у разных типов?
Значение по умолчанию. int→0, string→"", bool`→false, pointer/slice/map/chan/interface/func→nil.
2. Разница между var x int = 0, x := 0, var x int?
Первое и второе создают переменную с значением 0. Третье — то же, но без явной инициализации (zero value). := работает только внутри функций.
3. Что такое iota?
Идентификатор-счётчик в const блоке, начинается с 0 и увеличивается на 1 на каждой строке.
4. В чём разница между массивом и слайсом?
Массив имеет фиксированную длину, длина — часть типа, передаётся по значению (полная копия). Слайс — это структура (ptr, len, cap) поверх массива, динамический, при передаче копируется header, но базовый массив общий.
5. Что внутри слайса (slice header)? Три поля: указатель на underlying array, длина (len), вместимость (cap). На 64-битной системе размер 24 байта.
6. Как растёт capacity слайса при append? До 256 — ×2. После — постепенно снижается до ×1.25. Если cap превышен — создаётся новый массив, данные копируются.
7. Чем отличается nil slice от empty slice?
var s []int — nil slice, s == nil → true. s := []int{} или make([]int, 0) — не nil. Оба имеют len=0, cap=0. append работает с nil.
8. Подвох с append:
func modify(s []int) { s = append(s, 999) }x := []int{1,2,3}modify(x)// x НЕ изменился, потому что header скопирован, и если cap был превышен, append создал новый массив9. Что такое map в Go? Как устроен? Хеш-таблица с separate chaining через overflow buckets. Каждый bucket хранит 8 пар. При load factor > 6.5 удваивается. С Go 1.24 — Swiss Tables.
10. Почему порядок итерации map случайный? Намеренно — чтобы программисты не зависели от порядка (порядок не часть спеки). Реализовано через случайный стартовый индекс.
11. Как проверить, есть ли ключ в map?
v, ok := m["key"]if ok { /* есть */ }12. Что такое rune и byte?
byte = alias для uint8 (1 байт). rune = alias для int32, представляет Unicode code point. Строка — слайс байт, но в range — итерация по рунам.
13. Почему len("Привет") возвращает 12, а не 6?
Потому что len возвращает кол-во байт. Кириллица в UTF-8 — 2 байта на букву. Для подсчёта символов: utf8.RuneCountInString(s).
14. Что такое pointer в Go? Когда использовать?
Адрес в памяти. &x — взять адрес, *p — разыменовать. Используем для (1) мутации значения, (2) избежать копирования больших структур, (3) реализации nil-safe методов.
15. Разница между value receiver и pointer receiver у методов? Value — копия структуры, метод не может изменить оригинал. Pointer — указатель, может изменить. Если структура большая или мутирующая — pointer receiver. Внутри одного типа лучше быть консистентным.
16. Что такое interface в Go?
Тип, описывающий набор методов. Реализация неявная (duck typing): если тип реализует все методы интерфейса — он его удовлетворяет. Никакого implements.
17. Чем интерфейс отличается от структуры? Структура — данные с полями. Интерфейс — контракт поведения (методы). Структура реализует интерфейс, имея его методы.
18. Что такое empty interface interface{} / any?
Интерфейс без методов — все типы его реализуют. Используется когда тип неизвестен заранее. С Go 1.18 есть алиас any.
19. Что такое type assertion и type switch?
v, ok := i.(string) // assertionswitch v := i.(type) { // switchcase int: ...case string: ...}20. Объясните typed nil interface gotcha:
var p *MyError = nilvar err error = pfmt.Println(err == nil) // false!Интерфейс = (тип, значение). Тип установлен (*MyError), значит интерфейс не nil, даже если значение nil. Поэтому возвращать стоит nil напрямую, а не nil-pointer типизированный.
21. Что такое embedding (встраивание) и зачем? Включение одного типа в другой без явного поля → composition. Методы и поля встроенного типа промотируются. Заменяет наследование.
22. Как реализовано ООП в Go? Нет классов, наследования, перегрузки. Есть: структуры (данные), методы (привязаны к типу), интерфейсы (полиморфизм), embedding (композиция). Используется композиция вместо наследования.
23. Что такое defer и как он работает?
Откладывает вызов функции до выхода из текущей функции. Несколько defer — LIFO. Аргументы вычисляются при объявлении, функция — при выходе. Используется для cleanup: defer f.Close(), defer mu.Unlock().
24. Что такое panic и recover?
panic — прерывает выполнение, раскручивает стек, выполняя defer. recover (только в defer) — перехватывает panic. Использовать только в исключительных случаях, не как замену error.
25. Что такое named return values?
func div(a, b int) (result int, err error) { result = a / b return // вернёт result, err}Может улучшать читаемость, но не злоупотреблять.
Concurrency
Заголовок раздела «Concurrency»26. Что такое goroutine?
Лёгкий поток (~2 KB), управляемый рантаймом Go (модель G-M-P). Запуск: go funcCall(). Можно запустить миллионы.
27. Чем goroutine отличается от потока ОС? Goroutine — пользовательский уровень, маленький стек (динамически растёт), переключение в Go-рантайме. Потоки ОС — большие стеки, переключение через ядро.
28. Что такое канал? Зачем нужен? Типизированная труба для передачи данных между горутинами. “Не общайтесь через память — общайтесь через каналы” (motto). Синхронизирует горутины.
29. Разница между буферизованным и небуферизованным каналом?
Небуферизованный (make(chan int)) — отправитель блокируется до приёма. Буферизованный (make(chan int, N)) — блокируется только когда буфер полон.
30. Что произойдёт при чтении из закрытого канала? При записи?
Чтение — вернёт zero value, ok=false. Запись — panic! Закрытие закрытого — panic!
31. Что произойдёт при отправке/приёме на nil-канал? Блокируется навсегда (deadlock).
32. Зачем нужен select?
Ожидать несколько канальных операций одновременно. Выбирает случайно среди готовых. С default — неблокирующий.
33. Как sync.WaitGroup работает?
Add(n) — увеличить счётчик, Done() — уменьшить (в горутине), Wait() — ждать, пока счётчик не станет 0. Add должен быть до go, иначе race.
34. Mutex vs RWMutex? Mutex — один владелец (читает или пишет). RWMutex — много читателей ИЛИ один писатель. Используем RWMutex если читателей сильно больше.
35. Что такое race condition? Как обнаружить?
Когда 2+ горутины обращаются к общей переменной, и хотя бы одна пишет, без синхронизации. Обнаружение: go run -race, go test -race.
36. Что такое deadlock? Приведите пример. Все горутины ждут друг друга, никто не двигается. Пример: 2 горутины ждут друг от друга чтение из небуферизованного канала.
37. Подвох с loop variable и closures (до Go 1.22):
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() // печатает 5 пять раз!}Решение (до 1.22): i := i внутри цикла. В Go 1.22+ — каждая итерация имеет свою переменную, проблема исчезла.
38. Что такое context и зачем? Передача дедлайнов, сигналов отмены и request-scoped значений через цепочку вызовов. Обязательно использовать в HTTP/DB-запросах.
39. Что такое утечка горутины и как её избежать? Горутина никогда не завершается (например, ждёт чтения из канала, в который никто не пишет). Решение: всегда иметь способ её остановить (context, close канала).
Стандартная библиотека
Заголовок раздела «Стандартная библиотека»40. Как сериализовать struct в JSON?
data, err := json.Marshal(user)Только экспортируемые поля. Контроль через теги: json:"name,omitempty".
41. Что делает json:"-"?
Игнорирует поле при сериализации/десериализации.
42. Что делает omitempty?
Не включает поле в JSON, если оно zero value (пустая строка, 0, false, nil).
43. Как создать HTTP-сервер?
http.HandleFunc("/", handler)http.ListenAndServe(":8080", nil)44. Что такое middleware и как реализовать в Go?
Функция, оборачивающая http.Handler, добавляющая cross-cutting concerns (логи, auth, recover).
45. Как написать тест в Go?
Файл _test.go, функция func TestXxx(t *testing.T). Запуск: go test ./....
46. Что такое table-driven test? Множество кейсов в виде структуры, перебор циклом. Идиоматичный паттерн Go.
Инструменты и Git
Заголовок раздела «Инструменты и Git»47. Что такое go.mod и go.sum?
go.mod — описание модуля и зависимостей. go.sum — хеши версий для проверки целостности.
48. Как обновить зависимость?
go get -u github.com/foo/bar или go get github.com/foo/bar@v1.2.3. Затем go mod tidy.
49. В чём разница между go build и go run?
go build создаёт исполняемый файл. go run компилирует и сразу запускает (без сохранения бинарника).
50. Что делает go vet?
Статический анализ — ищет подозрительные конструкции (Printf-формат, копирование Mutex, и т.п.). Запускать в CI.
Базовые знания
Заголовок раздела «Базовые знания»51. В чём разница между PUT и PATCH? PUT — полная замена ресурса. PATCH — частичное обновление.
52. Что такое индекс в SQL? Когда нужен? Структура данных (обычно B-tree) для быстрого поиска. Ускоряет SELECT/JOIN по индексированному полю, замедляет INSERT/UPDATE.
53. Что такое JOIN? Какие бывают? Соединение таблиц по условию. INNER (пересечение), LEFT (всё из левой + совпадения), RIGHT, FULL OUTER.
54. Что такое транзакция?
Атомарный набор операций. ACID: Atomicity, Consistency, Isolation, Durability. BEGIN ... COMMIT/ROLLBACK.
55. В чём разница между git merge и git rebase?
Merge — создаёт merge-коммит, сохраняя историю обеих веток. Rebase — переписывает коммиты текущей ветки поверх целевой, история линейная. Не делать rebase публичной ветки.
Практические задачи
Заголовок раздела «Практические задачи»Задача 1. Реализовать функцию, возвращающую пересечение двух слайсов:
func Intersect(a, b []int) []int { set := make(map[int]bool, len(a)) for _, v := range a { set[v] = true } var result []int for _, v := range b { if set[v] { result = append(result, v); delete(set, v) } } return result}Задача 2. Worker pool: распараллелить обработку N задач K воркерами.
Задача 3. Реализовать rate limiter (например, token bucket).
Задача 4. Развернуть строку с учётом UTF-8 (через []rune).
Задача 5. Реализовать “потокобезопасный счётчик” (counter с инкрементом из множества горутин).
Задача 6. FizzBuzz, но с использованием каналов.
Задача 7. Дано: канал чисел. Напишите функцию, которая считает сумму всех чисел из канала, пока он не закроется.
Задача 8. Реализовать LRU cache (простую версию).
Задача 9. “Бэкенды ненадёжны”: реализовать Balancer (round-robin) с retry и поддержкой ctx.Done().
Задача 10. Распарсить JSON в структуру и сделать HTTP-сервер с endpoints для CRUD.
7. Pet-проекты для портфолио Junior
Заголовок раздела «7. Pet-проекты для портфолио Junior»Цель — показать понимание Go, REST API, БД, тестов, Docker и Git.
7.1 To-Do REST API (база) ⭐ обязательно
Заголовок раздела «7.1 To-Do REST API (база) ⭐ обязательно»- CRUD через HTTP (GET/POST/PUT/DELETE)
- Хранение в PostgreSQL (через
database/sql+ pgx) - Структура:
cmd/,internal/,pkg/ - Unit + integration тесты
- Dockerfile + docker-compose
- README с описанием API
Стек: Go + Gin/Chi + PostgreSQL + Docker
7.2 URL Shortener (классика)
Заголовок раздела «7.2 URL Shortener (классика)»- Принимает длинный URL, возвращает короткий
- Редирект по коду
- Кэш в Redis
- Статистика переходов
- Аутентификация (опционально)
Стек: Go + chi/Gin + PostgreSQL + Redis
7.3 Telegram-бот
Заголовок раздела «7.3 Telegram-бот»- Что-то полезное: погода, переводчик, заметки, валюта
- Использование
github.com/go-telegram-bot-api/telegram-bot-api - Команды, inline-клавиатуры
- Хранение состояния пользователя в БД
7.4 CLI-утилита
Заголовок раздела «7.4 CLI-утилита»- Например: парсер логов, генератор паролей, ASCII-art, конвертер валют
- Использование
cobraилиflag - Цветной вывод
- Тесты
7.5 Сервис с конкурентностью
Заголовок раздела «7.5 Сервис с конкурентностью»Что-то, где Go блистает: web crawler (с rate limit), пинговалка хостов, генератор отчётов из API нескольких источников. Демонстрирует goroutines, channels, context.
7.6 Чат / WebSocket-приложение
Заголовок раздела «7.6 Чат / WebSocket-приложение»- Real-time чат через WebSocket (
gorilla/websocket) - Несколько комнат
- Простая аутентификация
- Демонстрирует concurrency
Что обязательно должно быть в каждом проекте:
Заголовок раздела «Что обязательно должно быть в каждом проекте:»- README на английском (структура, как запустить, скриншоты/curl-примеры)
Makefileс командамиmake build,make test,make runDockerfile(multi-stage build).gitignore,.golangci.yml- Тесты (хотя бы unit для бизнес-логики)
- CI через GitHub Actions (хотя бы
go test+golangci-lint) - Структура папок:
cmd/,internal/,pkg/,migrations/,configs/ - Commit history — осмысленная, не “fix” × 50
8. Ресурсы для обучения (2025-2026)
Заголовок раздела «8. Ресурсы для обучения (2025-2026)»8.1 Книги
Заголовок раздела «8.1 Книги»Обязательные:
- Алан Донован, Брайан Керниган — «Язык программирования Go» (есть русский перевод, ISBN 978-5-8459-2051-5). Классика, must-read. Местами устарела (до generics), но фундамент.
- Jon Bodner — Learning Go (2-е изд. 2024). Самая актуальная книга для входа в Go, охватывает generics, modules, error handling 1.13+.
- William Kennedy — Ultimate Go Notebook / Go in Action. Идиомы Go, конкурентность.
Дополнительно: 4. Mat Ryer — Writing An Interpreter In Go — для глубокого понимания. 5. Teiva Harsanyi — 100 Go Mistakes and How to Avoid Them ⭐ — очень рекомендуется к собесу. 6. Katherine Cox-Buday — Concurrency in Go — для углубления в каналы/горутины.
8.2 Бесплатные онлайн-ресурсы
Заголовок раздела «8.2 Бесплатные онлайн-ресурсы»- A Tour of Go — https://go.dev/tour/ (must-do при старте)
- Effective Go — https://go.dev/doc/effective_go (после Tour)
- Go by Example — https://gobyexample.com/ (быстрый справочник)
- Go Wiki — https://go.dev/wiki/
- roadmap.sh/golang — структурированный roadmap
- Exercism Go track — задачи с менторством
- gophercises.com — практические задания
8.3 Курсы
Заголовок раздела «8.3 Курсы»Платные/полуплатные (RU):
- Stepik — Golang. Уровень 1-3 (бесплатно).
- Stepik — Горутины и каналы в Go: задачи с собеседований и паттерны (Тузов).
- Yandex Practicum — Go-разработчик (платный, с трудоустройством).
- OTUS — Golang Developer / Backend.
- Slurm — Golang для инженеров.
- Balun Courses — Golang курсы.
Бесплатные стажировки/курсы (RU):
- Route 256 от Ozon (route256.ozon.ru) — бесплатный курс для студентов 3+ курса.
- VK Стажировка Go-разработчик.
- Школа разработки Тинькофф (T-Образование).
Английский:
- boot.dev — Learn Go, Learn Backend (платно, но качественно).
- Frontend Masters — Go Fundamentals (Brian Holt).
- Udemy — Go: The Complete Developer’s Guide (Stephen Grider).
8.4 YouTube каналы (русский)
Заголовок раздела «8.4 YouTube каналы (русский)»- Николай Тузов — Golang (https://www.youtube.com/@nikolay_tuzov) — лучший русский ютубер по Go. От основ до продвинутого.
- Юра Семёнов / Учитель Java — есть треды по Go.
- Технотрек / Технострим Mail.ru — лекции от VK.
- Образовательные плейлисты от Ozon, Avito, Тинькофф.
8.5 YouTube (английский)
Заголовок раздела «8.5 YouTube (английский)»- Anthony GG
- TutorialEdge (Elliot Forbes)
- Tech with Tim
- Jon Calhoun (calhoun.io)
- GopherCon talks (официальный канал)
- Just for Func (Francesc Campoy) — продвинутое
- Ardan Labs (Bill Kennedy)
8.6 Telegram-каналы / чаты (русский)
Заголовок раздела «8.6 Telegram-каналы / чаты (русский)»- @golang_google — крупный канал по Go
- @goproglib — Библиотека Go-разработчика (23k+)
- @gophernews — 4gophers
- @gogolang — новости и материалы
- @go_perf — Go performance
- @ntuzov — Николай Тузов (анонсы)
- Чаты: Gopher Club, SPb Go, Golang_RU
8.7 Сайты со статьями
Заголовок раздела «8.7 Сайты со статьями»- Habr (RU) — теги “golang”, “go” — много годных статей и переводов
- Medium (тег “go”) — много инди-блогов
- dev.to/golang
- gobyexample.com
- eli.thegreenplace.net (Эли Бендерский, глубокие посты)
- dave.cheney.net (Dave Cheney, идиомы и нюансы)
- github.com/avelino/awesome-go (большой список библиотек)
- github.com/goavengers/go-interview — вопросы для собеса
8.8 Документация
Заголовок раздела «8.8 Документация»- pkg.go.dev — официальная документация всех пакетов
- go.dev/doc/ — официальные туториалы
- go.dev/blog/ — официальный блог Go (релизы и важные темы)
9. Что НЕ требуется от Junior (но требуется от Middle/Senior)
Заголовок раздела «9. Что НЕ требуется от Junior (но требуется от Middle/Senior)»Чтобы понять “потолок” уровня и не учить лишнее, вот что не требуется от Junior, но потребуется выше:
Не требуется Junior:
Заголовок раздела «Не требуется Junior:»- Глубокое понимание планировщика Go (G-M-P, work stealing, syscall handling) — на собесе Middle/Senior
- Внутренности GC (tricolor mark-and-sweep, write barrier, фазы GC, тюнинг GOGC/GOMEMLIMIT)
- Escape analysis в деталях, профайлинг через pprof, оптимизация аллокаций
- Тонкие нюансы memory model Go, happens-before
- Внутренняя реализация каналов (hchan, runtime/chan.go)
- sync.Pool, atomic операции, lock-free структуры данных
- Микросервисная архитектура: service mesh, sagas, distributed tracing
- gRPC / protobuf на продакшн-уровне
- Kubernetes глубоко (только базовое понимание контейнеров и k8s от Middle)
- Kafka / RabbitMQ / NATS — паттерны producer/consumer, partition assignment, exactly-once
- Базы данных: репликация (master-slave, multi-master), шардинг, выбор изоляции для конкретной нагрузки
- Тонкости PostgreSQL: MVCC, vacuum, query plans, partitioning
- Redis: лок-механизмы (Redlock), Lua-скрипты, persistence-стратегии
- Метрики/observability: Prometheus + Grafana, OpenTelemetry, structured logging
- DDD, гексагональная архитектура, clean architecture (от Middle уже могут спросить)
- CI/CD pipelines (хотя базовое от Junior уже желательно)
- Авторская архитектура с нуля, мнения о технических решениях
- Глубокие знания сетевых протоколов: TCP handshake, congestion control, QUIC
- Внутренности HTTP/2, HTTP/3, gRPC streaming
- Performance tuning, бенчмарки, профайлинг
- Security: OWASP Top 10 глубоко, SQL-инъекции, JWT-нюансы, TLS-настройки
От Junior достаточно:
Заголовок раздела «От Junior достаточно:»- Уверенный синтаксис, типы данных
- Умение читать чужой код
- Базовая concurrency (горутины, каналы, мьютексы)
- Базовый HTTP-сервер и БД-запросы
- Тесты пишет
- Git: branch/merge/conflict
- Docker базово
- Понимает, чего не знает, и умеет искать в документации
Главное у Junior — не объём знаний, а обучаемость, аккуратность кода, понимание основ и здравый смысл.
10. Реалистичный план изучения с нуля до Junior
Заголовок раздела «10. Реалистичный план изучения с нуля до Junior»Условие: опыт программирования есть (например, базовый Python/JS), 2-3 часа в день. Если ноль опыта — добавить 2-3 недели на основы программирования.
Неделя 1: Установка и основы
Заголовок раздела «Неделя 1: Установка и основы»- Установить Go 1.24+, VS Code + Go extension
- Пройти A Tour of Go полностью (https://go.dev/tour/)
- Прочитать главы 1-2 Donovan & Kernighan
- Базовый синтаксис: переменные, типы, циклы, условия, функции
- Написать 5-10 микро-программ (FizzBuzz, факториал, Фибоначчи, реверс строки, проверка простого числа)
Неделя 2: Composite types
Заголовок раздела «Неделя 2: Composite types»- Массивы, слайсы (углублённо: header, capacity, append-подвохи)
- Maps (внутреннее устройство, итерация)
- Strings vs runes vs bytes
- Структуры, методы, указатели
- Пакеты, видимость
- Donovan & Kernighan главы 3-4
- Решить 15-20 задач на Exercism Go track
Неделя 3: Интерфейсы и error handling
Заголовок раздела «Неделя 3: Интерфейсы и error handling»- Интерфейсы, duck typing, embedding
- Empty interface / any, type assertion, type switch
- Error handling, errors.Is/As, wrapping, custom errors
- defer, panic, recover
- Donovan & Kernighan главы 6-7
- Написать собственный простой пакет с тестами
Неделя 4: Concurrency
Заголовок раздела «Неделя 4: Concurrency»- Goroutines, каналы (буферизованные/нет), select
- sync.Mutex, sync.WaitGroup, sync.Once
- Контекст (context)
- Базовые паттерны: worker pool, fan-in/fan-out, pipeline
- Race condition,
-race - Donovan & Kernighan глава 8
- Решить 10 задач на горутины (есть курс Тузова на Stepik)
- Прочитать “Concurrency in Go” Кэтрин Кокс-Бьюди (хотя бы первые 4 главы)
Неделя 5: Стандартная библиотека и HTTP
Заголовок раздела «Неделя 5: Стандартная библиотека и HTTP»- fmt, strings, strconv, time, os, io, bufio
- encoding/json (теги, omitempty, custom Marshaler)
- net/http: сервер, клиент, middleware
- testing: unit, table-driven, testify
- Pet-проект 1: CLI-утилита (например, парсер логов или конвертер)
Неделя 6: Базы данных + первый веб-проект
Заголовок раздела «Неделя 6: Базы данных + первый веб-проект»- SQL базово: SELECT, INSERT, UPDATE, DELETE, JOIN, индексы
database/sql+pgxдля PostgreSQL- Миграции (
gooseилиmigrate) - Pet-проект 2: To-Do REST API с PostgreSQL
- Использовать Gin или chi
- Тесты с testify
Неделя 7: Инфраструктура
Заголовок раздела «Неделя 7: Инфраструктура»- Docker базово: Dockerfile, docker-compose
- Multi-stage build для Go
- Git углублённо: branches, rebase, merge, conflicts, PR-workflow
- Linux: bash, ssh, основные команды
- Завернуть pet-проект в Docker, поднять через compose
Неделя 8: Concurrency-проект + инструменты
Заголовок раздела «Неделя 8: Concurrency-проект + инструменты»- golangci-lint, gofmt, go vet, делв (dlv)
- Makefile, CI через GitHub Actions
- Pet-проект 3: что-то с concurrency (web crawler / пинговалка / Telegram-бот с обработкой апдейтов)
Неделя 9: Углубление и нюансы
Заголовок раздела «Неделя 9: Углубление и нюансы»- Прочитать “100 Go Mistakes” Harsanyi (хотя бы 30-40 главных)
- Прочитать оба тома вопросов на собес с Habr
- Изучить typed nil, loop variable capture, slice gotchas
- Generics базово (когда использовать, когда нет)
Неделя 10: Подготовка к собеседованию
Заголовок раздела «Неделя 10: Подготовка к собеседованию»- Решить 100 вопросов с собеса (Habr, GitHub goavengers/go-interview)
- Решить 20-30 задач LeetCode на Go (easy/medium)
- Повторить SQL, HTTP, REST, JSON
- Прорешать “5 заданий с собеса” с tproger
- Записать собственное mock-интервью (записать и пересмотреть)
Неделя 11: Полировка портфолио
Заголовок раздела «Неделя 11: Полировка портфолио»- Доделать README в каждом pet-проекте (английский, скриншоты, curl-примеры)
- Добавить CI, тесты, golangci-lint
- Сделать pinned-репозитории на GitHub
- Резюме на hh.ru, habr career, LinkedIn
Неделя 12: Откликаться и собеседоваться
Заголовок раздела «Неделя 12: Откликаться и собеседоваться»- Откликаться на 5-10 вакансий в день (Junior Go / Стажёр Go)
- Стажировки: Yandex, VK, Ozon (Route 256), Тинькофф, Сбер, Avito
- Готовиться к каждому собесу: смотреть стек компании заранее
- Записывать вопросы с собесов, разбирать ошибки
- Не сдаваться — Junior Go это узкий рынок, нужно 30-50+ откликов
Бонус: чек-лист “к собеседованию готов”
Заголовок раздела «Бонус: чек-лист “к собеседованию готов”»Можешь объяснить за 1-2 минуты, без подглядывания:
- Что такое слайс, его header, почему
appendможет вернуть новый массив - Чем отличается nil slice от empty slice
- Как устроен map внутри, почему случайный порядок
- Что такое rune, почему
len("Привет")= 12 - Когда использовать pointer receiver, когда value
- Что такое interface, как работает duck typing
- Typed nil gotcha (interface != nil но значение nil)
- Что такое горутина, отличие от потока
- Что произойдёт при send/receive/close на nil/closed канале
- Что делает select, когда блокируется
- Когда WaitGroup, когда Mutex, когда канал
- Что такое context и зачем
- defer order (LIFO), вычисление аргументов
- errors.Is vs errors.As, wrapping через %w
- Race condition и как обнаружить (-race)
- Loop variable capture (и фикс в Go 1.22)
- Что в go.mod / go.sum
- Как написать table-driven test
- Базовый CRUD REST API с PostgreSQL и Docker
- Git: rebase vs merge, fix conflict
- SQL: JOIN типы, что такое индекс
- HTTP: коды, методы, идемпотентность
Источники
Заголовок раздела «Источники»- Вопросы на собеседовании по Go 2026: от Junior до Senior — ENIGMA AI
- Собеседование Golang разработчика (теоретические вопросы), Часть I — Хабр
- Собеседование Golang разработчика, Часть II — Хабр
- Go — 100 вопросов/заданий с собеседований — Хабр
- Шпаргалка для собеса по GoLang — Хабр
- 5 заданий с собеседования на Junior Golang — tproger
- Реальные задачи с собеседований в Яндекс, VK, Ozon, Сбер — Хабр
- Вопросы по Go на собеседовании — Kata.academy
- goavengers/go-interview — GitHub
- Зарплата Junior Go-разработчика — hh.ru
- Зарплата Go-разработчика 2026 — ENIGMA AI
- Чего хотят от Go-разработчиков в середине 2025 года — Хабр
- Go Roadmap 2025 — GeeksforGeeks
- Learn to become a Go developer — roadmap.sh
- Tricky Golang interview questions: Slice Header — dev.to
- Tricky Golang interview questions: interface == nil — dev.to
- Slice Internals in Go — themsaid.com
- Visual Guide to Go Maps: Hash Tables — sazak.io
- Go Generics: Use Cases and Patterns — glukhov.org
- Go 1.24 Release Notes — go.dev
- Go 1.22’s Loop Variable Fix — Medium/Krzyś
- Go Concurrency Patterns: Worker Pool, Fan-In/Fan-Out & Pipeline — Medium
- Go Error Handling Best Practices — Datadog
- Making and Using HTTP Middleware in Go — alexedwards.net
- Go Database Patterns: GORM, sqlx, and pgx Compared — dasroot.net
- golangci-lint v2 docs
- Welcome to golangci-lint v2 — ldez.github.io
- Top 6 Go Web Frameworks for 2025 — Medium
- Что читать Golang-разработчику. 7 главных книг — Хабр
- Николай Тузов — Golang YouTube
- Route 256 (Ozon) — Go Developer Junior
- Стажировка VK — Go Developer
- How to Avoid Race Conditions in Go — Medium
- Top 10 Golang Project Ideas with Source Code — GeeksforGeeks
- How Garbage Collector Works in Go — allegro.tech