What namespaces do in Clojure, how they differ from Java packages, and how to organize code without creating naming conflicts.
A namespace is the main unit of code organization in Clojure. If you come from Java, the closest comparison is a package, but that comparison is only partial.
A Java package mainly groups classes. A Clojure namespace groups vars: functions, values, macros, and other definitions. It also controls how code from other namespaces becomes visible.
A namespace helps you:
In practice, a namespace is both an organizational boundary and a loading boundary.
Most namespaces start with an ns form.
1(ns myapp.orders
2 (:require [clojure.string :as str]
3 [myapp.money :as money]))
This does several jobs at once:
myapp.ordersLater in the file, you might write:
1(defn order-label [order]
2 (str/upper-case (:id order)))
The str/upper-case call is explicit about where the function comes from. That explicitness is usually a good thing.
Java packages and imports are mostly about class organization. In Clojure, namespaces matter more during active development because you are constantly loading code, evaluating forms, switching REPL context, and choosing which vars to expose.
That means a namespace is not just a folder label. It is part of the running development model.
For a Java developer, a better analogy is:
require with aliasesThe common default is to use :require and alias other namespaces.
1(ns myapp.reports
2 (:require [clojure.set :as set]
3 [myapp.db :as db]))
That gives you code that stays readable as the project grows.
1(set/union a b)
2(db/load-report id)
For Java engineers, this is roughly equivalent to preferring explicit imports and qualified usage where clarity matters.
:referYou can also bring selected vars directly into scope.
1(ns myapp.math
2 (:require [clojure.string :refer [join split]]))
That can be fine for a small number of very common functions, but overusing :refer makes larger namespaces harder to read because it becomes less obvious where names come from.
A good rule is:
If a namespace feels confused, the problem is often not just naming. It may mean the code is doing too many jobs.
A strong namespace usually has:
When a namespace grows messy, that is often the Clojure version of a Java package or service class that has become too broad.
A namespace is not a class. It is a container for related definitions. Do not force class-like structure into it.
Pulling many external symbols directly into scope can make code harder to trace. Prefer clear aliases.
In REPL-driven work, loading and reloading namespaces matters. If your namespace has side effects at load time, development can become confusing quickly.
When you read a namespace, ask:
If yes, the namespace is probably doing its job well.