Skip to content
backend-go logo

Backend Go Development

backend-go

Enforces Go backend best practices following clean architecture, dependency injection, and test-driven development. Automatically applied for Go backend implementation, API development, and microservices construction.

SKILL.md

Full skill instructions

Backend Go Development

Overview

This skill provides best practices for Go backend development. It emphasizes clean architecture, dependency injection, and test-driven development to build scalable and maintainable codebases.

Auto-Trigger Conditions

This skill is automatically applied when:

  • Creating or editing Go files (.go)
  • Working on backend API development
  • Implementing microservices
  • Database operations
  • Keywords like "backend implementation", "API creation" are mentioned

Project Structure (Clean Architecture)

project/
├── cmd/
│   └── api/                    # Application entry point
│       └── main.go
├── internal/                   # Private code (cannot be imported externally)
│   ├── domain/                 # Business logic layer (innermost)
│   │   ├── entity/            # Entities (business objects)
│   │   ├── repository/        # Repository interfaces
│   │   └── service/           # Domain services
│   ├── usecase/               # Application business rules
│   │   └── user/
│   │       ├── create.go
│   │       └── get.go
│   ├── handler/               # Presentation layer (outermost)
│   │   ├── http/              # HTTP handlers
│   │   └── grpc/              # gRPC handlers
│   ├── repository/            # Data access layer
│   │   ├── postgres/          # PostgreSQL implementation
│   │   └── redis/             # Redis implementation
│   └── infrastructure/        # External dependencies
│       ├── config/            # Configuration management
│       ├── database/          # DB connection
│       └── logger/            # Logger
├── pkg/                       # Public libraries (can be imported externally)
│   ├── validator/
│   ├── middleware/
│   └── errors/
├── test/                      # Integration tests
│   ├── integration/
│   └── e2e/
├── go.mod
├── go.sum
├── Makefile
└── README.md

Layer Design Principles

1. Domain Layer (Innermost)

// internal/​domain/​entity/​user.go
package entity

import (
    "time"
    "github.com/​google/​uuid"
)

// User entity - business logic only
type User struct {
    ID        uuid.UUID
    Email     string
    Name      string
    CreatedAt time.Time
    UpdatedAt time.Time
}

// Validate domain validation
func (u *User) Validate() error {
    if u.Email == "" {
        return ErrInvalidEmail
    }
    if u.Name == "" {
        return ErrInvalidName
    }
    return nil
}

// internal/​domain/​repository/​user.go
package repository

import (
    "context"
    "github.com/​google/​uuid"
    "yourproject/​internal/​domain/​entity"
)

// UserRepository interface definition (implementation in outer layers)
type UserRepository interface {
    Create(ctx context.Context, user *entity.User) error
    GetByID(ctx context.Context, id uuid.UUID) (*entity.User, error)
    Update(ctx context.Context, user *entity.User) error
    Delete(ctx context.Context, id uuid.UUID) error
}

2. UseCase Layer (Business Rules)

// internal/​usecase/​user/​create.go
package user

import (
    "context"
    "github.com/​google/​uuid"
    "yourproject/​internal/​domain/​entity"
    "yourproject/​internal/​domain/​repository"
)

type CreateUserInput struct {
    Email string
    Name  string
}

type CreateUserUseCase struct {
    userRepo repository.UserRepository
}

func NewCreateUserUseCase(userRepo repository.UserRepository) *CreateUserUseCase {
    return &CreateUserUseCase{
        userRepo: userRepo,
    }
}

func (uc *CreateUserUseCase) Execute(ctx context.Context, input CreateUserInput) (*entity.User, error) {
    // 1. Create entity
    user := &entity.User{
        ID:    uuid.New(),
        Email: input.Email,
        Name:  input.Name,
    }

    // 2. Domain validation
    if err := user.Validate(); err != nil {
        return nil, err
    }

    // 3. Persist through repository
    if err := uc.userRepo.Create(ctx, user); err != nil {
        return nil, err
    }

    return user, nil
}

3. Handler Layer (Presentation)

// internal/​handler/​http/​user.go
package http

import (
    "net/​http"
    "github.com/​gin-gonic/​gin"
    "yourproject/​internal/​usecase/​user"
)

type UserHandler struct {
    createUserUseCase *user.CreateUserUseCase
}

