15 промтов для Go: микросервисы, API и CLI-утилиты

Введение

Go (Golang) — язык, созданный для масштабируемых и производительных приложений. По данным опроса JetBrains за 2025 год, Go входит в топ-5 языков для бэкенд-разработки, а его экосистема активно используется в микросервисной архитектуре (источник: JetBrains Developer Ecosystem 2025). Однако даже опытные разработчики тратят до 30% времени на шаблонный код: настройку роутеров, обработку ошибок, парсинг аргументов командной строки. Именно здесь помогают промпты — готовые шаблоны, которые ускоряют рутину.

В этой подборке — 15 конкретных промтов для Go. Каждый промт — это готовая конструкция, которую можно скопировать и адаптировать под свою задачу. Я разбил их на три категории: микросервисы, API и CLI-утилиты. Все примеры проверены на Go 1.22+ и используют популярные библиотеки (net/http, gorilla/mux, cobra, gin).

Промты для микросервисов

1. Базовый HTTP-сервер с graceful shutdown

Задача: Запустить HTTP-сервер, который корректно завершает работу при получении сигнала SIGINT/SIGTERM.
Промт:

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("ok"))
    })

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

    go func() {
        log.Println("Server starting on :8080")
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("Shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }
    log.Println("Server exited")
}

Пояснение: Этот код — стандартный скелет для микросервиса. Endpoint /health нужен для health checks в Kubernetes или Docker Compose. Graceful shutdown гарантирует, что все активные запросы завершатся до остановки.

2. Middleware для логирования запросов

Задача: Логировать каждый входящий HTTP-запрос (метод, путь, статус, длительность).
Промт:

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
        next.ServeHTTP(wrapped, r)
        log.Printf("%s %s %d %v", r.Method, r.URL.Path, wrapped.statusCode, time.Since(start))
    })
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}

Пояснение: Middleware оборачивает стандартный http.ResponseWriter, чтобы перехватить код статуса. Полезно для отладки и мониторинга в production.

3. Конфигурация через environment variables

Задача: Загрузить настройки микросервиса из переменных окружения с дефолтными значениями.
Промт:

import "os"

type Config struct {
    Port    string
    DBURL   string
    Timeout int
}

func LoadConfig() Config {
    return Config{
        Port:    getEnv("PORT", "8080"),
        DBURL:   getEnv("DATABASE_URL", "postgres://localhost:5432/mydb"),
        Timeout: getEnvInt("TIMEOUT_SECONDS", 30),
    }
}

func getEnv(key, fallback string) string {
    if value, ok := os.LookupEnv(key); ok {
        return value
    }
    return fallback
}

func getEnvInt(key string, fallback int) int {
    if value, ok := os.LookupEnv(key); ok {
        if i, err := strconv.Atoi(value); err == nil {
            return i
        }
    }
    return fallback
}

Пояснение: Используйте этот промт для 12-factor app. Избегайте хранения конфигов в коде — это упрощает деплой в разных окружениях.

4. Подключение к PostgreSQL с пулом соединений

Задача: Создать пул соединений к PostgreSQL с настройками таймаутов.
Промт:

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func NewDB(dsn string) (*sql.DB, error) {
    db, err := sql.Open("postgres", dsn)
    if err != nil {
        return nil, fmt.Errorf("failed to open db: %w", err)
    }

    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
    db.SetConnMaxIdleTime(1 * time.Minute)

    if err := db.Ping(); err != nil {
        return nil, fmt.Errorf("failed to ping db: %w", err)
    }

    return db, nil
}

Пояснение: Параметры пула (25/5) подходят для большинства микросервисов. Для высоконагруженных систем увеличьте MaxOpenConns до 100, но следите за лимитами базы.

5. Graceful shutdown для gRPC-сервера

Задача: Запустить gRPC-сервер с корректным завершением.
Промт:

import (
    "google.golang.org/grpc"
    "net"
)

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    // регистрация сервиса: pb.RegisterYourServiceServer(s, &server{})

    go func() {
        log.Println("gRPC server starting on :50051")
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }()

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

Пояснение: gRPC требует net.Listener и отдельного вызова GracefulStop(). Этот промт подходит для любого proto-сервиса.

6. Health check с readiness и liveness

Задача: Реализовать два эндпоинта для Kubernetes probes.
Промт:

func healthHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
    }
}

func readinessHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if err := db.Ping(); err != nil {
            w.WriteHeader(http.StatusServiceUnavailable)
            json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
            return
        }
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
    }
}

Пояснение: Liveness проверяет, жив ли процесс (просто 200 OK). Readiness проверяет зависимости (БД, кэш) — если БД недоступна, сервис не получает трафик.

Промты для API

7. REST API с gorilla/mux и JSON

Задача: Создать CRUD API для управления пользователями.
Промт:

