This comparison becomes much clearer once you stop treating the REPL and main as competitors.
They answer different questions.
main asks: how does an external caller start this program?
- the REPL asks: how does a developer enter this running system and interrogate pieces of it?
For a Java engineer, that is the real shift.
Java Trains You To Think In One Entry Point
In Java, public static void main(String[] args) is the normal door into a program.
That encourages a familiar loop:
- write code
- compile
- run
main
- observe output
- repeat
There is nothing wrong with that model. It is still useful for:
- command-line applications
- batch jobs
- startup scripts
- integration and packaging boundaries
But it is a coarse-grained way to answer small development questions.
The REPL Is A Developer Entry Point
The REPL is a much more surgical entry point.
Instead of committing to one whole execution path, you can:
- load a namespace
- call one function
- inspect one value
- redefine one function
- call it again immediately
This is why Clojure development feels different even when the final application still has a -main function.
The runtime entry point for users may still be -main.
The day-to-day entry point for developers often becomes the REPL.
-main Still Matters In Clojure
Clojure has a -main convention for exactly the same practical reasons Java has main:
- programs need a stable external start point
- scripts need argument handling
- deployment needs a predictable launch path
So the lesson is not “stop using -main.”
The lesson is “stop forcing every development question through -main.”
A Better Comparison
| Question |
REPL |
-main |
| “What does this one function do on this input?” |
Excellent fit |
Overkill |
| “Can I redefine this namespace and try again?” |
Excellent fit |
Usually awkward |
| “How does a user or script start the program?” |
Poor fit |
Excellent fit |
| “What should production invoke?” |
Poor fit |
Excellent fit |
| “How do I inspect one intermediate value?” |
Excellent fit |
Usually indirect |
That table is more useful than arguing that one model is simply better.
Example: Keep The Core Logic Out Of -main
Here is a Clojure shape that works well:
1(ns orders.cli
2 (:gen-class)
3 (:require [clojure.edn :as edn]
4 [orders.pricing :as pricing]))
5
6(defn -main [& [path]]
7 (let [order (-> path slurp edn/read-string)]
8 (println (pricing/order-total order))))
Notice the design:
-main handles the outside world
- the real business logic lives elsewhere
That lets you use the same core function directly at the REPL:
1(require '[orders.pricing :as pricing])
2
3(pricing/order-total
4 {:order/id 1001
5 :items [{:sku "A-1" :qty 2 :unit-price 15M}
6 {:sku "B-2" :qty 1 :unit-price 9M}]})
7;; => 39M
This is the sweet spot for most Clojure applications:
- pure or mostly pure functions for the real work
- a thin
-main for process startup
- a REPL for rapid development and diagnosis
Where Java Developers Usually Overcorrect
There are two common mistakes during the transition.
The first is bringing too much main thinking into Clojure:
- putting important logic directly in startup code
- forcing every test through the whole process launch
- treating the REPL like a side toy instead of the primary development surface
The second is swinging too far the other way:
- doing everything ad hoc at the REPL
- never codifying startup behavior
- forgetting that deployment still needs a clean external entry point
Both mistakes come from treating the two entry paths as rivals instead of companions.
When To Reach For Each One
Use the REPL when you want to:
- explore a data transformation
- inspect a namespace
- debug one failing case
- redefine a function quickly
- develop a feature in small increments
Use -main when you want to:
- package a command-line program
- define a startup path for operations or deployment
- accept arguments from the outside world
- make application launch explicit and reproducible
In a healthy Clojure project, you use both constantly, just for different reasons.
The Real Productivity Gain
The REPL improves the inner development loop because it cuts away startup overhead for small questions.
-main improves operational clarity because it gives the application a stable external boundary.
When those concerns are separated cleanly:
- development gets faster
- tests get easier to write
- startup code gets thinner
- core logic becomes easier to reuse
That is a strong architectural improvement, not just a tooling preference.
Knowledge Check
### What is the most accurate relationship between the REPL and `-main` in a Clojure project?
- [x] They are complementary entry points for different purposes.
- [ ] The REPL should replace `-main` everywhere.
- [ ] `-main` should replace the REPL for debugging.
- [ ] They are interchangeable in all cases.
> **Explanation:** The REPL is usually the best developer entry point, while `-main` remains the best external startup entry point.
### Why is it usually a good idea to keep business logic out of `-main`?
- [x] It lets the same logic be called directly from tests and the REPL without process startup overhead.
- [ ] It makes namespaces unnecessary.
- [ ] It avoids the need for dependencies.
- [ ] It prevents the program from taking arguments.
> **Explanation:** Thin startup code and reusable functions make both development and operations cleaner.
### When is `-main` the better tool than the REPL?
- [x] When you need a stable launch path for scripts, packaging, or deployment
- [ ] When you want to inspect one intermediate value
- [ ] When you want to redefine a function repeatedly
- [ ] When you want to probe a namespace interactively
> **Explanation:** `-main` is for externally starting the program, not for day-to-day exploratory development.
### What is a common migration mistake for Java developers learning Clojure?
- [x] Forcing small development questions through the full startup path instead of using the REPL directly
- [ ] Using namespaces
- [ ] Writing pure functions
- [ ] Keeping startup code thin
> **Explanation:** Treating every experiment like a full application launch loses much of the speed advantage that the REPL provides.