17. Workspaces. go work
До появления Go 1.18 работа с несколькими модулями одновременно (например, когда вы параллельно разрабатываете внутреннюю библиотеку и сервис, который её использует) была настоящей головной болью инженеров. Единственным способом связать их локально была директива replace в go.mod, указывающая путь к соседней директории на диске. Это приводило к тому, что файл go.mod засорялся временными локальными путями, которые разработчики регулярно забывали удалить перед git commit и ломали CI/CD пайплайны всей команде.
Механизм Workspaces (рабочие области) и файл go.work навсегда решили эту проблему, предоставив чистый, элегантный и изолированный способ работы с монорепозиториями и мульти-модульными проектами.
Проблема: Мульти-модульная разработка
Представьте типичную структуру современного проекта:
my-project/
├── service/ # Основной микросервис (модуль)
│ └── go.mod
└── lib/ # Внутренняя shared-библиотека (модуль)
└── go.mod
Сервис service импортирует github.com/myorg/lib. Если вы хотите внести правку в lib и тут же протестировать её в service без публикации промежуточных релизов на GitHub, раньше приходилось писать в service/go.mod:
replace github.com/myorg/lib => ../lib
Если вы забывали убрать эту строку перед git push, удаленный сборщик падал с ошибкой: директории ../lib по этому пути в изолированном контейнере сборки попросту не существовало.
Решение: go.work
Workspaces позволяют определить внешний контекст для локальной разработки, вообще не трогая файлы go.mod внутри модулей.
Файл go.work создается на один уровень выше — в корневой директории репозитория. Он сообщает компилятору: "Все перечисленные модули образуют единую рабочую область и должны разрешаться локально друг в друга".
go 1.22
use (
./service
./lib
)
Теперь, когда код внутри service импортирует github.com/myorg/lib, Go проверяет файл go.work, видит, что этот модуль включен в блок use, и прозрачно читает исходный код прямо из соседней папки ./lib, полностью игнорируя сетевой прокси и версии в go.mod.
flowchart TD
A["go build service"] --> B["Поиск go.work"]
B --> C{"Модуль в списке use?"}
C -- "Да" --> D["Локальное разрешение"]
D --> E["Чтение из ./lib"]
C -- "Нет" --> F["Стандартное разрешение"]
F --> G["Go Proxy или Vendor"]
style D fill:#064e3b,stroke:#34d399,color:#f8fafc
style F fill:#7f1d1d,stroke:#f87171,color:#f8fafc
Команды управления
Управление рабочей областью происходит через штатные CLI-команды тулчейна:
-
Инициализация:
Bash go work init ./service ./libСоздает файл
go.workи добавляет в него указанные пути модулей. -
Добавление модуля:
Bash go work use ./newmoduleДобавляет новую строку
useв файл. Если директории нет на диске, строка будет закомментирована. -
Синхронизация:
Bash go work syncСверяет зависимости всех модулей workspace и проталкивает необходимые обновления в файлы
go.mod, гарантируя консистентность версий.
Связь с Vendor и CI/CD
Workspaces прекрасно интегрируются с вендорингом. Команда go work vendor создаст единую директорию vendor/ в корне проекта, собрав туда транзитивные зависимости всех модулей, перечисленных в use.
Это критически важно для CI/CD в монорепозиториях. Вы можете настроить пайплайн так, чтобы он запускал сборку с флагом -workfile=off (принудительно игнорировать workspace) для проверки независимости модулей, или использовать go.work для сборки всего монорепозитория целиком.
Переменная GOWORK
Вы можете управлять файлом рабочей области через переменную окружения GOWORK:
# Принудительно использовать конкретный файл
export GOWORK=/path/to/custom.work
# Отключить workspace mode (вернуться к стандартному поведению)
export GOWORK=off
Это незаменимо в сложных сборочных скриптах, где на одном раннере нужно изолировать этапы сборки разных сервисов.
Итог
- Workspaces (
go.work) — стандартный инструмент для локальной разработки мульти-модульных проектов и монорепозиториев. - Они навсегда устранили необходимость во временных хаках с директивами
replaceвнутриgo.mod. - Управляются через команды
go work init/use/sync. - Безопасны для коммита в монорепозиториях, но должны быть добавлены в
.gitignoreпри ad-hoc отладке внешних библиотек.
Мы настроили структуру проекта и управление зависимостями. Теперь перейдем к обеспечению качества кода. В следующей статье мы разберем главный инструмент статического анализа в индустрии: 18. Линтеры. golangci lint.