After three articles of our journey—why choose Go, basic syntax and concurrency, interfaces and generics—we’ve reached the finale of the Go chapter, time to connect the engineering practices.
Go’s engineering capacity also shows up in its standard library, testing framework, and toolchain. They make up the daily working environment for Go developers and underpin rapid construction of reliable, maintainable software.
Standard Library Overview
Go’s standard library is known for being “small but refined.” While the number of packages isn’t large, it covers most common scenarios. The standard library design follows Go’s philosophy: simple, practical, and efficient.
Common Standard Library Packages
| Package Name | Purpose | Common Types/Functions |
|---|
strings | String operations | Contains, HasPrefix, Replace, Split, Join |
strconv | Type conversions | Atoi, Itoa, ParseInt, FormatFloat |
fmt | Formatted I/O | Println, Printf, Sprintf, Scanf |
io | Basic I/O interfaces | Reader, Writer, Copy, ReadFull |
os | OS interfaces | Open, Create, Mkdir, Remove |
net/http | HTTP server/client | Server, Client, Handler, Request |
encoding/json | JSON encoding/decoding | Marshal, Unmarshal, Encoder, Decoder |
time | Time handling | Now, Parse, Format, Sleep, Ticker |
sync | Concurrency synchronization | Mutex, RWMutex, WaitGroup, Once |
context | Request context | Background, WithCancel, WithTimeout, WithValue |
Strings and Type Conversions
The strings and strconv packages are among the most frequently used in daily development:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
// strings package examples
text := "Hello, Go World!"
fmt.Println(strings.Contains(text, "Go")) // true
fmt.Println(strings.HasPrefix(text, "Hello")) // true
fmt.Println(strings.Replace(text, "Go", "Rust", 1)) // Hello, Rust World!
fmt.Println(strings.Split(text, ", ")) // [Hello Go World!]
fmt.Println(strings.Join([]string{"Go", "Rust"}, " & ")) // Go & Rust
// strconv package examples
numStr := "123"
num, err := strconv.Atoi(numStr)
if err != nil {
fmt.Println("Conversion failed:", err)
} else {
fmt.Println("Number:", num) // 123
fmt.Println("String:", strconv.Itoa(num)) // 123
}
// Float conversion
pi := 3.14159
piStr := strconv.FormatFloat(pi, 'f', 2, 64)
fmt.Println("π ≈", piStr) // π ≈ 3.14
// Parsing with error handling
invalid := "not a number"
_, err = strconv.Atoi(invalid)
if err != nil {
fmt.Println("Expected error:", err) // strconv.Atoi: parsing "not a number": invalid syntax
}
}
|
I/O and File Operations
Go’s I/O interface design is simple and elegant. The io package defines basic Reader and Writer interfaces, while the os package provides concrete file operation implementations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| package main
import (
"fmt"
"io"
"os"
)
func main() {
// Write to file
content := "Hello, Go File I/O!\n"
err := os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
fmt.Println("Write failed:", err)
return
}
// Read file (complete read)
data, err := os.ReadFile("output.txt")
if err != nil {
fmt.Println("Read failed:", err)
return
}
fmt.Print("File content:", string(data))
// Using streaming I/O (suitable for large files)
file, err := os.Open("output.txt")
if err != nil {
fmt.Println("Open failed:", err)
return
}
defer file.Close()
buf := make([]byte, 32)
for {
n, err := file.Read(buf)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Read error:", err)
return
}
fmt.Printf("Read %d bytes: %q\n", n, buf[:n])
}
// Clean up temporary file
os.Remove("output.txt")
}
|
HTTP Server
The net/http package makes creating HTTP servers incredibly simple:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Define route handler functions
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to Go HTTP Server!")
})
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
user := User{Name: "Alice", Email: "[email protected]"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
})
// Start server
addr := ":8080"
fmt.Printf("Server listening on %s\n", addr)
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatal("Server failed:", err)
}
}
|
JSON Encoding/Decoding
The encoding/json package provides structured data serialization capabilities:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Hobbies []string `json:"hobbies,omitempty"` // omitempty: omit zero values
}
func main() {
// JSON encoding (struct → JSON)
person := Person{
Name: "Bob",
Age: 30,
Hobbies: []string{"programming", "reading"},
}
jsonData, err := json.Marshal(person)
if err != nil {
log.Fatal("Encoding failed:", err)
}
fmt.Println("JSON output:", string(jsonData))
// {"name":"Bob","age":30,"hobbies":["programming","reading"]}
// JSON decoding (JSON → struct)
jsonStr := `{"name":"Alice","age":25,"hobbies":["swimming","travel"]}`
var decoded Person
err = json.Unmarshal([]byte(jsonStr), &decoded)
if err != nil {
log.Fatal("Decoding failed:", err)
}
fmt.Printf("Decoded result: %+v\n", decoded)
// {Name:Alice Age:25 Hobbies:[swimming travel]}
// Streaming decoder (suitable for large JSON)
jsonStr = `{"name":"Charlie","age":35,"hobbies":["music","gaming"]}`
decoder := json.NewDecoder(strings.NewReader(jsonStr))
var streamDecoded Person
if err := decoder.Decode(&streamDecoded); err != nil {
log.Fatal("Streaming decoding failed:", err)
}
fmt.Printf("Streaming decoded: %+v\n", streamDecoded)
}
|
Time Handling
The time package provides rich time manipulation capabilities:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| package main
import (
"fmt"
"time"
)
func main() {
// Get current time
now := time.Now()
fmt.Println("Current time:", now.Format("2006-01-02 15:04:05")) // Go's birth time as format template
// Parse time
layout := "2006-01-02"
dateStr := "2026-03-26"
parsed, err := time.Parse(layout, dateStr)
if err != nil {
fmt.Println("Parsing failed:", err)
} else {
fmt.Println("Parsed result:", parsed.Format(layout))
}
// Time calculations
tomorrow := now.AddDate(0, 0, 1)
nextHour := now.Add(time.Hour)
fmt.Println("Tomorrow:", tomorrow.Format("2006-01-02"))
fmt.Println("One hour later:", nextHour.Format("15:04:05"))
// Time duration
duration := time.Since(now)
fmt.Println("Elapsed:", duration)
// Ticker
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
fmt.Println("Waiting for 2 seconds...")
timeout := time.After(2 * time.Second)
for i := 1; i <= 2; i++ {
select {
case <-ticker.C:
fmt.Printf("Tick %d\n", i)
case <-timeout:
fmt.Println("Timeout!")
return
}
}
}
|
testing Package: Complete Testing Framework
Go’s testing framework is simple yet powerful, built into the standard library. You don’t need to install additional testing frameworks—everything is ready.
Basic Unit Tests
Test files end with _test.go and use the testing package:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // calculator.go
package main
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| // calculator_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d, want %d", got, want)
}
}
func TestSubtract(t *testing.T) {
tests := []struct {
a, b int
want int
}{
{5, 3, 2},
{10, 4, 6},
{-1, 1, -2},
}
for _, tt := range tests {
if got := Subtract(tt.a, tt.b); got != tt.want {
t.Errorf("Subtract(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
}
}
func TestDivide(t *testing.T) {
// Normal case
result, err := Divide(10, 2)
if err != nil {
t.Errorf("Divide(10, 2) returned error: %v", err)
}
if result != 5 {
t.Errorf("Divide(10, 2) = %d, want 5", result)
}
// Division by zero error
_, err = Divide(10, 0)
if err == nil {
t.Error("Divide(10, 0) should return error, but didn't")
}
}
|
Run tests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Run all tests
go test
# Run specific test
go test -run TestAdd
# Show verbose output
go test -v
# Show test coverage
go test -cover
# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
|
Table-Driven Tests
Table-driven tests are the recommended testing pattern in Go, especially suitable for testing multiple input/output groups:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| // string_test.go
package main
import (
"strings"
"testing"
)
func TestStringsContains(t *testing.T) {
tests := []struct {
name string
input string
substr string
expected bool
}{
{"Simple match", "hello world", "world", true},
{"No match", "hello world", "goodbye", false},
{"Empty string", "hello world", "", true},
{"Case sensitive", "Hello World", "hello", false},
{"Substring same", "hello", "hello", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := strings.Contains(tt.input, tt.substr)
if got != tt.expected {
t.Errorf("Contains(%q, %q) = %v, want %v",
tt.input, tt.substr, got, tt.expected)
}
})
}
}
|
Benchmark Tests
Benchmark tests are used to measure code performance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // benchmark_test.go
package main
import (
"strings"
"testing"
)
func BenchmarkStringsContains(b *testing.B) {
text := "Hello, World! This is a test string for benchmarking."
substr := "test"
b.ResetTimer() // Reset timer, exclude initialization time
for i := 0; i < b.N; i++ {
strings.Contains(text, substr)
}
}
func BenchmarkStringsIndex(b *testing.B) {
text := "Hello, World! This is a test string for benchmarking."
substr := "test"
b.ResetTimer()
for i := 0; i < b.N; i++ {
strings.Index(text, substr) >= 0
}
}
|
Run benchmark tests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Run benchmark tests
go test -bench=.
# Run specific benchmark test
go test -bench=BenchmarkStringsContains
# Run for specified time (default 1 second)
go test -bench=. -benchtime=3s
# Memory allocation statistics
go test -bench=. -benchmem
# Run tests and benchmark tests
go test -v -bench=. -benchtime=2s
|
Example output:
1
2
| BenchmarkStringsContains-8 50000000 28.5 ns/op
BenchmarkStringsIndex-8 30000000 42.1 ns/op 0 B/op 0 allocs/op
|
Fuzz Testing
Go 1.18 introduced fuzz testing, used to discover edge case errors:
1
2
3
4
5
6
7
8
9
10
| // reverse.go
package main
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| // reverse_fuzz_test.go
package main
import (
"strings"
"testing"
)
func FuzzReverse(f *testing.F) {
// Add seed corpus
f.Add("hello")
f.Add("")
f.Add("123456")
f.Add("中文测试")
f.Fuzz(func(t *testing.T, input string) {
reversed := Reverse(input)
doubleReversed := Reverse(reversed)
if input != doubleReversed {
t.Errorf("Reversing twice should restore original string: %q -> %q -> %q",
input, reversed, doubleReversed)
}
})
}
// Traditional test for comparison
func TestReverse(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"hello", "olleh"},
{"", ""},
{"a", "a"},
{"中文测试", "试测文中"},
}
for _, tt := range tests {
if got := Reverse(tt.input); got != tt.expected {
t.Errorf("Reverse(%q) = %q, want %q", tt.input, got, tt.expected)
}
}
}
|
Run fuzz tests:
1
2
3
4
5
6
7
8
9
10
11
| # Run fuzz tests (default 1 minute)
go test -fuzz=FuzzReverse
# Run fuzz tests for specified time
go test -fuzz=FuzzReverse -fuzztime=30s
# Save failed inputs to seed corpus
go test -fuzz=FuzzReverse -fuzztime=10s
# Run all tests simultaneously
go test -fuzz=FuzzReverse -v
|
Subtests and Parallel Tests
Subtests make test structure clearer, parallel tests accelerate execution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| // subtest_test.go
package main
import (
"sync"
"testing"
)
func TestParallelAdd(t *testing.T) {
tests := []struct {
a, b, want int
}{
{1, 2, 3},
{3, 4, 7},
{5, 6, 11},
}
var wg sync.WaitGroup
for _, tt := range tests {
tt := tt // Capture loop variable
wg.Add(1)
t.Run(tt.name, func(t *testing.T) {
defer wg.Done()
t.Parallel() // Mark as parallel test
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
wg.Wait()
}
|
Go’s toolchain is the core of its engineering capabilities. All commands are invoked through the go command—unified and concise.
go build: Compile Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # Compile package in current directory
go build
# Compile specified package
go build ./path/to/package
# Compile and specify output filename
go build -o myapp ./cmd/app
# Cross-compilation (different platforms and architectures)
GOOS=linux GOARCH=amd64 go build -o app-linux-amd64
GOOS=windows GOARCH=amd64 go build -o app-windows-amd64.exe
GOOS=darwin GOARCH=arm64 go build -o app-darwin-arm64
# Compile without optimization (for debugging)
go build -gcflags="-N -l" -o app-debug
# View build details
go build -x
go build -v
|
go test: Run Tests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # Run all tests
go test
# Run specific test
go test -run TestAdd
go test -run "TestAdd|TestSubtract"
# Show verbose output
go test -v
# Test coverage
go test -cover
go test -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
go tool cover -func=coverage.out
# Benchmark tests
go test -bench=.
go test -bench=. -benchtime=5s
go test -bench=. -benchmem
# Fuzz tests
go test -fuzz=FuzzReverse
go test -fuzz=FuzzReverse -fuzztime=30s
# Run tests and generate report
go test -json > test-results.json
|
go mod: Module Management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # Initialize new module
go mod init github.com/username/myproject
# Tidy dependencies (download missing, remove unused)
go mod tidy
# Download dependencies
go mod download
# Verify dependencies
go mod verify
# View dependency graph
go mod graph
# View dependencies for specific module
go mod why github.com/pkg/errors
# Update dependencies to latest versions
go get -u ./...
go get -u github.com/pkg/errors
# Downgrade or specify version
go get github.com/pkg/[email protected]
go get github.com/pkg/errors@master
# Edit go.mod file
go mod edit -require=github.com/pkg/[email protected]
go mod edit -replace=github.com/pkg/errors=./vendor/pkg/errors
# Create dependency copy (for offline builds)
go mod vendor
|
go vet: Static Analysis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Run all checks
go vet
# Run checks for specific package
go vet ./...
# Enable specific analyzer
go vet -vettool=myanalyzer ./...
# Disable specific check
go vet -printf=false ./...
# View all vet-supported checks
go tool vet help
# Run printf check
go vet -printf ./...
|
Common go vet checks:
printf: Check formatted stringsbool: Check boolean expression errorscomposites: Check composite literalscopylocks: Check lock copyingmethods: Check method signaturesnilfunc: Check nil function callsprintf: Check formatted stringsrangeloops: Check loop variable captureunreachable: Check unreachable codeunsafeptr: Check unsafe pointers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Format current directory
go fmt
# Format specific file
go fmt main.go
go fmt ./...
# View formatting differences without modifying
go fmt -d
# Format and show modified files
go fmt -l
# Format all files and rewrite
go fmt -w
|
Best Practice: Add .editorconfig to the project root or run go fmt ./... in a Git pre-commit hook to ensure committed code is always consistently formatted.
go doc: View Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # View documentation for current package
go doc
# View documentation for specific function
go doc fmt.Println
# View documentation for specific package
go doc http.Server
# View all exported symbols
go doc -all
# View source code
go doc -src fmt.Println
# View documentation in browser
godoc -http=:8080
# Then visit http://localhost:8080
# View Go standard library documentation
godoc -http=:8080
|
Other Common Commands
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # go run: Run code directly (suitable for quick testing)
go run main.go
go run main.go arg1 arg2
# go list: List package information
go list -m all # List all dependencies
go list -f '{{.ImportPath}}' ./...
go list -json ./...
# go clean: Clean build files
go clean
go clean -cache
go clean -testcache
go clean -modcache
# go env: View environment variables
go env
go env GOROOT
go env GOPATH
go env GOOS GOARCH
# go version: View Go version
go version
# go fix: Automatically fix code (for version upgrades)
go fix ./...
|
Project Structure Conventions
The Go community has a widely followed set of project structure conventions. Following these conventions makes projects easier to understand, maintain, and collaborate on.
GOPATH vs Go Modules
GOPATH (Old way):
1
2
3
4
5
6
7
8
9
10
11
12
13
| GOPATH=~/go
src/
github.com/
username/
myproject/
main.go
bin/
myproject
pkg/
github.com/
username/
myproject/
...
|
Go Modules (Recommended way, Go 1.11+):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| myproject/
go.mod # Module definition
go.sum # Dependency verification
main.go
internal/ # Internal packages (cannot be imported externally)
pkg/ # Public packages
cmd/ # Entry commands
api/ # API definitions (OpenAPI/Swagger)
web/ # Web resources
configs/ # Configuration files
deployments/ # Deployment files
test/ # Additional tests
docs/ # Documentation
scripts/ # Build and deployment scripts
tools/ # Tools
third_party/ # Third-party tools
githooks/ # Git hooks
assets/ # Static resources
website/ # Project website
|
Standard Project Layout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| myproject/
├── cmd/
│ ├── app/ # Main application
│ │ └── main.go
│ └── cli/ # CLI tool
│ └── main.go
├── internal/
│ ├── auth/ # Internal auth package
│ │ ├── auth.go
│ │ └── auth_test.go
│ ├── database/ # Internal database package
│ │ └── db.go
│ └── config/ # Internal config package
│ └── config.go
├── pkg/
│ ├── api/ # Reusable API package
│ │ ├── api.go
│ │ └── api_test.go
│ └── utils/ # Common utilities
│ └── utils.go
├── api/ # API definitions
│ └── openapi.yaml
├── web/ # Web resources
│ ├── static/
│ └── templates/
├── configs/ # Configuration files
│ ├── config.yaml
│ └── config.dev.yaml
├── deployments/ # Deployment configurations
│ ├── docker/
│ │ └── Dockerfile
│ └── kubernetes/
│ └── deployment.yaml
├── test/ # Additional tests
│ ├── integration/
│ │ └── integration_test.go
│ └── e2e/
│ └── e2e_test.go
├── docs/ # Documentation
│ ├── README.md
│ └── api.md
├── scripts/ # Scripts
│ ├── build.sh
│ └── deploy.sh
├── tools/ # Tools
│ └── tools.go
├── go.mod # Module definition
├── go.sum # Dependency verification
├── Makefile # Build script
├── Dockerfile # Docker image
├── docker-compose.yml # Docker Compose
├── .gitignore # Git ignore file
├── .editorconfig # Editor configuration
├── .golangci.yml # Linter configuration
└── README.md # Project description
|
Directory Descriptions
| Directory | Purpose | Externally Importable |
|---|
cmd/ | Entry commands (each subdirectory is an executable) | Yes |
internal/ | Internal packages (used internally, cannot be imported externally) | No |
pkg/ | Reusable public packages | Yes |
api/ | API definitions (OpenAPI/Swagger) | - |
web/ | Web resources (HTML/CSS/JS) | - |
configs/ | Configuration files | - |
deployments/ | Deployment configurations | - |
test/ | Additional tests (integration tests, end-to-end tests) | - |
docs/ | Documentation | - |
scripts/ | Build and deployment scripts | - |
tools/ | Tools and dependencies | - |
Makefile Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| .PHONY: build test clean lint fmt vet install run docker-build docker-run
# Variables
APP_NAME := myapp
VERSION := $(shell git describe --tags --always --dirty)
LDFLAGS := -ldflags "-X main.Version=$(VERSION)"
# Build
build:
go build $(LDFLAGS) -o bin/$(APP_NAME) ./cmd/app
# Test
test:
go test -v -cover ./...
# Code linting
lint:
golangci-lint run ./...
# Format
fmt:
go fmt ./...
# Static analysis
vet:
go vet ./...
# Install dependencies
install:
go mod download
go mod tidy
# Run
run:
go run ./cmd/app
# Clean
clean:
rm -rf bin/
# Docker build
docker-build:
docker build -t $(APP_NAME):$(VERSION) .
docker tag $(APP_NAME):$(VERSION) $(APP_NAME):latest
# Docker run
docker-run:
docker run -p 8080:8080 $(APP_NAME):latest
|
Learning Resources
Official Resources
- Go Official Website: go.dev/ — Go’s official website
- Go Official Documentation: go.dev/doc/ — Complete official documentation
- Go Standard Library Docs: pkg.go.dev/std — Standard library API documentation
- Go Tour: tour.go.dev/ — Interactive Go tutorial
- Effective Go: go.dev/doc/effective_go — Best practices for writing high-quality Go code
- Go by Example: gobyexample.com/ — Collection of code examples
Chinese Resources
- Go Language Bible: gopl-zh.github.io/ — Chinese version of “The Go Programming Language”
- Go Getting Started Guide: go-zh.org/doc/ — Go Chinese official documentation
- Go Language Chinese Website: studygolang.com/ — Chinese community and tutorials
- Go in Action: learning.golang.com/ — Practical tutorials
Advanced Resources
- Go Blog: go.dev/blog/ — Official Go team blog
- Go Modules Wiki: github.com/golang/go/wiki/Modules — Complete Go Modules documentation
- Go Performance Optimization: dave.cheney.net/ — Dave Cheney’s blog (Go core contributor)
- Go Wiki: github.com/golang/go/wiki — Go official Wiki
Summary
After four articles of learning, we started with Go’s design philosophy, understood basic syntax and concurrency, delved into interfaces and generics, and concluded with standard library, testing, and toolchain. Go’s engineering advantages are reflected in every detail: concise standard library, built-in testing framework, unified toolchain.
Go lacks some advanced features and can feel “too simple” in certain scenarios. But code that is easy to read, maintain, and deploy is, for team collaboration, a practical advantage: fewer constraints mean less room for errors and disagreements.
If you’ve read this far, I hope these four articles can be the beginning of your Go journey. Go has an active community, comprehensive standard library, and a complete toolchain—these are its strengths. But ultimately, language is just a tool; what matters is using it to build valuable software.
The next post moves into Rust—a language that solves similar problems from a different perspective. Rust focuses on zero-cost abstractions, memory safety, and expressiveness, in contrast with Go’s pragmatism. We will compare the two design philosophies and engineering practices.