Bạn đang sửa một thư viện dùng chung lib và ứng dụng app dùng nó — cả hai là module riêng, cùng nằm trên máy bạn, và lib chưa được đẩy lên remote. Làm sao để app thấy bản lib bạn vừa sửa mà không phải publish sau mỗi lần đổi một dòng? Trước Go 1.18, câu trả lời là thêm replace vào go.mod — một hack dễ commit nhầm và làm bẩn lịch sử. Go 1.18 giới thiệu workspace qua file go.work, giải bài toán này sạch sẽ. Bài này đo thật cách workspace hoạt động và vì sao nó hơn hẳn replace.
Vấn đề: module local chưa publish
Xét hai module riêng nằm cạnh nhau:
/work/
├── app/ module example.com/app (import example.com/lib)
└── lib/ module example.com/lib (chưa đẩy lên remote)
app/main.go import example.com/lib, nhưng lib mới chỉ ở đĩa local. Đo thật trong go-lab (Go 1.23), build app cho lỗi:
$ go build ./app
main.go:4:2: no required module provides package example.com/lib;
to add it: go get example.com/lib
Go module cố tải example.com/lib từ mạng — nó không biết bản local ngay bên cạnh. go get cũng vô ích vì module chưa tồn tại trên remote. Đây chính là ma sát mà mọi người phát triển đa module gặp phải.

Hình 1: app dùng lib local chưa publish → build lỗi. go work init ./app ./lib sinh file go.work với danh sách use, ánh xạ nhiều module local — khác cách cũ phải thêm replace vào go.mod.
Giải pháp: go work init và use
Workspace được dựng bằng lệnh go work init với danh sách các module tham gia:
go work init ./app ./lib # sinh go.work ở thư mục cha
Lệnh này tạo file go.work ở thư mục cha, chứa:
go 1.23
use (
./app
./lib
)
Mệnh đề use liệt kê các thư mục module thuộc workspace. Khi build từ bất kỳ đâu trong cây workspace, Go ưu tiên các module local này thay vì tải từ mạng. Đo thật, sau khi có go.work:
$ go build . # từ thư mục app
build OK (dùng lib local qua go.work)
$ go run ./app
Cong(2,3) = 5 # app gọi hàm Cong trong lib local
app giờ thấy lib local ngay, gọi được lib.Cong(2, 3) trả về 5. Thêm module mới vào workspace về sau bằng go work use ./tool.
Điểm mấu chốt: go.mod vẫn sạch
Đây là lý do go.work hơn hẳn replace. Đo thật, sau khi dựng workspace, app/go.mod không có một dòng replace nào:

Hình 2: Từ thư mục app — GOWORK=off build lỗi (mất lib local), workspace bật mặc định thì Cong(2,3)=5. app/go.mod có 0 dòng replace; go env GOWORK cho thấy file workspace đang dùng. go work use thêm module, go work edit -json cho tooling.
replace (cách cũ) sửa thẳng go.mod — file này được commit, nên đường dẫn local kiểu replace example.com/lib => ../lib dễ lọt vào git và làm hỏng build của người khác (đường dẫn ../lib không tồn tại trên máy họ). go.work cố ý không nên commit (thêm vào .gitignore): mỗi lập trình viên tự dựng workspace của mình, còn go.mod giữ nguyên sạch sẽ cho mọi người. Người khác clone repo về vẫn build bình thường qua module remote thật.
Workspace điều khiển bằng biến GOWORK: go env GOWORK cho biết file đang dùng (/work/t080/go.work), GOWORK=off go build tắt workspace để build như module thuần (kiểm tra xem code có build đúng ngoài workspace không — quan trọng trước khi push). go work edit -json xuất cấu hình dạng JSON cho công cụ đọc.
Đánh đổi cần cân nhắc
go.work cho phát triển local, không cho build sản xuất. Workspace là công cụ lúc phát triển: nó ghi đè việc phân giải module để bạn sửa nhiều module cùng lúc. Nhưng khi build/CI cho sản xuất, bạn muốn dùng đúng phiên bản trong go.mod/go.sum (có kiểm tra checksum) — nên CI thường chạy GOWORK=off hoặc đơn giản không có go.work. Đừng dựa vào workspace cho reproducible build.
Đừng commit go.work trong repo đa người dùng. Vì đường dẫn use là đường dẫn local (./app, ../lib), commit go.work làm build của người khác lệ thuộc vào bố cục thư mục của bạn. Ngoại lệ: một monorepo nơi mọi module nằm cố định trong cùng cây thư mục — khi đó commit go.work có thể hợp lý vì đường dẫn nhất quán với mọi người. Cân theo bố cục dự án.
Vẫn phải cập nhật go.mod khi hoàn tất. Workspace chỉ là cầu tạm. Khi bạn sửa xong lib và publish nó, phải cập nhật require trong app/go.mod sang phiên bản mới thật (go get example.com/lib@v1.2.0), rồi mới bỏ khỏi workspace. go.work không thay thế việc quản lý phụ thuộc thật — nó chỉ giúp giai đoạn phát triển song song mượt hơn.
Ba ý mang về
go.workcho phép làm việc nhiều module local cùng lúc:go work init ./app ./libsinh file workspace với danh sáchuse, đểappdùng ngayliblocal chưa publish — đo thật, không go.work thì build lỗino required module provides package, có go.work thìCong(2,3)=5.- go.mod vẫn sạch — hơn hẳn replace thủ công: đo thật
app/go.modcó 0 dòng replace;go.workgiữ ánh xạ local riêng và không nên commit (mỗi dev tự dựng), khácreplacesửa thẳng go.mod dễ commit nhầm làm hỏng build người khác. - Workspace là công cụ phát triển, không cho build sản xuất: điều khiển bằng
GOWORK(=offđể tắt,go env GOWORKđể xem), CI nên build không workspace để dùng đúng phiên bản có checksum — và nhớ cập nhậtgo.modthật khi module đã publish.
Phần sau ta chuyển từ công cụ sang kiến trúc phần mềm: Phần sau mổ xẻ Clean Architecture trong Go — cách tổ chức tầng, hướng phụ thuộc, và những chỗ triết lý "đơn giản" của Go va chạm với các lớp trừu tượng.