TL;DR: Leiningen provides facilities that make using a REPL driven workflow a breeze. You should consider using them on your projects for a more pleasant development experience.
Make your system reloadable
First of all the application should be reloadable, that means restartable in the REPL without restarting the entire process. If the app can shutdown and start quickly, this makes development way faster, less error prone and more pleasing experience. If you use component, this can be easily done with the reloaded.repl library provided by weavejester.
Application of this library is straightforward as the README states:
(ns user
(:require [reloaded.repl :refer [system init start stop go reset reset-all]]
[your-app.system :refer [new-system]]))
(reloaded.repl/set-init! #(new-system {:port 3000}))
user.clj
The code above should go into a user.clj file, which should be on the classpath during development, but you normally do not want to ship it (in a jar). I like to put it in a dev folder next to src and test. Let's notfiy leiningen about that and add the necessary dependency:
:profiles {
:dev {:jvm-opts ["-Dlog_appender=consoleAppender"]
:source-paths ["dev"]
:dependencies [[reloaded.repl "0.2.4"]]
:repl-options {:welcome (println "Run (go) to start the system and then (reset) to restart!")
:init-ns user}}}
The namespace of the started repl will be the prepared user namespace and the provide message will communicate to the user, that he can immediately start the system under development.
Guardrails
Finally in order to avoid reloading woes because of class files tampering with our REPL workflow, let's add this to our project.clj:
:target-path "target/%s"
That means, that if you use AOT compilation (for example for the uberjar profile) the class files produced there do not interfere with the REPL.
Low investment, high reward
Those little changes make a big difference. I noticed people starting to use the REPL that haven't done it before, because it is so easy getting started with a project setup like this.