Modules เชิงลึก
npm vs Go modules
หัวข้อที่มีชื่อว่า “npm vs Go modules”ถ้าคุณรู้จัก npm ก็เท่ากับเข้าใจ Go modules ไปแล้ว 90% ทั้งสองระบบใช้ semantic versioning, lock file และ manifest file เหมือนกัน mental model จึง map กันได้ลงตัว — แต่กลไกเบื้องหลังต่างกันในจุดสำคัญ
// package.json{ "name": "my-app", "version": "1.0.0", "dependencies": { "express": "^4.18.2", "lodash": "^4.17.21" }, "devDependencies": { "jest": "^29.0.0" }}// go.modmodule github.com/myorg/my-app
go 1.27
require ( github.com/gin-gonic/gin v1.9.1 github.com/stretchr/testify v1.8.4)
// No separate devDependencies in Go.// Test-only deps are included in require; the// compiler excludes them from production binaries.go.mod — manifest
หัวข้อที่มีชื่อว่า “go.mod — manifest”go.mod เทียบเท่า package.json มี field ที่ต้องมี 3 ตัว:
module github.com/myorg/my-app ← module path (import prefix)go 1.27 ← minimum Go versionrequire (...) ← direct dependenciesจุดที่ต่างจาก package.json:
- module path เป็น string คล้าย URL มักตรงกับ path ของ repo บน VCS
- ไม่มี
devDependenciesdependency ทุกตัวอยู่ในrequireแต่ Go linker จะรวมเฉพาะ package ที่ binary นั้น ๆ import จริง ๆ - ไม่มี syntax range แบบ
^คุณ pin version แบบเป๊ะ ๆ (เช่นv1.9.1)
go.sum — lock file
หัวข้อที่มีชื่อว่า “go.sum — lock file”go.sum เทียบเท่า package-lock.json เก็บ cryptographic hash ของทุก dependency แต่ละ version:
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BerypkHKpy7LtzLuviaCYD2h8zebo=github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=แต่ละ dependency มี hash 2 บรรทัด:
h1:...— hash ของเนื้อหา module zip/go.mod h1:...— hash ของเฉพาะไฟล์go.mod
ทั้งคู่ต้องตรงกันทุกเครื่องและใน CI — go mod verify คอยตรวจให้ และคุณควร commit go.sum เข้า version control ด้วย (เหมือน package-lock.json)
คำสั่ง module ทั่วไป
หัวข้อที่มีชื่อว่า “คำสั่ง module ทั่วไป”# Initialise a new modulego mod init github.com/myorg/my-app
# Add a dependency (updates go.mod + go.sum)
# Upgrade to latest patch/minorgo get github.com/gin-gonic/gin@latest
# Remove unused dependenciesgo mod tidy
# Verify all downloaded modules match go.sumgo mod verify
# List all direct + indirect dependenciesgo list -m all
# Show available versions of a modulego list -m -versions github.com/gin-gonic/ginสิ่งที่เพิ่งเปลี่ยนใน go.mod (Go 1.25–1.27)
หัวข้อที่มีชื่อว่า “สิ่งที่เพิ่งเปลี่ยนใน go.mod (Go 1.25–1.27)”go mod initเลือกเวอร์ชันแบบระวังตัว (1.26+): toolchain Go 1.27.x จะเขียนgo 1.26.0ไม่ใช่go 1.27.0เพื่อไม่ให้ module ของคุณเผลอบังคับใช้รุ่นใหม่ล่าสุด อยากใช้ฟีเจอร์เฉพาะ 1.27 ก็แก้เป็นgo 1.27เอง — vet checkstdversionจะคอยเตือนให้- directive
ignore(1.25+): บอกคำสั่งgoให้ข้ามโฟลเดอร์ที่ไม่ควรถูกมองเป็น package เช่น build output ของ frontend —.npmignoreในโลก module requireแบบสองบล็อก (1.27+, สำหรับ module ที่ระบุgo 1.27):go mod tidyรวมบล็อกซ้ำให้เหลือบล็อก direct dependency หนึ่งบล็อกและ// indirectอีกหนึ่งบล็อกพอดี โดยเก็บ comment ที่ติดอยู่ไว้
module github.com/myorg/my-app
go 1.27
ignore ./web/dist
require ( github.com/labstack/echo/v5 v5.3.1)
require ( golang.org/x/net v0.45.0 // indirect)Major version semantics
หัวข้อที่มีชื่อว่า “Major version semantics”Go modules ฝัง major version ไว้ใน import path สำหรับ v2 ขึ้นไป นี่คือจุดที่ต่างจาก npm มากที่สุดจนหลายคนแปลกใจ:
// npm — same package name, just a new version// package.json"dependencies": { "some-lib": "^3.0.0" // v3, same import}// import someLib from 'some-lib'; ← unchanged// Go — v2+ changes the import path// go.modrequire github.com/some-lib/core v2.0.0+incompatible
// If the library has a proper /v2 module:require github.com/some-lib/core/v2 v2.0.0
// Your imports change:import "github.com/some-lib/core/v2"// v1: import "github.com/some-lib/core"// v2: import "github.com/some-lib/core/v2"replace directive — local development
หัวข้อที่มีชื่อว่า “replace directive — local development”ตอนที่คุณ develop dependency ไปพร้อมกับ main module replace จะ map module path ไปยัง directory บนเครื่อง — เทียบเท่า npm link หรือ path แบบ file: ใน package.json:
require github.com/myorg/shared-lib v1.2.3
replace github.com/myorg/shared-lib => ../shared-libลบ replace ออกก่อน commit ขึ้น production แล้วรัน go mod tidy ตามเพื่อเก็บกวาดให้เรียบร้อย
Vendoring
หัวข้อที่มีชื่อว่า “Vendoring”Vendoring คือการ copy dependency ทั้งหมดมาไว้ใน directory vendor/ ภายใน repo — มีประโยชน์กับ build แบบ air-gapped หรือเวลาต้องการรับประกัน reproducibility โดยไม่ต้องพึ่ง module proxy:
# Create/update vendor directorygo mod vendor
# Build using only vendored deps (ignores module cache)go build -mod=vendor ./...
# Verify vendor matches go.sumgo mod verifyGo workspaces (go work)
หัวข้อที่มีชื่อว่า “Go workspaces (go work)”Workspace (เพิ่มเข้ามาใน Go 1.18) ให้คุณ develop หลาย module ที่พึ่งพากันได้พร้อมกัน โดยไม่ต้องใช้ replace directive — เทียบเท่า npm workspaces หรือ yarn workspaces:
// npm workspaces — package.json at root{ "workspaces": [ "packages/api", "packages/shared" ]}// Go workspaces — go.work at repo rootgo 1.27
use ( ./api // module: github.com/myorg/api ./shared // module: github.com/myorg/shared)
// Now api can import shared directly from disk,// no replace directive needed.# Initialise a workspace (creates go.work)go work init ./api ./shared
# Add another module to the workspacego work use ./cli
# Sync go.work.sum (lock file for workspaces)go work syncปกติ go.work มักถูก git-ignore เพราะเป็นของอำนวยความสะดวกระดับ local ไม่ใช่ deployment artefact ถ้าจำเป็นก็ใช้ GOWORK=off เพื่อปิดใน CI ได้