Build Tags & Constraints

Why Build Tags Matter

When building a universal remote control that works with different TV brands—Sony, Samsung, LG—instead of buying a separate remote for each brand, you get one smart remote that automatically knows which buttons to show for each TV. Build tags are like that smart remote for your Go code—they automatically select the right implementation based on the target platform.

Real-World Impact:

Docker - 500K+ lines, runs on Windows/Linux/macOS:

  • file_linux.go, file_windows.go, file_darwin.go - Platform-specific syscalls
  • Zero runtime platform detection—compiler chooses correct files
  • Result: Single binary, optimal performance per platform

Kubernetes - Compiles for ARM, x86, PowerPC:

  • CPU-specific optimizations selected at compile time
  • //go:build arm64 for ARM-specific crypto acceleration
  • 40% performance gain on ARM vs generic code

Discord - Cross-platform voice communication:

  • Build tags handle platform-specific audio APIs
  • Windows: DirectSound, WASAPI
  • macOS: CoreAudio, AVFoundation
  • Linux: ALSA, PulseAudio
  • Result: Native performance on each platform

Build tags enable Go's "write once, compile anywhere" philosophy—the same codebase compiles to 20+ platforms without #ifdef hell. Understanding build tags separates simple projects from production-grade cross-platform systems.

Learning Objectives

By the end of this article, you will master:

  • Build Tag Syntax: Write effective build constraints using modern //go:build syntax
  • Platform Detection: Target specific operating systems, architectures, and compiler features
  • Conditional Compilation: Create different implementations for different environments
  • Feature Flags: Implement compile-time feature toggles and A/B testing
  • Production Patterns: Apply build tags in real-world scenarios like CI/CD and multi-environment deployments

Core Concepts - Understanding Build Tags

What are Build Tags?

Build tags are special comments at the top of Go files that tell the compiler when to include or exclude that file during compilation. They act as conditional compilation directives, allowing your single codebase to adapt to different target environments.

Real-world Analogy: Think of build tags like a smart construction crew that automatically uses different building techniques based on the location. If they're building in earthquake-prone California, they use reinforced foundations. If they're building in snowy Minnesota, they use insulated walls and heated floors. The building plan adapts to the environment—build tags do the same for your code.

The Modern Build Tag Revolution

Go 1.17 introduced the modern //go:build syntax, replacing the legacy // +build format. The new syntax provides:

  • Better readability with standard boolean operators
  • Parentheses for complex expressions
  • Clearer logical flow with AND, OR, and NOT operations

Types of Build Constraints

Platform Tags - Target specific operating systems and architectures:

1//go:build linux && amd64     // 64-bit Linux
2//go:build windows           // Windows
3//go:build darwin && arm64   // Apple Silicon Macs

Feature Tags - Enable or disable code features:

1//go:build production        // Production-only optimizations
2//go:build debug            // Debug logging and checks
3//go:build experimental      // Experimental features

Compiler Tags - Target specific Go compilers:

1//go:build gc              // Standard Go compiler
2//go:build gccgo           // GCC Go compiler
3//go:build !cgo           // Disable CGo

Why Build Tags Over Runtime Checks?

The Problem with Runtime Checks:

1// ❌ WRONG: Runtime platform detection
2func GetHomePath() string {
3    if runtime.GOOS == "windows" {
4        return os.Getenv("USERPROFILE") + "\\AppData"
5    }
6    return os.Getenv("HOME")
7}

The Build Tag Solution:

 1// ✅ BETTER: Compile-time selection
 2// file_windows.go
 3//go:build windows
 4func GetHomePath() string {
 5    return os.Getenv("USERPROFILE") + "\\AppData"
 6}
 7
 8// file_unix.go
 9//go:build !windows
10func GetHomePath() string {
11    return os.Getenv("HOME")
12}

Benefits:

  • Zero runtime overhead - Platform check happens at compile time
  • Dead code elimination - Unused platform code never reaches binary
  • Type safety - Compiler only sees code that actually exists
  • Smaller binaries - Only relevant code included

Build Tag Syntax Rules

