День из жизни Go-разработчика: код, микросервисы и тишина в продакшене

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Вступление: Мифы и реальность Go-программиста

В IT-сообществе бытует два полярных мнения о работе Go-разработчика. Одни говорят, что это рай для перфекциониста: строгая типизация, отсутствие наследования классов и молниеносная компиляция. Другие — что это бесконечные if err != nil и борьба с дженериками (хотя их уже добавили).

Где правда? Как на самом деле проходит рабочий день Go разработчика в средней компании, которая пишет высоконагруженные микросервисы? Сегодня я открою дверь в свою реальность. Никакой воды — только код, тулы и реальные кейсы.

Я работаю в продуктовой компании, где Golang — основной язык бэкенда. Наш стек: gRPC, PostgreSQL, Kafka, Kubernetes. Типичная история для современной разработки на Go. Поехали.

08:30 — Утро: План и код-ревью

День начинается не с кофе, а с просмотра уведомлений в GitLab и Slack. Пока мозг свежий, лучшее время для проверки Merge Requests коллег.

Утренний рутинг: Что я смотрю в первую очередь?

  • Ошибки в CI/CD — если сборка упала ночью, это первая задача.
  • Комментарии к моему коду — в Go принято делать детальные ревью.
  • Графики мониторинга — не просел ли latency после вчерашнего деплоя.

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

// Плохо: избыточная абстракция
type Processor interface {
    Process(ctx context.Context, data []byte) ([]byte, error)
}

// Хорошо: просто функция
func Process(ctx context.Context, data []byte) ([]byte, error) {
    // логика
}

В 09:00 — ежедневный стендап. Мы не тратим время на долгие рассказы. Формат: что сделал вчера, что буду делать сегодня, есть ли блокеры. Никакого «как прошёл ваш вечер».

09:30 — Погружение в код: Пишем микросервис на Go

Сегодня в планах — доработка сервиса уведомлений. Это классический микросервис на Go, который принимает события из Kafka и шлёт email/push. Задача: добавить поддержку шаблонов.

Первым делом открываю структуру проекта. В Go принято минималистичное дерево папок:

notifier/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handler/
│   ├── service/
│   └── repository/
├── pkg/
│   └── models/
└── go.mod

Я начинаю с модели данных. В Go для работы с БД часто используют Go ORM (например, GORM) или чистые SQL-запросы через sqlx. Я предпочитаю sqlx — меньше магии, больше контроля.

type Template struct {
    ID      int64  `db:"id"`
    Name    string `db:"name"`
    Content string `db:"content"`
    CreatedAt time.Time `db:"created_at"`
}

func (r *Repository) GetTemplateByName(ctx context.Context, name string) (*Template, error) {
    var t Template
    err := r.db.GetContext(ctx, &t, "SELECT * FROM templates WHERE name = $1", name)
    if err != nil {
        return nil, fmt.Errorf("get template: %w", err)
    }
    return &t, nil
}

Обратите внимание на обработку ошибок. В Golang нет try-catch. Каждый вызов функции проверяется на ошибку. Это не баг, а фича — код становится предсказуемым. За 5 лет работы я ни разу не поймал неожиданный null pointer в продакшене.

11:00 — Интеграция с gRPC

Следующий этап — добавить gRPC-эндпоинт для админки. В современной разработке на Go gRPC стал стандартом для внутренних сервисов. Он быстрее REST и генерирует клиентов автоматически.

Сначала описываю proto-файл:

syntax = "proto3";

package notifier;

service TemplateService {
    rpc CreateTemplate (CreateTemplateRequest) returns (TemplateResponse);
}

message CreateTemplateRequest {
    string name = 1;
    string content = 2;
}

message TemplateResponse {
    int64 id = 1;
    string name = 2;
}

Затем генерирую код через protoc и пишу реализацию. Типичный обработчик в Go выглядит так:

func (s *TemplateServer) CreateTemplate(ctx context.Context, req *pb.CreateTemplateRequest) (*pb.TemplateResponse, error) {
    if req.GetName() == "" {
        return nil, status.Error(codes.InvalidArgument, "name is required")
    }

    template, err := s.service.Create(ctx, req.GetName(), req.GetContent())
    if err != nil {
        return nil, status.Errorf(codes.Internal, "create template: %v", err)
    }

    return &pb.TemplateResponse{
        Id:   template.ID,
        Name: template.Name,
    }, nil
}

Важный момент: в Go принято использовать пустые интерфейсы (interface{}) только в крайнем случае. Для gRPC всё строго типизировано — это спасает от багов на этапе компиляции.

12:30 — Обед и рефлексия

Перерыв — святое. Но даже за едой я иногда ловлю себя на мысли: «А не забыл ли я закрыть контекст в горутине?». Кстати, о горутинах. Это главный козырь Go разработчика. Лёгкие потоки, которые позволяют обрабатывать тысячи запросов на одном ядре.

Пример из реального кода — пул воркеров для Kafka:

func (c *Consumer) Start(ctx context.Context, workers int) {
    for i := 0; i < workers; i++ {
        go func(id int) {
            for {
                select {
                case <-ctx.Done():
                    log.Printf("worker %d stopped", id)
                    return
                default:
                    msg, err := c.readMessage(ctx)
                    if err != nil {
                        continue
                    }
                    c.processMessage(msg)
                }
            }
        }(i)
    }
}

Этот код —典型ный пример паттерна worker pool. Он стабильно работает в продакшене уже год. Ни одной утечки памяти.

14:00 — Послеобеденный слот: Деплой и мониторинг

Вторая половина дня часто посвящена инфраструктуре. Go продакшен — это не только код, но и метрики, логи, алерты.

Мы используем Prometheus для сбора метрик. Добавить их в Go-сервис — дело 10 минут:

var (
    requestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: "http_request_duration_seconds",
            Help: "Duration of HTTP requests.",
        },
        []string{"method", "path", "status"},
    )                

Блоги

Рекомендаций по книгам