Бросаем Event Loop, переходим на Горутины: Go для JS-девелоперов (Часть 1)
- воскресенье, 14 декабря 2025 г. в 00:00:07
Если 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 претерпел стратегические изменения, которые значительно снизили барьер входа для разработчиков из других языков, таких как TypeScript.
Дженерики (Go 1.18+). Самое большое изменение в истории языка. Это сделало язык гораздо более привычным для TS-разработчиков, которые ожидают Array<T> и Map<K, V>.
Исправление семантики цикла for (Go 1.22+): Go исправил давнюю ловушку для новичков с захватом переменных в замыканиях. Эта проблема была идентична проблеме var в циклах JavaScript, которая была решена с помощью let. Теперь Go ведет себя так, как вы ожидаете.
Итераторы (Go 1.23+). Введен новый, более эффективный способ итерации по пользовательским коллекциям с использованием range.
Новый encoding/json/v2 (Go 1.25) решает давние проблемы с JSON, которые часто приводили к багам на фронтенде, например, nil-слайсы теперь кодируются как ``, а не null.
Структурированное логгирование (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.