Modern Syntax (//go:build):

1//go:build linux && amd64     // AND operation
2//go:build linux || darwin    // OR operation
3//go:build !windows           // NOT operation
4//go:build (linux || darwin) && amd64  // Complex expression
5
6package main

Critical Rules:

  1. Build tags must appear before the package declaration
  2. A blank line must separate build tags from package declaration
  3. Use standard boolean operators: && (AND), || (OR), ! (NOT)
  4. Parentheses group complex expressions
  5. Multiple build tag lines are combined with AND logic

File Naming Conventions

Go recognizes special file naming patterns without explicit build tags:

 1# Operating system suffixes
 2file_linux.go       # Only Linux
 3file_windows.go     # Only Windows
 4file_darwin.go      # Only macOS
 5file_freebsd.go     # Only FreeBSD
 6
 7# Architecture suffixes
 8file_amd64.go       # Only AMD64/x86-64
 9file_arm64.go       # Only ARM64
10file_386.go         # Only 32-bit x86
11file_arm.go         # Only 32-bit ARM
12
13# Combined suffixes
14file_linux_amd64.go    # 64-bit Linux only
15file_windows_arm64.go  # ARM64 Windows only
16file_darwin_amd64.go   # Intel Macs only

Example:

1// network_linux_amd64.go - No explicit build tag needed!
2package network
3
4func OptimizedRead(fd int, buf []byte) (int, error) {
5    // Linux AMD64-specific optimizations using syscalls
6    return syscall.Read(fd, buf)
7}

Practical Examples - Hands-On Build Tags

Example 1: Platform-Specific File Operations

Let's build a cross-platform file operations package that handles path differences between Windows and Unix-like systems:

 1// fileops/file.go - Common interface
 2package fileops
 3
 4// FileSystem provides platform-agnostic file operations
 5type FileSystem interface {
 6    HomePath() string
 7    TempPath() string
 8    Separator() string
 9    Join(parts ...string) string
10}
11
12// GetFileSystem returns platform-specific implementation
13func GetFileSystem() FileSystem {
14    return newFileSystem()
15}
 1// fileops/file_windows.go - Windows implementation
 2//go:build windows
 3
 4package fileops
 5
 6import (
 7    "os"
 8    "path/filepath"
 9)
10
11type windowsFS struct{}
12
13func newFileSystem() FileSystem {
14    return &windowsFS{}
15}
16
17func (w *windowsFS) HomePath() string {
18    // Windows: C:\Users\Username
19    return os.Getenv("USERPROFILE")
20}
21
22func (w *windowsFS) TempPath() string {
23    // Windows: C:\Users\Username\AppData\Local\Temp
24    return os.Getenv("TEMP")
25}
26
27func (w *windowsFS) Separator() string {
28    return "\\"
29}
30
31func (w *windowsFS) Join(parts ...string) string {
32    return filepath.Join(parts...)
33}
 1// fileops/file_unix.go - Unix-like implementation
 2//go:build !windows
 3
 4package fileops
 5
 6import (
 7    "os"
 8    "path/filepath"
 9)
10
11type unixFS struct{}
12
13func newFileSystem() FileSystem {
14    return &unixFS{}
15}
16
17func (u *unixFS) HomePath() string {
18    // Unix/Linux/macOS: /home/username
19    return os.Getenv("HOME")
20}
21
22func (u *unixFS) TempPath() string {
23    // Unix/Linux/macOS: /tmp
24    return "/tmp"
25}
26
27func (u *unixFS) Separator() string {
28    return "/"
29}
30
31func (u *unixFS) Join(parts ...string) string {
32    return filepath.Join(parts...)
33}

Usage:

 1package main
 2
 3import (
 4    "fmt"
 5    "yourproject/fileops"
 6)
 7
 8func main() {
 9    fs := fileops.GetFileSystem()
10
11    fmt.Printf("Home: %s\n", fs.HomePath())
12    fmt.Printf("Temp: %s\n", fs.TempPath())
13    fmt.Printf("Separator: %s\n", fs.Separator())
14
15    // Cross-platform path joining
16    fullPath := fs.Join(fs.HomePath(), "Documents", "myfile.txt")
17    fmt.Printf("Full path: %s\n", fullPath)
18}
19
20// run

Example 2: Architecture-Specific Optimizations

Let's implement CPU-specific optimizations for cryptographic operations:

 1// crypto/hasher.go - Common interface
 2package crypto
 3
 4// Hasher provides fast hashing with platform optimizations
 5type Hasher interface {
 6    Hash(data []byte) []byte
 7    HashString(s string) []byte
 8}
 9
10// NewHasher returns optimized hasher for current platform
11func NewHasher() Hasher {
12    return newHasher()
13}
 1// crypto/hasher_amd64.go - x86-64 optimizations
 2//go:build amd64
 3
 4package crypto
 5
 6type amd64Hasher struct{}
 7
 8func newHasher() Hasher {
 9    return &amd64Hasher{}
10}
11
12func (h *amd64Hasher) Hash(data []byte) []byte {
13    // Use SIMD instructions available on AMD64
14    return hashOptimized(data)
15}
16
17func (h *amd64Hasher) HashString(s string) []byte {
18    return h.Hash([]byte(s))
19}
20
21// hashOptimized uses AVX2 instructions for speed
22func hashOptimized(data []byte) []byte {
23    var hash uint64 = 14695981039346656037 // FNV-1a offset basis
24
25    // Process in larger chunks on 64-bit platforms
26    for i := 0; i < len(data); i++ {
27        hash ^= uint64(data[i])
28        hash *= 1099511628211 // FNV-1a prime
29    }
30
31    result := make([]byte, 8)
32    for i := 0; i < 8; i++ {
33        result[i] = byte(hash >> (i * 8))
34    }
35    return result
36}
 1// crypto/hasher_generic.go - Generic implementation
 2//go:build !amd64
 3
 4package crypto
 5
 6type genericHasher struct{}
 7
 8func newHasher() Hasher {
 9    return &genericHasher{}
10}
11
12func (h *genericHasher) Hash(data []byte) []byte {
13    return hashGeneric(data)
14}
15
16func (h *genericHasher) HashString(s string) []byte {
17    return h.Hash([]byte(s))
18}
19
20// hashGeneric provides a platform-independent hash implementation
21func hashGeneric(data []byte) []byte {
22    var hash uint32 = 2166136261 // FNV-1a 32-bit offset basis
23
24    for _, b := range data {
25        hash ^= uint32(b)
26        hash *= 16777619 // FNV-1a 32-bit prime
27    }
28
29    result := make([]byte, 4)
30    for i := 0; i < 4; i++ {
31        result[i] = byte(hash >> (i * 8))
32    }
33    return result
34}

Example 3: Feature Flags for A/B Testing

Build tags enable powerful A/B testing and gradual feature rollouts:

 1// auth/auth.go - Main authentication interface
 2package auth
 3
 4// Authenticator provides user authentication
 5type Authenticator interface {
 6    Authenticate(username, password string) (bool, error)
 7    CreateUser(username, password string) error
 8    ValidateToken(token string) (bool, string, error)
 9}
10
11// NewAuthenticator returns configured authenticator
12func NewAuthenticator() Authenticator {
13    return newAuthenticator()
14}
 1// auth/auth_v1.go - Version 1 implementation
 2//go:build !auth_v2
 3
 4package auth
 5
 6import (
 7    "errors"
 8    "fmt"
 9)
10
11type v1Auth struct {
12    users map[string]string // username -> password hash
13}
14
15func newAuthenticator() Authenticator {
16    return &v1Auth{
17        users: make(map[string]string),
18    }
19}
20
21func (a *v1Auth) Authenticate(username, password string) (bool, error) {
22    storedHash, exists := a.users[username]
23    if !exists {
24        return false, errors.New("user not found")
25    }
26
27    // Simple hash comparison
28    inputHash := fmt.Sprintf("%x", password) // Simplified for example
29    if inputHash == storedHash {
30        return true, nil
31    }
32
33    return false, errors.New("invalid password")
34}
35
36func (a *v1Auth) CreateUser(username, password string) error {
37    if _, exists := a.users[username]; exists {
38        return errors.New("user already exists")
39    }
40
41    a.users[username] = fmt.Sprintf("%x", password) // Simplified for example
42    return nil
43}
44
45func (a *v1Auth) ValidateToken(token string) (bool, string, error) {
46    // V1 doesn't support tokens
47    return false, "", errors.New("tokens not supported in v1")
48}
 1// auth/auth_v2.go - Version 2 implementation with tokens
 2//go:build auth_v2
 3
 4package auth
 5
 6import (
 7    "crypto/rand"
 8    "encoding/base64"
 9    "errors"
10    "fmt"
11    "time"
12)
13
14type v2Auth struct {
15    users  map[string]string // username -> password hash
16    tokens map[string]tokenInfo
17}
18
19type tokenInfo struct {
20    username string
21    expires  time.Time
22}
23
24func newAuthenticator() Authenticator {
25    return &v2Auth{
26        users:  make(map[string]string),
27        tokens: make(map[string]tokenInfo),
28    }
29}
30
31func (a *v2Auth) Authenticate(username, password string) (bool, error) {
32    storedHash, exists := a.users[username]
33    if !exists {
34        return false, errors.New("user not found")
35    }
36
37    inputHash := fmt.Sprintf("%x", password) // Simplified for example
38    if inputHash == storedHash {
39        return true, nil
40    }
41
42    return false, errors.New("invalid password")
43}
44
45func (a *v2Auth) CreateUser(username, password string) error {
46    if _, exists := a.users[username]; exists {
47        return errors.New("user already exists")
48    }
49
50    a.users[username] = fmt.Sprintf("%x", password) // Simplified for example
51    return nil
52}
53
54func (a *v2Auth) ValidateToken(token string) (bool, string, error) {
55    info, exists := a.tokens[token]
56    if !exists {
57        return false, "", errors.New("invalid token")
58    }
59
60    if time.Now().After(info.expires) {
61        delete(a.tokens, token)
62        return false, "", errors.New("token expired")
63    }
64
65    return true, info.username, nil
66}
67
68// GenerateToken creates a JWT-like token
69func (a *v2Auth) GenerateToken(username string) (string, error) {
70    // In production, use proper JWT library
71    tokenBytes := make([]byte, 32)
72    if _, err := rand.Read(tokenBytes); err != nil {
73        return "", err
74    }
75
76    token := base64.StdEncoding.EncodeToString(tokenBytes)
77    a.tokens[token] = tokenInfo{
78        username: username,
79        expires:  time.Now().Add(24 * time.Hour), // 24 hour expiry
80    }
81
82    return token, nil
83}

Build commands for different versions:

1# Build with V1 authentication
2go build -tags "!auth_v2" -o app-v1
3
4# Build with V2 authentication
5go build -tags "auth_v2" -o app-v2
6
7# Build experimental version
8go build -tags "auth_v2,experimental" -o app-v2-exp

Common Patterns and Best Practices

Pattern 1: Environment-Specific Configuration

Build tags excel at creating different configurations for different environments:

 1// config/config.go - Common configuration types
 2package config
 3
 4import (
 5    "time"
 6)
 7
 8// DatabaseConfig holds database connection settings
 9type DatabaseConfig struct {
10    Host         string
11    Port         int
12    Name         string
13    Username     string
14    Password     string
15    MaxOpenConns int
16    Timeout      time.Duration
17    SSLMode      bool
18}
19
20// APIConfig holds API server settings
21type APIConfig struct {
22    Port           int
23    Timeout        time.Duration
24    RateLimit      int
25    EnableCORS     bool
26    TrustedOrigins []string
27}
28
29// Config holds all application configuration
30type Config struct {
31    Environment string
32    Database    DatabaseConfig
33    API         APIConfig
34    LogLevel    string
35    Debug       bool
36    Features    map[string]bool
37}
38
39// GetConfig returns environment-specific configuration
40func GetConfig() *Config {
41    return getConfig()
42}
 1// config/config_development.go - Development configuration
 2//go:build development
 3
 4package config
 5
 6import "time"
 7
 8func getConfig() *Config {
 9    return &Config{
10        Environment: "development",
11        Database: DatabaseConfig{
12            Host:         "localhost",
13            Port:         5432,
14            Name:         "myapp_dev",
15            Username:     "dev_user",
16            Password:     "dev_password",
17            MaxOpenConns: 5,
18            Timeout:      30 * time.Second,
19            SSLMode:      false,
20        },
21        API: APIConfig{
22            Port:           8080,
23            Timeout:        5 * time.Second,
24            RateLimit:      1000,
25            EnableCORS:     true,
26            TrustedOrigins: []string{"http://localhost:3000"},
27        },
28        LogLevel: "debug",
29        Debug:    true,
30        Features: map[string]bool{
31            "new_ui":          true,
32            "analytics":       false,
33            "beta_features":   true,
34            "verbose_logging": true,
35        },
36    }
37}
 1// config/config_production.go - Production configuration
 2//go:build !development && !staging
 3
 4package config
 5
 6import (
 7    "os"
 8    "strconv"
 9    "time"
10)
11
12func getConfig() *Config {
13    return &Config{
14        Environment: "production",
15        Database: DatabaseConfig{
16            Host:         getEnv("DB_HOST", "prod-db.example.com"),
17            Port:         getEnvInt("DB_PORT", 5432),
18            Name:         getEnv("DB_NAME", "myapp_prod"),
19            Username:     getEnv("DB_USER", "app_user"),
20            Password:     getEnv("DB_PASSWORD", ""),
21            MaxOpenConns: getEnvInt("DB_MAX_CONNS", 100),
22            Timeout:      10 * time.Second,
23            SSLMode:      true,
24        },
25        API: APIConfig{
26            Port:      getEnvInt("API_PORT", 80),
27            Timeout:   time.Duration(getEnvInt("API_TIMEOUT", 10)) * time.Second,
28            RateLimit: getEnvInt("API_RATELIMIT", 10000),
29            EnableCORS: false,
30            TrustedOrigins: []string{
31                "https://api.example.com",
32                "https://admin.example.com",
33            },
34        },
35        LogLevel: "info",
36        Debug:    false,
37        Features: map[string]bool{
38            "new_ui":          false,
39            "analytics":       true,
40            "beta_features":   false,
41            "verbose_logging": false,
42        },
43    }
44}
45
46func getEnv(key, defaultValue string) string {
47    if value := os.Getenv(key); value != "" {
48        return value
49    }
50    return defaultValue
51}
52
53func getEnvInt(key string, defaultValue int) int {
54    if value := os.Getenv(key); value != "" {
55        if intValue, err := strconv.Atoi(value); err == nil {
56            return intValue
57        }
58    }
59    return defaultValue
60}

Build commands:

1# Development build
2go build -tags "development" -o app-dev
3
4# Production build
5go build -o app-prod
6
7# Staging build
8go build -tags "staging" -o app-staging

Pattern 2: Testing with Build Tags

Separate unit tests from integration tests using build tags:

 1// database/user_test.go - Unit tests
 2package database
 3
 4import (
 5    "testing"
 6)
 7
 8func TestUserCreation(t *testing.T) {
 9    db := NewMemoryDB()
10    defer db.Close()
11
12    user := &User{
13        ID:       1,
14        Username: "testuser",
15        Email:    "test@example.com",
16    }
17
18    err := db.CreateUser(user)
19    if err != nil {
20        t.Fatalf("Failed to create user: %v", err)
21    }
22
23    retrieved, err := db.GetUser(1)
24    if err != nil {
25        t.Fatalf("Failed to get user: %v", err)
26    }
27
28    if retrieved.Username != "testuser" {
29        t.Errorf("Expected username 'testuser', got '%s'", retrieved.Username)
30    }
31}
32
33func BenchmarkUserOperations(b *testing.B) {
34    db := NewMemoryDB()
35    defer db.Close()
36
37    user := &User{
38        ID:       1,
39        Username: "benchmark_user",
40        Email:    "bench@example.com",
41    }
42
43    b.ResetTimer()
44    for i := 0; i < b.N; i++ {
45        db.CreateUser(user)
46        db.GetUser(1)
47    }
48}
 1// database/user_integration_test.go - Integration tests
 2//go:build integration
 3
 4package database
 5
 6import (
 7    "testing"
 8)
 9
10func TestUserIntegration(t *testing.T) {
11    // This test requires a real database
12    db, err := NewPostgresDB("postgres://localhost:5432/testdb?sslmode=disable")
13    if err != nil {
14        t.Skipf("Skipping integration test: %v", err)
15    }
16    defer db.Close()
17
18    user := &User{
19        ID:       1,
20        Username: "integration_user",
21        Email:    "integration@example.com",
22    }
23
24    err = db.CreateUser(user)
25    if err != nil {
26        t.Fatalf("Failed to create user: %v", err)
27    }
28
29    // Test that user persists across connections
30    db2, err := NewPostgresDB("postgres://localhost:5432/testdb?sslmode=disable")
31    if err != nil {
32        t.Fatalf("Failed to connect second database: %v", err)
33    }
34    defer db2.Close()
35
36    retrieved, err := db2.GetUser(1)
37    if err != nil {
38        t.Fatalf("Failed to get user from second connection: %v", err)
39    }
40
41    if retrieved.Username != "integration_user" {
42        t.Errorf("Expected username 'integration_user', got '%s'", retrieved.Username)
43    }
44}

Test commands:

1# Run unit tests only
2go test ./database/...
3
4# Run all tests including integration
5go test -tags "integration" ./database/...
6
7# Run specific test
8go test -tags "integration" -run TestUserIntegration ./database/

Pattern 3: Fallback Implementations

Always provide fallbacks for unsupported platforms:

 1// notifications/notifier.go - Main notification interface
 2package notifications
 3
 4type Notifier interface {
 5    Send(title, message string) error
 6    IsSupported() bool
 7}
 8
 9// NewNotifier returns platform-appropriate notifier
10func NewNotifier() Notifier {
11    return newNotifier()
12}
13
14func IsDesktopNotificationSupported() bool {
15    return isDesktopSupported()
16}
 1// notifications/notifier_desktop.go - Desktop notifications
 2//go:build (linux || darwin || windows) && !mobile
 3
 4package notifications
 5
 6import (
 7    "errors"
 8    "runtime"
 9)
10
11type desktopNotifier struct{}
12
13func newNotifier() Notifier {
14    return &desktopNotifier{}
15}
16
17func (d *desktopNotifier) Send(title, message string) error {
18    // Platform-specific implementation would go here
19    // This is a simplified example
20    switch runtime.GOOS {
21    case "darwin":
22        return sendMacOSNotification(title, message)
23    case "linux":
24        return sendLinuxNotification(title, message)
25    case "windows":
26        return sendWindowsNotification(title, message)
27    default:
28        return errors.New("unsupported platform")
29    }
30}
31
32func (d *desktopNotifier) IsSupported() bool {
33    return true
34}
35
36func isDesktopSupported() bool {
37    return runtime.GOOS == "darwin" || runtime.GOOS == "linux" || runtime.GOOS == "windows"
38}
39
40// Placeholder implementations
41func sendMacOSNotification(title, message string) error {
42    // Use osascript or NotificationCenter
43    return nil
44}
45
46func sendLinuxNotification(title, message string) error {
47    // Use libnotify or D-Bus
48    return nil
49}
50
51func sendWindowsNotification(title, message string) error {
52    // Use Windows Toast API
53    return nil
54}
 1// notifications/notifier_fallback.go - Fallback for unsupported platforms
 2//go:build !(linux || darwin || windows) || mobile
 3
 4package notifications
 5
 6import (
 7    "fmt"
 8    "log"
 9)
10
11type fallbackNotifier struct{}
12
13func newNotifier() Notifier {
14    return &fallbackNotifier{}
15}
16
17func (f *fallbackNotifier) Send(title, message string) error {
18    // Log the notification instead
19    logMsg := fmt.Sprintf("[NOTIFICATION] %s: %s", title, message)
20    log.Println(logMsg)
21    return nil
22}
23
24func (f *fallbackNotifier) IsSupported() bool {
25    return false
26}
27
28func isDesktopSupported() bool {
29    return false
30}

Integration and Mastery - Advanced Techniques

Technique 1: Complex Build Tag Expressions

Master complex boolean expressions for fine-grained control:

Common Patterns:

 1// Platform AND architecture
 2//go:build linux && amd64
 3
 4// Platform OR platform
 5//go:build linux || darwin
 6
 7// NOT platform AND feature
 8//go:build !windows && production
 9
10// Complex nested expression
11//go:build (linux && amd64) || (darwin && arm64) && !debug
12
13// Multiple conditions
14//go:build (production || staging) && !test && (amd64 || arm64)

Technique 2: Build Tag Validation System

Create a system to validate build tag combinations:

 1// build/validator.go - Build tag validation
 2package build
 3
 4import (
 5    "fmt"
 6    "os"
 7    "runtime"
 8)
 9
10type ValidationResult struct {
11    Valid    bool
12    Errors   []string
13    Warnings []string
14}
15
16type BuildContext struct {
17    OS       string
18    Arch     string
19    Compiler string
20    Tags     []string
21}
22
23func ValidateTags(tags []string) ValidationResult {
24    result := ValidationResult{Valid: true}
25
26    ctx := BuildContext{
27        OS:       runtime.GOOS,
28        Arch:     runtime.GOARCH,
29        Compiler: getCompiler(),
30        Tags:     tags,
31    }
32
33    // Check for conflicting tags
34    if contains(tags, "dev") && contains(tags, "prod") {
35        result.Valid = false
36        result.Errors = append(result.Errors, "cannot use both 'dev' and 'prod' tags")
37    }
38
39    // Check for incompatible platform tags
40    if contains(tags, "windows-only") && ctx.OS != "windows" {
41        result.Valid = false
42        result.Errors = append(result.Errors, "windows-only tag requires Windows OS")
43    }
44
45    // Check for missing required tags
46    if contains(tags, "postgres") && !contains(tags, "database") {
47        result.Warnings = append(result.Warnings, "postgres tag typically requires database tag")
48    }
49
50    // Validate tag syntax
51    for _, tag := range tags {
52        if !isValidTag(tag) {
53            result.Errors = append(result.Errors, fmt.Sprintf("invalid tag format: %s", tag))
54        }
55    }
56
57    return result
58}
59
60func contains(slice []string, item string) bool {
61    for _, s := range slice {
62        if s == item {
63            return true
64        }
65    }
66    return false
67}
68
69func isValidTag(tag string) bool {
70    // Basic validation - tags should be alphanumeric and underscores
71    for _, r := range tag {
72        if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {
73            return false
74        }
75    }
76    return len(tag) > 0
77}
78
79func getCompiler() string {
80    if os.Getenv("GCCGO") == "1" {
81        return "gccgo"
82    }
83    return "gc"
84}

Technique 3: Build Tag Generation System

Automate build tag generation for CI/CD:

 1// build/generator.go - Build tag generator for CI/CD
 2package build
 3
 4import (
 5    "fmt"
 6    "os"
 7    "strings"
 8)
 9
10type BuildConfig struct {
11    Environment string   // dev, staging, prod
12    Features    []string
13    Platform    string // auto, linux, windows, darwin
14    Arch        string // auto, amd64, arm64
15    Compiler    string // auto, gc, gccgo
16}
17
18func GenerateTags(config BuildConfig) (string, error) {
19    var tags []string
20
21    // Environment tags
22    if config.Environment != "" && config.Environment != "auto" {
23        tags = append(tags, config.Environment)
24    }
25
26    // Platform tags
27    if config.Platform != "" && config.Platform != "auto" {
28        tags = append(tags, config.Platform)
29    }
30
31    // Architecture tags
32    if config.Arch != "" && config.Arch != "auto" {
33        tags = append(tags, config.Arch)
34    }
35
36    // Compiler tags
37    if config.Compiler != "" && config.Compiler != "auto" {
38        tags = append(tags, config.Compiler)
39    }
40
41    // Feature tags
42    for _, feature := range config.Features {
43        if feature != "" {
44            tags = append(tags, feature)
45        }
46    }
47
48    if len(tags) == 0 {
49        return "", fmt.Errorf("no build tags specified")
50    }
51
52    return strings.Join(tags, ","), nil
53}
54
55// GetDefaultConfig returns build config based on environment
56func GetDefaultConfig() BuildConfig {
57    config := BuildConfig{
58        Environment: os.Getenv("BUILD_ENV"),
59        Platform:    os.Getenv("BUILD_PLATFORM"),
60        Arch:        os.Getenv("BUILD_ARCH"),
61        Compiler:    os.Getenv("BUILD_COMPILER"),
62    }
63
64    // Parse feature flags from comma-separated list
65    if features := os.Getenv("BUILD_FEATURES"); features != "" {
66        config.Features = strings.Split(features, ",")
67        for i, feature := range config.Features {
68            config.Features[i] = strings.TrimSpace(feature)
69        }
70    }
71
72    return config
73}

Technique 4: Version Constraints

Use build tags to support different Go versions:

 1// features_new.go - Features for Go 1.20+
 2//go:build go1.20
 3
 4package features
 5
 6import "fmt"
 7
 8func UseNewFeature() string {
 9    // Use features available in Go 1.20+
10    return fmt.Sprintf("Using Go 1.20+ features")
11}
 1// features_old.go - Fallback for older Go versions
 2//go:build !go1.20
 3
 4package features
 5
 6import "fmt"
 7
 8func UseNewFeature() string {
 9    // Fallback implementation
10    return fmt.Sprintf("Using fallback for older Go versions")
11}

Technique 5: CGo Control

Control CGo usage with build tags:

 1// database_cgo.go - CGo-enabled SQLite driver
 2//go:build cgo
 3
 4package database
 5
 6/*
 7#cgo LDFLAGS: -lsqlite3
 8#include <sqlite3.h>
 9*/
10import "C"
11
12func OpenDatabase(path string) (*DB, error) {
13    // Use CGo-based SQLite driver for best performance
14    return openNativeDB(path)
15}
 1// database_nocgo.go - Pure Go SQLite driver
 2//go:build !cgo
 3
 4package database
 5
 6import (
 7    "github.com/mattn/go-sqlite3"
 8)
 9
10func OpenDatabase(path string) (*DB, error) {
11    // Use pure Go implementation
12    return openPureGoDB(path)
13}

Best Practices and Common Pitfalls

Best Practices

  1. Use modern syntax - Always prefer //go:build over // +build
  2. Keep expressions simple - Complex tags are hard to maintain and debug
  3. Document tag purpose - Add comments explaining why each tag is needed
  4. Provide fallbacks - Always have a default implementation for unsupported platforms
  5. Test all combinations - Ensure your CI tests all build tag variations
  6. Use naming conventions - Prefer *_GOOS.go over explicit build tags when possible
  7. Version control compatibility - Use both syntaxes for backward compatibility if needed

Common Pitfalls and Solutions

Pitfall 1: Missing Blank Line

Problem:

1// ❌ WRONG - Missing blank line after build tag
2//go:build linux
3package main

Solution:

1// ✅ CORRECT - Blank line required before package
2//go:build linux
3
4package main

Pitfall 2: Conflicting Modern and Legacy Tags

Problem:

1//go:build linux && amd64
2// +build linux,amd64  // These don't match!
3package main

Solution:

1//go:build linux && amd64
2// +build linux,amd64  // Must be equivalent
3package main

Pitfall 3: No Fallback Implementation

Problem:

1//go:build windows
2func GetPlatformInfo() string {
3    return "Windows specific info"
4}
5// What happens on Linux? Compilation error!

Solution:

1//go:build windows
2func GetPlatformInfo() string {
3    return "Windows specific info"
4}
5
6//go:build !windows
7func GetPlatformInfo() string {
8    return "Generic platform info"
9}

Pitfall 4: Overly Complex Expressions

Problem:

1//go:build (windows && amd64) || (linux && (amd64 || arm64)) && !development && (production || staging)

Solution:
Split into multiple files with simpler tags:

1// platform_windows_amd64.go
2//go:build windows && amd64
3
4// platform_linux_amd64.go
5//go:build linux && amd64
6
7// platform_generic.go
8//go:build !windows && !linux

Pitfall 5: Using Build Tags for Runtime Behavior

Wrong:

1//go:build feature_x
2func ProcessData(data []byte) []byte {
3    if hasFeatureX() { // Runtime check defeats purpose!
4        return processWithFeatureX(data)
5    }
6    return processDataNormal(data)
7}

Right:

1//go:build feature_x
2func ProcessData(data []byte) []byte {
3    return processWithFeatureX(data)
4}
5
6//go:build !feature_x
7func ProcessData(data []byte) []byte {
8    return processDataNormal(data)
9}

Build Tag Troubleshooting

Common issues and solutions:

  1. Build tag not recognized

    • Check for blank line before package
    • Verify tag syntax
    • Use go vet to check for issues
  2. Unexpected files included/excluded

    • Use go list -f '{{.GoFiles}}' ./... to see which files are included
    • Check for conflicting tags
    • Verify environment variables
  3. Cross-compilation issues

    • Build tags can make cross-compilation complex
    • Consider using CGo-free alternatives for broad compatibility
    • Test on target platforms early

Practice Exercises

Exercise 1: Cross-Platform Configuration Manager

Learning Objectives: Master platform-specific implementations and file naming conventions

Difficulty: Intermediate

Real-World Context: Configuration management systems need different paths, defaults, and behaviors on each platform. This exercise simulates building a production-ready configuration loader that adapts to Windows, Linux, and macOS.

Task: Create a configuration package that:

  1. Uses file naming conventions (no explicit build tags) for platform detection
  2. Implements platform-specific config file paths
  3. Provides different default values per platform
  4. Includes a test suite that validates platform behavior

Requirements:

  • config_windows.go - Windows-specific paths (AppData, Registry)
  • config_linux.go - Linux-specific paths (/etc, ~/.config)
  • config_darwin.go - macOS-specific paths (~/Library/Preferences)
  • Common interface for all platforms
  • Unit tests for each platform

Solution:

Show Solution
 1// config/config.go - Common interface
 2package config
 3
 4import (
 5    "encoding/json"
 6    "fmt"
 7    "os"
 8    "path/filepath"
 9)
10
11// Config represents application configuration
12type Config struct {
13    AppName    string            `json:"app_name"`
14    Version    string            `json:"version"`
15    Settings   map[string]string `json:"settings"`
16    ConfigPath string            `json:"-"`
17}
18
19// Loader defines platform-specific config loading
20type Loader interface {
21    GetConfigDir() string
22    GetCacheDir() string
23    GetLogDir() string
24    GetDefaultEditor() string
25}
26
27// LoadConfig loads configuration from platform-specific location
28func LoadConfig(appName string) (*Config, error) {
29    loader := newLoader()
30    configDir := loader.GetConfigDir()
31    configPath := filepath.Join(configDir, appName+".json")
32
33    cfg := &Config{
34        AppName:    appName,
35        Version:    "1.0.0",
36        Settings:   make(map[string]string),
37        ConfigPath: configPath,
38    }
39
40    // Try to load existing config
41    data, err := os.ReadFile(configPath)
42    if err == nil {
43        if err := json.Unmarshal(data, cfg); err != nil {
44            return nil, fmt.Errorf("failed to parse config: %w", err)
45        }
46    }
47
48    // Set platform-specific defaults
49    if cfg.Settings["editor"] == "" {
50        cfg.Settings["editor"] = loader.GetDefaultEditor()
51    }
52    if cfg.Settings["cache_dir"] == "" {
53        cfg.Settings["cache_dir"] = loader.GetCacheDir()
54    }
55    if cfg.Settings["log_dir"] == "" {
56        cfg.Settings["log_dir"] = loader.GetLogDir()
57    }
58
59    return cfg, nil
60}
61
62// Save writes config to disk
63func (c *Config) Save() error {
64    // Ensure config directory exists
65    configDir := filepath.Dir(c.ConfigPath)
66    if err := os.MkdirAll(configDir, 0755); err != nil {
67        return fmt.Errorf("failed to create config directory: %w", err)
68    }
69
70    data, err := json.MarshalIndent(c, "", "  ")
71    if err != nil {
72        return fmt.Errorf("failed to marshal config: %w", err)
73    }
74
75    if err := os.WriteFile(c.ConfigPath, data, 0644); err != nil {
76        return fmt.Errorf("failed to write config: %w", err)
77    }
78
79    return nil
80}
 1// config/config_windows.go - Windows implementation
 2package config
 3
 4import (
 5    "os"
 6    "path/filepath"
 7)
 8
 9type windowsLoader struct{}
10
11func newLoader() Loader {
12    return &windowsLoader{}
13}
14
15func (w *windowsLoader) GetConfigDir() string {
16    appData := os.Getenv("APPDATA")
17    if appData == "" {
18        appData = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Roaming")
19    }
20    return appData
21}
22
23func (w *windowsLoader) GetCacheDir() string {
24    localAppData := os.Getenv("LOCALAPPDATA")
25    if localAppData == "" {
26        localAppData = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local")
27    }
28    return filepath.Join(localAppData, "Cache")
29}
30
31func (w *windowsLoader) GetLogDir() string {
32    return filepath.Join(w.GetCacheDir(), "Logs")
33}
34
35func (w *windowsLoader) GetDefaultEditor() string {
36    return "notepad.exe"
37}
 1// config/config_linux.go - Linux implementation
 2package config
 3
 4import (
 5    "os"
 6    "path/filepath"
 7)
 8
 9type linuxLoader struct{}
10
11func newLoader() Loader {
12    return &linuxLoader{}
13}
14
15func (l *linuxLoader) GetConfigDir() string {
16    configHome := os.Getenv("XDG_CONFIG_HOME")
17    if configHome == "" {
18        configHome = filepath.Join(os.Getenv("HOME"), ".config")
19    }
20    return configHome
21}
22
23func (l *linuxLoader) GetCacheDir() string {
24    cacheHome := os.Getenv("XDG_CACHE_HOME")
25    if cacheHome == "" {
26        cacheHome = filepath.Join(os.Getenv("HOME"), ".cache")
27    }
28    return cacheHome
29}
30
31func (l *linuxLoader) GetLogDir() string {
32    return "/var/log"
33}
34
35func (l *linuxLoader) GetDefaultEditor() string {
36    editor := os.Getenv("EDITOR")
37    if editor == "" {
38        return "vi"
39    }
40    return editor
41}
 1// config/config_darwin.go - macOS implementation
 2package config
 3
 4import (
 5    "os"
 6    "path/filepath"
 7)
 8
 9type darwinLoader struct{}
10
11func newLoader() Loader {
12    return &darwinLoader{}
13}
14
15func (d *darwinLoader) GetConfigDir() string {
16    home := os.Getenv("HOME")
17    return filepath.Join(home, "Library", "Application Support")
18}
19
20func (d *darwinLoader) GetCacheDir() string {
21    home := os.Getenv("HOME")
22    return filepath.Join(home, "Library", "Caches")
23}
24
25func (d *darwinLoader) GetLogDir() string {
26    home := os.Getenv("HOME")
27    return filepath.Join(home, "Library", "Logs")
28}
29
30func (d *darwinLoader) GetDefaultEditor() string {
31    return "nano"
32}
 1// config/config_test.go - Tests
 2package config
 3
 4import (
 5    "os"
 6    "path/filepath"
 7    "runtime"
 8    "testing"
 9)
10
11func TestLoadConfig(t *testing.T) {
12    cfg, err := LoadConfig("testapp")
13    if err != nil {
14        t.Fatalf("Failed to load config: %v", err)
15    }
16
17    if cfg.AppName != "testapp" {
18        t.Errorf("Expected app name 'testapp', got '%s'", cfg.AppName)
19    }
20
21    // Verify platform-specific defaults
22    loader := newLoader()
23    expectedEditor := loader.GetDefaultEditor()
24    if cfg.Settings["editor"] != expectedEditor {
25        t.Errorf("Expected editor '%s', got '%s'", expectedEditor, cfg.Settings["editor"])
26    }
27}
28
29func TestPlatformSpecificPaths(t *testing.T) {
30    loader := newLoader()
31
32    configDir := loader.GetConfigDir()
33    if configDir == "" {
34        t.Error("Config dir should not be empty")
35    }
36
37    cacheDir := loader.GetCacheDir()
38    if cacheDir == "" {
39        t.Error("Cache dir should not be empty")
40    }
41
42    // Platform-specific validations
43    switch runtime.GOOS {
44    case "windows":
45        if !filepath.IsAbs(configDir) {
46            t.Error("Windows config dir should be absolute path")
47        }
48    case "linux":
49        if !filepath.IsAbs(configDir) {
50            t.Error("Linux config dir should be absolute path")
51        }
52    case "darwin":
53        if !filepath.IsAbs(configDir) {
54            t.Error("macOS config dir should be absolute path")
55        }
56    }
57}
58
59func TestConfigSaveLoad(t *testing.T) {
60    // Create temporary directory
61    tempDir, err := os.MkdirTemp("", "config-test")
62    if err != nil {
63        t.Fatalf("Failed to create temp dir: %v", err)
64    }
65    defer os.RemoveAll(tempDir)
66
67    // Create config
68    cfg := &Config{
69        AppName:    "testapp",
70        Version:    "1.0.0",
71        Settings:   map[string]string{"key": "value"},
72        ConfigPath: filepath.Join(tempDir, "testapp.json"),
73    }
74
75    // Save config
76    if err := cfg.Save(); err != nil {
77        t.Fatalf("Failed to save config: %v", err)
78    }
79
80    // Verify file exists
81    if _, err := os.Stat(cfg.ConfigPath); os.IsNotExist(err) {
82        t.Error("Config file was not created")
83    }
84}

Usage Example:

 1package main
 2
 3import (
 4    "fmt"
 5    "yourproject/config"
 6)
 7
 8func main() {
 9    cfg, err := config.LoadConfig("myapp")
10    if err != nil {
11        panic(err)
12    }
13
14    fmt.Printf("Platform: %s\n", runtime.GOOS)
15    fmt.Printf("Config path: %s\n", cfg.ConfigPath)
16    fmt.Printf("Default editor: %s\n", cfg.Settings["editor"])
17    fmt.Printf("Cache dir: %s\n", cfg.Settings["cache_dir"])
18    fmt.Printf("Log dir: %s\n", cfg.Settings["log_dir"])
19
20    // Save configuration
21    cfg.Settings["theme"] = "dark"
22    if err := cfg.Save(); err != nil {
23        panic(err)
24    }
25}
26
27// run

Exercise 2: CPU Architecture-Optimized Bitmap Operations

Learning Objectives: Implement architecture-specific optimizations using build tags

Difficulty: Advanced

Real-World Context: Graphics libraries, compression algorithms, and cryptographic systems benefit massively from CPU-specific SIMD instructions. This exercise simulates building optimized bitmap operations.

Task: Create a bitmap package that:

  1. Implements generic bitmap operations (AND, OR, XOR, NOT)
  2. Provides AMD64-optimized versions using 64-bit operations
  3. Provides ARM64-optimized versions
  4. Includes benchmarks comparing architectures
  5. Falls back gracefully on unsupported architectures

Solution:

Show Solution
 1// bitmap/bitmap.go - Common interface
 2package bitmap
 3
 4// Bitmap represents a bit array
 5type Bitmap struct {
 6    data []byte
 7    size int
 8}
 9
10// New creates a new bitmap with specified size in bits
11func New(size int) *Bitmap {
12    byteSize := (size + 7) / 8
13    return &Bitmap{
14        data: make([]byte, byteSize),
15        size: size,
16    }
17}
18
19// Set sets a bit at position
20func (b *Bitmap) Set(pos int) {
21    if pos < 0 || pos >= b.size {
22        return
23    }
24    byteIdx := pos / 8
25    bitIdx := pos % 8
26    b.data[byteIdx] |= 1 << bitIdx
27}
28
29// Clear clears a bit at position
30func (b *Bitmap) Clear(pos int) {
31    if pos < 0 || pos >= b.size {
32        return
33    }
34    byteIdx := pos / 8
35    bitIdx := pos % 8
36    b.data[byteIdx] &^= 1 << bitIdx
37}
38
39// Get returns the bit value at position
40func (b *Bitmap) Get(pos int) bool {
41    if pos < 0 || pos >= b.size {
42        return false
43    }
44    byteIdx := pos / 8
45    bitIdx := pos % 8
46    return (b.data[byteIdx] & (1 << bitIdx)) != 0
47}
48
49// Size returns bitmap size in bits
50func (b *Bitmap) Size() int {
51    return b.size
52}
53
54// And performs bitwise AND with another bitmap
55func (b *Bitmap) And(other *Bitmap) *Bitmap {
56    return bitwiseAnd(b, other)
57}
58
59// Or performs bitwise OR with another bitmap
60func (b *Bitmap) Or(other *Bitmap) *Bitmap {
61    return bitwiseOr(b, other)
62}
63
64// Xor performs bitwise XOR with another bitmap
65func (b *Bitmap) Xor(other *Bitmap) *Bitmap {
66    return bitwiseXor(b, other)
67}
68
69// Not performs bitwise NOT
70func (b *Bitmap) Not() *Bitmap {
71    return bitwiseNot(b)
72}
73
74// Count returns number of set bits
75func (b *Bitmap) Count() int {
76    return popcount(b.data)
77}
  1// bitmap/bitmap_amd64.go - AMD64 optimizations
  2package bitmap
  3
  4// bitwiseAnd performs AND operation using 64-bit chunks
  5func bitwiseAnd(a, b *Bitmap) *Bitmap {
  6    size := a.size
  7    if b.size < size {
  8        size = b.size
  9    }
 10
 11    result := New(size)
 12
 13    // Process in 64-bit chunks
 14    chunks := len(a.data) / 8
 15    for i := 0; i < chunks; i++ {
 16        offset := i * 8
 17        aChunk := uint64(a.data[offset]) | uint64(a.data[offset+1])<<8 |
 18            uint64(a.data[offset+2])<<16 | uint64(a.data[offset+3])<<24 |
 19            uint64(a.data[offset+4])<<32 | uint64(a.data[offset+5])<<40 |
 20            uint64(a.data[offset+6])<<48 | uint64(a.data[offset+7])<<56
 21
 22        bChunk := uint64(b.data[offset]) | uint64(b.data[offset+1])<<8 |
 23            uint64(b.data[offset+2])<<16 | uint64(b.data[offset+3])<<24 |
 24            uint64(b.data[offset+4])<<32 | uint64(b.data[offset+5])<<40 |
 25            uint64(b.data[offset+6])<<48 | uint64(b.data[offset+7])<<56
 26
 27        resultChunk := aChunk & bChunk
 28
 29        result.data[offset] = byte(resultChunk)
 30        result.data[offset+1] = byte(resultChunk >> 8)
 31        result.data[offset+2] = byte(resultChunk >> 16)
 32        result.data[offset+3] = byte(resultChunk >> 24)
 33        result.data[offset+4] = byte(resultChunk >> 32)
 34        result.data[offset+5] = byte(resultChunk >> 40)
 35        result.data[offset+6] = byte(resultChunk >> 48)
 36        result.data[offset+7] = byte(resultChunk >> 56)
 37    }
 38
 39    // Process remaining bytes
 40    for i := chunks * 8; i < len(a.data) && i < len(b.data); i++ {
 41        result.data[i] = a.data[i] & b.data[i]
 42    }
 43
 44    return result
 45}
 46
 47func bitwiseOr(a, b *Bitmap) *Bitmap {
 48    size := a.size
 49    if b.size > size {
 50        size = b.size
 51    }
 52
 53    result := New(size)
 54
 55    chunks := len(a.data) / 8
 56    for i := 0; i < chunks; i++ {
 57        offset := i * 8
 58        aChunk := uint64(a.data[offset]) | uint64(a.data[offset+1])<<8 |
 59            uint64(a.data[offset+2])<<16 | uint64(a.data[offset+3])<<24 |
 60            uint64(a.data[offset+4])<<32 | uint64(a.data[offset+5])<<40 |
 61            uint64(a.data[offset+6])<<48 | uint64(a.data[offset+7])<<56
 62
 63        bChunk := uint64(b.data[offset]) | uint64(b.data[offset+1])<<8 |
 64            uint64(b.data[offset+2])<<16 | uint64(b.data[offset+3])<<24 |
 65            uint64(b.data[offset+4])<<32 | uint64(b.data[offset+5])<<40 |
 66            uint64(b.data[offset+6])<<48 | uint64(b.data[offset+7])<<56
 67
 68        resultChunk := aChunk | bChunk
 69
 70        result.data[offset] = byte(resultChunk)
 71        result.data[offset+1] = byte(resultChunk >> 8)
 72        result.data[offset+2] = byte(resultChunk >> 16)
 73        result.data[offset+3] = byte(resultChunk >> 24)
 74        result.data[offset+4] = byte(resultChunk >> 32)
 75        result.data[offset+5] = byte(resultChunk >> 40)
 76        result.data[offset+6] = byte(resultChunk >> 48)
 77        result.data[offset+7] = byte(resultChunk >> 56)
 78    }
 79
 80    for i := chunks * 8; i < len(a.data) && i < len(b.data); i++ {
 81        result.data[i] = a.data[i] | b.data[i]
 82    }
 83
 84    return result
 85}
 86
 87func bitwiseXor(a, b *Bitmap) *Bitmap {
 88    size := a.size
 89    if b.size > size {
 90        size = b.size
 91    }
 92
 93    result := New(size)
 94
 95    chunks := len(a.data) / 8
 96    for i := 0; i < chunks; i++ {
 97        offset := i * 8
 98        aChunk := uint64(a.data[offset]) | uint64(a.data[offset+1])<<8 |
 99            uint64(a.data[offset+2])<<16 | uint64(a.data[offset+3])<<24 |
100            uint64(a.data[offset+4])<<32 | uint64(a.data[offset+5])<<40 |
101            uint64(a.data[offset+6])<<48 | uint64(a.data[offset+7])<<56
102
103        bChunk := uint64(b.data[offset]) | uint64(b.data[offset+1])<<8 |
104            uint64(b.data[offset+2])<<16 | uint64(b.data[offset+3])<<24 |
105            uint64(b.data[offset+4])<<32 | uint64(b.data[offset+5])<<40 |
106            uint64(b.data[offset+6])<<48 | uint64(b.data[offset+7])<<56
107
108        resultChunk := aChunk ^ bChunk
109
110        result.data[offset] = byte(resultChunk)
111        result.data[offset+1] = byte(resultChunk >> 8)
112        result.data[offset+2] = byte(resultChunk >> 16)
113        result.data[offset+3] = byte(resultChunk >> 24)
114        result.data[offset+4] = byte(resultChunk >> 32)
115        result.data[offset+5] = byte(resultChunk >> 40)
116        result.data[offset+6] = byte(resultChunk >> 48)
117        result.data[offset+7] = byte(resultChunk >> 56)
118    }
119
120    for i := chunks * 8; i < len(a.data) && i < len(b.data); i++ {
121        result.data[i] = a.data[i] ^ b.data[i]
122    }
123
124    return result
125}
126
127func bitwiseNot(a *Bitmap) *Bitmap {
128    result := New(a.size)
129
130    chunks := len(a.data) / 8
131    for i := 0; i < chunks; i++ {
132        offset := i * 8
133        aChunk := uint64(a.data[offset]) | uint64(a.data[offset+1])<<8 |
134            uint64(a.data[offset+2])<<16 | uint64(a.data[offset+3])<<24 |
135            uint64(a.data[offset+4])<<32 | uint64(a.data[offset+5])<<40 |
136            uint64(a.data[offset+6])<<48 | uint64(a.data[offset+7])<<56
137
138        resultChunk := ^aChunk
139
140        result.data[offset] = byte(resultChunk)
141        result.data[offset+1] = byte(resultChunk >> 8)
142        result.data[offset+2] = byte(resultChunk >> 16)
143        result.data[offset+3] = byte(resultChunk >> 24)
144        result.data[offset+4] = byte(resultChunk >> 32)
145        result.data[offset+5] = byte(resultChunk >> 40)
146        result.data[offset+6] = byte(resultChunk >> 48)
147        result.data[offset+7] = byte(resultChunk >> 56)
148    }
149
150    for i := chunks * 8; i < len(a.data); i++ {
151        result.data[i] = ^a.data[i]
152    }
153
154    return result
155}
156
157// popcount counts set bits using optimized algorithm
158func popcount(data []byte) int {
159    count := 0
160    for _, b := range data {
161        // Brian Kernighan's algorithm
162        v := uint64(b)
163        for v != 0 {
164            v &= v - 1
165            count++
166        }
167    }
168    return count
169}
 1// bitmap/bitmap_generic.go - Generic fallback
 2//go:build !amd64 && !arm64
 3
 4package bitmap
 5
 6// bitwiseAnd performs AND operation byte by byte
 7func bitwiseAnd(a, b *Bitmap) *Bitmap {
 8    size := a.size
 9    if b.size < size {
10        size = b.size
11    }
12
13    result := New(size)
14    minLen := len(a.data)
15    if len(b.data) < minLen {
16        minLen = len(b.data)
17    }
18
19    for i := 0; i < minLen; i++ {
20        result.data[i] = a.data[i] & b.data[i]
21    }
22
23    return result
24}
25
26func bitwiseOr(a, b *Bitmap) *Bitmap {
27    size := a.size
28    if b.size > size {
29        size = b.size
30    }
31
32    result := New(size)
33    minLen := len(a.data)
34    if len(b.data) < minLen {
35        minLen = len(b.data)
36    }
37
38    for i := 0; i < minLen; i++ {
39        result.data[i] = a.data[i] | b.data[i]
40    }
41
42    return result
43}
44
45func bitwiseXor(a, b *Bitmap) *Bitmap {
46    size := a.size
47    if b.size > size {
48        size = b.size
49    }
50
51    result := New(size)
52    minLen := len(a.data)
53    if len(b.data) < minLen {
54        minLen = len(b.data)
55    }
56
57    for i := 0; i < minLen; i++ {
58        result.data[i] = a.data[i] ^ b.data[i]
59    }
60
61    return result
62}
63
64func bitwiseNot(a *Bitmap) *Bitmap {
65    result := New(a.size)
66    for i := range a.data {
67        result.data[i] = ^a.data[i]
68    }
69    return result
70}
71
72func popcount(data []byte) int {
73    count := 0
74    for _, b := range data {
75        for b != 0 {
76            count += int(b & 1)
77            b >>= 1
78        }
79    }
80    return count
81}
 1// bitmap/bitmap_test.go - Tests and benchmarks
 2package bitmap
 3
 4import (
 5    "runtime"
 6    "testing"
 7)
 8
 9func TestBitmapOperations(t *testing.T) {
10    bm1 := New(16)
11    bm1.Set(0)
12    bm1.Set(3)
13    bm1.Set(7)
14
15    bm2 := New(16)
16    bm2.Set(3)
17    bm2.Set(5)
18    bm2.Set(7)
19
20    // Test AND
21    result := bm1.And(bm2)
22    if !result.Get(3) || !result.Get(7) {
23        t.Error("AND operation failed")
24    }
25    if result.Get(0) || result.Get(5) {
26        t.Error("AND operation produced incorrect results")
27    }
28
29    // Test OR
30    result = bm1.Or(bm2)
31    if !result.Get(0) || !result.Get(3) || !result.Get(5) || !result.Get(7) {
32        t.Error("OR operation failed")
33    }
34
35    // Test XOR
36    result = bm1.Xor(bm2)
37    if !result.Get(0) || !result.Get(5) {
38        t.Error("XOR operation failed")
39    }
40    if result.Get(3) || result.Get(7) {
41        t.Error("XOR operation produced incorrect results")
42    }
43
44    // Test NOT
45    result = bm1.Not()
46    if result.Get(0) || result.Get(3) || result.Get(7) {
47        t.Error("NOT operation failed")
48    }
49}
50
51func TestPopCount(t *testing.T) {
52    bm := New(64)
53    for i := 0; i < 10; i++ {
54        bm.Set(i * 6)
55    }
56
57    count := bm.Count()
58    if count != 10 {
59        t.Errorf("Expected count 10, got %d", count)
60    }
61}
62
63func BenchmarkBitwiseAnd(b *testing.B) {
64    bm1 := New(1024)
65    bm2 := New(1024)
66
67    // Set every other bit
68    for i := 0; i < 1024; i += 2 {
69        bm1.Set(i)
70        bm2.Set(i + 1)
71    }
72
73    b.ResetTimer()
74    b.ReportAllocs()
75
76    for i := 0; i < b.N; i++ {
77        _ = bm1.And(bm2)
78    }
79
80    b.ReportMetric(float64(b.N*1024)/b.Elapsed().Seconds()/1e9, "Gbits/sec")
81}
82
83func BenchmarkPopCount(b *testing.B) {
84    bm := New(10240)
85    for i := 0; i < 10240; i += 3 {
86        bm.Set(i)
87    }
88
89    b.ResetTimer()
90
91    for i := 0; i < b.N; i++ {
92        _ = bm.Count()
93    }
94}
95
96func TestArchitectureInfo(t *testing.T) {
97    t.Logf("Running on architecture: %s", runtime.GOARCH)
98    t.Logf("Operating system: %s", runtime.GOOS)
99}

Usage Example:

 1package main
 2
 3import (
 4    "fmt"
 5    "runtime"
 6    "yourproject/bitmap"
 7)
 8
 9func main() {
10    fmt.Printf("Architecture: %s\n", runtime.GOARCH)
11
12    // Create bitmaps
13    bm1 := bitmap.New(32)
14    bm2 := bitmap.New(32)
15
16    // Set some bits
17    for i := 0; i < 32; i += 2 {
18        bm1.Set(i)
19    }
20    for i := 1; i < 32; i += 2 {
21        bm2.Set(i)
22    }
23
24    // Perform operations
25    andResult := bm1.And(bm2)
26    orResult := bm1.Or(bm2)
27    xorResult := bm1.Xor(bm2)
28
29    fmt.Printf("AND result: %d bits set\n", andResult.Count())
30    fmt.Printf("OR result: %d bits set\n", orResult.Count())
31    fmt.Printf("XOR result: %d bits set\n", xorResult.Count())
32}
33
34// run

Exercise 3: Multi-Environment Feature Flag System

Learning Objectives: Master feature flags using build tags for A/B testing and gradual rollouts

Difficulty: Intermediate

Real-World Context: Modern SaaS platforms deploy multiple versions simultaneously to test features with different user groups. This exercise simulates a feature flag system that's compiled in, not runtime-checked.

Task: Create a feature flag system that:

  1. Defines features with build tags (stable, beta, experimental)
  2. Implements different feature sets for each environment
  3. Provides compile-time guarantees about feature availability
  4. Includes a feature registry and discovery mechanism

Solution:

Show Solution
 1// features/features.go - Common interface
 2package features
 3
 4import (
 5    "fmt"
 6    "sort"
 7)
 8
 9// Feature represents a single feature flag
10type Feature struct {
11    Name        string
12    Enabled     bool
13    Description string
14    Category    string
15}
16
17// Registry holds all available features
18type Registry struct {
19    features map[string]Feature
20}
21
22// Global registry instance
23var globalRegistry = &Registry{
24    features: make(map[string]Feature),
25}
26
27// Register adds a feature to the registry
28func Register(name, description, category string, enabled bool) {
29    globalRegistry.features[name] = Feature{
30        Name:        name,
31        Enabled:     enabled,
32        Description: description,
33        Category:    category,
34    }
35}
36
37// IsEnabled checks if a feature is enabled
38func IsEnabled(name string) bool {
39    if feature, exists := globalRegistry.features[name]; exists {
40        return feature.Enabled
41    }
42    return false
43}
44
45// GetFeature returns a feature by name
46func GetFeature(name string) (Feature, bool) {
47    feature, exists := globalRegistry.features[name]
48    return feature, exists
49}
50
51// ListFeatures returns all registered features
52func ListFeatures() []Feature {
53    features := make([]Feature, 0, len(globalRegistry.features))
54    for _, feature := range globalRegistry.features {
55        features = append(features, feature)
56    }
57
58    // Sort by name
59    sort.Slice(features, func(i, j int) bool {
60        return features[i].Name < features[j].Name
61    })
62
63    return features
64}
65
66// ListEnabledFeatures returns only enabled features
67func ListEnabledFeatures() []Feature {
68    var enabled []Feature
69    for _, feature := range globalRegistry.features {
70        if feature.Enabled {
71            enabled = append(enabled, feature)
72        }
73    }
74
75    sort.Slice(enabled, func(i, j int) bool {
76        return enabled[i].Name < enabled[j].Name
77    })
78
79    return enabled
80}
81
82// PrintFeatures prints all features to stdout
83func PrintFeatures() {
84    fmt.Println("Feature Flags:")
85    fmt.Println("==============")
86
87    for _, feature := range ListFeatures() {
88        status := "disabled"
89        if feature.Enabled {
90            status = "enabled"
91        }
92        fmt.Printf("[%s] %s - %s (%s)\n", status, feature.Name, feature.Description, feature.Category)
93    }
94}
 1// features/features_stable.go - Stable features only
 2//go:build !beta && !experimental
 3
 4package features
 5
 6func init() {
 7    // Core stable features
 8    Register("user_authentication", "User login and authentication", "core", true)
 9    Register("user_profile", "User profile management", "core", true)
10    Register("basic_search", "Basic search functionality", "core", true)
11    Register("email_notifications", "Email notification system", "core", true)
12
13    // Stable premium features
14    Register("advanced_analytics", "Advanced analytics dashboard", "premium", true)
15    Register("export_data", "Data export functionality", "premium", true)
16
17    // Beta features - disabled in stable
18    Register("ai_recommendations", "AI-powered recommendations", "beta", false)
19    Register("real_time_collab", "Real-time collaboration", "beta", false)
20
21    // Experimental features - disabled in stable
22    Register("voice_commands", "Voice command interface", "experimental", false)
23    Register("blockchain_verify", "Blockchain verification", "experimental", false)
24}
 1// features/features_beta.go - Stable + beta features
 2//go:build beta && !experimental
 3
 4package features
 5
 6func init() {
 7    // Core stable features
 8    Register("user_authentication", "User login and authentication", "core", true)
 9    Register("user_profile", "User profile management", "core", true)
10    Register("basic_search", "Basic search functionality", "core", true)
11    Register("email_notifications", "Email notification system", "core", true)
12
13    // Stable premium features
14    Register("advanced_analytics", "Advanced analytics dashboard", "premium", true)
15    Register("export_data", "Data export functionality", "premium", true)
16
17    // Beta features - enabled in beta
18    Register("ai_recommendations", "AI-powered recommendations", "beta", true)
19    Register("real_time_collab", "Real-time collaboration", "beta", true)
20    Register("dark_mode", "Dark mode theme", "beta", true)
21    Register("api_v2", "Next-generation API", "beta", true)
22
23    // Experimental features - disabled in beta
24    Register("voice_commands", "Voice command interface", "experimental", false)
25    Register("blockchain_verify", "Blockchain verification", "experimental", false)
26}
 1// features/features_experimental.go - All features including experimental
 2//go:build experimental
 3
 4package features
 5
 6func init() {
 7    // Core stable features
 8    Register("user_authentication", "User login and authentication", "core", true)
 9    Register("user_profile", "User profile management", "core", true)
10    Register("basic_search", "Basic search functionality", "core", true)
11    Register("email_notifications", "Email notification system", "core", true)
12
13    // Stable premium features
14    Register("advanced_analytics", "Advanced analytics dashboard", "premium", true)
15    Register("export_data", "Data export functionality", "premium", true)
16
17    // Beta features
18    Register("ai_recommendations", "AI-powered recommendations", "beta", true)
19    Register("real_time_collab", "Real-time collaboration", "beta", true)
20    Register("dark_mode", "Dark mode theme", "beta", true)
21    Register("api_v2", "Next-generation API", "beta", true)
22
23    // Experimental features - all enabled
24    Register("voice_commands", "Voice command interface", "experimental", true)
25    Register("blockchain_verify", "Blockchain verification", "experimental", true)
26    Register("quantum_encryption", "Quantum-resistant encryption", "experimental", true)
27    Register("ar_interface", "Augmented reality interface", "experimental", true)
28}
 1// features/features_test.go - Tests
 2package features
 3
 4import (
 5    "testing"
 6)
 7
 8func TestFeatureRegistration(t *testing.T) {
 9    features := ListFeatures()
10    if len(features) == 0 {
11        t.Error("No features registered")
12    }
13
14    // Core features should always be enabled
15    coreFeatures := []string{"user_authentication", "user_profile", "basic_search"}
16    for _, name := range coreFeatures {
17        if !IsEnabled(name) {
18            t.Errorf("Core feature %s should be enabled", name)
19        }
20    }
21}
22
23func TestFeatureCategories(t *testing.T) {
24    categories := make(map[string]int)
25
26    for _, feature := range ListFeatures() {
27        categories[feature.Category]++
28    }
29
30    if categories["core"] == 0 {
31        t.Error("Should have at least one core feature")
32    }
33}
34
35func TestEnabledFeatures(t *testing.T) {
36    enabled := ListEnabledFeatures()
37
38    for _, feature := range enabled {
39        if !feature.Enabled {
40            t.Errorf("Feature %s in enabled list but not enabled", feature.Name)
41        }
42    }
43}

Usage Example:

 1package main
 2
 3import (
 4    "fmt"
 5    "yourproject/features"
 6)
 7
 8func main() {
 9    fmt.Println("Application Feature Flags\n")
10
11    // Print all features
12    features.PrintFeatures()
13
14    fmt.Println("\nFeature Checks:")
15
16    // Check specific features
17    if features.IsEnabled("ai_recommendations") {
18        fmt.Println("✓ AI Recommendations are enabled")
19    } else {
20        fmt.Println("✗ AI Recommendations are disabled")
21    }
22
23    if features.IsEnabled("voice_commands") {
24        fmt.Println("✓ Voice Commands are enabled")
25    } else {
26        fmt.Println("✗ Voice Commands are disabled")
27    }
28
29    // Get enabled feature count
30    enabled := features.ListEnabledFeatures()
31    fmt.Printf("\nTotal enabled features: %d\n", len(enabled))
32}
33
34// run

Build Commands:

1# Stable build (production)
2go build -o app-stable
3
4# Beta build (beta testers)
5go build -tags "beta" -o app-beta
6
7# Experimental build (internal testing)
8go build -tags "experimental" -o app-experimental

Exercise 4: Cross-Platform System Information Collector

Learning Objectives: Implement platform-specific system calls and information gathering

Difficulty: Advanced

Real-World Context: Monitoring tools, system diagnostics, and infrastructure management need to collect platform-specific metrics. This exercise builds a cross-platform system information collector.

Task: Create a sysinfo package that:

  1. Collects CPU, memory, disk, and network information
  2. Uses platform-specific syscalls and APIs
  3. Provides a unified interface across platforms
  4. Handles unsupported platforms gracefully
  5. Includes JSON serialization for reporting

Solution:

Show Solution
  1// sysinfo/sysinfo.go - Common interface
  2package sysinfo
  3
  4import (
  5    "encoding/json"
  6    "time"
  7)
  8
  9// SystemInfo contains all system information
 10type SystemInfo struct {
 11    Hostname    string        `json:"hostname"`
 12    Platform    string        `json:"platform"`
 13    Arch        string        `json:"architecture"`
 14    CPUCount    int           `json:"cpu_count"`
 15    MemoryTotal uint64        `json:"memory_total_bytes"`
 16    MemoryFree  uint64        `json:"memory_free_bytes"`
 17    MemoryUsed  uint64        `json:"memory_used_bytes"`
 18    DiskTotal   uint64        `json:"disk_total_bytes"`
 19    DiskFree    uint64        `json:"disk_free_bytes"`
 20    Uptime      time.Duration `json:"uptime_seconds"`
 21    LoadAverage []float64     `json:"load_average,omitempty"`
 22    Timestamp   time.Time     `json:"timestamp"`
 23}
 24
 25// Collector defines platform-specific collection methods
 26type Collector interface {
 27    CollectCPU() (int, error)
 28    CollectMemory() (total, free, used uint64, err error)
 29    CollectDisk(path string) (total, free uint64, err error)
 30    CollectUptime() (time.Duration, error)
 31    CollectLoadAverage() ([]float64, error)
 32}
 33
 34// Collect gathers all system information
 35func Collect() (*SystemInfo, error) {
 36    collector := newCollector()
 37
 38    info := &SystemInfo{
 39        Hostname:  getHostname(),
 40        Platform:  getPlatform(),
 41        Arch:      getArch(),
 42        Timestamp: time.Now(),
 43    }
 44
 45    // Collect CPU information
 46    if cpuCount, err := collector.CollectCPU(); err == nil {
 47        info.CPUCount = cpuCount
 48    }
 49
 50    // Collect memory information
 51    if total, free, used, err := collector.CollectMemory(); err == nil {
 52        info.MemoryTotal = total
 53        info.MemoryFree = free
 54        info.MemoryUsed = used
 55    }
 56
 57    // Collect disk information (root/C: drive)
 58    diskPath := "/"
 59    if getPlatform() == "windows" {
 60        diskPath = "C:\\"
 61    }
 62    if total, free, err := collector.CollectDisk(diskPath); err == nil {
 63        info.DiskTotal = total
 64        info.DiskFree = free
 65    }
 66
 67    // Collect uptime
 68    if uptime, err := collector.CollectUptime(); err == nil {
 69        info.Uptime = uptime
 70    }
 71
 72    // Collect load average (Unix-like systems only)
 73    if loadAvg, err := collector.CollectLoadAverage(); err == nil {
 74        info.LoadAverage = loadAvg
 75    }
 76
 77    return info, nil
 78}
 79
 80// ToJSON converts system info to JSON
 81func (s *SystemInfo) ToJSON() ([]byte, error) {
 82    return json.MarshalIndent(s, "", "  ")
 83}
 84
 85// MemoryUsagePercent returns memory usage as percentage
 86func (s *SystemInfo) MemoryUsagePercent() float64 {
 87    if s.MemoryTotal == 0 {
 88        return 0
 89    }
 90    return float64(s.MemoryUsed) / float64(s.MemoryTotal) * 100
 91}
 92
 93// DiskUsagePercent returns disk usage as percentage
 94func (s *SystemInfo) DiskUsagePercent() float64 {
 95    if s.DiskTotal == 0 {
 96        return 0
 97    }
 98    used := s.DiskTotal - s.DiskFree
 99    return float64(used) / float64(s.DiskTotal) * 100
100}
  1// sysinfo/sysinfo_linux.go - Linux implementation
  2package sysinfo
  3
  4import (
  5    "bufio"
  6    "fmt"
  7    "os"
  8    "runtime"
  9    "strconv"
 10    "strings"
 11    "syscall"
 12    "time"
 13)
 14
 15type linuxCollector struct{}
 16
 17func newCollector() Collector {
 18    return &linuxCollector{}
 19}
 20
 21func getPlatform() string {
 22    return "linux"
 23}
 24
 25func getArch() string {
 26    return runtime.GOARCH
 27}
 28
 29func getHostname() string {
 30    hostname, err := os.Hostname()
 31    if err != nil {
 32        return "unknown"
 33    }
 34    return hostname
 35}
 36
 37func (l *linuxCollector) CollectCPU() (int, error) {
 38    return runtime.NumCPU(), nil
 39}
 40
 41func (l *linuxCollector) CollectMemory() (uint64, uint64, uint64, error) {
 42    file, err := os.Open("/proc/meminfo")
 43    if err != nil {
 44        return 0, 0, 0, err
 45    }
 46    defer file.Close()
 47
 48    var total, free, available uint64
 49    scanner := bufio.NewScanner(file)
 50
 51    for scanner.Scan() {
 52        line := scanner.Text()
 53        fields := strings.Fields(line)
 54        if len(fields) < 2 {
 55            continue
 56        }
 57
 58        value, err := strconv.ParseUint(fields[1], 10, 64)
 59        if err != nil {
 60            continue
 61        }
 62        value *= 1024 // Convert from KB to bytes
 63
 64        switch fields[0] {
 65        case "MemTotal:":
 66            total = value
 67        case "MemFree:":
 68            free = value
 69        case "MemAvailable:":
 70            available = value
 71        }
 72    }
 73
 74    used := total - available
 75    return total, free, used, nil
 76}
 77
 78func (l *linuxCollector) CollectDisk(path string) (uint64, uint64, error) {
 79    var stat syscall.Statfs_t
 80    if err := syscall.Statfs(path, &stat); err != nil {
 81        return 0, 0, err
 82    }
 83
 84    total := stat.Blocks * uint64(stat.Bsize)
 85    free := stat.Bfree * uint64(stat.Bsize)
 86
 87    return total, free, nil
 88}
 89
 90func (l *linuxCollector) CollectUptime() (time.Duration, error) {
 91    data, err := os.ReadFile("/proc/uptime")
 92    if err != nil {
 93        return 0, err
 94    }
 95
 96    fields := strings.Fields(string(data))
 97    if len(fields) < 1 {
 98        return 0, fmt.Errorf("invalid uptime format")
 99    }
100
101    seconds, err := strconv.ParseFloat(fields[0], 64)
102    if err != nil {
103        return 0, err
104    }
105
106    return time.Duration(seconds * float64(time.Second)), nil
107}
108
109func (l *linuxCollector) CollectLoadAverage() ([]float64, error) {
110    data, err := os.ReadFile("/proc/loadavg")
111    if err != nil {
112        return nil, err
113    }
114
115    fields := strings.Fields(string(data))
116    if len(fields) < 3 {
117        return nil, fmt.Errorf("invalid loadavg format")
118    }
119
120    loadAvg := make([]float64, 3)
121    for i := 0; i < 3; i++ {
122        load, err := strconv.ParseFloat(fields[i], 64)
123        if err != nil {
124            return nil, err
125        }
126        loadAvg[i] = load
127    }
128
129    return loadAvg, nil
130}
  1// sysinfo/sysinfo_darwin.go - macOS implementation
  2package sysinfo
  3
  4import (
  5    "os"
  6    "os/exec"
  7    "runtime"
  8    "strconv"
  9    "strings"
 10    "syscall"
 11    "time"
 12)
 13
 14type darwinCollector struct{}
 15
 16func newCollector() Collector {
 17    return &darwinCollector{}
 18}
 19
 20func getPlatform() string {
 21    return "darwin"
 22}
 23
 24func getArch() string {
 25    return runtime.GOARCH
 26}
 27
 28func getHostname() string {
 29    hostname, err := os.Hostname()
 30    if err != nil {
 31        return "unknown"
 32    }
 33    return hostname
 34}
 35
 36func (d *darwinCollector) CollectCPU() (int, error) {
 37    return runtime.NumCPU(), nil
 38}
 39
 40func (d *darwinCollector) CollectMemory() (uint64, uint64, uint64, error) {
 41    // Use sysctl to get memory info on macOS
 42    cmd := exec.Command("sysctl", "hw.memsize")
 43    output, err := cmd.Output()
 44    if err != nil {
 45        return 0, 0, 0, err
 46    }
 47
 48    fields := strings.Fields(string(output))
 49    if len(fields) < 2 {
 50        return 0, 0, 0, nil
 51    }
 52
 53    total, err := strconv.ParseUint(fields[1], 10, 64)
 54    if err != nil {
 55        return 0, 0, 0, err
 56    }
 57
 58    // Get free memory using vm_stat
 59    cmd = exec.Command("vm_stat")
 60    output, err = cmd.Output()
 61    if err != nil {
 62        return total, 0, 0, nil
 63    }
 64
 65    // Parse vm_stat output (simplified)
 66    free := total / 4 // Placeholder estimation
 67    used := total - free
 68
 69    return total, free, used, nil
 70}
 71
 72func (d *darwinCollector) CollectDisk(path string) (uint64, uint64, error) {
 73    var stat syscall.Statfs_t
 74    if err := syscall.Statfs(path, &stat); err != nil {
 75        return 0, 0, err
 76    }
 77
 78    total := stat.Blocks * uint64(stat.Bsize)
 79    free := stat.Bfree * uint64(stat.Bsize)
 80
 81    return total, free, nil
 82}
 83
 84func (d *darwinCollector) CollectUptime() (time.Duration, error) {
 85    cmd := exec.Command("sysctl", "-n", "kern.boottime")
 86    output, err := cmd.Output()
 87    if err != nil {
 88        return 0, err
 89    }
 90
 91    // Parse boot time and calculate uptime
 92    // Simplified version
 93    return time.Hour * 24, nil // Placeholder
 94}
 95
 96func (d *darwinCollector) CollectLoadAverage() ([]float64, error) {
 97    cmd := exec.Command("sysctl", "-n", "vm.loadavg")
 98    output, err := cmd.Output()
 99    if err != nil {
100        return nil, err
101    }
102
103    // Parse load average
104    fields := strings.Fields(string(output))
105    if len(fields) < 3 {
106        return nil, nil
107    }
108
109    loadAvg := make([]float64, 3)
110    for i := 0; i < 3 && i < len(fields); i++ {
111        load, err := strconv.ParseFloat(fields[i], 64)
112        if err != nil {
113            continue
114        }
115        loadAvg[i] = load
116    }
117
118    return loadAvg, nil
119}
  1// sysinfo/sysinfo_windows.go - Windows implementation
  2package sysinfo
  3
  4import (
  5    "os"
  6    "runtime"
  7    "syscall"
  8    "time"
  9    "unsafe"
 10)
 11
 12type windowsCollector struct{}
 13
 14func newCollector() Collector {
 15    return &windowsCollector{}
 16}
 17
 18func getPlatform() string {
 19    return "windows"
 20}
 21
 22func getArch() string {
 23    return runtime.GOARCH
 24}
 25
 26func getHostname() string {
 27    hostname, err := os.Hostname()
 28    if err != nil {
 29        return "unknown"
 30    }
 31    return hostname
 32}
 33
 34func (w *windowsCollector) CollectCPU() (int, error) {
 35    return runtime.NumCPU(), nil
 36}
 37
 38func (w *windowsCollector) CollectMemory() (uint64, uint64, uint64, error) {
 39    kernel32 := syscall.NewLazyDLL("kernel32.dll")
 40    globalMemoryStatusEx := kernel32.NewProc("GlobalMemoryStatusEx")
 41
 42    type memoryStatusEx struct {
 43        dwLength                uint32
 44        dwMemoryLoad            uint32
 45        ullTotalPhys            uint64
 46        ullAvailPhys            uint64
 47        ullTotalPageFile        uint64
 48        ullAvailPageFile        uint64
 49        ullTotalVirtual         uint64
 50        ullAvailVirtual         uint64
 51        ullAvailExtendedVirtual uint64
 52    }
 53
 54    var memStatus memoryStatusEx
 55    memStatus.dwLength = uint32(unsafe.Sizeof(memStatus))
 56
 57    ret, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memStatus)))
 58    if ret == 0 {
 59        return 0, 0, 0, nil
 60    }
 61
 62    total := memStatus.ullTotalPhys
 63    free := memStatus.ullAvailPhys
 64    used := total - free
 65
 66    return total, free, used, nil
 67}
 68
 69func (w *windowsCollector) CollectDisk(path string) (uint64, uint64, error) {
 70    kernel32 := syscall.NewLazyDLL("kernel32.dll")
 71    getDiskFreeSpaceEx := kernel32.NewProc("GetDiskFreeSpaceExW")
 72
 73    pathPtr, err := syscall.UTF16PtrFromString(path)
 74    if err != nil {
 75        return 0, 0, err
 76    }
 77
 78    var freeBytesAvailable, totalBytes, totalFreeBytes uint64
 79
 80    ret, _, _ := getDiskFreeSpaceEx.Call(
 81        uintptr(unsafe.Pointer(pathPtr)),
 82        uintptr(unsafe.Pointer(&freeBytesAvailable)),
 83        uintptr(unsafe.Pointer(&totalBytes)),
 84        uintptr(unsafe.Pointer(&totalFreeBytes)),
 85    )
 86
 87    if ret == 0 {
 88        return 0, 0, nil
 89    }
 90
 91    return totalBytes, freeBytesAvailable, nil
 92}
 93
 94func (w *windowsCollector) CollectUptime() (time.Duration, error) {
 95    kernel32 := syscall.NewLazyDLL("kernel32.dll")
 96    getTickCount64 := kernel32.NewProc("GetTickCount64")
 97
 98    ret, _, _ := getTickCount64.Call()
 99    milliseconds := uint64(ret)
100
101    return time.Duration(milliseconds) * time.Millisecond, nil
102}
103
104func (w *windowsCollector) CollectLoadAverage() ([]float64, error) {
105    // Windows doesn't have load average concept
106    return nil, nil
107}
 1// sysinfo/sysinfo_test.go - Tests
 2package sysinfo
 3
 4import (
 5    "testing"
 6)
 7
 8func TestCollect(t *testing.T) {
 9    info, err := Collect()
10    if err != nil {
11        t.Fatalf("Failed to collect system info: %v", err)
12    }
13
14    if info.Hostname == "" {
15        t.Error("Hostname should not be empty")
16    }
17
18    if info.Platform == "" {
19        t.Error("Platform should not be empty")
20    }
21
22    if info.CPUCount == 0 {
23        t.Error("CPU count should be greater than 0")
24    }
25
26    if info.MemoryTotal == 0 {
27        t.Error("Memory total should be greater than 0")
28    }
29}
30
31func TestMemoryUsagePercent(t *testing.T) {
32    info, err := Collect()
33    if err != nil {
34        t.Fatalf("Failed to collect system info: %v", err)
35    }
36
37    usage := info.MemoryUsagePercent()
38    if usage < 0 || usage > 100 {
39        t.Errorf("Memory usage percent should be 0-100, got %.2f", usage)
40    }
41}
42
43func TestJSON(t *testing.T) {
44    info, err := Collect()
45    if err != nil {
46        t.Fatalf("Failed to collect system info: %v", err)
47    }
48
49    jsonData, err := info.ToJSON()
50    if err != nil {
51        t.Fatalf("Failed to convert to JSON: %v", err)
52    }
53
54    if len(jsonData) == 0 {
55        t.Error("JSON output should not be empty")
56    }
57}

