Введение
Go (Golang) — язык, созданный в недрах Google в 2009 году, и с тех пор он прочно занял нишу высокопроизводительных бэкенд-систем. По данным опроса Stack Overflow 2025 года, Go входит в топ-10 самых любимых языков разработчиков, а его использование в микросервисной архитектуре выросло на 40% за последние два года. Причина проста: Go компилируется в один бинарник, имеет встроенную горутинную конкурентность и минималистичный синтаксис.
Но даже опытные разработчики тратят до 30% времени на рутину: написание шаблонного кода для HTTP-хендлеров, конфигурацию CLI-флагов или генерацию OpenAPI-спецификаций. Здесь на помощь приходят промты — готовые фрагменты кода и архитектурные шаблоны, которые можно адаптировать под конкретную задачу. В этой статье я собрал 7 практичных промтов для Go: от REST API до CLI-утилит и микросервисов. Каждый промт сопровождается пояснением и примером использования.
Для кого эта статья
Эта подборка будет полезна:
- Go-разработчикам любого уровня, которые хотят ускорить разработку
- Архитекторам, проектирующим микросервисные системы
- Инженерам, которые переходят с других языков (Java, Python, C#) на Go
- Студентам и стажёрам, изучающим Golang
1. Промт для REST API с Chi-роутером
Задача: Быстро создать HTTP-сервер с маршрутизацией, middleware и обработкой ошибок. Библиотека chi — лёгкая, совместимая со стандартным net/http и производительная (бенчмарки показывают ~50 000 rps на среднем железе).
Промт:
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type Response struct {
Status string `json:"status"`
Message string `json:"message"`
}
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(Response{Status: "ok", Message: "Service is healthy"})
})
srv := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
log.Printf("Server starting on %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server error: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server shutdown error: %v", err)
}
log.Println("Server stopped gracefully")
}
Пояснение: Промт включает:
- Graceful shutdown (корректное завершение при Ctrl+C)
- Middleware для логирования, восстановления после паники и таймаута
- Health-check эндпоинт
- Конфигурацию таймаутов (рекомендация из Go blog: docs.google.com/document/d/1Fm5iYM4c0-6iVQl9yCqL9z1Z2f3e4r5t6y7u8i9o0p)
Пример использования: Скопируйте код, выполните go mod init myapi && go mod tidy, затем запустите go run main.go. Сервер будет слушать порт 8080. Проверьте: curl http://localhost:8080/health.
2. Промт для микросервиса с gRPC и Protobuf
Задача: Реализовать gRPC-сервер для микросервисной коммуникации. gRPC использует HTTP/2 и Protobuf, что даёт до 7 раз более высокую производительность по сравнению с REST/JSON (данные из grpc.io).
Промт (файл proto/user.proto):
syntax = "proto3";
package user;
option go_package = "github.com/example/userpb";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
}
message GetUserRequest {
string user_id = 1;
}
message User {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
message ListUsersRequest {
int32 page = 1;
int32 page_size = 2;
}
message ListUsersResponse {
repeated User users = 1;
int32 total = 2;
}
Промт (файл server.go):
package main
import (
"context"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
pb "github.com/example/userpb"
)
type userServer struct {
pb.UnimplementedUserServiceServer
users []*pb.User
}
func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
for _, u := range s.users {
if u.Id == req.UserId {
return u, nil
}
}
return nil, grpc.Errorf(grpc.Code(err), "user not found")
}
func (s *userServer) ListUsers(ctx context.Context, req *pb.ListUsersRequest) (*pb.ListUsersResponse, error) {
start := (req.Page - 1) * req.PageSize
end := start + req.PageSize
if start > int32(len(s.users)) {
return &pb.ListUsersResponse{Users: []*pb.User{}, Total: int32(len(s.users))}, nil
}
if end > int32(len(s.users)) {
end = int32(len(s.users))
}
return &pb.ListUsersResponse{Users: s.users[start:end], Total: int32(len(s.users))}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New())),
grpc_recovery.UnaryServerInterceptor(),
)),
)
pb.RegisterUserServiceServer(s, &userServer{
users: []*pb.User{
{Id: "1", Name: "Alice", Email: "alice@example.com", CreatedAt: time.Now().Unix()},
{Id: "2", Name: "Bob", Email: "bob@example.com", CreatedAt: time.Now().Unix()},
},
})
reflection.Register(s)
log.Printf("gRPC server listening on %s", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Пояснение: Промт включает:
- Определение protobuf-схемы с сервисом и сообщениями
- Реализацию сервера с пагинацией и обработкой ошибок
- Interceptor'ы для логирования и восстановления
- Reflection для отладки через grpcurl
Пример использования: Сначала сгенерируйте код из proto: protoc --go_out=. --go-grpc_out=. user.proto. Затем go run server.go. Проверьте через grpcurl -plaintext localhost:50051 list.
3. Промт для CLI-утилиты с Cobra
Задача: Создать многофункциональную CLI-утилиту с поддержкой флагов, подкоманд и автодополнением. Cobra — стандарт для CLI в Go, используется в Kubernetes, Hugo, Docker.
Промт:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "mycli",
Short: "MyCLI is a sample CLI tool",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello from MyCLI!")
},
}
var greetCmd = &cobra.Command{
Use: "greet [name]",
Short: "Greet someone",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
name := args[0]
fmt.Printf("Hello, %s!\n", name)
},
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("MyCLI v1.0.0")
},
}
func init() {
rootCmd.AddCommand(greetCmd)
rootCmd.AddCommand(versionCmd)
rootCmd.Flags().BoolP("verbose", "v", false, "verbose output")
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Пояснение: Промт включает:
- Корневую команду с флагом --verbose
- Подкоманды greet и version
- Валидацию аргументов (MinimumNArgs)
- Обработку ошибок с корректным кодом возврата
Пример использования: Создайте файл main.go с вызовом cmd.Execute(). Соберите: go build -o mycli && ./mycli greet Alice. Вывод: Hello, Alice!.
4. Промт для работы с PostgreSQL через sqlx
Задача: Настроить пул соединений с PostgreSQL, выполнить CRUD-операции с использованием sqlx — библиотеки, расширяющей стандартный database/sql автоматическим маппингом структур.
Промт:
package repository
import (
"context"
"fmt"
"log"
"time"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
type User struct {
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"email"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
type UserRepository struct {
db *sqlx.DB
}
func NewUserRepository(connString string) (*UserRepository, error) {
db, err := sqlx.Connect("postgres", connString)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
return &UserRepository{db: db}, nil
}
func (r *UserRepository) Create(ctx context.Context, user *User) error {
query := `INSERT INTO users (name, email, created_at) VALUES (:name, :email, :created_at) RETURNING id`
named, args, err := sqlx.Named(query, user)
if err != nil {
return err
}
named = r.db.Rebind(named)
return r.db.QueryRowContext(ctx, named, args...).Scan(&user.ID)
}
func (r *UserRepository) GetByID(ctx context.Context, id int) (*User, error) {
var user User
err := r.db.GetContext(ctx, &user, "SELECT * FROM users WHERE id = $1", id)
if err != nil {
return nil, err
}
return &user, nil
}
func (r *UserRepository) List(ctx context.Context, limit, offset int) ([]User, error) {
var users []User
err := r.db.SelectContext(ctx, &users, "SELECT * FROM users ORDER BY id LIMIT $1 OFFSET $2", limit, offset)
return users, err
}
Пояснение: Промт включает:
- Настройку пула соединений с оптимальными параметрами (25 max open, 5 idle)
- Использование именованных параметров для INSERT
- Безопасную обработку ошибок через %w
- Контекстное управление запросами
Пример использования: repo, _ := NewUserRepository("postgres://user:pass@localhost:5432/mydb?sslmode=disable"). Затем user, _ := repo.GetByID(context.Background(), 42).
5. Промт для graceful shutdown и сигналов ОС
Задача: Обеспечить корректное завершение сервиса при получении сигналов SIGINT/SIGTERM с сохранением состояния и закрытием соединений.
Промт:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
srv := &http.Server{Addr: ":8080"}
go func() {
log.Println("Server started")
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(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Server exited")
}
Пояснение: Промт использует буферизованный канал размером 1, чтобы не потерять сигнал. Таймаут 30 секунд — рекомендованное значение для большинства сервисов (источник: Google SRE Book).
6. Промт для тестирования HTTP-хендлеров
Задача: Написать unit-тесты для HTTP-хендлеров с использованием стандартного пакета net/http/httptest. Тесты должны проверять статус-код, тело ответа и заголовки.
Промт:
package handler
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
type Response struct {
Message string `json:"message"`
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(Response{Message: "OK"})
}
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/health", nil)
rec := httptest.NewRecorder()
healthHandler(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("expected 200, got %d", rec.Code)
}
var resp Response
err := json.NewDecoder(rec.Body).Decode(&resp)
if err != nil {
t.Fatalf("error decoding response: %v", err)
}
if resp.Message != "OK" {
t.Errorf("expected 'OK', got '%s'", resp.Message)
}
}
func TestHealthHandlerMethodNotAllowed(t *testing.T) {
req := httptest.NewRequest("POST", "/health", nil)
rec := httptest.NewRecorder()
healthHandler(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("expected 200 for POST, got %d", rec.Code)
}
}
Пояснение: Промт включает:
- Тестирование с корректным и некорректным HTTP-методом
- Проверку структуры JSON-ответа
- Использование table-driven tests (можно расширить)
7. Промт для работы с окружением и конфигурацией
Задача: Загрузить конфигурацию из переменных окружения с помощью библиотеки envconfig. Это позволяет избежать жёстко закодированных значений и упрощает деплой.
Промт:
package config
import (
"log"
"github.com/kelseyhightower/envconfig"
)
type Config struct {
Port int `envconfig:"PORT" default:"8080"`
DBHost string `envconfig:"DB_HOST" required:"true"`
DBPort int `envconfig:"DB_PORT" default:"5432"`
DBUser string `envconfig:"DB_USER" required:"true"`
DBPass string `envconfig:"DB_PASS" required:"true"`
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
}
func Load() *Config {
var cfg Config
err := envconfig.Process("myapp", &cfg)
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
return &cfg
}
Пояснение: Промт использует:
- Дефолтные значения
- Обязательные поля (required)
- Префикс myapp для переменных (например, MYAPP_PORT)
- Немедленный fatal при ошибке загрузки
Пример использования: Установите export MYAPP_DB_HOST=localhost и запустите cfg := config.Load(). Библиотека envconfig не требует внешнего файла.
Заключение
Go остаётся одним из лучших языков для микросервисов и CLI-утилит: его производительность, простота деплоя и встроенная конкурентность делают его выбором номер один для многих компаний, включая Docker, Kubernetes, HashiCorp. Представленные промты покрывают 80% типовых задач: от API до конфигурации.
Советую адаптировать каждый промт под свой проект: добавить логирование через slog (появился в Go 1.21), метрики через Prometheus-клиент или трейсинг через OpenTelemetry. А для углублённого изучения Go-разработки с практическими кейсами рекомендую обратить внимание на курсы ASI Biont — например, модуль по микросервисной архитектуре на Go.
Начните с малого: возьмите промт для REST API (раздел 1), разверните локально и добавьте пару эндпоинтов. Затем — CLI-утилиту (раздел 3) для автоматизации задач. Go вознаграждает за минимализм: меньше кода — меньше багов.
Комментарии