javascript

Бросаем Event Loop, переходим на Горутины: Go для JS-девелоперов (Часть 1)

  • воскресенье, 14 декабря 2025 г. в 00:00:07
https://habr.com/ru/articles/976312/

Если JS/TS это гибкий асинхронный клей для веба на одном потоке, то Go это строгий инструмент для облака с честным параллелизмом.

Главная проблема новичка сейчас это старые учебники. Классический «Effective Go» писали в 2009-м, там нет ни модулей, ни дженериков. Учить по нему Go всё равно что учить JS по учебникам времен ES3.

Здесь не будет истории. Это гайд по современному Go (1.25+), который использует твой опыт в JS/TS, чтобы ты начал писать рабочий код, а не копался в легаси.

Главные отличия парадигм: Что вам нужно забыть

  • От асинхронности к параллелизму. В Node.js ваш главный враг блокировка цикла событий. Вы используете async/await, чтобы отпустить поток во время ожидания I/O. В Go, благодаря легким горутинам, блокировка это нормально. Блокирующий вызов (например, чтение из базы данных) блокирует только текущую горутину, а не весь поток ОС, что делает код линейным и читаемым.

  • От экосистемы к стандартной библиотеке. В мире JS/TS вы полагаетесь на npm для всего: веб-сервер, тестирование и даже базовые утилиты. Это минимальное ядро, максимальная экосистема. Go придерживается философии батарейки в комплекте. Производственный веб-сервер, JSON-парсер, фреймворк для тестирования и HTTP-клиент встроены в стандартную библиотеку.

  • От class к struct. В Go нет классов и нет наследования. Вместо этого вы используете структуры для данных и композицию (встраивание) для их объединения.

  • От try…catch к if err!= nil. В Go нет исключений. Функции, которые могут завершиться неудачно, просто возвращают ошибку как последнее значение. Вы должны явно проверять ее, а не ловить неявно.

Что нового и актуального (Go 1.18 - 1.25)

 За последние несколько лет Go претерпел стратегические изменения, которые значительно снизили барьер входа для разработчиков из других языков, таких как TypeScript.

  1. Дженерики (Go 1.18+). Самое большое изменение в истории языка. Это сделало язык гораздо более привычным для TS-разработчиков, которые ожидают Array<T> и Map<K, V>.

  2. Исправление семантики цикла for (Go 1.22+): Go исправил давнюю ловушку для новичков с захватом переменных в замыканиях. Эта проблема была идентична проблеме var в циклах JavaScript, которая была решена с помощью let. Теперь Go ведет себя так, как вы ожидаете.

  3. Итераторы (Go 1.23+). Введен новый, более эффективный способ итерации по пользовательским коллекциям с использованием range.

  4. Новый encoding/json/v2 (Go 1.25) решает давние проблемы с JSON, которые часто приводили к багам на фронтенде, например, nil-слайсы теперь кодируются как ``, а не null.

  5. Структурированное логгирование (slog). В стандартную библиотеку добавлен новый, мощный пакет slog для современного JSON-логгирования.

Настройка и рабочий процесс

Первое, что нужно забыть, это GOPATH. В прошлом Go требовал, чтобы весь ваш код находился в одной глобальной директории $GOPATH. Эта эра закончилась. Начиная с Go 1.14, Модули Go (go.mod) являются стандартом. Теперь вы можете создавать проект в любой папке, точно так же, как вы это делаете с npm init.

  • go mod init (npm init): Эта команда создает файл go.mod.

  • go.mod (package.json): Это центральный файл вашего проекта. Он определяет имя вашего модуля (например, github.com/my-user/my-project) и его прямые зависимости (секция require).

  • go.sum (package-lock.json / yarn.lock): Этот файл генерируется автоматически. Он содержит контрольные суммы всех зависимостей (прямых и транзитивных) для обеспечения целостности и воспроизводимости сборок. В отличие от package-lock.json, он не блокирует версии, а гарантирует, что содержимое загруженных модулей не было подделано. Всегда коммитьте go.sum в свой репозиторий.

  • Где node_modules? Его нет.
    В JS-проекте npm install создает локальную папку node_modules, которая может занимать гигабайты. В Go этого нет. Go загружает зависимости в единый глобальный кеш (обычно в $GOPATH/pkg/mod). Все ваши проекты на диске остаются маленькими и ссылаются на этот кеш. Это не только экономит место, но и делает CI/CD-пайплайны невероятно быстрыми, поскольку зависимости кешируются глобально, а не устанавливаются заново для каждого проекта.

  • Вендоринг (Vendoring): Если вам абсолютно необходима полная изоляция (например, для air-gapped сборок), вы можете выполнить go mod vendor. Это создаст локальную папку vendor, похожую на node_modules, и сборка будет использовать ее.

Команды и инструменты

  • go mod tidy (npm install):
    Эта команда умнее, чем npm install. go mod tidy сканирует ваши .go файлы, смотрит на ваши import statements и делает две вещи: 1) добавляет в go.mod все зависимости, которые вы импортировали, но еще не скачали, и 2) удаляет из go.mod зависимости, которые вы больше не используете. Используйте ее перед каждым коммитом.

  • go get (npm install <pkg>):
    Используется для явного добавления новой зависимости или обновления существующей, например, go get github.com/gin-gonic/gin@latest.

  • go run . (ts-node index.ts):
    Компилирует и немедленно запускает вашу программу. Идеально для быстрой разработки.

  • go build (tsc / webpack):
    Это фундаментальное отличие. npm run build или tsc транспайлит TS в JS и бандлит ассеты. go build компилирует ваш код в один статический бинарный файл (например, myapp.exe или myapp). Развертывание вашего приложения это просто копирование этого одного файла на сервер. Вам не нужно запускать npm install --production на сервере.

  • Горячая перезагрузка (Live Reload):
    В JS вы привыкли к nodemon или встроенным dev-серверам (HMR). В Go нет встроенной горячей перезагрузки. Сообщество использует сторонние инструменты, самый популярный из которых air. Он работает аналогично nodemon: следит за вашими .go файлами и автоматически перекомпилирует и перезапускает ваш бинарный файл при сохранении.

Структура проекта

Существует неофициальный, но очень популярный репозиторий golang-standards/project-layout. Не используйте его для начала. Он не является официальным стандартом и часто является избыточным для большинства проектов.

Вместо этого придерживайтесь официальных рекомендаций, которые намного проще:

  • /go.mod Файл вашего модуля в корне.

  • /cmd/my-app/main.go Точка входа для вашего бинарного файла (приложения). cmd используется, если у вас несколько бинарных файлов (например, сервер и CLI-утилита). Если у вас только один, main.go может лежать в корне.

  • /internal/ Это самая важная папка. Код в internal не может быть импортирован никаким другим проектом за пределами вашего модуля. Это приватный код вашего приложения, и это правило принудительно enforced компилятором.

  • /pkg/ Если вы пишете библиотеку, которую должны импортировать другие, поместите этот публичный код сюда.

На этом пока всё, во второй части поговорим об основах языка в сравнении с JS/TS.