15 Prompts for Go: Microservices, APIs, and CLI Utilities

Introduction

Go (Golang) has become the language of choice for building scalable microservices, high-performance APIs, and robust command-line tools. According to the 2025 Stack Overflow Developer Survey, Go is used by 13.5% of professional developers, with a particular concentration in backend and systems programming. Its simplicity, built-in concurrency, and fast compilation make it ideal for modern cloud-native development.

But even experienced Go developers can get stuck on repetitive tasks: designing service boundaries, writing boilerplate HTTP handlers, or debugging race conditions. That's where AI prompts come in. By using well-crafted prompts, you can generate production-ready code, architecture patterns, and testing strategies in seconds.

This collection contains 15 battle-tested prompts I use daily in my Go workflow. Each prompt includes a real usage example, code output, and practical tips. Whether you're building a new microservice, refactoring an API, or writing a CLI utility, these prompts will save you hours.

1. Generate a Minimal HTTP API Server

Prompt:

"Create a Go HTTP API server with the following: use the standard net/http package (no external frameworks), define routes for GET /users and POST /users, parse JSON request bodies, return JSON responses with proper status codes, include middleware for logging requests, and add graceful shutdown with signal handling. Output the complete main.go file."

Why it works: This prompt specifies constraints (standard library), clear endpoints, middleware, and deployment concerns. It produces a ready-to-run server.

Example output (partial):

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{{ID: 1, Name: "Alice"}}

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 %s", r.Method, r.URL.Path, time.Since(start))
    })
}

func getUsers(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /users", getUsers)

    srv := &http.Server{Addr: ":8080", Handler: loggingMiddleware(mux)}

    go func() {
        log.Println("Server starting on :8080")
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatal(err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit
    log.Println("Shutting down...")
}

Tips: Adjust port and routes as needed. The prompt can be extended with database integration or authentication.

2. Design a Microservice with Clean Architecture

Prompt:

"Generate a Go microservice project structure following Clean Architecture principles. Include directories: cmd/server/ (entry point), internal/handler/ (HTTP handlers), internal/usecase/ (business logic), internal/repository/ (data access), and internal/entity/ (domain models). Provide a sample User entity with CRUD operations, using PostgreSQL as the database and database/sql for queries. Output the directory tree and the content of internal/usecase/user.go."

Why it works: Clean Architecture separates concerns, making the code testable and maintainable. This prompt produces a template you can reuse for any service.

Example output (directory tree):

my-service/
├── cmd/server/main.go
├── internal/
   ├── entity/user.go
   ├── handler/user.go
   ├── usecase/user.go
   └── repository/user.go
├── go.mod
└── go.sum

Example output (usecase/user.go):

package usecase

import (
    "my-service/internal/entity"
    "my-service/internal/repository"
)

type UserUsecase struct {
    repo *repository.UserRepository
}

func NewUserUsecase(repo *repository.UserRepository) *UserUsecase {
    return &UserUsecase{repo: repo}
}

func (uc *UserUsecase) GetUser(id int) (*entity.User, error) {
    return uc.repo.FindByID(id)
}

func (uc *UserUsecase) CreateUser(name, email string) (*entity.User, error) {
    user := &entity.User{Name: name, Email: email}
    if err := user.Validate(); err != nil {
        return nil, err
    }
    return uc.repo.Save(user)
}

3. Add Structured Logging with slog

Prompt:

"Write a Go function that configures structured logging using the log/slog package (introduced in Go 1.21). The logger should output JSON to stdout, include a service name attribute, support different log levels (DEBUG, INFO, WARN, ERROR), and be context-aware (accept context.Context to extract trace IDs). Provide an example of usage in an HTTP handler."

Why it works: Structured logging is essential for debugging microservices. The prompt focuses on the modern slog package, which is now part of the standard library.

Example output:

package logger

import (
    "context"
    "log/slog"
    "os"
)

func New(serviceName string) *slog.Logger {
    return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelDebug,
    })).With("service", serviceName)
}