Usage Example:

 1package main
 2
 3import (
 4    "fmt"
 5    "yourproject/sysinfo"
 6)
 7
 8func main() {
 9    info, err := sysinfo.Collect()
10    if err != nil {
11        panic(err)
12    }
13
14    fmt.Printf("System Information\n")
15    fmt.Printf("==================\n")
16    fmt.Printf("Hostname: %s\n", info.Hostname)
17    fmt.Printf("Platform: %s\n", info.Platform)
18    fmt.Printf("Architecture: %s\n", info.Arch)
19    fmt.Printf("CPU Count: %d\n", info.CPUCount)
20    fmt.Printf("Memory Total: %.2f GB\n", float64(info.MemoryTotal)/1024/1024/1024)
21    fmt.Printf("Memory Used: %.2f GB (%.1f%%)\n",
22        float64(info.MemoryUsed)/1024/1024/1024,
23        info.MemoryUsagePercent())
24    fmt.Printf("Disk Total: %.2f GB\n", float64(info.DiskTotal)/1024/1024/1024)
25    fmt.Printf("Disk Free: %.2f GB (%.1f%% used)\n",
26        float64(info.DiskFree)/1024/1024/1024,
27        info.DiskUsagePercent())
28    fmt.Printf("Uptime: %v\n", info.Uptime)
29
30    if len(info.LoadAverage) > 0 {
31        fmt.Printf("Load Average: %.2f %.2f %.2f\n",
32            info.LoadAverage[0], info.LoadAverage[1], info.LoadAverage[2])
33    }
34
35    // Print JSON representation
36    fmt.Println("\nJSON Output:")
37    jsonData, _ := info.ToJSON()
38    fmt.Println(string(jsonData))
39}
40
41// run

