Claude-skill-registry 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.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/backend-go" ~/.claude/skills/majiayu000-claude-skill-registry-backend-go && rm -rf "$T"
manifest:
skills/data/backend-go/SKILL.mdsource content
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: "test@example.com", 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: "test@example.com", Name: "Test"}, wantErr: false, }, { name: "empty email", user: &entity.User{Email: "", Name: "Test"}, wantErr: true, }, { name: "empty name", user: &entity.User{Email: "test@example.com", 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