- Model every HTTP handler as `(fn [request] response)` — `request` and `response` are plain Clojure maps with `:status`, `:headers`, `:body` keys.
- Compose middleware as `(wrap-middleware handler options)` — apply `ring.middleware.defaults/wrap-defaults` with `site-defaults` or `api-defaults`.
- Use `reitit` or `compojure` for routing on top of Ring — never write manual URI dispatch with `cond` and `(:uri request)`.
- Use `ring.middleware.json/wrap-json-body` and `wrap-json-response` for JSON APIs — set `:keywords? true` to auto-keywordise parsed keys.
- Use `ring.middleware.session` with a server-side store (`ring.middleware.session.memory/memory-store` in dev, Redis in prod) for session management.
- Use `ring-mock` (`ring.mock.request`) to construct test request maps without starting an HTTP server — call handlers directly in unit tests.
- Use `ring.middleware.resource` and `ring.middleware.file-info` to serve static assets with correct content-type and caching headers.