Применяли ли вы `make` в работе? Расскажите, для чего.

Ответ

Да, применял. Этот вопрос можно трактовать двояко, поэтому я отвечу на обе возможные интерпретации: встроенная функция make в Go и утилита make с Makefile для автоматизации задач.

1. Встроенная функция make()

В языке Go функция make используется для инициализации и выделения памяти под слайсы, мапы и каналы.

В отличие от new, которая только выделяет память и возвращает указатель, make инициализирует внутреннюю структуру этих типов данных.

Примеры использования:

// Слайс: создаем с длиной 0, но с ёмкостью 10.
// Это позволяет добавить 10 элементов без новых аллокаций памяти.
s := make([]int, 0, 10)

// Мапа: создаем мапу с начальной ёмкостью 100.
// Это снижает вероятность рехеширования при добавлении элементов.
m := make(map[string]int, 100)

// Канал: создаем буферизованный канал на 5 элементов.
// Отправка в него не заблокируется, пока буфер не заполнится.
ch := make(chan string, 5)

Почему это важно: Предварительное выделение памяти с помощью make — это ключевая оптимизация производительности в Go, позволяющая избежать лишних аллокаций и копирований данных при росте коллекций.

2. Утилита make и Makefile

В Go-проектах, как и во многих других, Makefile — это стандарт де-факто для автоматизации рутинных задач разработчика. Он позволяет стандартизировать команды для всей команды и для CI/CD систем.

Типичные задачи в Makefile для Go-проекта:

  • make build: Сборка бинарного файла.
  • make test: Запуск тестов.
  • make lint: Проверка кода линтером (например, golangci-lint).
  • make run: Сборка и запуск приложения.
  • make tidy: Выполнение go mod tidy.
  • make docker-build: Сборка Docker-образа.

Пример простого Makefile:

# Имя бинарного файла
BINARY_NAME=my-app

.PHONY: build run test lint

build:
    @echo "Building..."
    @go build -o $(BINARY_NAME) ./cmd/app

run:
    @go run ./cmd/app

test:
    @echo "Running tests..."
    @go test -v ./...

lint:
    @echo "Running linter..."
    @golangci-lint run

Использование Makefile значительно упрощает жизненный цикл разработки, делая его предсказуемым и унифицированным.

Ответ 18+ 🔞

А, про make? Ну, применял, конечно, применял. Вопрос-то, как выясняется, с подвохом, как говно в проруби — с виду одно, а копнёшь, их там два. Ладно, разберём оба случая, а то потом скажешь, что я тебя, как последнего мудака, обманул.

1. Встроенная функция make() — она же «память, я тебя создал»

В Гоше эта make — не утилита, а прям волшебная палочка для трёх вещей: слайсов, мапов и каналов. В отличие от её сестрицы new, которая просто выделяет память и тыкает в неё пальцем (возвращает указатель), make — она хозяйственная. Она не только место находит, но и полочки расставляет, всё по полочкам, блядь, раскладывает, внутреннюю кухню настраивает.

Смотри, как это выглядит вживую:

// Слайс: делаем с длиной ноль, но ёмкостью на 10 элементов.
// Это чтобы потом, когда будем пихать данные, не орать каждый раз «ой, мама, места нет!» и не копировать всё заново.
s := make([]int, 0, 10)

// Мапа: создаём с запасом на 100 ключей.
// Это чтобы она не начала истерить и не перестраивала свои внутренности при каждом чихе.
m := make(map[string]int, 100)

// Канал: буферизованный, на 5 сообщений.
// Можно накидать в него пять раз, и он не будет тебе мозг выносить блокировкой, пока ты оттуда не заберёшь. Удобно, ёпта.
ch := make(chan string, 5)

А зачем это всё? Да чтобы не тормозить, вот зачем! Это как приехать на склад не на «Оке», а сразу на фуре — сразу всё забрал и поехал, а не двадцать пять рейсов туда-сюда. Оптимизация, блядь, производительности. Меньше аллокаций — меньше работы для сборщика мусора — все счастливы.

2. Утилита make и файл Makefile — она же «автомат для ленивых»

А это уже классика, которая в любом нормальном проекте живёт. Makefile — это такая священная скрижаль, где записано, какую кнопку нажать, чтобы всё само собралось, проверилось и запустилось. Чтобы каждый раз не вспоминать, как там эта длинная команда с кучей флагов выглядит. Особенно когда в команде есть тот один чувак, который всегда всё забывает.

Что обычно в этом Makefile прописано, а?

  • make build — собрать бинарь. Вжух — и готово.
  • make test — прогнать тесты. Пусть поработают.
  • make lint — прогнать линтер (типа golangci-lint). Пусть покритикует наш код, сука.
  • make run — собрать и сразу запустить. Для быстрой проверки.
  • make tidy — выполнить go mod tidy. Прибраться в зависимостях, а то там иногда как у свиней.
  • make docker-build — собрать докер-образ. Для деплоя.

Вот тебе пример такого файла, простенький:

# Как бинарник назовём
BINARY_NAME=my-app

.PHONY: build run test lint

build:
    @echo "Собираю, погоди..."
    @go build -o $(BINARY_NAME) ./cmd/app

run:
    @go run ./cmd/app

test:
    @echo "Гоняю тесты, ща дым пойдёт..."
    @go test -v ./...

lint:
    @echo "Линтер пошёл всё проверять, ща найдёт косяки..."
    @golangci-lint run

Короче, использование Makefile — это признак того, что проект не на коленке сделан. Всё чётко, стандартно, и любой новый человек, пришедший в команду, не будет тратить полдня на то, чтобы понять, как эту хуйню запустить. Просто пишешь make и нужную команду — и жизнь налаживается.