Skip to content
Development
Skill

/go-architect

Go application architecture with net/http 1.22+ routing, project structure patterns, graceful shutdown, and dependency injection. Use when building Go web servers, designing project layout, or structuring application dependencies.

From plugin
beagle
82139 skills2 commands
Install
$ npx -y skills add existential-birds/beagle --skill go-architect --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/go-architect

Context preview

The summary Claude sees to decide when to auto-load this skill.

Go application architecture with net/http 1.22+ routing, project structure patterns, graceful shutdown, and dependency injection. Use when building Go web servers, designing project layout, or structuring application dependencies.

SKILL.md

go-architect.SKILL.md
name: go-architect
description: Go application architecture with net/http 1.22+ routing, project structure patterns, graceful shutdown, and dependency injection. Use when building Go web servers, designing project layout, or structuring application dependencies.

Lead Go Architect

Quick Reference

| Topic | Reference | |-------|-----------| | Flat vs modular project layout, migration signals | [references/project-structure.md](references/project-structure.md) | | Graceful shutdown with signal handling | [references/graceful-shutdown.md](references/graceful-shutdown.md) | | Dependency injection patterns, testing seams | [references/dependency-injection.md](references/dependency-injection.md) |

Core Principles

1. **Standard library first** -- Use `net/http` and the Go 1.22+ enhanced `ServeMux` for routing. Only reach for a framework (chi, echo, gin) when you have a concrete need the stdlib cannot satisfy (e.g., complex middleware chains, regex routes). 2. **Dependency injection over globals** -- Pass databases, loggers, and services through struct fields and constructors, never package-level `var`. 3. **Explicit over magic** -- No `init()` side effects, no framework auto-wiring. `main.go` is the composition root where everything is assembled visibly. 4. **Small interfaces, big structs** -- Define interfaces at the consumer, keep them narrow (1-3 methods). Concrete types carry the implementation.

Hard gates

Use this sequence when implementing or reviewing work that claims to follow this skill. Do not skip ahead; each step has a **pass condition** you can answer with tooling or a concrete file path.

1. **Toolchain vs APIs** — If the code uses Go 1.22+ `ServeMux` features (method+path patterns like `"GET /x/{id}"`, `r.PathValue`, or `{path...}`): run `go version` and **pass** only if the reported toolchain is **go1.22+**. If the project must stay on an older Go, **pass** only by not using those APIs (use a compatible router or older patterns) and say so in the review or PR. 2. **Composition root** — **Pass** when `main.go` or `cmd/.../main.go` visibly constructs the server and injects shared dependencies (DB, logger, config). **Fail** if shared dependencies are wired in `init()` or package-level `var` instead of explicit construction in `main` (or a `run()` called from `main`). 3. **Production HTTP shutdown** — For a long-lived HTTP service, **pass** only if shutdown uses `http.Server.Shutdown` with a **bounded** context (e.g. `context.WithTimeout`) after waiting on `signal.NotifyContext` (or equivalent). Cite the file path when reporting; see [references/graceful-shutdown.md](references/graceful-shutdown.md) for the full pattern. 4. **No env/globals in handlers** — **Pass** when handlers and domain code take dependencies via structs/arguments. **Fail** if handlers read `os.Getenv` for secrets or use package-level `var` for DB/clients (loading env in `main` or a dedicated config package is fine).

Go 1.22+ Enhanced Routing

Go 1.22 upgraded `http.ServeMux` with method-based routing and path parameters, eliminating the most common reason for third-party routers.

Method-Based Routing and Path Parameters

mux := http.NewServeMux()
mux.HandleFunc("GET /api/users", s.handleListUsers)
mux.HandleFunc("GET /api/users/{id}", s.handleGetUser)
mux.HandleFunc("POST /api/users", s.handleCreateUser)
mux.HandleFunc("DELETE /api/users/{id}", s.handleDeleteUser)

Extracting Path Parameters

func (s *Server) handleGetUser(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    if id == "" {
        http.Error(w, "missing id", http.StatusBadRequest)
        return
    }

    user, err := s.users.GetUser(r.Context(), id)
    if err != nil {
        s.logger.Error("getting user", "err", err, "id", id)
        http.Error(w, "internal error", http.StatusInternalServerError)
        return
    }

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

Wildcard and Exact Match

// Exact match on trailing slash -- serves /api/files/ only
mux.HandleFunc("GET /api/files/", s.handleListFiles)

// Wildcard to end of path -- /api/files/path/to/doc.txt
mux.HandleFunc("GET /api/files/{path...}", s.handleGetFile)

Routing Precedence

The new `ServeMux` uses most-specific-wins precedence:

  • `GET /api/users/{id}` is more specific than `GET /api/users/`
  • `GET /api/users/me` is more specific than `GET /api/users/{id}`
  • Method routes take precedence over method-less routes

Server Struct Pattern

The Server struct is the central dependency container for your application. It holds all shared dependencies and implements `http.Handler`.

type Server struct {
    db     *sql.DB
    logger *slog.Logger
    router *http.ServeMux
}

func NewServer(db *sql.DB, logger *slog.Logger) *Server {
    s := &Server{
        db:     db,
        logger: logger,
        router: http.NewServeMux(),
    }
    s.routes()
    return s
}

func (s *Server) routes() {
    s.router.HandleFunc("GET /api/users/{id}", s.handleGetUser)
    s.router.HandleFunc("POST /api/users", s.handleCreateUser)
    s.router.HandleFunc("GET /healthz", s.handleHealth)
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.router.ServeHTTP(w, r)
}

Middleware Wrapping

Apply middleware at the `http.Server` level or per-route:

// Wrap entire server
httpServer := &http.Server{
    Addr:    ":8080",
    Handler: requestLogger(s),
}

// Or per-route
s.router.Handle("GET /api/admin/", adminOnly(http.HandlerFunc(s.handleAdmin)))

Middleware Signature

func requestLogger(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        slog.Info("request", "method", r.Method, "path", r.URL.Path, "dur", time.Since(start))
    })
}

Project Structure

Choose based on project size:

Read more
Ships withbeagle

Image: NASA, Public Domain. Source Beagle is an Agent Skills marketplace: framework-aware code review, documentation, testing, architectural analysis, and git workflows for any compatible coding agent.

Get the whole plugin

Other skills on beagle.