بنقرة واحدة
golang-patterns
Idiomatic Go patterns, modern best practices, and production-grade code standards
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Idiomatic Go patterns, modern best practices, and production-grade code standards
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Identify and avoid common anti-patterns in code, architecture, and event-driven systems
Software architecture patterns including microservices, DDD, cloud-native, API design, security, and performance
Creative, interactive, and delightful application patterns including generative design, gamification, and sensory feedback
Modern Flutter and Dart patterns, production-grade standards, and iOS-optimized practices
Modern frontend patterns, TypeScript best practices, and production-grade standards for Vue.js and Angular
Game design principles, mechanics, architecture patterns, and prototyping workflows
| name | golang-patterns |
| description | Idiomatic Go patterns, modern best practices, and production-grade code standards |
Apply idiomatic Go patterns, modern best practices, and production-grade code standards. Use this skill when writing, reviewing, or refactoring Go code. Follow Go's philosophy: simplicity, clarity, composition.
Reference: Effective Go, Go Code Review Comments, Uber Go Style Guide.
Abstraction over data access, enabling testability and swappable backends.
type OrderRepository interface {
Create(ctx context.Context, order *Order) error
GetByID(ctx context.Context, id uuid.UUID) (*Order, error)
List(ctx context.Context, filter OrderFilter) ([]Order, error)
Update(ctx context.Context, order *Order) error
Delete(ctx context.Context, id uuid.UUID) error
}
// PostgreSQL implementation
type postgresOrderRepo struct {
db *sql.DB
}
func NewPostgresOrderRepo(db *sql.DB) OrderRepository {
return &postgresOrderRepo{db: db}
}
func (r *postgresOrderRepo) GetByID(ctx context.Context, id uuid.UUID) (*Order, error) {
var o Order
err := r.db.QueryRowContext(ctx,
`SELECT id, customer_id, total, status, created_at
FROM orders WHERE id = $1`, id,
).Scan(&o.ID, &o.CustomerID, &o.Total, &o.Status, &o.CreatedAt)
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("order %s: %w", id, ErrNotFound)
}
if err != nil {
return nil, fmt.Errorf("querying order %s: %w", id, err)
}
return &o, nil
}
Business logic encapsulated in services with injected dependencies.
type OrderService struct {
repo OrderRepository
events EventPublisher
logger *slog.Logger
}
func NewOrderService(repo OrderRepository, events EventPublisher, logger *slog.Logger) *OrderService {
return &OrderService{repo: repo, events: events, logger: logger}
}
func (s *OrderService) PlaceOrder(ctx context.Context, cmd PlaceOrderCommand) (*Order, error) {
order, err := NewOrder(cmd.CustomerID, cmd.Items)
if err != nil {
return nil, fmt.Errorf("creating order: %w", err)
}
if err := s.repo.Create(ctx, order); err != nil {
return nil, fmt.Errorf("saving order: %w", err)
}
if err := s.events.Publish(ctx, "orders.placed", OrderPlacedEvent{
OrderID: order.ID,
CustomerID: order.CustomerID,
Total: order.Total,
}); err != nil {
s.logger.WarnContext(ctx, "event publish failed", slog.String("error", err.Error()))
}
return order, nil
}
// Sentinel errors for domain-level error classification
var (
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrUnauthorized = errors.New("unauthorized")
ErrForbidden = errors.New("forbidden")
ErrValidation = errors.New("validation error")
ErrConflict = errors.New("conflict")
)
// Structured validation error
type FieldError struct {
Field string `json:"field"`
Message string `json:"message"`
}
type ValidationErrors struct {
Errors []FieldError `json:"errors"`
}
func (e *ValidationErrors) Error() string {
return fmt.Sprintf("validation failed: %d errors", len(e.Errors))
}
func (e *ValidationErrors) Unwrap() error {
return ErrValidation
}
// Map domain errors to HTTP in handler
func mapError(err error) (int, string) {
switch {
case errors.Is(err, ErrNotFound):
return http.StatusNotFound, "Resource not found"
case errors.Is(err, ErrUnauthorized):
return http.StatusUnauthorized, "Unauthorized"
case errors.Is(err, ErrForbidden):
return http.StatusForbidden, "Forbidden"
case errors.Is(err, ErrValidation):
return http.StatusBadRequest, err.Error()
case errors.Is(err, ErrConflict):
return http.StatusConflict, "Resource conflict"
default:
return http.StatusInternalServerError, "Internal server error"
}
}
type ClientOption func(*Client)
func WithTimeout(d time.Duration) ClientOption {
return func(c *Client) { c.timeout = d }
}
func WithRetries(n int) ClientOption {
return func(c *Client) { c.maxRetries = n }
}
func WithBaseURL(url string) ClientOption {
return func(c *Client) { c.baseURL = url }
}
func NewClient(opts ...ClientOption) *Client {
c := &Client{
timeout: 30 * time.Second,
maxRetries: 3,
baseURL: "https://api.example.com",
httpClient: http.DefaultClient,
}
for _, opt := range opts {
opt(c)
}
return c
}
import "golang.org/x/sync/errgroup"
func ProcessBatch(ctx context.Context, items []Item, concurrency int) error {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(concurrency)
for _, item := range items {
item := item // capture loop variable (Go < 1.22)
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return processItem(ctx, item)
}
})
}
return g.Wait()
}
type Middleware func(http.Handler) http.Handler
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
func RequestIDMiddleware() Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.NewString()
}
ctx := context.WithValue(r.Context(), requestIDKey, requestID)
w.Header().Set("X-Request-ID", requestID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func TimeoutMiddleware(d time.Duration) Middleware {
return func(next http.Handler) http.Handler {
return http.TimeoutHandler(next, d, "request timeout")
}
}
func TestParseAmount(t *testing.T) {
tests := []struct {
name string
input string
want float64
wantErr bool
}{
{name: "valid integer", input: "100", want: 100.0},
{name: "valid decimal", input: "99.99", want: 99.99},
{name: "negative", input: "-50", want: -50.0},
{name: "empty string", input: "", wantErr: true},
{name: "not a number", input: "abc", wantErr: true},
{name: "overflow", input: "1e309", wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseAmount(tt.input)
if tt.wantErr {
if err == nil {
t.Fatal("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tt.want {
t.Errorf("ParseAmount(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
// Fake repository for unit tests — no external dependencies
type FakeOrderRepo struct {
mu sync.Mutex
orders map[uuid.UUID]*Order
}
func NewFakeOrderRepo() *FakeOrderRepo {
return &FakeOrderRepo{orders: make(map[uuid.UUID]*Order)}
}
func (r *FakeOrderRepo) Create(_ context.Context, order *Order) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.orders[order.ID]; exists {
return fmt.Errorf("order %s: %w", order.ID, ErrAlreadyExists)
}
r.orders[order.ID] = order
return nil
}
func (r *FakeOrderRepo) GetByID(_ context.Context, id uuid.UUID) (*Order, error) {
r.mu.Lock()
defer r.mu.Unlock()
order, ok := r.orders[id]
if !ok {
return nil, fmt.Errorf("order %s: %w", id, ErrNotFound)
}
return order, nil
}
type Config struct {
ServerAddr string `env:"SERVER_ADDR" default:":8080"`
DatabaseURL string `env:"DATABASE_URL" required:"true"`
RedisURL string `env:"REDIS_URL" default:"redis://localhost:6379"`
LogLevel string `env:"LOG_LEVEL" default:"info"`
ReadTimeout time.Duration `env:"READ_TIMEOUT" default:"15s"`
WriteTimeout time.Duration `env:"WRITE_TIMEOUT" default:"15s"`
}
func LoadConfig() (*Config, error) {
cfg := &Config{}
if err := envconfig.Process("", cfg); err != nil {
return nil, fmt.Errorf("loading config: %w", err)
}
return cfg, nil
}
func Run(ctx context.Context, cfg *Config, logger *slog.Logger) error {
srv := &http.Server{
Addr: cfg.ServerAddr,
Handler: setupRouter(cfg, logger),
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
}
errCh := make(chan error, 1)
go func() {
logger.Info("server starting", slog.String("addr", cfg.ServerAddr))
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
errCh <- err
}
}()
select {
case err := <-errCh:
return fmt.Errorf("server error: %w", err)
case <-ctx.Done():
logger.Info("shutting down...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return srv.Shutdown(shutdownCtx)
}
}
func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
cfg, err := LoadConfig()
if err != nil {
logger.Error("config error", slog.String("error", err.Error()))
os.Exit(1)
}
if err := Run(ctx, cfg, logger); err != nil {
logger.Error("server failed", slog.String("error", err.Error()))
os.Exit(1)
}
}
cmd/
├── api/main.go # API entrypoint
└── worker/main.go # Worker entrypoint
internal/
├── domain/ # Pure domain logic, no external deps
│ ├── order.go
│ ├── order_test.go
│ └── errors.go
├── service/ # Application use cases
│ ├── order_service.go
│ └── order_service_test.go
├── repository/ # Interfaces + implementations
│ ├── interfaces.go
│ └── postgres/
├── handler/ # HTTP/gRPC transport
│ └── order_handler.go
├── middleware/ # Cross-cutting concerns
└── config/ # Configuration
pkg/ # Public reusable code (optional)
├── httputil/
└── testutil/
✅ Accept interfaces, return structs
✅ Handle every error — wrap with fmt.Errorf("context: %w", err)
✅ Use context.Context as the first parameter
✅ Prefer composition over embedding for non-trivial types
✅ Use slog for structured logging
✅ Table-driven tests with t.Run subtests
✅ internal/ for private application code
✅ Functional options for configurable constructors
✅ errgroup for concurrent operations with error handling
✅ go vet, golangci-lint, -race in CI
❌ init() with side effects
❌ context.Context stored in structs
❌ Ignoring errors with _
❌ interface{} / any when generics or specific types work
❌ Global mutable state
❌ panic for expected errors
❌ Premature interfaces — wait until you need 2+ implementations
❌ Giant main() functions — extract into Run(ctx, cfg) functions
❌ Naked returns in complex functions