Table of Contents
Quick Navigation
- Go Fundamentals
- Concurrency Patterns
- Goroutines & Channels
- HTTP Servers
- REST API Development
- Database Integration
- Error Handling
- Testing Strategies
- Performance Optimization
- Microservices Patterns
- Security Best Practices
- Deployment & Build
- Observability
- Production Best Practices
- Resources & Learning Path
Go Fundamentals
Project Structure
myproject/
├── cmd/
│ └── api/
│ └── main.go # Application entry point
├── internal/
│ ├── handlers/ # HTTP handlers
│ │ ├── user.go
│ │ └── order.go
│ ├── models/ # Data models
│ │ ├── user.go
│ │ └── order.go
│ ├── services/ # Business logic
│ │ ├── user_service.go
│ │ └── order_service.go
│ ├── repository/ # Data access
│ │ ├── user_repo.go
│ │ └── order_repo.go
│ ├── middleware/ # HTTP middleware
│ │ ├── auth.go
│ │ └── logging.go
│ └── config/ # Configuration
│ └── config.go
├── pkg/ # Public libraries
│ ├── logger/
│ │ └── logger.go
│ └── database/
│ └── postgres.go
├── api/ # API definitions
│ ├── openapi.yaml
│ └── proto/
│ └── service.proto
├── migrations/ # Database migrations
│ ├── 001_initial.up.sql
│ └── 001_initial.down.sql
├── scripts/ # Build/deploy scripts
│ └── build.sh
├── deployments/ # Deployment configs
│ ├── docker/
│ │ └── Dockerfile
│ └── k8s/
│ └── deployment.yaml
├── tests/ # Integration tests
│ └── integration_test.go
├── go.mod # Go modules
├── go.sum
├── Makefile
└── README.md
# go.mod
module github.com/username/myproject
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/lib/pq v1.10.9
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/go-redis/redis/v8 v8.11.5
github.com/stretchr/testify v1.8.4
)
# Makefile
.PHONY: build test run clean
build:
go build -o bin/api cmd/api/main.go
test:
go test -v -race -coverprofile=coverage.out ./...
run:
go run cmd/api/main.go
clean:
rm -rf bin/
docker-build:
docker build -t myapp:latest .
lint:
golangci-lint run ./...
Basic Syntax & Types
// Variables
var name string = "Alice"
var age int = 30
var isActive bool = true
// Short declaration
name := "Alice"
age := 30
// Multiple variables
var (
firstName string = "John"
lastName string = "Doe"
age int = 25
)
// Constants
const Pi = 3.14159
const (
StatusOK = 200
StatusError = 500
)
// Basic types
var (
b bool = true
i int = 42
i8 int8 = 127
u uint = 42
f float64 = 3.14
c complex128 = 1 + 2i
s string = "hello"
r rune = 'A' // int32, Unicode code point
bt byte = 255 // uint8
)
// Arrays (fixed size)
var arr [5]int
arr := [5]int{1, 2, 3, 4, 5}
arr := [...]int{1, 2, 3} // Size inferred
// Slices (dynamic)
slice := []int{1, 2, 3}
slice := make([]int, 5) // Length 5, capacity 5
slice := make([]int, 3, 5) // Length 3, capacity 5
// Append
slice = append(slice, 4, 5, 6)
// Slicing
sub := slice[1:3] // Elements 1, 2
// Maps
m := make(map[string]int)
m["alice"] = 25
m["bob"] = 30
// Literal
m := map[string]int{
"alice": 25,
"bob": 30,
}
// Check existence
value, exists := m["alice"]
if exists {
fmt.Println(value)
}
// Delete
delete(m, "alice")
// Structs
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
// Initialize
user := User{
ID: 1,
Name: "Alice",
Email: "[email protected]",
}
// Anonymous struct
config := struct {
Host string
Port int
}{
Host: "localhost",
Port: 8080,
}
// Pointers
var p *int
i := 42
p = &i // Address of i
fmt.Println(*p) // Dereference (42)
// Functions
func add(a, b int) int {
return a + b
}
// Multiple return values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Named return values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // naked return
}
// Variadic function
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// Methods
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Pointer receiver (can modify)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Interfaces
type Shape interface {
Area() float64
Perimeter() float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// Empty interface (any type)
func print(v interface{}) {
fmt.Println(v)
}
// Type assertion
var i interface{} = "hello"
s := i.(string)
s, ok := i.(string) // Safe type assertion
// Type switch
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
default:
fmt.Println("unknown type")
}
// Control flow
// If
if x > 0 {
fmt.Println("positive")
} else if x < 0 {
fmt.Println("negative")
} else {
fmt.Println("zero")
}
// If with initialization
if err := doSomething(); err != nil {
return err
}
// For loop
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While-style
i := 0
for i < 10 {
i++
}
// Infinite loop
for {
// Break or return to exit
}
// Range
for i, v := range slice {
fmt.Println(i, v)
}
for key, value := range m {
fmt.Println(key, value)
}
// Switch
switch day {
case "Monday":
fmt.Println("Start of week")
case "Friday":
fmt.Println("End of week")
default:
fmt.Println("Midweek")
}
// Switch without expression
switch {
case x < 0:
fmt.Println("negative")
case x > 0:
fmt.Println("positive")
default:
fmt.Println("zero")
}
// Defer (executes when function returns)
func readFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close() // Guaranteed to execute
// Read file
return nil
}
Concurrency Patterns
Goroutines & Channels
// Basic goroutine
func main() {
go sayHello() // Run in background
time.Sleep(time.Second)
}
func sayHello() {
fmt.Println("Hello from goroutine")
}
// Anonymous goroutine
go func() {
fmt.Println("Anonymous goroutine")
}()
// Channels (communication between goroutines)
// Unbuffered channel
ch := make(chan int)
// Buffered channel
ch := make(chan int, 10)
// Send
ch <- 42
// Receive
value := <-ch
// Close channel
close(ch)
// Select statement (multiplexing)
select {
case msg := <-ch1:
fmt.Println("Received from ch1:", msg)
case msg := <-ch2:
fmt.Println("Received from ch2:", msg)
case <-time.After(time.Second):
fmt.Println("Timeout")
default:
fmt.Println("No message")
}
// Worker pool pattern
func workerPool(jobs <-chan int, results chan<- int, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for job := range jobs {
result := process(job)
results <- result
}
}(i)
}
wg.Wait()
close(results)
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start workers
go workerPool(jobs, results, 5)
// Send jobs
for i := 0; i < 100; i++ {
jobs <- i
}
close(jobs)
// Collect results
for result := range results {
fmt.Println(result)
}
}
// Fan-out, Fan-in pattern
func fanOut(input <-chan int, numWorkers int) []<-chan int {
outputs := make([]<-chan int, numWorkers)
for i := 0; i < numWorkers; i++ {
outputs[i] = worker(input)
}
return outputs
}
func worker(input <-chan int) <-chan int {
output := make(chan int)
go func() {
defer close(output)
for n := range input {
output <- n * 2
}
}()
return output
}
func fanIn(inputs ...<-chan int) <-chan int {
output := make(chan int)
var wg sync.WaitGroup
for _, input := range inputs {
wg.Add(1)
go func(ch <-chan int) {
defer wg.Done()
for n := range ch {
output <- n
}
}(input)
}
go func() {
wg.Wait()
close(output)
}()
return output
}
// Pipeline pattern
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
out <- n * n
}
}()
return out
}
func main() {
// Build pipeline
nums := generator(1, 2, 3, 4, 5)
squared := square(nums)
// Consume results
for result := range squared {
fmt.Println(result)
}
}
// Context for cancellation
import "context"
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker cancelled")
return
default:
// Do work
time.Sleep(100 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(3 * time.Second)
}
// Semaphore pattern (limit concurrency)
type Semaphore struct {
sem chan struct{}
}
func NewSemaphore(maxConcurrent int) *Semaphore {
return &Semaphore{
sem: make(chan struct{}, maxConcurrent),
}
}
func (s *Semaphore) Acquire() {
s.sem <- struct{}{}
}
func (s *Semaphore) Release() {
<-s.sem
}
// Usage
sem := NewSemaphore(5) // Max 5 concurrent operations
for i := 0; i < 100; i++ {
sem.Acquire()
go func(id int) {
defer sem.Release()
process(id)
}(i)
}
// Rate limiting
import "golang.org/x/time/rate"
limiter := rate.NewLimiter(10, 5) // 10 requests/sec, burst of 5
for i := 0; i < 20; i++ {
if err := limiter.Wait(context.Background()); err != nil {
log.Fatal(err)
}
go handleRequest(i)
}
// Broadcast pattern
type Broadcaster struct {
mu sync.Mutex
channels []chan interface{}
}
func (b *Broadcaster) Subscribe() <-chan interface{} {
b.mu.Lock()
defer b.mu.Unlock()
ch := make(chan interface{}, 10)
b.channels = append(b.channels, ch)
return ch
}
func (b *Broadcaster) Broadcast(msg interface{}) {
b.mu.Lock()
defer b.mu.Unlock()
for _, ch := range b.channels {
select {
case ch <- msg:
default:
// Channel full, skip
}
}
}
// Done channel pattern
func doWork(done <-chan bool) {
for {
select {
case <-done:
return
default:
// Do work
}
}
}
func main() {
done := make(chan bool)
go doWork(done)
time.Sleep(5 * time.Second)
close(done) // Signal completion
}
// Mutex for shared state
import "sync"
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
// RWMutex (multiple readers, single writer)
type Cache struct {
mu sync.RWMutex
items map[string]string
}
func (c *Cache) Get(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.items[key]
}
func (c *Cache) Set(key, value string) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = value
}
// sync.Once (execute once)
var once sync.Once
var instance *Singleton
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
// Atomic operations
import "sync/atomic"
var counter int64
atomic.AddInt64(&counter, 1)
value := atomic.LoadInt64(&counter)
atomic.StoreInt64(&counter, 100)
atomic.CompareAndSwapInt64(&counter, 100, 200)
HTTP Servers
Standard Library HTTP
// Basic HTTP server
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/api/users", usersHandler)
fmt.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getUsers(w, r)
case http.MethodPost:
createUser(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
// Custom ServeMux
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/api/users", usersHandler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(server.ListenAndServe())
// JSON response
import "encoding/json"
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
user := User{
ID: 1,
Name: "Alice",
Email: "[email protected]",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
// Parse JSON request
func createUserHandler(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close()
// Process user
// ...
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
// Middleware pattern
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Verify token
// ...
next.ServeHTTP(w, r)
})
}
// Chain middleware
handler := loggingMiddleware(authMiddleware(http.HandlerFunc(usersHandler)))
http.Handle("/api/users", handler)
// Query parameters
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
q := query.Get("q")
page := query.Get("page")
if q == "" {
http.Error(w, "Missing query parameter", http.StatusBadRequest)
return
}
// Search logic
}
// Path parameters (using mux vars)
import "github.com/gorilla/mux"
router := mux.NewRouter()
router.HandleFunc("/users/{id}", getUserByIDHandler).Methods("GET")
func getUserByIDHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
// Fetch user by ID
}
// File upload
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// Parse multipart form (max 10MB)
r.ParseMultipartForm(10 << 20)
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
// Save file
dst, err := os.Create("./uploads/" + handler.Filename)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer dst.Close()
io.Copy(dst, file)
fmt.Fprintf(w, "File uploaded: %s", handler.Filename)
}
// Graceful shutdown
func main() {
server := &http.Server{
Addr: ":8080",
Handler: router,
}
// Start server in goroutine
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server error: %v", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exited")
}
Gin Framework
// Basic Gin server
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default() // With logger and recovery middleware
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run(":8080")
}
// Route groups
r := gin.Default()
v1 := r.Group("/api/v1")
{
v1.GET("/users", getUsers)
v1.POST("/users", createUser)
v1.GET("/users/:id", getUserByID)
v1.PUT("/users/:id", updateUser)
v1.DELETE("/users/:id", deleteUser)
}
// Path and query parameters
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"id": id})
})
r.GET("/search", func(c *gin.Context) {
query := c.Query("q")
page := c.DefaultQuery("page", "1")
c.JSON(200, gin.H{
"query": query,
"page": page,
})
})
// JSON binding
type User struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"gte=0,lte=130"`
}
r.POST("/users", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Process user
c.JSON(201, user)
})
// Custom middleware
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
// Verify token
// Set user in context
c.Set("user_id", userID)
c.Next()
}
}
// Apply middleware
r.Use(AuthMiddleware())
// Or to specific routes
authorized := r.Group("/api/v1")
authorized.Use(AuthMiddleware())
{
authorized.GET("/profile", getProfile)
}
// Error handling
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
user, err := getUserByID(id)
if err != nil {
if errors.Is(err, ErrNotFound) {
c.JSON(404, gin.H{"error": "User not found"})
return
}
c.JSON(500, gin.H{"error": "Internal server error"})
return
}
c.JSON(200, user)
})
// File upload
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
dst := "./uploads/" + file.Filename
if err := c.SaveUploadedFile(file, dst); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "File uploaded"})
})
// Response types
// JSON
c.JSON(200, gin.H{"message": "success"})
// XML
c.XML(200, gin.H{"message": "success"})
// String
c.String(200, "Hello, %s", name)
// HTML
c.HTML(200, "index.html", gin.H{
"title": "Home",
})
// File
c.File("./path/to/file.pdf")
// Redirect
c.Redirect(302, "/new-path")
// Custom validators
import "github.com/go-playground/validator/v10"
type LoginRequest struct {
Username string `json:"username" binding:"required,min=3,max=20"`
Password string `json:"password" binding:"required,min=8,strongpwd"`
}
// Register custom validator
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("strongpwd", validateStrongPassword)
}
func validateStrongPassword(fl validator.FieldLevel) bool {
password := fl.Field().String()
// Check for uppercase, lowercase, number, special char
return hasUpperCase(password) && hasLowerCase(password) &&
hasNumber(password) && hasSpecialChar(password)
}
Resources & Learning Path
Learning Progression
Phase 1: Go Basics (2-3 weeks) □ Syntax and basic types □ Functions and methods □ Structs and interfaces □ Error handling □ Basic concurrency Phase 2: Intermediate Go (3-4 weeks) □ Advanced concurrency patterns □ HTTP servers and REST APIs □ Database integration □ Testing strategies □ Package management Phase 3: Advanced Go (4-6 weeks) □ Performance optimization □ Microservices architecture □ gRPC and Protocol Buffers □ Advanced testing □ Production patterns Phase 4: Production Go (Ongoing) □ Deployment strategies □ Monitoring and observability □ Security best practices □ Distributed systems □ Cloud-native patterns
Related Comprehensive Sheets
Languages & Frameworks → Python Advanced (comparison) → Rust Advanced (systems programming) → Node.js Production (async patterns) → Java Advanced (enterprise apps) Infrastructure → Docker & Kubernetes → Microservices Patterns → gRPC & Protocol Buffers → API Gateway Patterns Databases → PostgreSQL Advanced → Redis Advanced → MongoDB Advanced → Database Design Patterns DevOps → CI/CD Pipelines → Monitoring & Observability → Cloud Deployment → Performance Optimization
Pro Tips Summary
Concurrency ✓ Use goroutines for I/O-bound tasks ✓ Prefer channels over shared memory ✓ Always handle channel closure ✓ Use context for cancellation ✓ Avoid goroutine leaks ✓ Limit concurrency with semaphores Performance ✓ Use buffered channels appropriately ✓ Avoid unnecessary allocations ✓ Use sync.Pool for reusable objects ✓ Profile before optimizing ✓ Use pprof for profiling ✓ Benchmark critical paths Error Handling ✓ Always check errors ✓ Wrap errors with context ✓ Use custom error types ✓ Log errors appropriately ✓ Return errors, don't panic ✓ Handle errors at right level Code Quality ✓ Follow Go conventions ✓ Use gofmt and golint ✓ Write idiomatic Go ✓ Keep functions small ✓ Use interfaces wisely ✓ Document exported functions ✓ Write tests first Production ✓ Implement graceful shutdown ✓ Use structured logging ✓ Add health check endpoints ✓ Monitor goroutine count ✓ Set timeouts on operations ✓ Use vendoring or modules ✓ Build static binaries