func FromContext(ctx context.Context) *slog.Logger {
    if logger, ok := ctx.Value("logger").(*slog.Logger); ok {
        return logger
    }
    return slog.Default()
}

4. Create a RESTful API with Chi Router

Prompt:

"Build a RESTful API in Go using the chi router. Define routes for CRUD operations on a Product resource: GET /products, GET /products/{id}, POST /products, PUT /products/{id}, DELETE /products/{id}. Use an in-memory slice as the data store, return proper HTTP status codes (200, 201, 204, 400, 404, 500), and include request validation (name required, price > 0). Output the complete main.go."

Why it works: chi is a popular lightweight router. The prompt specifies validation and status codes, producing production-quality code.

Example output (route registration):

r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

r.Route("/products", func(r chi.Router) {
    r.Get("/", listProducts)
    r.Post("/", createProduct)
    r.Route("/{id}", func(r chi.Router) {
        r.Get("/", getProduct)
        r.Put("/", updateProduct)
        r.Delete("/", deleteProduct)
    })
})

5. Generate a CLI Tool with Cobra

Prompt:

"Write a Go CLI tool using the Cobra library. The tool should have three commands: init (initialize a config file), run (start a process with optional flags --verbose and --port), and version (print version info). Use Viper for configuration management. Output the main.go and the cmd/root.go file."

Why it works: Cobra is the de facto standard for Go CLI tools (used by Kubernetes, Hugo, etc.). The prompt covers commands, flags, and config.

Example output:

var rootCmd = &cobra.Command{
    Use:   "mycli",
    Short: "A CLI tool for managing services",
}

var runCmd = &cobra.Command{
    Use:   "run",
    Short: "Run the service",
    Run: func(cmd *cobra.Command, args []string) {
        verbose, _ := cmd.Flags().GetBool("verbose")
        port, _ := cmd.Flags().GetInt("port")
        // start service
    },
}

func init() {
    runCmd.Flags().BoolP("verbose", "v", false, "verbose output")
    runCmd.Flags().IntP("port", "p", 8080, "port to listen on")
    rootCmd.AddCommand(runCmd)
}

6. Implement Graceful Shutdown for a gRPC Server

Prompt:

"Write Go code for a gRPC server that handles graceful shutdown. Use the google.golang.org/grpc package. The server should listen on port 50051, accept OS signals (SIGINT, SIGTERM), stop accepting new requests, drain existing connections with a 30-second timeout, and log each step. Output the complete server setup code."

Why it works: Graceful shutdown prevents dropped requests during deployment. The prompt includes timeout and logging.

Example output (shutdown logic):

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    s := grpc.NewServer()
    // register services

    go func() {
        sigCh := make(chan os.Signal, 1)
        signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
        <-sigCh
        log.Println("Shutting down gRPC server...")
        s.GracefulStop()
    }()

    log.Println("Server starting on :50051")
    s.Serve(lis)
}

7. Write Unit Tests with Testify

Prompt:

"Generate Go unit tests for a Calculator struct with methods: Add, Subtract, Multiply, Divide. Use the testify/assert and testify/suite packages. Include table-driven tests for edge cases (division by zero, negative numbers, large integers). Output the complete test file."

Why it works: Table-driven tests are idiomatic in Go. Testify simplifies assertions.

Example output:

func (s *CalculatorTestSuite) TestDivide() {
    tests := []struct {
        name      string
        a, b      int
        expected  int
        errExpect bool
    }{
        {"positive", 10, 2, 5, false},
        {"divide by zero", 10, 0, 0, true},
        {"negative", -10, 2, -5, false},
    }
    for _, tt := range tests {
        s.Run(tt.name, func() {
            result, err := s.calc.Divide(tt.a, tt.b)
            if tt.errExpect {
                s.Error(err)
            } else {
                s.Equal(tt.expected, result)
            }
        })
    }
}

8. Add Middleware for Rate Limiting

Prompt:

