AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL unreviewed MIT Self-run

Golang Development

skill-kinhluan-skills-golang-development · by kinhluan

Modern Go (Golang) development best practices (2024-2025). Use for project setup, modules, generics, concurrency, testing, HTTP/gRPC services, databases, CLI, linting, Docker, and production deployment.

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-kinhluan-skills-golang-development

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Destructive filesystem operation.

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Golang Development? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Go Development

Modern Go development covering project structure, modules, generics, concurrency patterns, testing, web frameworks, gRPC, databases, CLI tools, linting, and production deployment. Targets Go 1.22+ with focus on 1.23+ and 1.24+ features.

> "Clear is better than clever." — The Go Proverbs


1. Project Setup & Modules

Go Modules (go.mod)

Single source of truth for dependencies and module path.

module github.com/example/myapp

go 1.23

require (
    github.com/gin-gonic/gin v1.10.0
    github.com/jackc/pgx/v5 v5.7.0
    google.golang.org/grpc v1.68.0
)

require (
    github.com/stretchr/testify v1.9.0 // indirect
)

Key commands:

go mod init github.com/example/myapp    # initialize module
go get github.com/gin-gonic/gin         # add dependency
go get -u ./...                         # update all dependencies
go mod tidy                             # remove unused, add missing
go mod download                         # download to module cache
go mod vendor                           # create vendor/ directory
go work init ./api ./worker             # Go workspace (multi-module)

Standard Project Layout

myapp/
├── api/                    # API definitions (protobuf, OpenAPI)
│   └── proto/
├── cmd/                    # Application entry points
│   ├── api/
│   │   └── main.go
│   └── worker/
│       └── main.go
├── internal/               # Private application code
│   ├── domain/             # Business logic (entities, value objects)
│   ├── service/            # Use cases / application services
│   ├── repository/         # Data access layer
│   ├── handler/            # HTTP/gRPC handlers
│   └── config/             # Configuration
├── pkg/                    # Public library code (reusable)
│   └── utils/
├── migrations/             # Database migrations
├── scripts/                # Build and deployment scripts
├── configs/                # Configuration files
├── test/                   # Integration and e2e tests
├── go.mod
├── go.sum
├── Makefile
├── Dockerfile
└── README.md

Rules:

  • cmd//main.go — one main per application
  • internal/ — cannot be imported by external modules
  • pkg/ — reusable packages, stable APIs

2. Go 1.22+ / 1.23+ / 1.24+ Features

Go 1.22 — Enhanced For Loops

// Loop variables are now per-iteration (no closure bugs)
funcs := []func(){}
for i := 0; i  b {  // ERROR: comparable doesn't support >
        return a
    }
    return b
}

// Correct: use constraints.Ordered
import "golang.org/x/exp/constraints"

