Docs Explicit Middleware Chains

Explicit Middleware Chains

Middleware is powerful — and dangerous.

It can:

  • Enforce cross-cutting concerns
  • Centralize behavior
  • Reduce duplication

But when middleware chains become implicit or uncontrolled, they introduce:

  • Hidden execution order
  • Surprising side effects
  • Debugging nightmares
  • Accidental coupling

In Plumego, middleware must be explicit, intentional, and readable.

This document defines a pattern for building middleware chains that remain understandable as systems grow.


The Core Problem with Middleware

Middleware sits between requests and handlers.

That makes it easy to add behavior —
and easy to hide it.

Common failure modes include:

  • “Why is this handler returning 401?”
  • “Where did this header come from?”
  • “Why does logging sometimes miss requests?”
  • “Why did adding middleware X break feature Y?”

The root cause is almost always the same:

The middleware chain is implicit.


The Explicit Middleware Chain Principle

The pattern is simple:

The full middleware chain for a request must be visible and predictable.

This implies:

  • Order is intentional
  • Scope is explicit
  • Responsibilities are clear
  • Side effects are known

Middleware should never feel magical.


Middleware Is Control Flow

Middleware is not just “helpers”.

It defines control flow:

  • Whether a request proceeds
  • When logic executes
  • What context is available
  • How errors are handled

Therefore, middleware deserves the same rigor as core application logic.


Classifying Middleware by Responsibility

A practical way to keep chains explicit is to classify middleware by role.

Common middleware categories

  1. Request identity

    • Trace ID
    • Request ID
    • Correlation metadata
  2. Observability

    • Logging
    • Metrics
    • Tracing hooks
  3. Safety

    • Panic recovery
    • Rate limiting
    • Timeouts
  4. Security

    • Authentication
    • Authorization (coarse-grained)
  5. Transport concerns

    • CORS
    • Content-type enforcement

Mixing these categories without structure leads to confusion.


While exact order may vary, a clear default order should exist.

A commonly effective order in Plumego systems:

  1. Trace ID
  2. Logging
  3. Panic Recovery
  4. Rate Limiting / Timeouts
  5. Authentication
  6. Authorization (coarse)
  7. Handlers

This order ensures:

  • Every request is traceable
  • Failures are logged
  • Panics are contained
  • Security checks are predictable

The important part is not the exact order —
it is that the order is deliberate and documented.


Making Middleware Order Visible in Code

Avoid patterns where middleware is scattered across files or auto-registered.

Prefer a single, readable composition point.

Example:

app := plumego.New()

app.Use(
	TraceIDMiddleware(),
	LoggingMiddleware(logger),
	RecoveryMiddleware(logger),
	RateLimitMiddleware(),
	JWTAuthMiddleware(verifier),
)

This makes the execution chain immediately visible.

If you cannot understand request flow by reading this block,
the chain is already too implicit.


Avoiding Conditional Middleware

Conditional middleware is a common source of hidden behavior.

Example to avoid:

if config.EnableAuth {
	app.Use(AuthMiddleware())
}

This creates environment-dependent behavior that is hard to reason about.

If conditional behavior is required, make it explicit:

  • Separate app instances
  • Separate routers
  • Separate startup paths

Global vs Route-Scoped Middleware

Not all middleware belongs globally.

Global middleware

  • Trace ID
  • Logging
  • Panic recovery

These apply to every request.

Route-scoped middleware

  • Authentication
  • Authorization
  • Feature-specific behavior

Apply them explicitly at route definition time.

Example:

app.Group("/admin",
	AuthMiddleware(),
	AdminOnlyMiddleware(),
).GET("/stats", StatsHandler)

Scope should be obvious from the route definition.


Middleware Should Be Boring

A good middleware:

  • Does one thing
  • Has clear inputs and outputs
  • Avoids hidden state
  • Is easy to remove

Middleware that tries to be “clever” usually becomes a liability.


Avoiding Middleware That Encodes Business Logic

Middleware should not:

  • Decide business outcomes
  • Encode domain rules
  • Perform workflow orchestration

Those belong in handlers and usecases.

Middleware should prepare the ground, not run the business.


Debugging Explicit Middleware Chains

Explicit chains make debugging straightforward:

  • You know the order
  • You know which middleware ran
  • You know where a request stopped

This dramatically reduces the cost of production debugging.


Middleware and Testing

Explicit chains improve testability:

  • Middleware can be tested in isolation
  • Chains can be tested end-to-end
  • Order-related bugs are easier to catch

Implicit chains are difficult to test reliably.


Common Anti-Patterns

Implicit Registration via init()

This hides behavior and makes ordering unpredictable.


Middleware That Mutates Global State

This breaks request isolation and causes race conditions.


Middleware That Does Too Much

Large middleware functions are a sign of mixed responsibilities.


Summary

In Plumego:

  • Middleware defines control flow
  • Control flow must be explicit
  • Order must be intentional
  • Scope must be visible
  • Behavior must be predictable

Explicit middleware chains are one of the most effective ways
to keep a system understandable as it grows.


Next

With explicit middleware chains in place, the next useful pattern is:

Explicit Error Mapping

This explains how to keep error handling consistent without scattering logic across handlers.