Exercise 5: Build Tag-Based Test Suite Manager

Learning Objectives: Create comprehensive testing infrastructure using build tags

Difficulty: Advanced

Real-World Context: Large projects need different test suites (unit, integration, performance, end-to-end) that run in different CI/CD stages. This exercise builds a test suite manager.

Task: Create a testing package that:

  1. Separates tests into categories using build tags
  2. Implements unit, integration, and e2e test suites
  3. Provides test fixtures and helpers for each suite
  4. Includes performance benchmarks with different tag combinations
  5. Creates a test runner with selective execution

Solution:

Show Solution
 1// testing/suite.go - Common test infrastructure
 2package testing
 3
 4import (
 5    "fmt"
 6    "testing"
 7    "time"
 8)
 9
10// TestSuite defines common test suite interface
11type TestSuite interface {
12    SetupSuite() error
13    TeardownSuite() error
14    SetupTest() error
15    TeardownTest() error
16    Name() string
17}
18
19// BaseSuite provides common functionality
20type BaseSuite struct {
21    T         *testing.T
22    StartTime time.Time
23}
24
25// LogInfo logs informational messages
26func (b *BaseSuite) LogInfo(format string, args ...interface{}) {
27    msg := fmt.Sprintf(format, args...)
28    b.T.Logf("[INFO] %s", msg)
29}
30
31// LogError logs error messages
32func (b *BaseSuite) LogError(format string, args ...interface{}) {
33    msg := fmt.Sprintf(format, args...)
34    b.T.Logf("[ERROR] %s", msg)
35}
36
37// AssertEqual asserts two values are equal
38func (b *BaseSuite) AssertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
39    if expected != actual {
40        msg := fmt.Sprintf("Expected %v, got %v", expected, actual)
41        if len(msgAndArgs) > 0 {
42            msg = fmt.Sprintf("%v: %s", msgAndArgs[0], msg)
43        }
44        b.T.Error(msg)
45    }
46}
47
48// AssertNotNil asserts value is not nil
49func (b *BaseSuite) AssertNotNil(value interface{}, msgAndArgs ...interface{}) {
50    if value == nil {
51        msg := "Expected non-nil value"
52        if len(msgAndArgs) > 0 {
53            msg = fmt.Sprintf("%v: %s", msgAndArgs[0], msg)
54        }
55        b.T.Error(msg)
56    }
57}
58
59// Elapsed returns time since test started
60func (b *BaseSuite) Elapsed() time.Duration {
61    return time.Since(b.StartTime)
62}
 1// testing/unit_test.go - Unit tests
 2package testing
 3
 4import (
 5    "testing"
 6    "time"
 7)
 8
 9type UnitTestSuite struct {
10    BaseSuite
11}
12
13func (u *UnitTestSuite) SetupSuite() error {
14    u.LogInfo("Setting up unit test suite")
15    return nil
16}
17
18func (u *UnitTestSuite) TeardownSuite() error {
19    u.LogInfo("Tearing down unit test suite")
20    return nil
21}
22
23func (u *UnitTestSuite) SetupTest() error {
24    u.StartTime = time.Now()
25    return nil
26}
27
28func (u *UnitTestSuite) TeardownTest() error {
29    u.LogInfo("Test completed in %v", u.Elapsed())
30    return nil
31}
32
33func (u *UnitTestSuite) Name() string {
34    return "Unit Tests"
35}
36
37// TestBasicOperations tests basic functionality
38func TestBasicOperations(t *testing.T) {
39    suite := &UnitTestSuite{BaseSuite: BaseSuite{T: t}}
40    suite.SetupSuite()
41    defer suite.TeardownSuite()
42
43    suite.SetupTest()
44    defer suite.TeardownTest()
45
46    // Test string concatenation
47    result := "hello" + " " + "world"
48    suite.AssertEqual("hello world", result, "String concatenation")
49
50    // Test arithmetic
51    sum := 2 + 2
52    suite.AssertEqual(4, sum, "Addition")
53
54    // Test nil check
55    var ptr *int
56    if ptr != nil {
57        suite.T.Error("Pointer should be nil")
58    }
59
60    suite.LogInfo("All basic operations passed")
61}
62
63// TestDataStructures tests data structure operations
64func TestDataStructures(t *testing.T) {
65    suite := &UnitTestSuite{BaseSuite: BaseSuite{T: t}}
66    suite.SetupSuite()
67    defer suite.TeardownSuite()
68
69    suite.SetupTest()
70    defer suite.TeardownTest()
71
72    // Test slice operations
73    slice := []int{1, 2, 3, 4, 5}
74    suite.AssertEqual(5, len(slice), "Slice length")
75
76    // Test map operations
77    m := make(map[string]int)
78    m["key"] = 42
79    suite.AssertEqual(42, m["key"], "Map access")
80
81    suite.LogInfo("All data structure tests passed")
82}
  1// testing/integration_test.go - Integration tests
  2//go:build integration
  3
  4package testing
  5
  6import (
  7    "testing"
  8    "time"
  9)
 10
 11type IntegrationTestSuite struct {
 12    BaseSuite
 13    DBConnection interface{}
 14    APIClient    interface{}
 15}
 16
 17func (i *IntegrationTestSuite) SetupSuite() error {
 18    i.LogInfo("Setting up integration test suite")
 19    i.LogInfo("Connecting to test database...")
 20    // Simulate database connection
 21    time.Sleep(100 * time.Millisecond)
 22    i.DBConnection = "test-db-connection"
 23
 24    i.LogInfo("Initializing API client...")
 25    time.Sleep(50 * time.Millisecond)
 26    i.APIClient = "test-api-client"
 27
 28    return nil
 29}
 30
 31func (i *IntegrationTestSuite) TeardownSuite() error {
 32    i.LogInfo("Tearing down integration test suite")
 33    i.LogInfo("Closing database connection...")
 34    i.DBConnection = nil
 35
 36    i.LogInfo("Closing API client...")
 37    i.APIClient = nil
 38
 39    return nil
 40}
 41
 42func (i *IntegrationTestSuite) SetupTest() error {
 43    i.StartTime = time.Now()
 44    i.LogInfo("Preparing test data...")
 45    return nil
 46}
 47
 48func (i *IntegrationTestSuite) TeardownTest() error {
 49    i.LogInfo("Cleaning up test data...")
 50    i.LogInfo("Test completed in %v", i.Elapsed())
 51    return nil
 52}
 53
 54func (i *IntegrationTestSuite) Name() string {
 55    return "Integration Tests"
 56}
 57
 58// TestDatabaseOperations tests database interactions
 59func TestDatabaseOperations(t *testing.T) {
 60    suite := &IntegrationTestSuite{BaseSuite: BaseSuite{T: t}}
 61    suite.SetupSuite()
 62    defer suite.TeardownSuite()
 63
 64    suite.SetupTest()
 65    defer suite.TeardownTest()
 66
 67    suite.AssertNotNil(suite.DBConnection, "Database connection")
 68
 69    // Simulate database query
 70    suite.LogInfo("Executing database query...")
 71    time.Sleep(50 * time.Millisecond)
 72
 73    // Simulate result validation
 74    rowCount := 10
 75    suite.AssertEqual(10, rowCount, "Query result count")
 76
 77    suite.LogInfo("Database operations test passed")
 78}
 79
 80// TestAPIIntegration tests API interactions
 81func TestAPIIntegration(t *testing.T) {
 82    suite := &IntegrationTestSuite{BaseSuite: BaseSuite{T: t}}
 83    suite.SetupSuite()
 84    defer suite.TeardownSuite()
 85
 86    suite.SetupTest()
 87    defer suite.TeardownTest()
 88
 89    suite.AssertNotNil(suite.APIClient, "API client")
 90
 91    // Simulate API call
 92    suite.LogInfo("Making API request...")
 93    time.Sleep(100 * time.Millisecond)
 94
 95    // Simulate response validation
 96    statusCode := 200
 97    suite.AssertEqual(200, statusCode, "API response status")
 98
 99    suite.LogInfo("API integration test passed")
100}
  1// testing/e2e_test.go - End-to-end tests
  2//go:build e2e
  3
  4package testing
  5
  6import (
  7    "testing"
  8    "time"
  9)
 10
 11type E2ETestSuite struct {
 12    BaseSuite
 13    Browser      interface{}
 14    TestServer   interface{}
 15    TestDatabase interface{}
 16}
 17
 18func (e *E2ETestSuite) SetupSuite() error {
 19    e.LogInfo("Setting up E2E test suite")
 20    e.LogInfo("Starting test server...")
 21    time.Sleep(200 * time.Millisecond)
 22    e.TestServer = "test-server"
 23
 24    e.LogInfo("Starting test database...")
 25    time.Sleep(150 * time.Millisecond)
 26    e.TestDatabase = "test-database"
 27
 28    e.LogInfo("Launching browser...")
 29    time.Sleep(300 * time.Millisecond)
 30    e.Browser = "headless-browser"
 31
 32    return nil
 33}
 34
 35func (e *E2ETestSuite) TeardownSuite() error {
 36    e.LogInfo("Tearing down E2E test suite")
 37    e.LogInfo("Closing browser...")
 38    e.Browser = nil
 39
 40    e.LogInfo("Stopping test server...")
 41    e.TestServer = nil
 42
 43    e.LogInfo("Stopping test database...")
 44    e.TestDatabase = nil
 45
 46    return nil
 47}
 48
 49func (e *E2ETestSuite) SetupTest() error {
 50    e.StartTime = time.Now()
 51    e.LogInfo("Preparing E2E test scenario...")
 52    return nil
 53}
 54
 55func (e *E2ETestSuite) TeardownTest() error {
 56    e.LogInfo("Cleaning up E2E test artifacts...")
 57    e.LogInfo("Test completed in %v", e.Elapsed())
 58    return nil
 59}
 60
 61func (e *E2ETestSuite) Name() string {
 62    return "E2E Tests"
 63}
 64
 65// TestUserRegistrationFlow tests complete user registration
 66func TestUserRegistrationFlow(t *testing.T) {
 67    suite := &E2ETestSuite{BaseSuite: BaseSuite{T: t}}
 68    suite.SetupSuite()
 69    defer suite.TeardownSuite()
 70
 71    suite.SetupTest()
 72    defer suite.TeardownTest()
 73
 74    suite.AssertNotNil(suite.Browser, "Browser")
 75    suite.AssertNotNil(suite.TestServer, "Test server")
 76
 77    // Step 1: Navigate to registration page
 78    suite.LogInfo("Navigating to registration page...")
 79    time.Sleep(100 * time.Millisecond)
 80
 81    // Step 2: Fill registration form
 82    suite.LogInfo("Filling registration form...")
 83    time.Sleep(150 * time.Millisecond)
 84
 85    // Step 3: Submit form
 86    suite.LogInfo("Submitting registration form...")
 87    time.Sleep(200 * time.Millisecond)
 88
 89    // Step 4: Verify success
 90    suite.LogInfo("Verifying registration success...")
 91    time.Sleep(100 * time.Millisecond)
 92
 93    success := true
 94    suite.AssertEqual(true, success, "Registration flow")
 95
 96    suite.LogInfo("User registration flow test passed")
 97}
 98
 99// TestCheckoutProcess tests complete checkout process
