moai-lang-go
Go best practices with modern cloud-native development, performance optimization, and concurrent programming for 2025
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install kivo360-quickhooks-moai-lang-go
Repository
Skill path: .claude/skills/moai-lang-go
Go best practices with modern cloud-native development, performance optimization, and concurrent programming for 2025
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: kivo360.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install moai-lang-go into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/kivo360/quickhooks before adding moai-lang-go to shared team environments
- Use moai-lang-go for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: moai-lang-go
version: 2.0.0
created: 2025-11-06
updated: 2025-11-06
status: active
description: "Go best practices with modern cloud-native development, performance optimization, and concurrent programming for 2025"
keywords: [go, golang, backend, microservices, cloud-native, performance, concurrency, devops]
allowed-tools:
- Read
- Write
- Edit
- Bash
- WebFetch
- WebSearch
---
# Go Development Mastery
**Modern Go Development with 2025 Best Practices**
> Comprehensive Go development guidance covering cloud-native services, high-performance concurrent programming, microservices architecture, and production-ready applications using the latest tools and methodologies.
## What It Does
- **Cloud-Native Services**: Kubernetes, Docker, and container-native applications
- **High-Performance APIs**: Fast HTTP servers with minimal memory footprint
- **Concurrent Programming**: Goroutines, channels, and advanced patterns
- **Microservices Architecture**: Service discovery, load balancing, distributed systems
- **Testing & Quality**: Comprehensive testing, benchmarking, and quality assurance
- **Database Integration**: SQL, NoSQL, caching, and data pipelines
- **DevOps Integration**: CI/CD, monitoring, observability, and deployment
- **Enterprise Patterns**: Clean architecture, domain-driven design, scalability
## When to Use
### Perfect Scenarios
- **Building microservices and distributed systems**
- **High-performance network services and APIs**
- **Cloud-native applications and DevOps tools**
- **Concurrent and parallel processing applications**
- **System programming and infrastructure tools**
- **CLI applications and automation scripts**
- **Real-time data processing and streaming**
### Common Triggers
- "Create Go microservice"
- "Set up Go API server"
- "Go concurrent programming"
- "Go best practices"
- "Optimize Go performance"
- "Deploy Go application"
## Tool Version Matrix (2025-11-06)
### Core Go
- **Go**: 1.25.x (latest) / 1.23.x (LTS)
- **Gin**: 1.10.x - HTTP web framework
- **Echo**: 4.12.x - High-performance web framework
- **Chi**: 5.0.x - Lightweight router
- **Fiber**: 3.x - Express.js-inspired framework
### Database & Storage
- **GORM**: 2.0.x - ORM for Go
- **sqlx**: 1.4.x - SQL extensions
- **pgx**: 5.6.x - PostgreSQL driver
- **Redis**: 9.x - Redis client
- **MongoDB**: 2.x - MongoDB driver
### Testing Tools
- **Testify**: 1.9.x - Testing toolkit
- **GoMock**: 1.6.x - Mock generation
- **gomock**: Built-in mocking framework
- **goleak**: 1.3.x - Goroutine leak detection
### Development Tools
- **Air**: 1.53.x - Live reload
- **Gin-swagger**: 1.6.x - API documentation
- **Zap**: 1.27.x - Structured logging
- **Viper**: 1.19.x - Configuration management
- **golangci-lint**: 1.62.x - Linter aggregator
### Observability
- **Prometheus**: 4.x client - Metrics
- **Jaeger**: 2.x client - Distributed tracing
- **OpenTelemetry**: 1.30.x - Observability framework
- **pprof**: Built-in profiling
## Ecosystem Overview
### Project Setup (2025 Best Practice)
```bash
# Modern Go project with modules
go mod init github.com/your-org/your-project
# Initialize project structure
mkdir -p {cmd/server,internal/{handler,service,repository},pkg/{utils,middleware},configs,deployments/{docker,k8s},scripts,test}
# Install essential tools
go install github.com/cosmtrek/air@latest
go install github.com/swaggo/swag/cmd/swag@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install go.uber.org/mock/mockgen@latest
# Generate code documentation
swag init -g cmd/server/main.go
```
### Modern Project Structure
```
my-go-project/
├── go.mod
├── go.sum
├── Makefile # Build commands and utilities
├── README.md
├── .github/
│ └── workflows/ # CI/CD pipelines
├── cmd/
│ └── server/
│ └── main.go # Application entry point
├── internal/ # Private application code
│ ├── handler/ # HTTP handlers
│ ├── service/ # Business logic
│ ├── repository/ # Data access layer
│ ├── model/ # Domain models
│ └── middleware/ # HTTP middleware
├── pkg/ # Public library code
│ ├── utils/ # Utility functions
│ ├── config/ # Configuration
│ └── logger/ # Logging utilities
├── configs/ # Configuration files
├── deployments/
│ ├── docker/ # Docker configurations
│ └── k8s/ # Kubernetes manifests
├── scripts/ # Build and utility scripts
├── test/ # Integration and e2e tests
├── docs/ # API documentation
└── migrations/ # Database migrations
```
## Modern Go Patterns
### Context-First Design
```go
// handler/user.go
package handler
import (
"context"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/your-org/your-project/internal/model"
"github.com/your-org/your-project/internal/service"
)
type UserHandler struct {
userService service.UserService
logger Logger
}
func NewUserHandler(userService service.UserService, logger Logger) *UserHandler {
return &UserHandler{
userService: userService,
logger: logger,
}
}
// CreateUser handles user creation requests
func (h *UserHandler) CreateUser(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
var req model.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
h.logger.Error("Invalid request body", "error", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user, err := h.userService.CreateUser(ctx, req)
if err != nil {
h.logger.Error("Failed to create user", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
return
}
c.JSON(http.StatusCreated, user)
}
// GetUser retrieves a user by ID
func (h *UserHandler) GetUser(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
defer cancel()
userID := c.Param("id")
user, err := h.userService.GetUser(ctx, userID)
if err != nil {
if err == service.ErrUserNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
h.logger.Error("Failed to get user", "userID", userID, "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
return
}
c.JSON(http.StatusOK, user)
}
```
### Service Layer with Error Handling
```go
// service/user.go
package service
import (
"context"
"errors"
"fmt"
"github.com/your-org/your-project/internal/model"
"github.com/your-org/your-project/internal/repository"
)
var (
ErrUserNotFound = errors.New("user not found")
ErrUserExists = errors.New("user already exists")
ErrInvalidInput = errors.New("invalid input")
)
type UserService interface {
CreateUser(ctx context.Context, req model.CreateUserRequest) (*model.User, error)
GetUser(ctx context.Context, id string) (*model.User, error)
UpdateUser(ctx context.Context, id string, req model.UpdateUserRequest) (*model.User, error)
DeleteUser(ctx context.Context, id string) error
ListUsers(ctx context.Context, filter model.UserFilter) ([]*model.User, int, error)
}
type userService struct {
userRepo repository.UserRepository
logger Logger
}
func NewUserService(userRepo repository.UserRepository, logger Logger) UserService {
return &userService{
userRepo: userRepo,
logger: logger,
}
}
func (s *userService) CreateUser(ctx context.Context, req model.CreateUserRequest) (*model.User, error) {
// Validate input
if err := req.Validate(); err != nil {
s.logger.Warn("Invalid user creation request", "error", err)
return nil, fmt.Errorf("%w: %v", ErrInvalidInput, err)
}
// Check if user already exists
existing, err := s.userRepo.GetByEmail(ctx, req.Email)
if err == nil && existing != nil {
s.logger.Warn("User already exists", "email", req.Email)
return nil, ErrUserExists
}
// Create user
user := &model.User{
ID: generateID(),
Email: req.Email,
Name: req.Name,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := s.userRepo.Create(ctx, user); err != nil {
s.logger.Error("Failed to create user", "error", err)
return nil, fmt.Errorf("failed to create user: %w", err)
}
s.logger.Info("User created successfully", "userID", user.ID)
return user, nil
}
func (s *userService) GetUser(ctx context.Context, id string) (*model.User, error) {
user, err := s.userRepo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("failed to get user: %w", err)
}
return user, nil
}
```
### Repository Pattern with SQLx
```go
// repository/user.go
package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" // PostgreSQL driver
"github.com/your-org/your-project/internal/model"
)
var (
ErrNotFound = errors.New("record not found")
ErrDuplicate = errors.New("duplicate record")
)
type UserRepository interface {
Create(ctx context.Context, user *model.User) error
GetByID(ctx context.Context, id string) (*model.User, error)
GetByEmail(ctx context.Context, email string) (*model.User, error)
Update(ctx context.Context, user *model.User) error
Delete(ctx context.Context, id string) error
List(ctx context.Context, filter UserFilter) ([]*model.User, int, error)
}
type userRepository struct {
db *sqlx.DB
}
func NewUserRepository(db *sqlx.DB) UserRepository {
return &userRepository{db: db}
}
func (r *userRepository) Create(ctx context.Context, user *model.User) error {
query := `
INSERT INTO users (id, email, name, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (email) DO NOTHING
`
result, err := r.db.ExecContext(ctx, query, user.ID, user.Email, user.Name, user.CreatedAt, user.UpdatedAt)
if err != nil {
return fmt.Errorf("failed to insert user: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rowsAffected == 0 {
return ErrDuplicate
}
return nil
}
func (r *userRepository) GetByID(ctx context.Context, id string) (*model.User, error) {
query := `
SELECT id, email, name, created_at, updated_at
FROM users
WHERE id = $1
`
var user model.User
err := r.db.GetContext(ctx, &user, query, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("failed to get user by ID: %w", err)
}
return &user, nil
}
func (r *userRepository) List(ctx context.Context, filter UserFilter) ([]*model.User, int, error) {
// Build dynamic query
baseQuery := `
SELECT id, email, name, created_at, updated_at
FROM users
WHERE 1=1
`
countQuery := `
SELECT COUNT(*)
FROM users
WHERE 1=1
`
var args []interface{}
conditions := []string{}
if filter.Email != "" {
conditions = append(conditions, "email = $"+fmt.Sprintf("%d", len(args)+1))
args = append(args, filter.Email)
}
if len(conditions) > 0 {
whereClause := " AND " + fmt.Sprintf(" AND ", conditions)
baseQuery += whereClause
countQuery += whereClause
}
// Get total count
var total int
err := r.db.GetContext(ctx, &total, countQuery, args...)
if err != nil {
return nil, 0, fmt.Errorf("failed to count users: %w", err)
}
// Add pagination
if filter.Limit > 0 {
baseQuery += fmt.Sprintf(" LIMIT $%d OFFSET $%d", len(args)+1, len(args)+2)
args = append(args, filter.Limit, filter.Offset)
}
var users []*model.User
err = r.db.SelectContext(ctx, &users, baseQuery, args...)
if err != nil {
return nil, 0, fmt.Errorf("failed to list users: %w", err)
}
return users, total, nil
}
```
## Concurrent Programming
### Worker Pool Pattern
```go
// pkg/worker/pool.go
package worker
import (
"context"
"sync"
"time"
)
type Task[T any] interface {
Execute(ctx context.Context) (T, error)
}
type Result[T any] struct {
Value T
Error error
}
type WorkerPool[T any] struct {
workers int
taskQueue chan Task[T]
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
func NewWorkerPool[T any](workers int) *WorkerPool[T] {
ctx, cancel := context.WithCancel(context.Background())
return &WorkerPool[T]{
workers: workers,
taskQueue: make(chan Task[T], workers*2),
ctx: ctx,
cancel: cancel,
}
}
func (p *WorkerPool[T]) Start() {
for i := 0; i < p.workers; i++ {
p.wg.Add(1)
go p.worker(i)
}
}
func (p *WorkerPool[T]) worker(id int) {
defer p.wg.Done()
for {
select {
case task := <-p.taskQueue:
if task == nil {
return
}
result, err := task.Execute(p.ctx)
if err != nil {
// Handle error (could use error channel or logging)
continue
}
// Process result
case <-p.ctx.Done():
return
}
}
}
func (p *WorkerPool[T]) Submit(task Task[T]) {
select {
case p.taskQueue <- task:
case <-p.ctx.Done():
return
}
}
func (p *WorkerPool[T]) Stop() {
close(p.taskQueue)
p.cancel()
p.wg.Wait()
}
// Example usage
type ImageProcessingTask struct {
ImagePath string
}
func (t *ImageProcessingTask) Execute(ctx context.Context) (string, error) {
// Simulate image processing
time.Sleep(100 * time.Millisecond)
return "processed_" + t.ImagePath, nil
}
func ProcessImages(imagePaths []string) []string {
pool := NewWorkerPool[string](10)
pool.Start()
defer pool.Stop()
var results []string
var mu sync.Mutex
for _, path := range imagePaths {
task := &ImageProcessingTask{ImagePath: path}
go func(t *ImageProcessingTask) {
result, err := t.Execute(context.Background())
if err == nil {
mu.Lock()
results = append(results, result)
mu.Unlock()
}
}(task)
}
return results
}
```
### Fan-In/Fan-Out Pattern
```go
// pkg/concurrent/fan.go
package concurrent
import (
"context"
"sync"
)
// FanIn merges multiple input channels into a single output channel
func FanIn[T any](ctx context.Context, channels ...<-chan T) <-chan T {
var wg sync.WaitGroup
out := make(chan T)
output := func(c <-chan T) {
defer wg.Done()
for {
select {
case v, ok := <-c:
if !ok {
return
}
select {
case out <- v:
case <-ctx.Done():
return
}
case <-ctx.Done():
return
}
}
}
wg.Add(len(channels))
for _, c := range channels {
go output(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
// FanOut distributes input across multiple output channels
func FanOut[T any](ctx context.Context, in <-chan T, n int) []<-chan T {
outs := make([]chan T, n)
for i := 0; i < n; i++ {
outs[i] = make(chan T)
}
distribute := func() {
defer func() {
for _, out := range outs {
close(out)
}
}()
for {
select {
case v, ok := <-in:
if !ok {
return
}
for _, out := range outs {
select {
case out <- v:
case <-ctx.Done():
return
}
}
case <-ctx.Done():
return
}
}
}
go distribute()
return outs
}
// Pipeline combines fan-out and fan-in for parallel processing
func Pipeline[T, R any](
ctx context.Context,
input []T,
workers int,
process func(context.Context, T) (R, error),
) (<-chan R, <-chan error) {
out := make(chan R, workers)
errChan := make(chan error, workers)
// Create input channel
in := make(chan T, len(input))
for _, item := range input {
in <- item
}
close(in)
// Fan out to workers
channels := FanOut(ctx, in, workers)
// Process each item
var wg sync.WaitGroup
for _, ch := range channels {
wg.Add(1)
go func(c <-chan T) {
defer wg.Done()
for item := range c {
result, err := process(ctx, item)
if err != nil {
select {
case errChan <- err:
case <-ctx.Done():
return
}
continue
}
select {
case out <- result:
case <-ctx.Done():
return
}
}
}(ch)
}
go func() {
wg.Wait()
close(out)
close(errChan)
}()
return out, errChan
}
```
## Performance Optimization
### Memory Pooling
```go
// pkg/pool/buffer.go
package pool
import (
"sync"
)
var (
bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, 1024) // Pre-allocate 1KB
},
}
)
// GetBuffer returns a buffer from the pool
func GetBuffer() []byte {
return bufferPool.Get().([]byte)
}
// PutBuffer returns a buffer to the pool
func PutBuffer(buf []byte) {
if cap(buf) <= 64*1024 { // Don't pool large buffers
bufferPool.Put(buf[:0]) // Reset length but keep capacity
}
}
// Usage example
func ProcessData(data []byte) ([]byte, error) {
buf := GetBuffer()
defer PutBuffer(buf)
// Process data using the pooled buffer
buf = append(buf, data...)
// ... processing logic ...
result := make([]byte, len(buf))
copy(result, buf)
return result, nil
}
```
### String Building Optimization
```go
// pkg/utils/strings.go
package utils
import (
"strings"
"sync"
)
var stringBuilderPool = sync.Pool{
New: func() interface{} {
return &strings.Builder{}
},
}
// StringBuilderPool provides a thread-safe string builder pool
func GetStringBuilder() *strings.Builder {
sb := stringBuilderPool.Get().(*strings.Builder)
sb.Reset()
return sb
}
func PutStringBuilder(sb *strings.Builder) {
if sb.Cap() <= 4096 { // Don't pool large builders
stringBuilderPool.Put(sb)
}
}
// FastJoin efficiently joins strings using a pooled builder
func FastJoin(strs ...string) string {
if len(strs) == 0 {
return ""
}
if len(strs) == 1 {
return strs[0]
}
sb := GetStringBuilder()
defer PutStringBuilder(sb)
for _, s := range strs {
sb.WriteString(s)
}
return sb.String()
}
// Usage example
func BuildMessage(parts []string) string {
sb := GetStringBuilder()
defer PutStringBuilder(sb)
for i, part := range parts {
if i > 0 {
sb.WriteString(" ")
}
sb.WriteString(part)
}
return sb.String()
}
```
## Testing Strategies
### Comprehensive Unit Testing
```go
// service/user_test.go
package service
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/your-org/your-project/internal/model"
"github.com/your-org/your-project/internal/repository"
)
// MockUserRepository is a mock implementation of UserRepository
type MockUserRepository struct {
mock.Mock
}
func (m *MockUserRepository) Create(ctx context.Context, user *model.User) error {
args := m.Called(ctx, user)
return args.Error(0)
}
func (m *MockUserRepository) GetByID(ctx context.Context, id string) (*model.User, error) {
args := m.Called(ctx, id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*model.User), args.Error(1)
}
func (m *MockUserRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {
args := m.Called(ctx, email)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*model.User), args.Error(1)
}
func TestUserService_CreateUser_Success(t *testing.T) {
// Arrange
mockRepo := new(MockUserRepository)
mockLogger := &MockLogger{} // Assume you have this
service := NewUserService(mockRepo, mockLogger)
req := model.CreateUserRequest{
Email: "[email protected]",
Name: "Test User",
}
expectedUser := &model.User{
ID: "user-123",
Email: req.Email,
Name: req.Name,
}
mockRepo.On("GetByEmail", mock.Anything, req.Email).Return(nil, repository.ErrNotFound)
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*model.User")).Return(nil)
// Act
ctx := context.Background()
user, err := service.CreateUser(ctx, req)
// Assert
require.NoError(t, err)
assert.NotNil(t, user)
assert.Equal(t, req.Email, user.Email)
assert.Equal(t, req.Name, user.Name)
mockRepo.AssertExpectations(t)
}
func TestUserService_CreateUser_EmailExists(t *testing.T) {
// Arrange
mockRepo := new(MockUserRepository)
mockLogger := &MockLogger{}
service := NewUserService(mockRepo, mockLogger)
req := model.CreateUserRequest{
Email: "[email protected]",
Name: "Test User",
}
existingUser := &model.User{
ID: "existing-123",
Email: req.Email,
}
mockRepo.On("GetByEmail", mock.Anything, req.Email).Return(existingUser, nil)
// Act
ctx := context.Background()
user, err := service.CreateUser(ctx, req)
// Assert
require.Error(t, err)
assert.Nil(t, user)
assert.Equal(t, ErrUserExists, err)
mockRepo.AssertExpectations(t)
}
func TestUserService_GetUser_NotFound(t *testing.T) {
// Arrange
mockRepo := new(MockUserRepository)
mockLogger := &MockLogger{}
service := NewUserService(mockRepo, mockLogger)
userID := "nonexistent"
mockRepo.On("GetByID", mock.Anything, userID).Return(nil, repository.ErrNotFound)
// Act
ctx := context.Background()
user, err := service.GetUser(ctx, userID)
// Assert
require.Error(t, err)
assert.Nil(t, user)
assert.Equal(t, ErrUserNotFound, err)
mockRepo.AssertExpectations(t)
}
// Benchmark example
func BenchmarkUserService_CreateUser(b *testing.B) {
mockRepo := new(MockUserRepository)
mockLogger := &MockLogger{}
service := NewUserService(mockRepo, mockLogger)
req := model.CreateUserRequest{
Email: "[email protected]",
Name: "Test User",
}
mockRepo.On("GetByEmail", mock.Anything, req.Email).Return(nil, repository.ErrNotFound)
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*model.User")).Return(nil)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := service.CreateUser(ctx, req)
if err != nil {
b.Fatal(err)
}
}
}
```
### Integration Testing with Testcontainers
```go
// test/integration/user_test.go
package integration
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"github.com/your-org/your-project/internal/model"
"github.com/your-org/your-project/internal/repository"
"github.com/your-org/your-project/internal/service"
"github.com/your-org/your-project/pkg/config"
)
func TestUserService_Integration(t *testing.T) {
// Setup test database container
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "postgres:16",
ExposedPorts: []string{"5432/tcp"},
Env: map[string]string{
"POSTGRES_DB": "testdb",
"POSTGRES_USER": "test",
"POSTGRES_PASSWORD": "test",
},
WaitingFor: wait.ForLog("database system is ready to accept connections"),
}
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
require.NoError(t, err)
defer container.Terminate(ctx)
// Get database connection info
host, err := container.Host(ctx)
require.NoError(t, err)
port, err := container.MappedPort(ctx, "5432")
require.NoError(t, err)
// Connect to database
dbConfig := config.Database{
Host: host,
Port: port.Int(),
User: "test",
Password: "test",
DBName: "testdb",
SSLMode: "disable",
}
db, err := setupTestDB(dbConfig)
require.NoError(t, err)
defer db.Close()
// Run migrations
err = runMigrations(db)
require.NoError(t, err)
// Setup service
userRepo := repository.NewUserRepository(db)
userService := service.NewUserService(userRepo, &MockLogger{})
// Test user creation
t.Run("Create and Get User", func(t *testing.T) {
req := model.CreateUserRequest{
Email: "[email protected]",
Name: "Integration Test User",
}
// Create user
user, err := userService.CreateUser(ctx, req)
require.NoError(t, err)
assert.NotEmpty(t, user.ID)
assert.Equal(t, req.Email, user.Email)
assert.Equal(t, req.Name, user.Name)
assert.False(t, user.CreatedAt.IsZero())
// Get user
retrievedUser, err := userService.GetUser(ctx, user.ID)
require.NoError(t, err)
assert.Equal(t, user.ID, retrievedUser.ID)
assert.Equal(t, user.Email, retrievedUser.Email)
assert.Equal(t, user.Name, retrievedUser.Name)
})
t.Run("List Users", func(t *testing.T) {
// Create multiple users
for i := 0; i < 5; i++ {
req := model.CreateUserRequest{
Email: fmt.Sprintf("user%[email protected]", i),
Name: fmt.Sprintf("User %d", i),
}
_, err := userService.CreateUser(ctx, req)
require.NoError(t, err)
}
// List users
users, total, err := userService.ListUsers(ctx, model.UserFilter{
Limit: 10,
Offset: 0,
})
require.NoError(t, err)
assert.Equal(t, 6, total) // 5 new users + 1 from previous test
assert.Equal(t, 6, len(users))
})
}
// Helper functions
func setupTestDB(cfg config.Database) (*sqlx.DB, error) {
dsn := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName, cfg.SSLMode,
)
db, err := sqlx.Connect(context.Background(), "postgres", dsn)
if err != nil {
return nil, err
}
// Wait for database to be ready
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
for {
err := db.PingContext(ctx)
if err == nil {
break
}
select {
case <-time.After(1 * time.Second):
continue
case <-ctx.Done():
return nil, ctx.Err()
}
}
return db, nil
}
```
## Security Best Practices
### Input Validation and Sanitization
```go
// pkg/validation/validator.go
package validation
import (
"regexp"
"strings"
"unicode"
"github.com/go-playground/validator/v10"
)
var (
validate = validator.New()
// Email regex pattern
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
// Strong password regex
passwordRegex = regexp.MustCompile(`^.{8,}$`) // At least 8 characters
)
// ValidationResult represents validation result
type ValidationResult struct {
Valid bool
Errors map[string]string
}
// New creates a new validator
func New() *Validator {
return &Validator{}
}
// Validator handles input validation
type Validator struct{}
// ValidateStruct validates a struct using validator tags
func (v *Validator) ValidateStruct(s interface{}) ValidationResult {
err := validate.Struct(s)
if err == nil {
return ValidationResult{Valid: true}
}
errors := make(map[string]string)
for _, err := range err.(validator.ValidationErrors) {
field := err.Field()
switch err.Tag() {
case "required":
errors[field] = field + " is required"
case "email":
errors[field] = field + " must be a valid email address"
case "min":
errors[field] = field + " must be at least " + err.Param() + " characters"
case "max":
errors[field] = field + " must be at most " + err.Param() + " characters"
default:
errors[field] = field + " is invalid"
}
}
return ValidationResult{Valid: false, Errors: errors}
}
// ValidateEmail validates email format
func (v *Validator) ValidateEmail(email string) bool {
email = strings.TrimSpace(strings.ToLower(email))
return emailRegex.MatchString(email) && len(email) <= 254
}
// ValidatePassword validates password strength
func (v *Validator) ValidatePassword(password string) ValidationResult {
errors := make(map[string]string)
if len(password) < 8 {
errors["length"] = "Password must be at least 8 characters long"
}
if !containsUppercase(password) {
errors["uppercase"] = "Password must contain at least one uppercase letter"
}
if !containsLowercase(password) {
errors["lowercase"] = "Password must contain at least one lowercase letter"
}
if !containsDigit(password) {
errors["digit"] = "Password must contain at least one digit"
}
if !containsSpecialChar(password) {
errors["special"] = "Password must contain at least one special character"
}
return ValidationResult{
Valid: len(errors) == 0,
Errors: errors,
}
}
// SanitizeInput sanitizes user input
func (v *Validator) SanitizeInput(input string) string {
// Remove potentially dangerous characters
sanitized := strings.ReplaceAll(input, "<", "<")
sanitized = strings.ReplaceAll(sanitized, ">", ">")
sanitized = strings.ReplaceAll(sanitized, "&", "&")
sanitized = strings.ReplaceAll(sanitized, "\"", """)
sanitized = strings.ReplaceAll(sanitized, "'", "'")
// Trim whitespace
return strings.TrimSpace(sanitized)
}
// Helper functions
func containsUppercase(s string) bool {
for _, r := range s {
if unicode.IsUpper(r) {
return true
}
}
return false
}
func containsLowercase(s string) bool {
for _, r := range s {
if unicode.IsLower(r) {
return true
}
}
return false
}
func containsDigit(s string) bool {
for _, r := range s {
if unicode.IsDigit(r) {
return true
}
}
return false
}
func containsSpecialChar(s string) bool {
for _, r := range s {
if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
return true
}
}
return false
}
```
### Authentication and Authorization
```go
// pkg/auth/jwt.go
package auth
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// Claims represents JWT claims
type Claims struct {
UserID string `json:"user_id"`
Email string `json:"email"`
Role string `json:"role"`
jwt.RegisteredClaims
}
// JWTManager handles JWT token operations
type JWTManager struct {
secretKey []byte
expiration time.Duration
}
// NewJWTManager creates a new JWT manager
func NewJWTManager(secretKey string, expiration time.Duration) *JWTManager {
return &JWTManager{
secretKey: []byte(secretKey),
expiration: expiration,
}
}
// GenerateToken generates a new JWT token
func (m *JWTManager) GenerateToken(userID, email, role string) (string, error) {
claims := &Claims{
UserID: userID,
Email: email,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(m.expiration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "your-app",
Subject: userID,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(m.secretKey)
}
// ValidateToken validates and parses a JWT token
func (m *JWTManager) ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return m.secretKey, nil
})
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
// RefreshToken generates a new token with extended expiration
func (m *JWTManager) RefreshToken(claims *Claims) (string, error) {
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(m.expiration))
claims.IssuedAt = jwt.NewNumericDate(time.Now())
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(m.secretKey)
}
// Middleware for authentication
func (m *JWTManager) AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Authorization header required", http.StatusUnauthorized)
return
}
tokenString := strings.Replace(authHeader, "Bearer ", "", 1)
claims, err := m.ValidateToken(tokenString)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Add claims to context
ctx := context.WithValue(r.Context(), "user_claims", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Role-based authorization middleware
func (m *JWTManager) RequireRole(requiredRole string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := r.Context().Value("user_claims").(*Claims)
if !ok {
http.Error(w, "User claims not found", http.StatusUnauthorized)
return
}
if claims.Role != requiredRole {
http.Error(w, "Insufficient permissions", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
```
## Integration Patterns
### gRPC Service Implementation
```go
// api/proto/user.proto
syntax = "proto3";
package user.v1;
option go_package = "github.com/your-org/your-project/api/user/v1;userv1";
service UserService {
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
}
message User {
string id = 1;
string email = 2;
string name = 3;
google.protobuf.Timestamp created_at = 4;
google.protobuf.Timestamp updated_at = 5;
}
message CreateUserRequest {
string email = 1;
string name = 2;
}
message CreateUserResponse {
User user = 1;
}
// server/grpc/user_service.go
package grpc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/your-org/your-project/api/user/v1"
"github.com/your-org/your-project/internal/service"
)
type GRPCUserServer struct {
userv1.UnimplementedUserServiceServer
userService service.UserService
}
func NewGRPCUserServer(userService service.UserService) *GRPCUserServer {
return &GRPCUserServer{
userService: userService,
}
}
func (s *GRPCUserServer) CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error) {
// Convert protobuf to domain model
createReq := model.CreateUserRequest{
Email: req.GetEmail(),
Name: req.GetName(),
}
// Call service layer
user, err := s.userService.CreateUser(ctx, createReq)
if err != nil {
if errors.Is(err, service.ErrUserExists) {
return nil, status.Error(codes.AlreadyExists, "user already exists")
}
if errors.Is(err, service.ErrInvalidInput) {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return nil, status.Error(codes.Internal, "internal server error")
}
// Convert domain model to protobuf
return &userv1.CreateUserResponse{
User: s.userToProto(user),
}, nil
}
func (s *GRPCUserServer) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.GetUserResponse, error) {
user, err := s.userService.GetUser(ctx, req.GetId())
if err != nil {
if errors.Is(err, service.ErrUserNotFound) {
return nil, status.Error(codes.NotFound, "user not found")
}
return nil, status.Error(codes.Internal, "internal server error")
}
return &userv1.GetUserResponse{
User: s.userToProto(user),
}, nil
}
func (s *GRPCUserServer) userToProto(user *model.User) *userv1.User {
return &userv1.User{
Id: user.ID,
Email: user.Email,
Name: user.Name,
CreatedAt: timestamppb.New(user.CreatedAt),
UpdatedAt: timestamppb.New(user.UpdatedAt),
}
}
```
### Message Queue Integration
```go
// pkg/messaging/nats.go
package messaging
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/nats-io/nats.go"
)
// Message represents a message payload
type Message struct {
ID string `json:"id"`
Type string `json:"type"`
Data map[string]interface{} `json:"data"`
Timestamp time.Time `json:"timestamp"`
}
// MessageHandler handles incoming messages
type MessageHandler func(ctx context.Context, msg *Message) error
// MessageBroker defines message broker interface
type MessageBroker interface {
Publish(ctx context.Context, subject string, msg *Message) error
Subscribe(ctx context.Context, subject string, handler MessageHandler) error
Close() error
}
// NATSMessageBroker implements MessageBroker using NATS
type NATSMessageBroker struct {
conn *nats.Conn
js nats.JetStreamContext
}
func NewNATSMessageBroker(url string) (*NATSMessageBroker, error) {
conn, err := nats.Connect(url,
nats.ReconnectWait(2*time.Second),
nats.MaxReconnects(5),
nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
fmt.Printf("NATS disconnected: %v\n", err)
}),
nats.ReconnectHandler(func(nc *nats.Conn) {
fmt.Printf("NATS reconnected to %v\n", nc.ConnectedUrl())
}),
)
if err != nil {
return nil, fmt.Errorf("failed to connect to NATS: %w", err)
}
js, err := conn.JetStream()
if err != nil {
return nil, fmt.Errorf("failed to get JetStream context: %w", err)
}
return &NATSMessageBroker{
conn: conn,
js: js,
}, nil
}
func (b *NATSMessageBroker) Publish(ctx context.Context, subject string, msg *Message) error {
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
// Use JetStream for persistent messaging
_, err = b.js.Publish(ctx, subject, data)
if err != nil {
return fmt.Errorf("failed to publish message: %w", err)
}
return nil
}
func (b *NATSMessageBroker) Subscribe(ctx context.Context, subject string, handler MessageHandler) error {
// Create stream if it doesn't exist
streamConfig := &nats.StreamConfig{
Name: "events",
Subjects: []string{subject},
Retention: nats.WorkQueuePolicy,
MaxBytes: 1024 * 1024 * 1024, // 1GB
Storage: nats.FileStorage,
Replicas: 1,
}
_, err := b.js.AddStream(streamConfig)
if err != nil && !errors.Is(err, nats.ErrStreamNameAlreadyInUse) {
return fmt.Errorf("failed to create stream: %w", err)
}
// Create consumer
consumerConfig := &nats.ConsumerConfig{
Durable: "worker",
AckPolicy: nats.AckExplicitPolicy,
}
_, err = b.js.AddConsumer("events", consumerConfig)
if err != nil && !errors.Is(err, nats.ErrConsumerAlreadyExists) {
return fmt.Errorf("failed to create consumer: %w", err)
}
// Subscribe to messages
_, err = b.js.Subscribe(subject, func(msg *nats.Msg) {
var message Message
if err := json.Unmarshal(msg.Data, &message); err != nil {
fmt.Printf("Failed to unmarshal message: %v\n", err)
msg.Nak()
return
}
if err := handler(ctx, &message); err != nil {
fmt.Printf("Handler failed: %v\n", err)
msg.Nak()
return
}
msg.Ack()
}, nats.Durable("worker"))
if err != nil {
return fmt.Errorf("failed to subscribe: %w", err)
}
return nil
}
func (b *NATSMessageBroker) Close() error {
b.conn.Close()
return nil
}
// Usage example
func setupEventHandlers(broker MessageBroker) {
// User created event handler
broker.Subscribe(context.Background(), "user.created", func(ctx context.Context, msg *Message) error {
fmt.Printf("User created: %v\n", msg)
// Send welcome email, update analytics, etc.
return nil
})
// User updated event handler
broker.Subscribe(context.Background(), "user.updated", func(ctx context.Context, msg *Message) error {
fmt.Printf("User updated: %v\n", msg)
// Update search index, notify other services, etc.
return nil
})
}
```
## Modern Development Workflow
### Configuration Management
```go
// pkg/config/config.go
package config
import (
"fmt"
"os"
"strconv"
"time"
"github.com/spf13/viper"
)
// Config holds application configuration
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
Auth AuthConfig `mapstructure:"auth"`
Logging LoggingConfig `mapstructure:"logging"`
Monitoring MonitoringConfig `mapstructure:"monitoring"`
}
// ServerConfig holds server configuration
type ServerConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
ReadTimeout time.Duration `mapstructure:"read_timeout"`
WriteTimeout time.Duration `mapstructure:"write_timeout"`
GracefulShutdownTimeout time.Duration `mapstructure:"graceful_shutdown_timeout"`
}
// DatabaseConfig holds database configuration
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"db_name"`
SSLMode string `mapstructure:"ssl_mode"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
}
// Load loads configuration from file and environment variables
func Load() (*Config, error) {
// Set default values
setDefaults()
// Load from config file
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./configs")
viper.AddConfigPath(".")
// Load environment variables
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
// Config file not found is OK, we'll use defaults and env vars
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
// Validate configuration
if err := validate(&config); err != nil {
return nil, fmt.Errorf("invalid configuration: %w", err)
}
return &config, nil
}
func setDefaults() {
viper.SetDefault("server.host", "0.0.0.0")
viper.SetDefault("server.port", 8080)
viper.SetDefault("server.read_timeout", 30*time.Second)
viper.SetDefault("server.write_timeout", 30*time.Second)
viper.SetDefault("server.graceful_shutdown_timeout", 30*time.Second)
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 5432)
viper.SetDefault("database.ssl_mode", "disable")
viper.SetDefault("database.max_open_conns", 25)
viper.SetDefault("database.max_idle_conns", 25)
viper.SetDefault("database.conn_max_lifetime", 5*time.Minute)
viper.SetDefault("redis.host", "localhost")
viper.SetDefault("redis.port", 6379)
viper.SetDefault("redis.db", 0)
viper.SetDefault("auth.jwt.expiration", 24*time.Hour)
viper.SetDefault("logging.level", "info")
viper.SetDefault("logging.format", "json")
}
func validate(config *Config) error {
if config.Server.Port <= 0 || config.Server.Port > 65535 {
return fmt.Errorf("invalid server port: %d", config.Server.Port)
}
if config.Database.Host == "" {
return fmt.Errorf("database host is required")
}
if config.Database.User == "" {
return fmt.Errorf("database user is required")
}
return nil
}
// GetDSN returns database connection string
func (c *DatabaseConfig) GetDSN() string {
return fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
c.Host, c.Port, c.User, c.Password, c.DBName, c.SSLMode,
)
}
// GetRedisAddr returns Redis connection address
func (c *RedisConfig) GetAddr() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
// GetServerAddr returns server address
func (c *ServerConfig) GetAddr() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
```
### Makefile for Development
```makefile
# Makefile
.PHONY: help build run test clean lint format docker-build docker-run
# Variables
APP_NAME := your-app
VERSION := $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
LDFLAGS := -ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)"
# Docker variables
DOCKER_REGISTRY := your-registry
DOCKER_TAG := $(DOCKER_REGISTRY)/$(APP_NAME):$(VERSION)
# Help
help:
@echo "Available commands:"
@echo " build Build the application"
@echo " run Run the application"
@echo " test Run tests"
@echo " test-coverage Run tests with coverage"
@echo " lint Run linter"
@echo " format Format code"
@echo " clean Clean build artifacts"
@echo " deps Install dependencies"
@echo " migrate Run database migrations"
@echo " docker-build Build Docker image"
@echo " docker-run Run Docker container"
@echo " generate Generate code (mocks, protobuf, etc.)"
# Build
build:
go build $(LDFLAGS) -o bin/$(APP_NAME) cmd/server/main.go
# Run
run:
go run $(LDFLAGS) cmd/server/main.go
# Development mode with hot reload
dev:
air
# Tests
test:
go test -v ./...
test-coverage:
go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
test-bench:
go test -bench=. -benchmem ./...
# Linting
lint:
golangci-lint run
# Formatting
format:
go fmt ./...
goimports -w .
# Dependencies
deps:
go mod download
go mod tidy
# Generate code
generate:
go generate ./...
mockgen -source=internal/service/user.go -destination=test/mocks/user_service_mock.go
protoc --go_out=. --go-grpc_out=. api/proto/user.proto
# Database
migrate-up:
migrate -path migrations -database "$(shell grep -A5 'database:' configs/config.yaml | tail -n1 | cut -d' ' -f2)" up
migrate-down:
migrate -path migrations -database "$(shell grep -A5 'database:' configs/config.yaml | tail -n1 | cut -d' ' -f2)" down
# Clean
clean:
rm -rf bin/
rm -f coverage.out coverage.html
# Docker
docker-build:
docker build -t $(DOCKER_TAG) .
docker-run:
docker run -p 8080:8080 $(DOCKER_TAG)
docker-push:
docker push $(DOCKER_TAG)
# Install tools
install-tools:
go install github.com/cosmtrek/air@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golang/mock/mockgen@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```
---
**Created by**: MoAI Language Skill Factory
**Last Updated**: 2025-11-06
**Version**: 2.0.0
**Go Target**: 1.25+ with latest language features
This skill provides comprehensive Go development guidance with 2025 best practices, covering everything from basic concurrent programming to advanced cloud-native patterns and enterprise-grade applications.