"Write a Go HTTP middleware that implements token bucket rate limiting. Use the standard library. The middleware should accept parameters: rate (requests per second) and burst (maximum burst). Use a sync.Mutex for thread safety. If the rate limit is exceeded, return HTTP 429 Too Many Requests with a Retry-After header. Output the middleware function and an example usage in an HTTP server."

Why it works: Rate limiting is critical for API stability. The prompt specifies algorithm and headers.

Example output:

type RateLimiter struct {
    mu       sync.Mutex
    tokens   float64
    rate     float64
    burst    int
    lastTime time.Time
}

func (rl *RateLimiter) Allow() bool {
    rl.mu.Lock()
    defer rl.mu.Unlock()
    now := time.Now()
    elapsed := now.Sub(rl.lastTime).Seconds()
    rl.tokens += elapsed * rl.rate
    if rl.tokens > float64(rl.burst) {
        rl.tokens = float64(rl.burst)
    }
    rl.lastTime = now
    if rl.tokens >= 1 {
        rl.tokens--
        return true
    }
    return false
}

9. Validate JSON Requests with Custom Struct Tags

Prompt:

"Write Go code that validates incoming JSON requests using struct tags and a custom validation function. Support tags: required, min, max, pattern. For example: Name string \json:\"name\" validate:\"required,min=3,max=50\"``. Return a map of field errors. Output the validator function and an example handler."

Why it works: Validation is a common pain point. This prompt provides a lightweight alternative to third-party libraries.

10. Generate SQL Queries with sqlx

Prompt:

"Write Go code using jmoiron/sqlx to perform CRUD operations on a users table with columns: id (SERIAL PRIMARY KEY), name (VARCHAR(100)), email (VARCHAR(255) UNIQUE), created_at (TIMESTAMP). Use named parameters for inserts and updates. Include a function to get a user by email. Output the repository file."

Why it works: sqlx extends database/sql with named parameters and struct scanning.

11. Implement a Worker Pool for Concurrent Tasks

Prompt:

"Write a Go worker pool that processes jobs concurrently. Use channels for job submission and results collection. The pool should accept a number of workers (goroutines) and a job function. Jobs should be cancellable via context. Output the worker pool implementation and an example that processes URLs."

12. Add Health Checks to a Microservice

Prompt:

"Write Go code that adds health check endpoints to a microservice. Implement two endpoints: GET /healthz (liveness) and GET /readyz (readiness). The readiness check should verify database connectivity by pinging PostgreSQL. Use the database/sql package. Return JSON with status and component details."

13. Create a Simple In-Memory Cache with TTL

Prompt:

"Write a Go in-memory cache with TTL (time-to-live) support. Use a map with a mutex for thread safety. Implement methods: Get(key string) (interface{}, bool), Set(key string, value interface{}, ttl time.Duration), Delete(key string), and Cleanup() to remove expired entries periodically. Output the complete cache package."

14. Parse and Transform JSON with Generics

Prompt:

"Write Go generic functions to parse JSON from an io.Reader and transform a slice of structs. The function ParseJSON[T any](r io.Reader) ([]T, error) should decode a JSON array. The function Transform[T, U any](items []T, fn func(T) U) []U should apply a transformation. Output both functions with an example."

15. Configure Environment-Based Settings

Prompt:

"Write Go code that loads configuration from environment variables using the os package and strconv. Define a Config struct with fields: Port (int), DatabaseURL (string), Debug (bool), MaxConnections (int). Provide a function LoadConfig() (*Config, error) that reads from env vars with sensible defaults. Output the config package."

Conclusion

These 15 prompts cover the most common tasks in Go development: from setting up servers and microservices to writing tests and CLI tools. By using them in your daily workflow, you can focus on business logic instead of boilerplate. I recommend saving them as a reference and adapting them to your specific project needs.

For more advanced patterns, check the official Go documentation at go.dev/doc and the Go blog at go.dev/blog. Happy coding!

← All posts

Comments