reviewable wiring
route registration, middleware order, and dependencies stay visible in code review
Adoption Decision
Use this page when the question is whether Plumego fits your team and review expectations — before committing time to the technical details.
This is about AI coding assistants (Claude, Copilot, Cursor) — tools that write or review code.
It is not about building AI agent services; for that, see x/ai.
Plumego is the first Go HTTP toolkit designed so those coding assistants know exactly where a change belongs — before writing any code. Machine-readable specs, per-module manifests, and explicit boundary rules give agents the same routing signals a senior reviewer uses.
specs/task-routing.yaml maps work types to owning modules specs/dependency-rules.yaml prevents agents from crossing stable-root boundaries AGENTS.md defines the shared protocol for human + AI collaboration These words have precise meanings in this repository — they are not general Go jargon.
core, router, contract, middleware, security, store, health, log, metrics. x/ directory — experimental unless labeled otherwise. Start from the owning family, not a subordinate primitive. docs/, specs/, and tasks/ directories that hold human-readable rules and machine-readable constraints for both humans and AI coding assistants. reference/standard-service — the one starting point every new service should compare against. The core question before adopting: should route ownership, middleware order, and dependency wiring be visible in code — or managed by the framework? Plumego keeps registration in internal/app/routes.go, where it appears in every code review.
hidden behind registration calls
func main() {
mux := http.NewServeMux()
items.Register(mux) // paths? middleware?
users.Register(mux) // must open each pkg
orders.Register(mux) // to find out
// full route map not visible here
http.ListenAndServe(addr, mux)
} plumego: visible in routes.go
func (a *App) Routes() http.Handler {
r := router.New()
r.Use(middleware.RequestID)
r.Use(middleware.Logger(a.log))
r.Get("/api/items", a.items.List)
r.Get("/api/orders", a.orders.List)
r.Get("/api/users", a.users.List)
// full route map visible here
return r
} // consistent JSON envelope from every handler
_ = contract.WriteResponse(w, r, http.StatusOK, items, nil)
// → {"data": [...], "meta": null}
_ = contract.WriteError(w, r,
contract.NewErrorBuilder().
Type(contract.TypeRequired).
Message("name required").Build())
// → {"error": {"type": "required", ...}} Plumego is a good match when the adoption question can be answered with visible engineering evidence, not framework enthusiasm.
route registration, middleware order, and dependencies stay visible in code review
new services start from one reference shape instead of custom bootstraps
stable roots and experimental families are separated before adoption
machine-readable specs, per-module manifests, and standardized check commands give code agents a clear operating model
handler signature is func(w http.ResponseWriter, r *http.Request) — no custom context, all net/http middleware works without adapters
the stable core imports only the Go standard library — no transitive dependency risk in the kernel layer
- route registration stays visible
- middleware order is reviewable
- reference app is the canonical service shape
- release posture limits adoption assumptions
- stdlib handler signature — no adapters needed
- zero external deps in the stable core Plumego becomes a stronger fit when review clarity, explicit ownership, and one default service shape matter more than framework-managed convenience.
best fit
Choose Plumego when a pull request should let you see which routes are registered, which middleware runs in order, and who owns each dependency — without reading framework source.
best fit
The framework is strongest when several teams need to start new services from the same reference shape and you want structural drift between repositories to stay low.
best fit
Plumego is a strong fit when adoption decisions need to be based on explicit maturity evidence — stable, supported, or experimental — rather than inferred from package presence alone.
best fit
Plumego is built for codebases where AI agents share execution and review responsibilities. Four machine-readable control planes — <code>docs/</code>, <code>specs/</code>, <code>tasks/</code>, and <code>reference/</code> — give agents a deterministic operating model: where to classify a task, which files to read first, what a correct change looks like, and which checks must pass before a contribution lands. The same structure that makes human review tractable also makes agent contributions safe to verify.
This section is about AI coding assistants (Copilot, Claude, Cursor) — tools that write
or review code. It is not about building AI agent services; that is the scope of x/ai.
Most teams using these tools today face the same problem: the assistant has to guess where a change belongs. Plumego's control plane gives those tools the same routing signals a senior reviewer uses.
work routing
specs/task-routing.yaml maps work types to owning modules. An agent reads the routing table
when evaluating a change instead of inferring ownership by guessing directory structure.
boundary enforcement
specs/dependency-rules.yaml and internal/checks/dependency-rules reject
PRs that cross stable-root boundaries — whether the change came from a human or an AI tool.
change recipes
specs/change-recipes/ gives agents an explicit execution sequence for known change
types: new stable module, bug fix, extension promotion, deprecation. Recipes reduce hallucination
surface and make agent output reviewable with the same rules as human PRs.
This comparison should help you decide, not merely admire the architecture. Use it when you need to know whether Plumego's explicit model actually improves the service in front of you.
All four toolkits are built on net/http. The differences are philosophy and scope — not performance.
Use the table below as a decision aid: each row answers "when should I choose this" and "when should I not."
gin.Context / echo.Context). Existing net/http middleware needs adapters, but the convention-first model is a worthwhile trade. You need handlers to stay as plain func(w, r) and want all existing stdlib middleware to work without wrappers. stdlib handler signature (func(w http.ResponseWriter, r *http.Request)), a structured contract layer, and an explicit canonical service shape — without hidden registration. You want the framework to handle structure invisibly, or your team is already comfortable with Gin/Echo and happy with the trade-offs. router overhead
Plumego's trie router adds per-request context injection, param map construction, and
middleware chain wiring on top of dispatch. Overhead vs Go 1.22+ http.ServeMux
ranges from 1.4× for deep param routes to 4.3× for
single-param routes — 110–1,036 ns absolute. For typical handlers spending 1 ms–100 ms
on I/O, router cost is 0.001%–0.1% of request time.
Full benchmark table on Releases →
Plumego is not meant to win every comparison. If the signals below match your team's goals more closely, the explicit model may add more friction than value right now.
not a fit
Gin and Echo have large middleware catalogs, authentication plugins, and community tooling built around their custom context types. If your team depends on gin.Context or echo.Context — and the ecosystem around it — switching handler signatures imposes real migration cost with no clear upside.
not a fit
Plumego does not carry database access, dependency injection, or auto-registration. If your team expects the framework to provide or wire these concerns, the explicit DI pattern will feel like unnecessary overhead. Reach for a full application framework instead.
not a fit
The 9 stable roots carry the v1 compatibility promise. The x/* extension families are experimental or beta — their APIs may still change until their own evidence is complete. If your organization requires a fully stable, versioned API across every capability you use, restrict adoption to stable roots and promoted beta surfaces only.
slow down
If the team mainly wants routing and dependency setup handled invisibly by the framework, Plumego may feel more deliberate than productive.
slow down
The more each service must define its own startup, routing, and dependency patterns, the less a shared reference baseline can offer.
Plumego is intentionally narrow at entry and explicit about where it expands. Here is what the first three stages of adoption typically involve.
Day 1
cd reference/standard-service
go run .
# → server started at :8080
# → GET /ping → {"data":{"message":"pong"}}
Clone the repo, run the reference service, read internal/app/routes.go. The full request path is visible
without reading any framework source. Start here before evaluating further.
Day 30
Once the stable-root baseline is working in your service, the next inflection point is whether you need a capability from
the x/* layer — WebSocket, multi-tenancy, AI streaming, or edge proxying. Each x/* family attaches at the transport layer
without modifying stable roots. You verify the boundary holds with go run ./internal/checks/dependency-rules.
The specs/task-routing.yaml table tells you which x/* family owns any new capability before you start.
The extension layer evolves at its own pace — stable roots stay unchanged.
Day 90 and release evidence
The 9 stable roots carry the v1 compatibility promise. After the v1 tag, the upgrade path for
core, router, contract, and the other stable roots is mechanical —
no handler signature changes, no breaking response envelope changes.
x/* families that remain experimental after v1 are tracked separately. Their maturity tier is explicit in the release matrix. You adopt only the capabilities that have reached beta or stable — not the whole extension catalog.
Plumego uses plain func(w http.ResponseWriter, r *http.Request) handlers, so existing stdlib-compatible middleware ports without adapters.
The migration guides cover the most common switching paths.
Handler signature, context type, middleware adapters, and response envelope changes.
echo.Context replacement, BindJSON equivalents, and middleware registration differences.
Route group migration, param extraction, and adding the contract response layer.
All stdlib-compatible middleware works without adapters. Custom context-based middleware needs porting.
Fit check done. Go to Architecture to browse x/* capability families and the full layer diagram, or jump straight into implementation.