100func TestCheckoutProcess(t *testing.T) {
101    suite := &E2ETestSuite{BaseSuite: BaseSuite{T: t}}
102    suite.SetupSuite()
103    defer suite.TeardownSuite()
104
105    suite.SetupTest()
106    defer suite.TeardownTest()
107
108    // Step 1: Add items to cart
109    suite.LogInfo("Adding items to cart...")
110    time.Sleep(100 * time.Millisecond)
111
112    // Step 2: Proceed to checkout
113    suite.LogInfo("Proceeding to checkout...")
114    time.Sleep(150 * time.Millisecond)
115
116    // Step 3: Fill payment info
117    suite.LogInfo("Filling payment information...")
118    time.Sleep(200 * time.Millisecond)
119
120    // Step 4: Complete purchase
121    suite.LogInfo("Completing purchase...")
122    time.Sleep(250 * time.Millisecond)
123
124    // Step 5: Verify order
125    suite.LogInfo("Verifying order completion...")
126    time.Sleep(100 * time.Millisecond)
127
128    orderComplete := true
129    suite.AssertEqual(true, orderComplete, "Checkout process")
130
131    suite.LogInfo("Checkout process test passed")
132}
 1// testing/performance_test.go - Performance benchmarks
 2//go:build performance
 3
 4package testing
 5
 6import (
 7    "testing"
 8)
 9