import (
    "github.com/gorilla/mux"
    "encoding/json"
    "net/http"
)

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

var users []User
var nextID = 1

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

func createUser(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
    }
    user.ID = nextID
    nextID++
    users = append(users, user)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", getUsers).Methods("GET")
    r.HandleFunc("/users", createUser).Methods("POST")
    http.ListenAndServe(":8080", r)
}

Пояснение: gorilla/mux удобен для REST. В production добавьте валидацию и БД вместо слайса.

8. Обработка ошибок с кастомным JSON

Задача: Возвращать структурированные ошибки в JSON.
Промт:

type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

func writeError(w http.ResponseWriter, status int, message string) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(APIError{Code: status, Message: message})
}

// Использование:
if err != nil {
    writeError(w, http.StatusNotFound, "user not found")
    return
}

Пояснение: Единый формат ошибок упрощает клиентскую обработку. Статус-коды должны соответствовать HTTP-стандартам.

9. JWT-аутентификация (middleware)

Задача: Проверять JWT-токен в заголовке Authorization.
Промт:

import (
    "github.com/golang-jwt/jwt/v5"
    "strings"
)

var jwtSecret = []byte("super-secret-key")

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            writeError(w, http.StatusUnauthorized, "missing token")
            return
        }

        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("unexpected signing method")
            }
            return jwtSecret, nil
        })

        if err != nil || !token.Valid {
            writeError(w, http.StatusUnauthorized, "invalid token")
            return
        }

        next.ServeHTTP(w, r)
    })
}

Пояснение: Храните jwtSecret в env-переменной, не в коде. Для production используйте RS256 с публичным/приватным ключом.

10. Rate limiting (ограничение запросов)

Задача: Ограничить количество запросов с одного IP.
Промт:

import (
    "golang.org/x/time/rate"
    "sync"
)

type RateLimiter struct {
    visitors map[string]*rate.Limiter
    mu       sync.Mutex
    rate     rate.Limit
    burst    int
}

func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
    return &RateLimiter{
        visitors: make(map[string]*rate.Limiter),
        rate:     r,
        burst:    b,
    }
}

func (rl *RateLimiter) GetLimiter(ip string) *rate.Limiter {
    rl.mu.Lock()
    defer rl.mu.Unlock()

    limiter, exists := rl.visitors[ip]
    if !exists {
        limiter = rate.NewLimiter(rl.rate, rl.burst)
        rl.visitors[ip] = limiter
    }
    return limiter
}

func RateLimitMiddleware(rl *RateLimiter) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ip := r.RemoteAddr
            limiter := rl.GetLimiter(ip)
            if !limiter.Allow() {
                writeError(w, http.StatusTooManyRequests, "rate limit exceeded")
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

Пояснение: 10 запросов в секунду с burst 20 — хороший старт. Для продакшена добавьте очистку старых IP через TTL.

11. Пагинация с query parameters

Задача: Реализовать пагинацию через page и limit.
Промт:

func parsePagination(r *http.Request) (int, int) {
    page, _ := strconv.Atoi(r.URL.Query().Get("page"))
    if page < 1 {
        page = 1
    }
    limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
    if limit < 1 || limit > 100 {
        limit = 10
    }
    offset := (page - 1) * limit
    return offset, limit
}

// Использование в хендлере:
offset, limit := parsePagination(r)
rows, err := db.Query("SELECT * FROM users LIMIT $1 OFFSET $2", limit, offset)

Пояснение: Всегда устанавливайте максимальный лимит (здесь 100), чтобы клиенты не запрашивали миллионы записей.

Промты для CLI-утилит

12. CLI с cobra (структура команды)

Задача: Создать многоуровневый CLI с подкомандами.
Промт:

import (
    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "MyApp is a CLI tool for managing resources",
    Long:  `A longer description that spans multiple lines.`,
}

var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "Start the HTTP server",
    Run: func(cmd *cobra.Command, args []string) {
        port, _ := cmd.Flags().GetString("port")
        fmt.Printf("Starting server on port %s\n", port)
        // запуск сервера
    },
}

func init() {
    serveCmd.Flags().StringP("port", "p", "8080", "Port to listen on")
    rootCmd.AddCommand(serveCmd)
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Пояснение: Cobra — стандарт для CLI в Go (используется в Docker, Kubernetes). Флаг -p — короткий вариант для --port.

13. Парсинг аргументов без библиотек

Задача: Простой парсер флагов для маленьких утилит.
Промт:

import "flag"

func main() {
    name := flag.String("name", "World", "Name to greet")
    count := flag.Int("count", 1, "Number of times to greet")
    flag.Parse()

    for i := 0; i < *count; i++ {
        fmt.Printf("Hello, %s!\n", *name)
    }
}

Пояснение: Стандартный пакет flag подходит для скриптов. Для сложных CLI используйте cobra.

14. Чтение stdin и запись в stdout

Задача: Утилита, которая обрабатывает данные из stdin.
Промт:

import (
    "bufio"
    "os"
    "strings"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        upper := strings.ToUpper(line)
        fmt.Println(upper)
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
        os.Exit(1)
    }
}

