Цели модуля

После модуля студент сможет:

  • Объяснить, почему Go подходит для бэкенд-разработки
  • Настроить рабочее окружение (Go, gopls, delve)
  • Инициализировать проект через go mod
  • Понимать структуру go.mod и go.sum
  • Использовать go work для монорепо
  • Запускать задачи через Makefile
  • Понимать стандартные соглашения структуры проекта

1.1 Почему Go для бэкенда и API

Преимущества Go

  1. Простой синтаксис. В Go нет наследования, перегрузки операторов, исключений. Меньше способов сделать одно и то же - меньше споров в команде, легче читать чужой код.

  2. Быстрая компиляция. Go компилируется в нативный бинарник. Время сборки больших проектов - секунды, а не минуты.

  3. Встроенная конкурентность. Горутины и каналы - часть языка, а не библиотека. Запустить 10 000 горутин так же просто, как 10.

  4. Мощная стандартная библиотека. net/http, encoding/json, database/sql - всё из коробки. Для большинства задач не нужны внешние зависимости.

  5. Статическая типизация. Ошибки типов ловятся на этапе компиляции, а не в production.

  6. Кроссплатформенность. Один бинарник работает на Linux, macOS, Windows без установки рантайма.

Где Go особенно силён

  • REST API и микросервисы
  • CLI-утилиты (kubectl, docker, terraform написаны на Go)
  • Сетевые сервисы и прокси
  • Фоновые воркеры и обработчики очередей
  • Cloud-native приложения

1.2 Экосистема Go и инструменты

Обязательные инструменты

go - основной инструмент (компилятор, менеджер пакетов, тестирование).

Установка (Linux):

bash
wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
source ~/.bashrc

Проверка установки:

bash
go version

gopls - language server для автодополнения в IDE.

bash
go install golang.org/x/tools/gopls@latest

delve - отладчик.

bash
go install github.com/go-delve/delve/cmd/dlv@latest

golangci-lint - агрегатор линтеров.

bash
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.60.0

Основные команды go

КомандаНазначение
go mod initИнициализация модуля
go mod tidyСинхронизация зависимостей
go buildСборка бинарника
go runЗапуск без сборки
go testЗапуск тестов
go test -raceТесты с race detector
go vetСтатический анализ
go fmtФорматирование кода
go generateГенерация кода

1.3 Структура Go-проекта

Стандартная структура

Привязка к шаблону проекта: корень проекта.

text
project-root/
├── cmd/                    # Entry points (main.go для каждого бинарника)
│   └── app/
│       └── main.go
├── internal/               # Приватный код проекта
│   ├── app/                # DI-контейнер и сборка модулей
│   ├── config/             # Конфигурация
│   ├── models/             # Доменные модели
│   ├── repository/         # Интерфейсы репозиториев
│   ├── service/            # Бизнес-логика
│   ├── usecase/            # Use Cases
│   ├── adapters/           # Реализации интерфейсов
│   ├── handlers/           # HTTP-хэндлеры
│   └── observability/      # Логи, метрики, трейсинг
├── api/                    # OpenAPI-спецификации
├── migrations/             # SQL-миграции
├── deploy/                 # Terraform, Ansible
├── docker/                 # Dockerfile, docker-compose
├── configs/                # Конфигурационные файлы
├── scripts/                # Вспомогательные скрипты
├── docs/                   # Документация, ADR
├── go.mod                  # Описание модуля
├── go.sum                  # Контрольные суммы зависимостей
├── Makefile                # Автоматизация задач
└── .gitignore

Зачем нужна папка internal

Папка internal - специальная директория в Go. Код внутри неё не может быть импортирован из других модулей. Это защищает внутренние реализации от внешнего использования.

Пример:

  • internal/service - только ваш проект использует эти сервисы
  • pkg/utils - можно вынести, если хотите переиспользовать в других проектах

Зачем нужна папка cmd

В cmd лежит точка входа - функция main. Если у проекта один бинарник:

text
cmd/app/main.go

Если несколько бинарей (как в serverless-версии шаблона):

text
cmd/api/main.go
cmd/git-gateway/main.go
cmd/outbox-relay/main.go

1.4 go mod и зависимости

Инициализация модуля

bash
go mod init github.com/yourname/projectname

Это создаёт файл go.mod:

text
module github.com/yourname/projectname

go 1.23

Добавление зависимостей

bash
go get github.com/jackc/pgx/v5

После этого go.mod обновляется:

text
module github.com/yourname/projectname

go 1.23

require (
    github.com/jackc/pgx/v5 v5.6.0
)

go.sum - что это

go.sum содержит криптографические хеши всех зависимостей. Это защита от supply-chain атак: если хеш не совпадёт при скачивании, сборка упадёт.

Никогда не редактируйте go.sum вручную. Он управляется командами go mod.

Синхронизация зависимостей

bash
go mod tidy

Эта команда:

  • Добавляет зависимости, которые импортируются в коде, но отсутствуют в go.mod
  • Удаляет зависимости, которые есть в go.mod, но не используются
  • Обновляет go.sum

Запускайте go mod tidy после любых изменений импортов.

Проверка зависимостей

bash
go mod verify

Проверяет, что все зависимости в go.sum соответствуют ожидаемым хешам.


1.5 go work (workspaces)

Когда нужен workspace

Если у вас монорепо с несколькими модулями, go work позволяет работать с ними как с одним проектом.

Пример: у вас есть основной сервис и общая библиотека.

text
workspace/
├── service/
│   ├── go.mod
│   └── main.go
├── common/
│   ├── go.mod
│   └── utils.go
└── go.work

Создание workspace

bash
go work init ./service ./common

Файл go.work:

