The Six Things We Didn't Build: What the Framework Teaches
The Six Things We Didn't Build: What the Framework Teaches
Part of a research project’s value is what it deliberately left out. This closing post inventories gortex’s six unbuilt directions and what the whole framework teaches.
The six unbuilt directions
Gortex’s own design doc lists six “worth doing, not done” directions. What they share: each looks one mile away, and each hides production-grade devils in the detail.
- The full graceful-shutdown chain: B6’s hub already has basic graceful shutdown, but production-grade also needs draining (remove from the load balancer first — K8s preStop + readiness flipped false — then let in-flight requests finish), shutdown ordering (HTTP listener → drain → WS Hub → DB pool), and propagating the OS signal’s deadline down to every subsystem.
- Error semantics in the middleware chain: today a middleware returns an
errorandServeHTTPhandles it uniformly. A production framework also distinguishes retryable vs non-retryable, should-log vs already-handled, and the error boundary — once a layer swallows an error, can the layers above still observe it? - Config hot-reload:
pkg/configloads once. A K8s ConfigMap is updated into the mounted directory, so anfsnotifywatch would let you change log level, rate-limit thresholds, or feature flags without a restart. - Request-level timeout propagation: there’s no “global request timeout” today. A production framework usually sets a deadline context in the outermost middleware, so a handler that forgets a timeout still can’t hang forever (B7’s per-check timeout is a local version of the same move).
- Structured errors:
pkg/errorsis a flat code→message registry today. A more advanced design has error-code namespaces, error chains, and i18n:
// today: a flat code → message
// wanted: namespaces + layered matching
err := errors.New("ORDER.PAYMENT_FAILED", ...)
if errors.Is(err, order.ErrPayment) { /* handle by layer */ }
- OpenAPI / Swagger generation:
core/app/doc/has aswagger.Providerskeleton, but spec generation isn’t finished. Deriving an OpenAPI spec from struct tags + method signatures is the natural extension of B2’s declarative routing.
What the framework teaches
Read the whole series backwards and a few recurring principles surface:
- The answer is context-dependent: a kitchen-sink is over-engineering for a microservice and a governance tool for a platform team (B1). No architecture is universal.
- Pay the cost once: reflect scans routes and builds the tree at startup, so the hot path is zero-allocation and reflection-free (B2–B4). Push expensive work to the boot moment.
- Pick the tool by the shape of the data: high-frequency counting wants atomics and sharded locks (B5); an owned-state event stream wants a channel (B6). Don’t reach for a channel reflexively, and don’t lock everything.
- Program to interfaces: swapping tracing to OTel or a health check to another implementation touches no business code (B7, A3).
- Fail loud, bound your inputs: an unknown middleware or the unimplemented rbac/inject crashes at startup (B2); body, JWT, and rate limit are all bounded at the boundary (B8).
- Be honest about limits:
LogResponseBodyis a no-op,injectonly parses,rbacisn’t implemented — saying so beats pretending to do everything.
Closing
Gortex isn’t an open-source framework out to compete with Gin or Echo; it’s a reference implementation — for research and record, not production. Its value is two-sided: it lays out the decisions a platform framework has to make (where B1 started), and the “six things we didn’t build” list is itself a map of where production-grade infrastructure actually gets hard.
Knowing what to build, what to deliberately leave out, and why — that’s probably the most worthwhile thing to take from reading a framework’s source.
Takeaways
- The six unbuilt directions (full graceful shutdown, middleware error semantics, config hot-reload, request-level timeout, structured errors, OpenAPI generation) each stall on production-grade detail.
- The series’ throughline: the answer is context-dependent, pay the cost once, pick the tool by shape, program to interfaces, fail loud + bound inputs, be honest about limits.
- Gortex is a reference implementation; its value is in demonstrating decisions and deliberate omissions, not in being a production framework.
- The “didn’t build” list is itself a map of where production-grade infrastructure is hard.
- The most worthwhile takeaway from reading a framework: knowing what to build, what to skip on purpose, and why.
Source: yshengliao/gortex.
Outline by Sheng, drafted with Claude · Go 1.25 (gortex go.mod) · compiled retroactively · part of the 2026-06-13 blog renovation, paint still drying.