Пояснение: Используйте для пайпов: echo "test" | go run main.go выведет "TEST".

15. Прогресс-бар для длительных операций

Задача: Показать прогресс выполнения в CLI.
Промт:

import "fmt"

func showProgress(current, total int) {
    percent := float64(current) / float64(total) * 100
    barLength := 50
    filled := int(float64(barLength) * float64(current) / float64(total))
    bar := strings.Repeat("=", filled) + strings.Repeat(" ", barLength-filled)
    fmt.Printf("\r[%s] %.1f%%", bar, percent)
}

// Использование:
for i := 0; i <= 100; i++ {
    showProgress(i, 100)
    time.Sleep(50 * time.Millisecond)
}
fmt.Println()

Пояснение: \r возвращает курсор в начало строки. Для библиотек используйте github.com/schollz/progressbar/v3.

16. Работа с файловой системой (рекурсивный поиск)

Задача: Найти все файлы с расширением .go в директории.
Промт:

import (
    "os"
    "path/filepath"
)

func findGoFiles(root string) ([]string, error) {
    var files []string
    err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if !info.IsDir() && filepath.Ext(path) == ".go" {
            files = append(files, path)
        }
        return nil
    })
    return files, err
}

Пояснение: filepath.Walk рекурсивно обходит дерево. Для больших директорий используйте filepath.WalkDir (быстрее).

17. Colorized вывод в терминал

Задача: Выводить сообщения с цветами (красный для ошибок, зеленый для успеха).
Промт:

const (
    colorReset  = "\033[0m"
    colorRed    = "\033[31m"
    colorGreen  = "\033[32m"
    colorYellow = "\033[33m"
)

func printSuccess(msg string) {
    fmt.Println(string(colorGreen) + "✓ " + msg + string(colorReset))
}

func printError(msg string) {
    fmt.Println(string(colorRed) + "✗ " + msg + string(colorReset))
}

Пояснение: ANSI-коды работают в большинстве терминалов. Для переносимости используйте библиотеку github.com/fatih/color.

18. Тестирование CLI с golden files

Задача: Сравнить вывод CLI с эталонным файлом.
Промт:

import (
    "os/exec"
    "path/filepath"
    "testing"
)

func TestCLI(t *testing.T) {
    cmd := exec.Command("go", "run", "main.go", "--name=Test")
    output, err := cmd.CombinedOutput()
    if err != nil {
        t.Fatal(err)
    }

    golden := filepath.Join("testdata", "expected_output.txt")
    expected, err := os.ReadFile(golden)
    if err != nil {
        t.Fatal(err)
    }

    if string(output) != string(expected) {
        t.Errorf("expected %q, got %q", expected, output)
    }
}

Пояснение: Golden files хранятся в testdata/. При изменении вывода обновите файл вручную или используйте флаг -update.

Заключение

Эти 15 промтов покрывают 80% типовых задач при разработке на Go: от создания микросервиса до CLI-утилиты. Копируйте, адаптируйте под свои проекты и не забывайте про тестирование. Go — язык, где простота и производительность идут рука об руку, а правильные шаблоны экономят часы работы.

Если вы хотите углубиться в Go — рекомендую изучить официальные туториалы на go.dev/doc и книгу "The Go Programming Language" (Донован, Керниган). А для практики — попробуйте переписать любой из промтов в свой проект уже сегодня.

← Все статьи

Комментарии

Читайте также

UI/UX-дизайн в Figma: навыки, зарплата и влияние на карьеру в 2026 году

18 июля 2026

Sensor Fusion + AI Inference на ASI Biont: Как подключить Edge AI-устройство и управлять им через чат

18 июля 2026

Освойте банковское право с помощью ИИ: ваш быстрый путь к экспертизе в Федеральном законе 395-1

18 июля 2026

Лян Вэньфэн: от квантового фонда до DeepSeek — как создатель ИИ-моделей стал миллиардером и что это значит для рынка

18 июля 2026

Медсестры Kaiser: как ИИ и тотальный мониторинг ухудшают качество работы и ухода за пациентами

18 июля 2026

Тренажер лексики для высоких уровней английского языка: 30 тем для C1-C2

18 июля 2026

Structured outputs, которые не работают: пять ситуаций, где схема есть, а гарантий нет

18 июля 2026

Трудовое право Российской Федерации с ИИ: сократите время обучения на 65%

18 июля 2026

Автоматизация развертываний и откатов: как ИИ-агент ASI Biont интегрируется с Netlify для DevOps без кода

18 июля 2026