func Max[T constraints.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

// Generic struct
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(v T) {
    s.items = append(s.items, v)
}

func (s *Stack[T]) Pop() (T, bool) {
    var zero T
    if len(s.items) == 0 {
        return zero, false
    }
    v := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return v, true
}

// Type sets
 type Number interface {
     ~int | ~int64 | ~float64  // ~ allows underlying types
 }

func Sum[T Number](vals []T) T {
    var sum T
    for _, v := range vals {
        sum += v
    }
    return sum
}

Interfaces & Embedding

// Interface composition
 type Reader interface {
     Read(p []byte) (n int, err error)
 }

 type Writer interface {
     Write(p []byte) (n int, err error)
 }

 type ReadWriter interface {
     Reader
     Writer
 }

// Any (alias for interface{})
 func PrintAny(v any) {
     fmt.Printf("%T: %v\n", v, v)
 }

// Type assertions and switches
 func Describe(v any) string {
     switch val := v.(type) {
     case string:
         return "string: " + val
     case int:
         return fmt.Sprintf("int: %d", val)
     case fmt.Stringer:
         return val.String()
     default:
         return "unknown"
     }
 }

4. Error Handling

Idiomatic Error Handling

// Sentinel errors
 var ErrNotFound = errors.New("resource not found")
 var ErrInvalidInput = errors.New("invalid input")

// Error wrapping
 func LoadUser(id string) (*User, error) {
     user, err := db.QueryUser(id)
     if err != nil {
         return nil, fmt.Errorf("loading user %s: %w", id, err)
     }
     if user == nil {
         return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
     }
     return user, nil
 }

// Error checking
 user, err := LoadUser("123")
 if err != nil {
     if errors.Is(err, ErrNotFound) {
         http.Error(w, "Not found", http.StatusNotFound)
         return
     }
     log.Printf("unexpected error: %v", err)
     http.Error(w, "Internal error", http.StatusInternalServerError)
     return
 }

// Error type assertion
 var notFound *NotFoundError
 if errors.As(err, &notFound) {
     fmt.Printf("Resource type: %s, ID: %s\n", notFound.Resource, notFound.ID)
 }

Custom Error Types

 type NotFoundError struct {
     Resource string
     ID       string
 }

 func (e *NotFoundError) Error() string {
     return fmt.Sprintf("%s %s not found", e.Resource, e.ID)
 }

// Validation errors with multiple fields
 type ValidationError struct {
     Field   string
     Message string
 }

 type ValidationErrors []ValidationError

 func (v ValidationErrors) Error() string {
     var msgs []string
     for _, e := range v {
         msgs = append(msgs, fmt.Sprintf("%s: %s", e.Field, e.Message))
     }
     return "validation failed: " + strings.Join(msgs, "; ")
 }

5. Concurrency

Goroutines & Channels

// Basic goroutine
 go func() {
     fmt.Println("running concurrently")
 }()

// Buffered channel
 ch := make(chan int, 10)
 go func() {
     for i := range 5 {
         ch <- i
     }
     close(ch)
 }()

 for v := range ch {
     fmt.Println(v)
 }

// Select for multiplexing
 select {
 case v := <-ch1:
     fmt.Println("from ch1:", v)
 case v := <-ch2:
     fmt.Println("from ch2:", v)
 case <-time.After(5 * time.Second):
     fmt.Println("timeout")
 default:
     fmt.Println("no channel ready")
 }

Context — Request Scoping & Cancellation

// Create context with timeout
 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 defer cancel()

// Pass through call chain
 func ProcessRequest(ctx context.Context, req Request) (*Response, error) {
     // Check cancellation
     select {
     case <-ctx.Done():
         return nil, ctx.Err()  // context.DeadlineExceeded or context.Canceled
     default:
     }

     // Pass to downstream
     user, err := userService.Get(ctx, req.UserID)
     if err != nil {
         return nil, err
     }
     // ...
 }

// Context with values (request-scoped metadata)
 type contextKey string
 const requestIDKey contextKey = "request-id"

 func WithRequestID(ctx context.Context, id string) context.Context {
     return context.WithValue(ctx, requestIDKey, id)
 }

 func RequestIDFrom(ctx context.Context) string {
     if id, ok := ctx.Value(requestIDKey).(string); ok {
         return id
     }
     return ""
 }

sync Package Patterns

// WaitGroup for goroutine synchronization
 var wg sync.WaitGroup
 for i := range 3 {
     wg.Add(1)
     go func(id int) {
         defer wg.Done()
         fmt.Printf("worker %d done\n", id)
     }(i)
 }
 wg.Wait()

// Mutex for shared state
 type Counter struct {
     mu    sync.Mutex
     count int
 }

 func (c *Counter) Inc() {
     c.mu.Lock()
     defer c.mu.Unlock()
     c.count++
 }

// RWMutex for read-heavy workloads
 type Cache struct {
     mu    sync.RWMutex
     data  map[string]string
 }

 func (c *Cache) Get(key string) (string, bool) {
     c.mu.RLock()
     defer c.mu.RUnlock()
     v, ok := c.data[key]
     return v, ok
 }

 func (c *Cache) Set(key, value string) {
     c.mu.Lock()
     defer c.mu.Unlock()
     c.data[key] = value
 }

// sync.Once for one-time initialization
 var (
     db     *sql.DB
     dbOnce sync.Once
 )

 func GetDB() *sql.DB {
     dbOnce.Do(func() {
         var err error
         db, err = sql.Open("postgres", dsn)
         if err != nil {
             log.Fatal(err)
         }
     })
     return db
 }

// sync.Map for concurrent map (rarely needed, prefer RWMutex)
 var m sync.Map
 m.Store("key", "value")
 if v, ok := m.Load("key"); ok {
     fmt.Println(v)
 }

errgroup — Concurrent Error Handling

 import "golang.org/x/sync/errgroup"

 func ProcessBatch(ctx context.Context, items []Item) error {
     g, ctx := errgroup.WithContext(ctx)
     g.SetLimit(10)  // max concurrent

     for _, item := range items {
         item := item  // capture loop variable (pre-1.22)
         g.Go(func() error {
             return processItem(ctx, item)
         })
     }

     if err := g.Wait(); err != nil {
         return fmt.Errorf("batch processing: %w", err)
     }
     return nil
 }

Worker Pools

 func WorkerPool(ctx context.Context, jobs <-chan Job, numWorkers int) <-chan Result {
     results := make(chan Result, numWorkers)

     var wg sync.WaitGroup
     for range numWorkers {
         wg.Add(1)
         go func() {
             defer wg.Done()
             for job := range jobs {
                 select {
                 case <-ctx.Done():
                     return
                 default:
                     results <- process(job)
                 }
             }
         }()
     }

     go func() {
         wg.Wait()
         close(results)
     }()

     return results
 }

6. Testing

Table-Driven Tests

 func TestCalculate(t *testing.T) {
     tests := []struct {
         name     string
         a, b     int
         op       string
         expected int
         wantErr  bool
     }{
         {"add", 2, 3, "+", 5, false},
         {"subtract", 5, 3, "-", 2, false},
         {"divide by zero", 5, 0, "/", 0, true},
     }

     for _, tt := range tests {
         t.Run(tt.name, func(t *testing.T) {
             got, err := Calculate(tt.a, tt.b, tt.op)
             if tt.wantErr {
                 if err == nil {
                     t.Errorf("expected error, got nil")
                 }
                 return
             }
             if err != nil {
                 t.Errorf("unexpected error: %v", err)
                 return
             }
             if got != tt.expected {
                 t.Errorf("Calculate(%d, %d, %q) = %d, want %d",
                     tt.a, tt.b, tt.op, got, tt.expected)
             }
         })
     }
 }

testify — Assertions & Mocks

 import "github.com/stretchr/testify/assert"
 import "github.com/stretchr/testify/require"
 import "github.com/stretchr/testify/mock"

 func TestSomething(t *testing.T) {
     // Assert continues on failure
     assert.Equal(t, 42, result)
     assert.NoError(t, err)

     // Require stops test on failure
     require.NotNil(t, obj)
     require.NoError(t, err)

     // Now safe to use obj
     assert.Equal(t, "expected", obj.Name)
 }

// Mock with testify/mock
 type MockRepository struct {
     mock.Mock
 }

 func (m *MockRepository) GetUser(id string) (*User, error) {
     args := m.Called(id)
     if args.Get(0) == nil {
         return nil, args.Error(1)
     }
     return args.Get(0).(*User), args.Error(1)
 }

 func TestService(t *testing.T) {
     repo := new(MockRepository)
     repo.On("GetUser", "123").Return(&User{ID: "123", Name: "Alice"}, nil)

     svc := NewService(repo)
     user, err := svc.GetUser("123")

     require.NoError(t, err)
     assert.Equal(t, "Alice", user.Name)
     repo.AssertExpectations(t)
 }

Fuzzing (Go 1.18+)

 func FuzzParse(f *testing.F) {
     // Seed corpus
     f.Add("hello")
     f.Add("12345")

     f.Fuzz(func(t *testing.T, input string) {
         result, err := Parse(input)
         if err != nil {
             t.Skip()  // invalid input is ok
         }
         // Invariant: result must be valid
         if result.Len() < 0 {
             t.Errorf("negative length: %d", result.Len())
         }
     })
 }
go test -fuzz=FuzzParse -fuzztime=30s ./...

Benchmarks

 func BenchmarkProcess(b *testing.B) {
     data := generateLargeDataset()
     b.ResetTimer()
     for range b.N {
         Process(data)
     }
 }

// Memory allocation benchmark
 func BenchmarkAlloc(b *testing.B) {
     b.ReportAllocs()
     for range b.N {
         _ = make([]byte, 1024)
     }
 }
go test -bench=. -benchmem ./...

Integration Tests

//go:build integration

 func TestDatabase(t *testing.T) {
     if testing.Short() {
         t.Skip("skipping integration test")
     }

     db := setupTestDB(t)
     defer teardownTestDB(t, db)

     // Run tests against real database
 }
go test -tags=integration ./...
go test -short ./...          # skip integration

7. HTTP Servers

Standard Library (net/http)

 package main

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

 type Handler struct {
     service Service
 }

 func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
     id := r.PathValue("id")  // Go 1.22+ path values

     user, err := h.service.GetUser(r.Context(), id)
     if err != nil {
         if errors.Is(err, ErrNotFound) {
             http.Error(w, "Not found", http.StatusNotFound)
             return
         }
         log.Printf("error: %v", err)
         http.Error(w, "Internal error", http.StatusInternalServerError)
         return
     }

     w.Header().Set("Content-Type", "application/json")
     json.NewEncoder(w).Encode(user)
 }

 func main() {
     mux := http.NewServeMux()
     h := &Handler{service: NewService()}

     // Go 1.22+ routing
     mux.HandleFunc("GET /users/{id}", h.GetUser)
     mux.HandleFunc("POST /users", h.CreateUser)
     mux.HandleFunc("GET /users", h.ListUsers)

     server := &http.Server{
         Addr:         ":8080",
         Handler:      mux,
         ReadTimeout:  5 * time.Second,
         WriteTimeout: 10 * time.Second,
         IdleTimeout:  120 * time.Second,
     }

     log.Println("Server starting on :8080")
     if err := server.ListenAndServe(); err != nil {
         log.Fatal(err)
     }
 }