func NewUserHandler(createUserUseCase *user.CreateUserUseCase) *UserHandler {
    return &UserHandler{
        createUserUseCase: createUserUseCase,
    }
}

type CreateUserRequest struct {
    Email string `json:"email" binding:"required,email"`
    Name  string `json:"name" binding:"required,min=1"`
}

func (h *UserHandler) CreateUser(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := h.createUserUseCase.Execute(c.Request.Context(), user.CreateUserInput{
        Email: req.Email,
        Name:  req.Name,
    })
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusCreated, user)
}

4. Repository Layer (Data Access)

// internal/​repository/​postgres/​user.go
package postgres

import (
    "context"
    "database/​sql"
    "github.com/​google/​uuid"
    "yourproject/​internal/​domain/​entity"
    "yourproject/​internal/​domain/​repository"
)

type userRepository struct {
    db *sql.DB
}

func NewUserRepository(db *sql.DB) repository.UserRepository {
    return &userRepository{db: db}
}

func (r *userRepository) Create(ctx context.Context, user *entity.User) error {
    query := `
        INSERT INTO users (id, email, name, created_at, updated_at)
        VALUES ($1, $2, $3, $4, $5)
    `
    _, err := r.db.ExecContext(ctx, query,
        user.ID,
        user.Email,
        user.Name,
        user.CreatedAt,
        user.UpdatedAt,
    )
    return err
}

func (r *userRepository) GetByID(ctx context.Context, id uuid.UUID) (*entity.User, error) {
    query := `
        SELECT id, email, name, created_at, updated_at
        FROM users
        WHERE id = $1
    `
    var user entity.User
    err := r.db.QueryRowContext(ctx, query, id).Scan(
        &user.ID,
        &user.Email,
        &user.Name,
        &user.CreatedAt,
        &user.UpdatedAt,
    )
    if err != nil {
        return nil, err
    }
    return &user, nil
}

Dependency Injection

// cmd/​api/​main.go
package main

import (
    "database/​sql"
    "log"

    "github.com/​gin-gonic/​gin"
    _ "github.com/​lib/​pq"

    "yourproject/​internal/​handler/​http"
    "yourproject/​internal/​repository/​postgres"
    "yourproject/​internal/​usecase/​user"
)

func main() {
    // 1. Initialize infrastructure
    db, err := sql.Open("postgres", "postgresql://...")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 2. Build repository layer
    userRepo := postgres.NewUserRepository(db)

    // 3. Build usecase layer
    createUserUseCase := user.NewCreateUserUseCase(userRepo)

    // 4. Build handler layer
    userHandler := http.NewUserHandler(createUserUseCase)

    // 5. Setup router
    r := gin.Default()
    r.POST("/​users", userHandler.CreateUser)

    // 6. Start server
    if err := r.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}

Error Handling

// pkg/​errors/​errors.go
package errors

import (
    "errors"
    "fmt"
)

type AppError struct {
    Code    string
    Message string
    Err     error
}

func (e *AppError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("%s: %v", e.Message, e.Err)
    }
    return e.Message
}

func (e *AppError) Unwrap() error {
    return e.Err
}

// Predefined errors
var (
    ErrNotFound      = &AppError{Code: "NOT_FOUND", Message: "resource not found"}
    ErrInvalidInput  = &AppError{Code: "INVALID_INPUT", Message: "invalid input"}
    ErrUnauthorized  = &AppError{Code: "UNAUTHORIZED", Message: "unauthorized"}
)

// Error wrapping
func Wrap(err error, message string) error {
    return &AppError{
        Message: message,
        Err:     err,
    }
}

Testing Strategy

Unit Tests

// internal/​usecase/​user/​create_test.go
package user_test

import (
    "context"
    "testing"

    "github.com/​stretchr/​testify/​assert"
    "github.com/​stretchr/​testify/​mock"

    "yourproject/​internal/​domain/​entity"
    "yourproject/​internal/​usecase/​user"
)

// Mock repository
type MockUserRepository struct {
    mock.Mock
}

func (m *MockUserRepository) Create(ctx context.Context, user *entity.User) error {
    args := m.Called(ctx, user)
    return args.Error(0)
}

