How to ns
Stuart’s Opinionated Style Guide for Clojure Namespace Declarations

Summary

Make it look like this:

(ns com.example.my-application.server
  "Example application HTTP server and routing."
  (:refer-clojure :exclude [send])
  (:require
   [clojure.core.async :as async :refer [<! <!! >! >!!]]
   [com.example.my-application.base]
   [com.example.my-application.server.sse :as server.sse]
   [io.pedestal.http :as http]
   [io.pedestal.http.sse :as http.sse]
   [ring.util.response :as response])
  (:import
   (java.nio.file Files LinkOption)
   (org.apache.commons.io FileUtils)))

The Rules

Roughly in order of importance:

Main clauses

Put clauses in this order, omitting those that are not needed:

  1. :refer-clojure
  2. :require
  3. :import

Use each clause at most once.

Do not :use.

Do not :refer :all.

Do not :rename.

Use :require :refer only for very frequently-used symbols.

Keywords or symbols?

Use keywords, not symbols, for :refer-clojure, :require, and :import.

;; Good
(ns com.example.my-application
  (:require
   [clojure.string :as string])
  (:import
   (java.util Date)))
;; Bad
(ns com.example.my-application
  (require
   [clojure.string :as string])
  (import
   (java.util Date)))

Symbols happen to work in most versions of Clojure, but were never correct syntax.

Reference: ns macro docstring.

Vectors or lists?

Use lists (parentheses) for the top-level clauses such as (:require ...). Because the first element, the :require or :import keyword, is special.

;; Good
(ns com.example.application
  (:require
   [clojure.string :as string])
  (:import
   (java.util Date)))
;; Bad
(ns com.example.application
  [:require
   [clojure.string :as string]]
  [:import
   (java.util Date)])

Use vectors (square brackets) inside :require. Because otherwise they would be interpreted as prefix lists.

;; Good
(:require
 [clojure.string :as string])
;; Bad
(:require
 (clojure.string :as string))

Use lists (parentheses) inside :import. Because the first symbol, the package name, is special.

;; Good
(:import
 (java.util Date UUID)
 (java.io File))
;; Bad
(:import
 [java.util Date UUID]
 [java.io File])

This (lists in :import) turns out to be the most contentious point. A number of people disagree with this recommendation, on the grounds that the package-class list does not represent an invocation, as lists usually do in Clojure. I stand by it, though, for the reason above and because both ns and import have historically been documented this way.

Put every :require’d namespace in a vector (square brackets) even if it doesn't have an :as or :refer. This makes visual scanning and sorting easier.

;; Good
(:require
 [com.example.client :as client]
 [com.example.server])
;; Bad
(:require
 [com.example.client :as client]
 com.example.server)

References: ns macro docstring, require docstring, import docstring.

Prefix lists

Never use prefix lists in :require. Write out the full name of each namespace. Because namespaces have no hierarchical relationship, and it makes searching easier.

;; Good
(:require
 [com.example.client]
 [com.example.routes]
 [com.example.server])
;; Bad
(:require
 (com.example client server routes))

Always use prefix lists in :import, even if you're only importing one class from a package. Because packages and classes do have a hierarchical relationship: packages contain classes.

;; Good
(:import
 (java.io File)
 (java.util Date UUID))
;; Bad
(:import
 java.io.File
 (java.util Date UUID))

Aliases

Use full words as namespace aliases, except for very frequently-used namespaces with established conventions such as
[clojure.spec :as s].

Reference: Clojure Do’s: Namespace Aliases

Vectors or lists? part 2

Put :refer’d or :exclude’d symbols in a vector (square brackets).

;; Good
(:require
 [clojure.string :refer [blank? capitalize ends-with?]])
;; Bad
(:require
 [clojure.string :refer (blank? capitalize ends-with?)])

The docstrings of ns and refer disagree here: refer says “list-of-symbols,” but ns shows an example with vectors. Vectors make more sense because the first symbol is not special or different from the others.

Sorting

Sorting makes scanning easier and reduces the likelihood of accidentally repeating symbols.

Sort :require’d namespaces lexicographically.

;; Good
(:require
 [clojure.java.io :as io]
 [cognitect.transit :as transit]
 [com.example.my-application.server.api :as api]
 [io.pedestal.http :as http])
;; Bad
(:require
 [clojure.java.io :as io]
 [io.pedestal.http :as http]
 [cognitect.transit :as transit]
 [com.example.my-application.server.api :as api])

Sort :import’ed packages lexicographically.

;; Good
(:import
 (com.example.java ExampleMain)
 (java.nio.file Files)
 (java.nio.file.attribute FileAttribute)
 (org.apache.commons.io FileUtils))
;; Bad
(:import
 (org.apache.commons.io FileUtils)
 (java.nio.file Files)
 (java.nio.file.attribute FileAttribute)
 (com.example.java ExampleMain))

Within a :refer or :exclude vector, sort symbols lexicographically.

;; Good
(:require
 [clojure.string :refer [blank? capitalize ends-with?]])
;; Bad
(:require
 [clojure.string :refer [capitalize ends-with? blank?]])

Within an :import prefix list, sort class names lexicographically.

;; Good
(java.nio.file.attribute FileAttribute FileAttributeView UserPrincipal)
;; Bad
(java.nio.file.attribute FileAttributeView UserPrincipal FileAttribute)

Docstring

Include a docstring. Describe what the purpose of this namespace is and how it fits into the rest of the codebase.

Prefer docstrings over large block comments where possible.

;; Good
(ns com.example.my-application-routes
  "HTTP server routes for my application.
  Routes generally follow the pattern /api/:type/:id
  where :type is a type of object and :id is an integer ID.")
;; Bad
(ns com.example.my-application-routes)

;; HTTP server routes for my application.
;; Routes generally follow the pattern /api/:type/:id
;; where :type is a type of object and :id is an integer ID.

Line breaks

Start the contents of :require or :import on a new line after the keyword.

;; Good
(:require
 [clojure.string :as string]
 [io.pedestal.http :as http])
;; Not so good
(:require [clojure.string :as string]
          [io.pedestal.http :as http])

This recommendation is slightly different from common practice, but it makes it easier to sort names with editor commands (sort-lines in Emacs).

Put each :require’d namespace or :import’ed package on its own line.

;; Good
(:require
 [clojure.string :as string]
 [io.pedestal.http :as http])
;; Bad
(:require
 [clojure.string :as string] [io.pedestal.http :as http])

:require :as :refer

When a :require’d namespace has both :as and :refer, put :as before :refer.

;; Good
(:require
 [clojure.core.async :as async :refer [<! <!! >! >!!]])
;; Bad
(:require
 [clojure.core.async :refer [<! <!! >! >!!] :as async])

Author: Stuart Sierra

Last updated: 2016-10-18 Tue 21:02