Chi Router

 import "github.com/go-chi/chi/v5"
 import "github.com/go-chi/chi/v5/middleware"

 r := chi.NewRouter()
 r.Use(middleware.Logger)
 r.Use(middleware.Recoverer)
 r.Use(middleware.RequestID)
 r.Use(middleware.Timeout(30 * time.Second))

 r.Route("/api/v1", func(r chi.Router) {
     r.Use(authMiddleware)

     r.Get("/users", listUsers)
     r.Post("/users", createUser)
     r.Get("/users/{id}", getUser)
     r.Put("/users/{id}", updateUser)
     r.Delete("/users/{id}", deleteUser)
 })

 http.ListenAndServe(":8080", r)

Gin

 import "github.com/gin-gonic/gin"

 r := gin.Default()

 r.GET("/users/:id", func(c *gin.Context) {
     id := c.Param("id")
     user, err := service.GetUser(c.Request.Context(), id)
     if err != nil {
         if errors.Is(err, ErrNotFound) {
             c.JSON(404, gin.H{"error": "not found"})
             return
         }
         c.JSON(500, gin.H{"error": "internal error"})
         return
     }
     c.JSON(200, user)
 })

 r.POST("/users", func(c *gin.Context) {
     var req CreateUserRequest
     if err := c.ShouldBindJSON(&req); err != nil {
         c.JSON(400, gin.H{"error": err.Error()})
         return
     }
     user, err := service.CreateUser(c.Request.Context(), req)
     if err != nil {
         c.JSON(500, gin.H{"error": err.Error()})
         return
     }
     c.JSON(201, user)
 })

 r.Run(":8080")

Middleware Patterns

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

// Authentication middleware
 func AuthMiddleware(next

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [kinhluan](https://github.com/kinhluan)
- **Source:** [kinhluan/skills](https://github.com/kinhluan/skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.