10// BenchmarkStringConcatenation benchmarks string operations
11func BenchmarkStringConcatenation(b *testing.B) {
12    b.ReportAllocs()
13
14    for i := 0; i < b.N; i++ {
15        result := "hello" + " " + "world"
16        _ = result
17    }
18}
19
20// BenchmarkMapOperations benchmarks map access
21func BenchmarkMapOperations(b *testing.B) {
22    m := make(map[int]string)
23    for i := 0; i < 1000; i++ {
24        m[i] = "value"
25    }
26
27    b.ResetTimer()
28    b.ReportAllocs()
29
30    for i := 0; i < b.N; i++ {
31        _ = m[i%1000]
32    }
33}
34
35// BenchmarkSliceAppend benchmarks slice operations
36func BenchmarkSliceAppend(b *testing.B) {
37    b.ReportAllocs()
38
39    for i := 0; i < b.N; i++ {
40        slice := make([]int, 0)
41        for j := 0; j < 100; j++ {
42            slice = append(slice, j)
43        }
44    }
45}

Test Commands:

 1# Run unit tests only (default)
 2go test ./testing/...
 3
 4# Run integration tests
 5go test -tags "integration" ./testing/...
 6
 7# Run E2E tests
 8go test -tags "e2e" ./testing/... -timeout 30m
 9
