Automatic Code reloading in Ring / Jetty
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Ring and Jetty development, automatic code reloading is usually a development convenience built around namespace reloading rather than a magical property of Jetty itself. The common approaches are Ring development middleware for request-time reloading or tools.namespace driven reloads from the REPL.
Jetty serves requests, but Clojure reloading is the real mechanism
Jetty is the HTTP server. Ring is the handler abstraction. Automatic reloading comes from the Clojure side, where changed namespaces are reloaded into the running process.
That distinction matters because the right setup is usually:
- Jetty for serving
- Ring middleware or REPL tooling for reload behavior
Not "Jetty does hot reload by itself."
Request-time reload middleware is the simple option
For development, ring.middleware.reload is a straightforward pattern:
Here, wrap-reload checks changed namespaces and reloads them during development requests. It is convenient because it keeps the feedback loop short without requiring a full server restart for ordinary source edits.
tools.namespace gives more explicit control
A more robust REPL-driven workflow uses clojure.tools.namespace.repl:
This approach is especially popular when you want controlled reload boundaries rather than request-triggered behavior. Many experienced Clojure developers prefer it because it is explicit and integrates well with interactive development.
Use the right tool for development only
Automatic code reloading is for development, not production. In production you usually want:
- immutable deploy artifacts
- explicit startup lifecycle
- predictable state initialization
Request-time reload checks or namespace-refresh loops are useful during iteration, but they are not a deployment strategy.
Stateful components need extra care
Code reloading is easiest when handlers are mostly pure and state is externalized. If the application holds stateful components such as:
- database pools
- background jobs
- caches
- long-lived atoms with implicit lifecycle
then reload behavior can become messy unless the system is designed to stop and restart components cleanly.
This is why Clojure development workflows often pair reloading with lifecycle tools such as Integrant, Component, or Mount-style patterns.
Keep the development feedback loop tight
A common productive setup is:
- Jetty running in-process
- Ring handler wrapped with reload middleware or REPL refresh flow
- an editor connected to the REPL
That gives quick iteration without restarting the JVM for every handler change.
Middleware reload and REPL reload can coexist
Many teams use lightweight request-time reload during early development and fall back to explicit REPL refresh when the application lifecycle becomes more complex. The two approaches are complementary rather than mutually exclusive. That flexibility is one reason the Clojure development experience stays productive even as applications grow.
Common Pitfalls
- Assuming Jetty alone provides code reloading.
- Using reload-oriented middleware in production.
- Relying on automatic reload while ignoring application state and lifecycle issues.
- Expecting every code change, including low-level startup code, to reload cleanly.
- Skipping REPL-based workflows even though they are often the most reliable Clojure development path.
Summary
- Automatic reloading in Ring and Jetty is mainly a Clojure namespace-reloading feature, not a Jetty feature.
- '
wrap-reloadis a simple development-time middleware solution.' - '
tools.namespaceoffers a more explicit and often more robust REPL-driven workflow.' - Reloading works best when application state is managed cleanly.
- Use these techniques for development speed, not for production serving.