func TestCreateUserUseCase_Execute(t *testing.T) {
    // Arrange
    mockRepo := new(MockUserRepository)
    useCase := user.NewCreateUserUseCase(mockRepo)

    input := user.CreateUserInput{
        Email: "[email protected]",
        Name:  "Test User",
    }

    mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*entity.User")).Return(nil)

    // Act
    result, err := useCase.Execute(context.Background(), input)

    // Assert
    assert.NoError(t, err)
    assert.NotNil(t, result)
    assert.Equal(t, input.Email, result.Email)
    mockRepo.AssertExpectations(t)
}

Table-Driven Tests

func TestValidate(t *testing.T) {
    tests := []struct {
        name    string
        user    *entity.User
        wantErr bool
    }{
        {
            name:    "valid user",
            user:    &entity.User{Email: "[email protected]", Name: "Test"},
            wantErr: false,
        },
        {
            name:    "empty email",
            user:    &entity.User{Email: "", Name: "Test"},
            wantErr: true,
        },
        {
            name:    "empty name",
            user:    &entity.User{Email: "[email protected]", Name: ""},
            wantErr: true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := tt.user.Validate()
            if tt.wantErr {
                assert.Error(t, err)
            } else {
                assert.NoError(t, err)
            }
        })
    }
}

Performance Optimization

1. Context Management

func (h *Handler) Handle(c *gin.Context) {
    // Set timeout
    ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
    defer cancel()

    result, err := h.useCase.Execute(ctx)
    // ...
}

2. Connection Pooling

func NewDB(connStr string) (*sql.DB, error) {
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        return nil, err
    }

    db.SetMaxOpenConns(25)                 // Maximum open connections
    db.SetMaxIdleConns(5)                  // Maximum idle connections
    db.SetConnMaxLifetime(5 * time.Minute) // Maximum connection lifetime

    return db, nil
}

3. Goroutines and Channels

func (s *Service) ProcessBatch(ctx context.Context, items []Item) error {
    errCh := make(chan error, len(items))
    sem := make(chan struct{}, 10) // Limit concurrency

    for _, item := range items {
        sem <- struct{}{} // Acquire semaphore

        go func(item Item) {
            defer func() { <-sem }() // Release semaphore

            if err := s.processItem(ctx, item); err != nil {
                errCh <- err
            }
        }(item)
    }

    // Wait for all goroutines to complete
    for i := 0; i < cap(sem); i++ {
        sem <- struct{}{}
    }
    close(errCh)

    // Collect errors
    for err := range errCh {
        if err != nil {
            return err
        }
    }

    return nil
}

Implementation Checklist

Design Phase

  • Define each layer of clean architecture
  • Identify entities and business rules
  • Clearly define interfaces
  • Verify dependency direction (no dependency on inner layers)

Implementation Phase

  • Domain layer: Entities and repository interfaces
  • UseCase layer: Business logic implementation
  • Repository layer: Data access implementation
  • Handler layer: HTTP handler implementation
  • Error handling implementation
  • Add logging

Testing Phase

  • Create unit tests (target 80%+ coverage)
  • Isolate dependencies using mocks
  • Apply table-driven tests
  • Create integration tests

Pre-Production Deployment

  • Conduct performance tests
  • Security review
  • Update documentation
  • Verify log levels

Best Practices

DO ✅

  • Follow clean architecture
  • Use interfaces for loose coupling
  • Practice test-driven development (TDD)
  • Handle errors appropriately
  • Use context for cancellation
  • Use pointer receivers for structs

DON'T ❌

  • Don't use global variables
  • Don't overuse panic (return errors instead)
  • Don't leak goroutines
  • Don't skip nil checks
  • Don't create large functions (keep functions small)
  • Don't create circular dependencies

Security

// 1. SQL Injection prevention: Use placeholders
query := "SELECT * FROM users WHERE id = $1"
db.QueryContext(ctx, query, userID)

// 2. Password hashing
import "golang.org/​x/​crypto/​bcrypt"

hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)

// 3. Rate limiting
import "golang.org/​x/​time/​rate"

limiter := rate.NewLimiter(rate.Limit(10), 100) // 10 req/​sec, burst 100
if !limiter.Allow() {
    return ErrTooManyRequests
}

Summary

This skill ensures:

  • 🏗️ Clean Architecture: Layer separation and dependency inversion
  • 🧪 Testability: High coverage and maintainability
  • ⚡ Performance: Efficient concurrency
  • 🔒 Security: Secure code implementation
  • 📦 Scalability: Microservice-ready
  • 📚 Maintainability: Readable and extensible code

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View