10# Run performance benchmarks
11go test -tags "performance" -bench=. ./testing/...
12
13# Run all tests
14go test -tags "integration,e2e,performance" -bench=. ./testing/...
15
16# Run specific test with verbose output
17go test -tags "integration" -v -run TestDatabaseOperations ./testing/

Summary

Key Takeaways

Build Tags Essentials:

  1. Compile-time selection - Build tags eliminate unused code at compilation time
  2. Platform targeting - Create optimized implementations for specific OS/architecture combinations
  3. Feature flags - Enable/disable functionality at compile time for different builds
  4. A/B testing - Deploy different variants for experimentation and gradual rollouts
  5. Environment configs - Create different configurations for dev/staging/prod environments

Critical Patterns:

  • Platform-specific files: Use *_GOOS.go naming conventions
  • Feature toggles: Enable experimental features with build tags
  • Environment configs: Different database URLs, API keys, debug settings
  • Testing separation: Unit tests vs integration tests with build tags
  • Fallback implementations: Always provide default implementations

Build Tag Workflow

  1. Identify variations - What needs to be different across platforms/environments?
  2. Choose strategy - Build tags vs runtime detection vs feature flags
  3. Implement tags - Use clear, maintainable tag expressions
  4. Test thoroughly - Verify all tag combinations work correctly
  5. Document clearly - Explain purpose and usage of each tag