text
go 1.23

use (
    ./service
    ./common
)

Теперь service может импортировать common без публикации в реестр:

text
import "github.com/yourname/common"

Когда использовать

  • Монорепо с несколькими сервисами
  • Разработка библиотеки и её использование одновременно
  • Локальная разработка с несколькими связанными модулями

1.6 Makefile для автоматизации

Привязка к шаблону: Makefile в корне проекта.

Пример Makefile из шаблона

makefile
.PHONY: build test lint run migrate clean

# Сборка бинарника
build:
    go build -o bin/app ./cmd/app

# Запуск тестов с race detector
test:
    go test -race -cover ./...

# Линтинг
lint:
    golangci-lint run ./...

# Запуск сервиса
run:
    go run ./cmd/app serve

# Применение миграций
migrate:
    go run ./cmd/app migrate

# Форматирование кода
fmt:
    gofmt -s -w .

# Очистка артефактов
clean:
    rm -rf bin/

ВАЖНО: В Makefile отступы - это табуляция, а не пробелы. Если использовать пробелы, make выдаст ошибку.

Зачем нужен Makefile

  • Единая точка входа для всех задач
  • Не нужно запоминать длинные команды
  • CI/CD использует те же команды, что и разработчик локально
  • Новый член команды запускает make build и сразу работает

1.7 Стандартные соглашения проекта

Именование пакетов

  • Короткие, без under_score: repo, user, auth
  • Не используйте множественное число: model, а не models (хотя в шаблоне используется models - это допустимое исключение для папки)
  • Не повторяйте имя пакета в именах типов: repo.Repository, а не repo.RepoRepository

Именование файлов

  • Маленькие буквы, без пробелов: repo_service.go
  • Тесты: repo_service_test.go
  • Один тип - один файл (для крупных типов)

Именование переменных

  • Короткие имена для коротких скоупов: i, j, ctx, err
  • Полные имена для длинных скоупов: repository, handler
  • Интерфейсы: Reader, Writer, Closer (суффикс -er)
  • Константы: MaxRetries, DefaultTimeout

Обработка ошибок

В Go нет исключений. Ошибки возвращаются как значения:

go
result, err := doSomething()
if err != nil {
    return fmt.Errorf("do something: %w", err)
}

Это не многословность - это явное управление потоком ошибок.


💱 Компромиссы

  • Стандартная библиотека vs внешние зависимости: net/http и encoding/json закрывают типовые задачи без записей в go.mod, но за специализированным (драйвер pgx) платим ростом графа зависимостей и хешами в go.sum, которые надо обслуживать.
  • internal/ vs pkg/: код в internal/ нельзя импортировать из чужих модулей — приватность по умолчанию, но переиспользовать его в другом проекте без выноса в отдельную библиотеку не выйдет.
  • go work vs версионированные модули: workspace позволяет править сервис и общую библиотеку одновременно без публикации, но локальный граф модулей расходится с тем, что получат потребители по go.mod.
  • Makefile vs сырые команды go: make build даёт единую точку входа, одинаковую у разработчика и в CI, но добавляет слой автоматизации: отступы только табами, цели надо поддерживать.
  • Простота языка vs выразительность: без наследования, перегрузки и исключений меньше способов сделать одно и то же; за читаемость платим ручной сборкой паттернов вроде DI из интерфейсов и композиции.

Практическое задание

Задание 1: Инициализация проекта

  1. Создайте директорию для проекта:
bash
mkdir my-go-project
cd my-go-project
  1. Инициализируйте модуль:
bash
go mod init github.com/yourname/my-go-project
  1. Создайте структуру папок:
bash
mkdir -p cmd/app internal/models internal/service
  1. Создайте файл cmd/app/main.go:
go
package main

import "fmt"

func main() {
    fmt.Println("Hello, Go!")
}
  1. Запустите:
bash
go run ./cmd/app

Задание 2: Добавление зависимости

  1. Добавьте зависимость pgx:
bash
go get github.com/jackc/pgx/v5
  1. Изучите изменения в go.mod и go.sum.

  2. Запустите go mod tidy и объясните, что изменилось.

Задание 3: Makefile

  1. Создайте Makefile с целями build, test, run, fmt.

  2. Запустите make build и убедитесь, что бинарник создан.

  3. Запустите make run.

Задание 4: Исследование шаблона

  1. Откройте go.mod шаблона проекта.

  2. Выпишите все зависимости и объясните назначение каждой.

  3. Откройте Makefile шаблона и объясните каждую цель.

  4. Нарисуйте структуру папок шаблона и объясните назначение каждой.


Quiz "Проверь себя"

1. Зачем нужен go work?
2. Что верно про файл go.sum? — выберите все подходящие
3. Что делает команда go mod tidy?
4. Зачем нужна папка internal?
5. Что произойдёт при выполнении go get github.com/pkg/errors?
6. Что описывает файл go.mod?
7. Что произойдёт при сборке этого кода, если пакет отсутствует в go.mod?
go
package main

import "github.com/pkg/errors"

func main() {
    errors.New("oops")
}
8. Почему в Go нет центрального реестра пакетов как в npm?

✅ Итоги модуля

Студент должен:

  • Установить Go, gopls, golangci-lint
  • Инициализировать проект через go mod init
  • Добавить зависимость через go get
  • Объяснить назначение каждой зависимости в go.mod шаблона
  • Создать Makefile с целями build, test, run
  • Объяснить структуру папок шаблона проекта
  • Ответить на все вопросы Quiz

🔗 Мостик к Модулю 2

Следующий модуль: Модуль 2. Базовые понятия языка - переменные, типы, слайсы, мапы, структуры, методы. Привязка к internal/models/ шаблона.