Commands & Tools

 1# Check which files would be included
 2go list -f '{{.GoFiles}}' ./...
 3
 4# Build for specific platform
 5GOOS=linux GOARCH=amd64 go build
 6
 7# Build with custom tags
 8go build -tags "development,debug"
 9
10# Check build tag syntax
11go vet
12
13# List all possible platforms
14go tool dist list
15
16# Test with specific tags
17go test -tags "integration"
18
19# Show included files for tags
20go list -tags "linux,amd64" -f '{{.GoFiles}}' ./...

Production Readiness Checklist

  • All critical paths have platform-specific implementations
  • Fallback implementations exist for edge cases
  • CI tests all build tag combinations
  • Documentation explains tag purpose and usage
  • Build processes handle all target platforms
  • Monitoring detects build tag usage issues
  • A/B testing workflow established with build tags

Next Steps in Your Go Journey

For Cross-Platform Development:

  • Study cross-platform GUI frameworks
  • Learn about platform-specific APIs and syscalls
  • Master container-based development
  • Explore platform abstraction layers

For System Programming:

  • Study operating system interfaces and kernel programming
  • Learn about hardware-specific optimizations
  • Master embedded systems development
  • Explore system monitoring and observability

For DevOps and CI/CD:

  • Study containerized build pipelines with build tags
  • Learn about multi-platform deployment strategies
  • Master infrastructure as code patterns
  • Explore blue-green deployment techniques

Build tags are a powerful feature that makes Go truly "write once, run anywhere." The techniques and patterns you've learned here will serve you throughout your Go development career, whether you're building desktop applications, server software, or embedded systems.

Remember the Build Tag Golden Rule: Compile early, compile once, run everywhere! Your users will thank you for applications that work perfectly on every platform they target!