Skip to content

A community coding style guide for the Clojure programming language

Notifications You must be signed in to change notification settings

Yuppiechef/clojure-style-guide

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 

Repository files navigation

The Clojure Style Guide

Role models are important.
-- Officer Alex J. Murphy / RoboCop

NOTE: This is a fork from https://github.com/bbatsov/clojure-style-guide - we have slight differing opinions and will highlight these along the way.

This Clojure style guide recommends best practices so that real-world Clojure programmers can write code that can be maintained by other real-world Clojure programmers. A style guide that reflects real-world usage gets used, and a style guide that holds to an ideal that has been rejected by the people it is supposed to help risks not getting used at all — no matter how good it is.

The guide is separated into several sections of related rules. I've tried to add the rationale behind the rules (if it's omitted, I've assumed that it's pretty obvious).

I didn't come up with all the rules out of nowhere; they are mostly based on my extensive career as a professional software engineer, feedback and suggestions from members of the Clojure community, and various highly regarded Clojure programming resources, such as "Clojure Programming" and "The Joy of Clojure".

The guide is still a work in progress; some sections are missing, others are incomplete, some rules are lacking examples, some rules don't have examples that illustrate them clearly enough. In due time these issues will be addressed — just keep them in mind for now.

Please note, that the Clojure developing community maintains a list of coding standards for libraries, too.

You can generate a PDF or an HTML copy of this guide using Pandoc.

Table of Contents

Source Code Layout & Organization

Nearly everybody is convinced that every style but their own is ugly and unreadable. Leave out the "but their own" and they're probably right...
-- Jerry Coffin (on indentation)

  • Use spaces for indentation. No hard tabs. [link]

  • Use 2 spaces to indent the bodies of forms that have body parameters. This covers all def forms, special forms and macros that introduce local bindings (e.g. loop, let, when-let) and many macros like when, cond, as->, cond->, case, with-*, etc. [link]

;; good
(when something
  (something-else))

(with-out-str
  (println "Hello, ")
  (println "world!"))

;; bad - four spaces
(when something
    (something-else))

;; bad - one space
(with-out-str
 (println "Hello, ")
 (println "world!"))
  • Departure from original Do not Vertically align function (macro) arguments spanning multiple lines. [link]

    Reasoning for departure: Formatting should be uniform without having to worry too much about whether the arguments are macro or not - see above 2 space rule. That dominates but with only a few exceptions.

      ;; bad
      (filter even?
              (range 1 10))
    
      ;; good
      (filter even?
        (range 1 10))
  • Departure from original Do not Use a single space indentation for function (macro) arguments when there are no arguments on the same line as the function name. [link]

    Reason for departure: This is pointless. Just stick with 2 spaces. Less parsing for the brain.

      ;; bad
      (filter
       even?
       (range 1 10))
    
      (or
       ala
       bala
       portokala)
    
      ;; good - two-space indent
      (filter
        even?
        (range 1 10))
    
      (or
        ala
        bala
        portokala)
  • Vertically align let bindings and map keywords. [link]

    ;; good
    (let [thing1 "some stuff"
          thing2 "other stuff"]
      {:thing1 thing1
       :thing2 thing2})
    
    ;; bad
    (let [thing1 "some stuff"
      thing2 "other stuff"]
      {:thing1 thing1
      :thing2 thing2})
  • Optionally omit the new line between the function name and argument vector for defn when there is no docstring. [link]

    ;; good
    (defn foo
      [x]
      (bar x))
    
    ;; good
    (defn foo [x]
      (bar x))
    
    ;; bad
    (defn foo
      [x] (bar x))
  • Place the dispatch-val of a multimethod on the same line as the function name. [link]

    ​```Clojure ;; good (defmethod foo :bar [x] (baz x))

    (defmethod foo :bar [x] (baz x))

    ;; bad (defmethod foo :bar [x] (baz x))

    (defmethod foo :bar [x] (baz x)) ​```

  • Optionally omit the new line between the argument vector and a short function body. [link]

    ;; good
    (defn foo [x]
      (bar x))
    
    ;; good for a small function body
    (defn foo [x] (bar x))
    
    ;; good for multi-arity functions
    (defn foo
      ([x] (bar x))
      ([x y]
       (if (predicate? x)
         (bar x)
         (baz x))))
    
    ;; bad
    (defn foo
      [x] (if (predicate? x)
            (bar x)
            (baz x)))
  • Indent each arity form of a function definition vertically aligned with its parameters.[link]

    ;; good
    (defn foo
      "I have two arities."
      ([x]
       (foo x 1))
      ([x y]
       (+ x y)))
    
    ;; bad - extra indentation
    (defn foo
      "I have two arities."
      ([x]
        (foo x 1))
      ([x y]
        (+ x y)))
  • Sort the arities of a function from fewest to most arguments. The common case of multi-arity functions is that some K arguments fully specifies the function's behavior, and that arities N < K partially apply the K arity, and arities N > K provide a fold of the K arity over varargs. [link]

    ;; good - it's easy to scan for the nth arity
    (defn foo
      "I have two arities."
      ([x]
       (foo x 1))
      ([x y]
       (+ x y)))
    
    ;; okay - the other arities are applications of the two-arity
    (defn foo
      "I have two arities."
      ([x y]
       (+ x y))
      ([x]
       (foo x 1))
      ([x y z & more]
       (reduce foo (foo x (foo y z)) more)))
    
    ;; bad - unordered for no apparent reason
    (defn foo
      ([x] 1)
      ([x y z] (foo x (foo y z)))
      ([x y] (+ x y))
      ([w x y z & more] (reduce foo (foo w (foo x (foo y z))) more)))
  • Use Unix-style line endings. (*BSD/Solaris/Linux/OSX users are covered by default, Windows users have to be extra careful.) [link]

    • If you're using Git you might want to add the following configuration setting to protect your project from Windows line endings creeping in:
    bash$ git config --global core.autocrlf true
    
  • If any text precedes an opening bracket((, { and [) or follows a closing bracket(), } and ]), separate that text from that bracket with a space. Conversely, leave no space after an opening bracket and before following text, or after preceding text and before a closing bracket. [link]

    ;; good
    (foo (bar baz) quux)
    
    ;; bad
    (foo(bar baz)quux)
    (foo ( bar baz ) quux)

Syntactic sugar causes semicolon cancer.
-- Alan Perlis

  • Don't use commas between the elements of sequential collection literals. [link]

    ;; good
    [1 2 3]
    (1 2 3)
    
    ;; bad
    [1, 2, 3]
    (1, 2, 3)
  • Consider enhancing the readability of map literals via judicious use of commas and line breaks. [link]

    Departure: Prefer the first two, use of comma's are painful.

      ;; good
      {:name "Bruce Wayne" :alter-ego "Batman"}
    
      ;; good and arguably a bit more readable
      {:name "Bruce Wayne"
       :alter-ego "Batman"}
    
      ;; good and arguably more compact
      {:name "Bruce Wayne", :alter-ego "Batman"}
  • Place all trailing parentheses on a single line instead of distinct lines. [link]

    ;; good; single line
    (when something
      (something-else))
    
    ;; bad; distinct lines
    (when something
      (something-else)
    )
  • Use empty lines between top-level forms. [link]

    ;; good
    (def x ...)
    
    (defn foo ...)
    
    ;; bad
    (def x ...)
    (defn foo ...)

    An exception to the rule is the grouping of related defs together.

    ;; good
    (def min-rows 10)
    (def max-rows 20)
    (def min-cols 15)
    (def max-cols 30)
  • Do not place blank lines in the middle of a function or macro definition. An exception can be made to indicate grouping of pairwise constructs as found in e.g. let and cond. [link]

    Departure: Possibly also for groupings of functionality within functions, but it's a good indication of something that needs to be split out into its own function.

  • Where feasible, avoid making lines longer than 100 characters. [link]

    Departure reason: The original is 80 character limits, but in practise I find that's overly prohibitive.

  • Avoid trailing whitespace. [link]

  • Use one file per namespace. [link]

  • Start every namespace with a comprehensive ns form, comprised of refers, requires, and imports, conventionally in that order. [link]

    Departure (Added): Put the subsequent forms on newlines

      (ns examples.ns
        (:refer-clojure :exclude [next replace remove])
        (:require 
          [clojure.string :as s :refer [blank?]]
          [clojure.set :as set]
          [clojure.java.shell :as sh])
        (:import 
          java.util.Date
          java.text.SimpleDateFormat
          [java.util.concurrent Executors
             LinkedBlockingQueue]))
  • In the ns form prefer :require :as over :require :refer over :require :refer :all . Prefer :require over :use; the latter form should be considered deprecated for new code. [link]

    ;; good
    (ns examples.ns
      (:require [clojure.zip :as zip]))
    
    ;; good
    (ns examples.ns
      (:require [clojure.zip :refer [lefts rights]]))
    
    ;; acceptable as warranted
    (ns examples.ns
      (:require [clojure.zip :refer :all]))
    
    ;; bad
    (ns examples.ns
      (:use clojure.zip))
  • Avoid single-segment namespaces. [link]

    ;; good
    (ns example.ns)
    
    ;; bad
    (ns example)
  • Avoid the use of overly long namespaces (i.e., more than 5 segments). [link]

  • Avoid functions longer than 10 LOC (lines of code). Ideally, most functions will be shorter than 5 LOC. [link]

  • Avoid parameter lists with more than three or four positional parameters. [link]

    Departure (Comment): You probably want to use destructuring for these cases, or your function is doing too much.

  • Avoid forward references. They are occasionally necessary, but such occasions are rare in practice. [link]

    Departure (Comment): This is where you need to (declare bob) ahead of time. Avoid.

Syntax

  • Avoid the use of namespace-manipulating functions like require and refer. They are entirely unnecessary outside of a REPL environment. [link]

  • Use declare to enable forward references when forward references are necessary. [link]

  • Prefer higher-order functions like map to loop/recur. [link]

  • Prefer function pre and post conditions to checks inside a function's body. [link]

    Departure (Comment): Though the pre & post conditions don't give you a way of dealing with the exceptions at the time, so only use this if you were going to simply throw an exception anyhow.

      ;; good
      (defn foo [x]
        {:pre [(pos? x)]}
        (bar x))
    
      ;; bad
      (defn foo [x]
        (if (pos? x)
          (bar x)
          (throw (IllegalArgumentException. "x must be a positive number!")))
  • Don't define vars inside functions. [link]

    ;; very bad
    (defn foo []
      (def x 5)
      ...)
  • Don't shadow clojure.core names with local bindings. [link]

    ;; bad - you're forced to use clojure.core/map fully qualified inside
    (defn foo [map]
      ...)
  • Departure: Do not use alter-var-root or def to change the value of a var. Find another way to implement your thing. Use an atom, probably. [link]

      ;; good
      (def thing (atom 1)) ; value of thing inside atom is now 1
      (reset! thing nil) ; value of thing inside atom is now nil.
      
      ;; bad
      (def thing 1) ; value of thing is now 1
      ; do some stuff with thing
      (alter-var-root #'thing (constantly nil)) ; value of thing is now nil
    
      ;; bad
      (def thing 1)
      ; do some stuff with thing
      (def thing nil)
      ; value of thing is now nil
  • Departure: Do not use seq as a terminating condition to test whether a sequence is empty (this technique is sometimes called nil punning). Reason: Use empty? because that indicates your intention correctly. seq means nothing.

    Clarification: This doesn't mean 'nil-punning' is bad - it's fine and you should use it, but it's more important to convey your intention through your code than it is to nil-pun.

    [link]

    ;; good
    (defn print-seq [s]
      (when (seq s)
        (prn (first s))
        (recur (rest s))))
    
    ;; bad
    (defn print-seq [s]
      (when-not (empty? s)
        (prn (first s))
        (recur (rest s))))
  • Prefer vec over into when you need to convert a sequence into a vector. [link]

    ;; good
    (vec some-seq)
    
    ;; bad
    (into [] some-seq)
  • Use when instead of (if ... (do ...). [link]

    Caveat: Be mindful when needing to replace the when with an if down the line. You should get compile error, so not too severe, but it's still a pain.

      ;; good
      (when pred
        (foo)
        (bar))
    
      ;; bad
      (if pred
        (do
          (foo)
          (bar)))
  • Departure: Do not use if-let instead of let + if. [link]

    Reason: On the surface, the if-let is nice and clean, but you usually end up having the pain of needing to split it, (to add more pairs, usually, or to introduce other things), and if-let doesn't handle it, so you end up with having to deal with something you didn't need to think about otherwise. So better to simply avoid the construct entirely.

      ;; bad
      (if-let [result (foo x)]
        (something-with result)
        (something-else))
    
      ;; good
      (let [result (foo x)]
        (if result
          (something-with result)
          (something-else)))
  • Departure: Do not use when-let instead of let + when. [link]

    Reason: See above.

      ;; bad
      (when-let [result (foo x)]
        (do-something-with result)
        (do-something-more-with result))
    
      ;; good
      (let [result (foo x)]
        (when result
          (do-something-with result)
          (do-something-more-with result)))
  • Use if-not instead of (if (not ...) ...). [link]

    ;; good
    (if-not pred
      (foo))
    
    ;; bad
    (if (not pred)
      (foo))
  • Use when-not instead of (when (not ...) ...). [link]

    ;; good
    (when-not pred
      (foo)
      (bar))
    
    ;; bad
    (when (not pred)
      (foo)
      (bar))
  • Use when-not instead of (if-not ... (do ...). [link]

    Caveat: See if vs when above

      ;; good
      (when-not pred
        (foo)
        (bar))
    
      ;; bad
      (if-not pred
        (do
          (foo)
          (bar)))
  • Use not= instead of (not (= ...)). [link]

    ;; good
    (not= foo bar)
    
    ;; bad
    (not (= foo bar))
  • Departure: Use printf instead of (print (format ...)). But only if a simple (print (str "Hello " name)) won't suffice (for actual formatting) or has unacceptable performance issues. [link]

    Reason: format and printf both use positional arguments that easily blows out of proportion. It's easier to maintain (str name " " lastname " is " age " years of age.") than (format "%s %s is %d years of age" name lastname age) - even if age needs to be formatted, simply format it on its own rather.

      ;; good
      (println (str "Hello, " name))
      
      ;; ok
      (printf "Hello, %s!\n" name)
    
      ;; ok
      (println (format "Hello, %s!" name))
  • When doing comparisons, keep in mind that Clojure's functions <, >, etc. accept a variable number of arguments. [link]

    ;; good
    (< 5 x 10)
    
    ;; bad
    (and (> x 5) (< x 10))
  • Prefer % over %1 in function literals with only one parameter. [link]

    ;; good
    #(Math/round %)
    
    ;; bad
    #(Math/round %1)
  • Prefer %1 over % in function literals with more than one parameter. [link]

    Departure: Consider not using function literals when there's more than one parameter.

      ;; good
      #(Math/pow %1 %2)
    
      ;; bad
      #(Math/pow % %2)
  • Don't wrap functions in anonymous functions when you don't need to. [link]

    ;; good
    (filter even? (range 1 10))
    
    ;; bad
    (filter #(even? %) (range 1 10))
  • Don't use function literals if the function body will consist of more than one form. [link]

    ;; good
    (fn [x]
      (println x)
      (* x 2))
    
    ;; bad (you need an explicit do form)
    #(do (println %)
         (* % 2))
  • Favor the use of complement versus the use of an anonymous function. [link]

    ;; good
    (filter (complement some-pred?) coll)
    
    ;; bad
    (filter #(not (some-pred? %)) coll)

    This rule should obviously be ignored if the complementing predicate exists in the form of a separate function (e.g. even? and odd?).

  • Leverage comp when doing so yields simpler code. [link]

    ;; Assuming `(:require [clojure.string :as str])`...
    
    ;; good
    (map #(str/capitalize (str/trim %)) ["top " " test "])
    
    ;; better
    (map (comp str/capitalize str/trim) ["top " " test "])
  • Leverage partial when doing so yields simpler code. [link]

    ;; good
    (map #(+ 5 %) (range 1 10))
    
    ;; (arguably) better
    (map (partial + 5) (range 1 10))
  • Prefer the use of the threading macros -> (thread-first) and ->> (thread-last) to heavy form nesting. [link]

    ;; good
    (-> [1 2 3]
      reverse
      (conj 4)
      prn)
    
    ;; not as good
    (prn (conj (reverse [1 2 3])
           4))
    
    ;; good
    (->> (range 1 10)
      (filter even?)
      (map (partial * 2)))
    
    ;; not as good
    (map (partial * 2)
      (filter even? (range 1 10)))
  • Use :else as the catch-all test expression in cond. [link]

    ;; good
    (cond
      (neg? n) "negative"
      (pos? n) "positive"
      :else "zero")
    
    ;; bad
    (cond
      (neg? n) "negative"
      (pos? n) "positive"
      true "zero")
  • Prefer condp instead of cond when the predicate & expression don't change. [link]

    ;; good
    (cond
      (= x 10) :ten
      (= x 20) :twenty
      (= x 30) :thirty
      :else :dunno)
    
    ;; much better
    (condp = x
      10 :ten
      20 :twenty
      30 :thirty
      :dunno)
  • Prefer case instead of cond or condp when test expressions are compile-time constants. [link]

    ;; good
    (cond
      (= x 10) :ten
      (= x 20) :twenty
      (= x 30) :forty
      :else :dunno)
    
    ;; better
    (condp = x
      10 :ten
      20 :twenty
      30 :forty
      :dunno)
    
    ;; best
    (case x
      10 :ten
      20 :twenty
      30 :forty
      :dunno)
  • Use short forms in cond and related. If not possible give visual hints for the pairwise grouping with comments or empty lines. [link]

    ;; good
    (cond
      (test1) (action1)
      (test2) (action2)
      :else   (default-action))
    
    ;; ok-ish
    (cond
      ;; test case 1
      (test1)
      (long-function-name-which-requires-a-new-line
        (complicated-sub-form
          (-> 'which-spans multiple-lines)))
    
      ;; test case 2
      (test2)
      (another-very-long-function-name
        (yet-another-sub-form
          (-> 'which-spans multiple-lines)))
    
      :else
      (the-fall-through-default-case
        (which-also-spans 'multiple
          'lines)))
  • Use a set as a predicate when appropriate. [link]

    ;; good
    (remove #{1} [0 1 2 3 4 5])
    
    ;; bad
    (remove #(= % 1) [0 1 2 3 4 5])
    
    ;; good
    (count (filter #{\a \e \i \o \u} "mary had a little lamb"))
    
    ;; bad
    (count (filter #(or (= % \a)
                      (= % \e)
                      (= % \i)
                      (= % \o)
                      (= % \u))
              "mary had a little lamb"))
  • Use (inc x) & (dec x) instead of (+ x 1) and (- x 1). [link]

  • Use (pos? x), (neg? x) & (zero? x) instead of (> x 0), (< x 0) & (= x 0). [link]

  • Use list* instead of a series of nested cons invocations. [link]

    ;; good
    (list* 1 2 3 [4 5])
    
    ;; bad
    (cons 1 (cons 2 (cons 3 [4 5])))
  • Use the sugared Java interop forms. [link]

    ;;; object creation
    ;; good
    (java.util.ArrayList. 100)
    
    ;; bad
    (new java.util.ArrayList 100)
    
    ;;; static method invocation
    ;; good
    (Math/pow 2 10)
    
    ;; bad
    (. Math pow 2 10)
    
    ;;; instance method invocation
    ;; good
    (.substring "hello" 1 3)
    
    ;; bad
    (. "hello" substring 1 3)
    
    ;;; static field access
    ;; good
    Integer/MAX_VALUE
    
    ;; bad
    (. Integer MAX_VALUE)
    
    ;;; instance field access
    ;; good
    (.someField some-object)
    
    ;; bad
    (. some-object someField)
  • Use the compact metadata notation for metadata that contains only slots whose keys are keywords and whose value is boolean true. [link]

    ;; good
    (def ^:private a 5)
    
    ;; bad
    (def ^{:private true} a 5)
  • Departure: Simply don't have private parts of code. [link]

    Reason: There's a rant in here. But I'm not going to. Just don't bother with 'privacy' veneer. It's rubbish. Move along.

      ;; bad
      (defn- private-fun [] ...)
    
      (def ^:private private-var ...)
    
      ;; even worse
      (defn private-fun [] ...) ; not private at all
    
      (defn ^:private private-fun [] ...) ; overly verbose
    
      (def private-var ...) ; not private at all
  • To access a private var (e.g. for testing), use the @#'some.ns/var form. [link]

    Departure: Again. no privates, no worries.

  • Be careful regarding what exactly do you attach metadata to. [link]

    ;; we attach the metadata to the var referenced by `a`
    (def ^:private a {})
    (meta a) ;=> nil
    (meta #'a) ;=> {:private true}
    
    ;; we attach the metadata to the empty hash-map value
    (def a ^:private {})
    (meta a) ;=> {:private true}
    (meta #'a) ;=> nil

Naming

The only real difficulties in programming are cache invalidation and naming things.
-- Phil Karlton

  • When naming namespaces favor the following two schemas: [link]

    • project.module
    • organization.project.module
  • Use lisp-case in composite namespace segments(e.g. bruce.project-euler) [link]

  • Use lisp-case for function and variable names. [link]

    ;; good
    (def some-var ...)
    (defn some-fun ...)
    
    ;; bad
    (def someVar ...)
    (defn somefun ...)
    (def some_fun ...)
  • Use CamelCase for protocols, records, structs, and types. (Keep acronyms like HTTP, RFC, XML uppercase.) [link]

  • The names of predicate methods (methods that return a boolean value) should end in a question mark (e.g., even?). [link]

    ;; good
    (defn palindrome? ...)
    
    ;; bad
    (defn palindrome-p ...) ; Common Lisp style
    (defn is-palindrome ...) ; Java style
  • The names of functions/macros that are not safe in STM transactions should end with an exclamation mark (e.g. reset!). [link]

    Departure: This pretty much means almost everything.. So no, nice idea, but hardly actually applied, so apply when you have something you want to use in STM, else don't stress about it.

  • Use -> instead of to in the names of conversion functions. [link]

    ;; good
    (defn f->c ...)
    
    ;; not so good
    (defn f-to-c ...)
  • Use *earmuffs* for things intended for rebinding (ie. are dynamic). [link]

    Departure: But don't actually intend anything for rebinding (see earlier point about def vs alter-var-root) - It just makes things more complicated. Come on, don thinking cap, you can get a better result.

      ;; good
      (def a (atom 10))
      
      ;; bad
      (def ^:dynamic *a* 10)
    
      ;; worse
      (def ^:dynamic a 10)
  • Don't use a special notation for constants; everything is assumed a constant unless specified otherwise. Departure: Case closed about rebinding. [link]

  • Use _ for destructuring targets and formal argument names whose value will be ignored by the code at hand. [link]

    Departure: unless that code has relevant names that you don't want to lose scope of for maintenance down the road, and you'd have to reach further out to find what that meant.

      ;; good
      (let [[a b _ c] [1 2 3 4]]
        (println a b c))
    
      (dotimes [_ 3]
        (println "Hello!"))
    
      (let [[qty id] [1 3]]
        (println id))
      
      ;; bad
      (let [[a b c d] [1 2 3 4]]
        (println a b d))
    
      (dotimes [i 3]
        (println "Hello!"))
      
      (let [[_ id] [1 3]]
        (println id)) ;; No idea what the first arg was supposed to _be_ anymore.
  • Follow clojure.core's example for idiomatic names like pred and coll. [link]

    • in functions:
      • f, g, h - function input
      • n - integer input usually a size
      • index, i - integer index
      • x, y - numbers
      • xs - sequence
      • m - map
      • s - string input
      • re - regular expression
      • coll - a collection
      • pred - a predicate closure
      • & more - variadic input
      • xf - xform, a transducer
    • in macros:
      • expr - an expression
      • body - a macro body
      • binding - a macro binding vector

Collections

It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures.
-- Alan J. Perlis

  • Avoid the use of lists for generic data storage (unless a list is exactly what you need). [link]

  • Prefer the use of keywords for hash keys. [link]

    ;; good
    {:name "Bruce" :age 30}
    
    ;; bad
    {"name" "Bruce" "age" 30}
  • Prefer the use of the literal collection syntax where applicable. However, when defining sets, only use literal syntax when the values are compile-time constants. [link]

    ;; good
    [1 2 3]
    #{1 2 3}
    (hash-set (func1) (func2)) ; values determined at runtime
    
    ;; bad
    (vector 1 2 3)
    (hash-set 1 2 3)
    #{(func1) (func2)} ; will throw runtime exception if (func1) = (func2)
  • Avoid accessing collection members by index whenever possible. [link]

  • Prefer the use of keywords as functions for retrieving values from maps, where applicable. [link]

    (def m {:name "Bruce" :age 30})
    
    ;; good
    (:name m)
    
    ;; more verbose than necessary
    (get m :name)
    
    ;; bad - susceptible to NullPointerException
    (m :name)

    Departure: However, when the lookup is not a direct keyword (passed in arg, for instance), use get or get-in form

    (def m {:name "Bruce" :age 30})
    
    ;; good
    (defn a [b]
      (get m b))
    
    ;; bad
    (defn a [b]
      (b m))

  • Leverage the fact that most collections are functions of their elements. [link]

    ;; good
    (filter #{\a \e \o \i \u} "this is a test")
    
    ;; bad - too ugly to share
  • Leverage the fact that keywords can be used as functions of a collection. [link]

    ((juxt :a :b) {:a "ala" :b "bala"})
  • Avoid the use of transient collections, except for performance-critical portions of the code. [link]

  • Avoid the use of Java collections. [link]

  • Avoid the use of Java arrays, except for interop scenarios and performance-critical code dealing heavily with primitive types. [link]

Mutation

Refs

  • Departure: Avoid refs as much as possible, an atom will achieve most things that a set of refs could do in most cases.

  • Consider wrapping all I/O calls with the io! macro to avoid nasty surprises if you accidentally end up calling such code in a transaction. [link]

  • Avoid the use of ref-set whenever possible. [link]

    (def r (ref 0))
    
    ;; good
    (dosync (alter r + 5))
    
    ;; bad
    (dosync (ref-set r 5))
  • Try to keep the size of transactions (the amount of work encapsulated in them) as small as possible. [link]

  • Avoid having both short- and long-running transactions interacting with the same Ref. [link]

Agents

  • Use send only for actions that are CPU bound and don't block on I/O or other threads. [link]

  • Use send-off for actions that might block, sleep, or otherwise tie up the thread. [link]

Atoms

  • Avoid atom updates inside STM transactions. [link]

  • Try to use swap! rather than reset!, where possible. [link]

    Departure: The decision is based on intention - if you want to do an update based on the previous value, swap! is the correct approach (90% of the time) - if you actually want to clobber the value and the previous value is of no significance (you wouldn't check or change what you're doing based on it in any way), then reset! is correct, and should be used.

      (def a (atom 0))
    
      ;; good
      (swap! a + 5)
    
      ;; not as good
      (reset! a 5)

Strings

  • Prefer string manipulation functions from clojure.string over Java interop or rolling your own. [link]

    ;; good
    (clojure.string/upper-case "bruce")
    
    ;; bad
    (.toUpperCase "bruce")

Exceptions

  • Reuse existing exception types. Idiomatic Clojure code — when it does throw an exception — throws an exception of a standard type (e.g. java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException, java.lang.IllegalStateException, java.io.IOException). [link]

  • Favor with-open over finally. [link]

Macros

  • Don't write a macro if a function will do. [link]
  • Create an example of a macro usage first and the macro afterwards. [link]
  • Break complicated macros into smaller functions whenever possible. [link]
  • A macro should usually just provide syntactic sugar and the core of the macro should be a plain function. Doing so will improve composability. [link]
  • Prefer syntax-quoted forms over building lists manually. [link]
  • Departure: Really, don't use macro's unless you absolutely have to and then work hard to get the footprint as absolutely small as you can. Build a way to not have to use the macro's first, if possible, then sugar with macro's on top where needed.

Comments

Good code is its own best documentation. As you're about to add a comment, ask yourself, "How can I improve the code so that this comment isn't needed?" Improve the code and then document it to make it even clearer.
-- Steve McConnell

  • Endeavor to make your code as self-documenting as possible. [link]

  • Write heading comments with at least four semicolons. [link]

  • Write top-level comments with three semicolons. [link]

  • Write comments on a particular fragment of code before that fragment and aligned with it, using two semicolons. [link]

  • Write margin comments with one semicolon. [link]

  • Always have at least one space between the semicolon and the text that follows it. [link]

    ;;;; Frob Grovel
    
    ;;; This section of code has some important implications:
    ;;;   1. Foo.
    ;;;   2. Bar.
    ;;;   3. Baz.
    
    (defn fnord [zarquon]
      ;; If zob, then veeblefitz.
      (quux zot
            mumble             ; Zibblefrotz.
            frotz))
  • Comments longer than a word begin with a capital letter and use punctuation. Separate sentences with one space. [link]

  • Avoid superfluous comments. [link]

    ;; bad
    (inc counter) ; increments counter by one
  • Keep existing comments up-to-date. An outdated comment is worse than no comment at all. [link]

  • Prefer the use of the #_ reader macro over a regular comment when you need to comment out a particular form. [link]

    ;; good
    (+ foo #_(bar x) delta)
    
    ;; bad
    (+ foo
       ;; (bar x)
       delta)

Good code is like a good joke - it needs no explanation.
-- Russ Olsen

  • Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory. ("Do, or do not. There is no try." --Yoda) [link]

Comment Annotations

  • Annotations should usually be written on the line immediately above the relevant code. [link]

  • The annotation keyword is followed by a colon and a space, then a note describing the problem. [link]

  • If multiple lines are required to describe the problem, subsequent lines should be indented as much as the first one. [link]

  • Tag the annotation with your initials and a date so its relevance can be easily verified. [link]

    (defn some-fun
      []
      ;; FIXME: This has crashed occasionally since v1.2.3. It may
      ;;        be related to the BarBazUtil upgrade. (xz 13-1-31)
      (baz))
  • In cases where the problem is so obvious that any documentation would be redundant, annotations may be left at the end of the offending line with no note. This usage should be the exception and not the rule. [link]

    (defn bar
      []
      (sleep 100)) ; OPTIMIZE
  • Use TODO to note missing features or functionality that should be added at a later date. [link]

  • Use FIXME to note broken code that needs to be fixed. [link]

  • Use OPTIMIZE to note slow or inefficient code that may cause performance problems. [link]

  • Use HACK to note "code smells" where questionable coding practices were used and should be refactored away. [link]

  • Use REVIEW to note anything that should be looked at to confirm it is working as intended. For example: REVIEW: Are we sure this is how the client does X currently? [link]

  • Use other custom annotation keywords if it feels appropriate, but be sure to document them in your project's README or similar. [link]

Documentation

Docstrings are the primary way to document Clojure code. Many definition forms (e.g. def, defn, defmacro, ns) support docstrings and usually it's a good idea to make good use of them, regardless of whether the var in question is something public or private.

If a definition form doesn't support docstrings directly you can still supply them via the :doc metadata attribute.

This section outlines some of the common conventions and best practices for documenting Clojure code.

  • If a form supports docstrings directly prefer them over using :doc metadata: [link]
;; good
(defn foo
  "This function doesn't do much."
  []
  ...)

(ns foo.bar.core
  "That's an awesome library.")

;; bad
(defn foo
  ^{:doc "This function doesn't do much."}
  []
  ...)

(ns ^{:doc "That's an awesome library.")
  foo.bar.core)
  • Let the first line in the doc string be a complete, capitalized sentence which concisely describes the var in question. This makes it easy for tooling (Clojure editors and IDEs) to display a short a summary of the docstring at various places. [link]
;; good
(defn frobnitz
  "This function does a frobnitz.
  It will do gnorwatz to achieve this, but only under certain
  cricumstances."
  []
  ...)

;; bad
(defn frobnitz
  "This function does a frobnitz. It will do gnorwatz to
  achieve this, but only under certain cricumstances."
  []
  ...)
  • Document all positional arguments, and wrap them them with backticks (`) so that editors and IDEs can identify them and potentially provide extra functionality for them. [link]
;; good
(defn watsitz
  "Watsitz takes a `frob` and converts it to a znoot.
  When the `frob` is negative, the znoot becomes angry."
  [frob]
  ...)

;; bad
(defn watsitz
  "Watsitz takes a frob and converts it to a znoot.
  When the frob is negative, the znoot becomes angry."
  [frob]
  ...)
  • Wrap any var references in the docstring with ` so that tooling can identify them. [link]
;; good
(defn wombat
  "Acts much like `clojure.core/identity` except when it doesn't.
  Takes `x` as an argument and returns that. If it feels like it."
  [x]
  ...)

;; bad
(defn wombat
  "Acts much like clojure.core/identity except when it doesn't.
  Takes `x` as an argument and returns that. If it feels like it."
  [x]
  ...)
  • Docstrings should be comprised from proper English sentences - this means every sentences should start with an capitalized word and should end with the proper punctuation. Sentences should be separated with a single space. [link]
;; good
(def foo
  "All sentences should end with a period (or maybe an exclamation mark).
  And the period should be followed by a space, unless it's the last sentence.")

;; bad
(def foo
  "all sentences should end with a period (or maybe an exclamation mark).
  And the period should be followed by a space, unless it's the last sentence")
  • Indent multi-line docstrings by two spaces. [link]
;; good
(ns my.ns
  "It is actually possible to document a ns.
  It's a nice place to describe the purpose of the namespace and maybe even
  the overall conventions used. Note how _not_ indenting the doc string makes
  it easier for tooling to display it correctly.")

;; bad
(ns my.ns
  "It is actually possible to document a ns.
It's a nice place to describe the purpose of the namespace and maybe even
the overall conventions used. Note how _not_ indenting the doc string makes
it easier for tooling to display it correctly.")
  • Neither start nor end your doc strings with any whitespace. [link]
;; good
(def foo
  "I'm so awesome."
  42)

;; bad
(def silly
  "    It's just silly to start a doc string with spaces.
  Just as silly as it is to end it with a bunch of them.      "
  42)
  • When adding a docstring – especially to a function using the above form – take care to correctly place the docstring after the function name, not after the argument vector. The latter is not invalid syntax and won’t cause an error, but includes the string as a form in the function body without attaching it to the var as documentation. [link]
;; good
(defn foo
  "docstring"
  [x]
  (bar x))

;; bad
(defn foo [x]
  "docstring"
  (bar x))

Existential

  • Code in a functional way, using mutation only when it makes sense. [link]

  • Be consistent. In an ideal world, be consistent with these guidelines. [link]

  • Use common sense. [link]

Tooling

There are some tools created by the Clojure community that might aid you in your endeavor to write idiomatic Clojure code.

  • Slamhound is a tool that will automatically generate proper ns declarations from your existing code.

  • kibit is a static code analyzer for Clojure which uses core.logic to search for patterns of code for which there might exist a more idiomatic function or macro.

Testing

  • Departure: We don't have tests. If you implement tests, make them integration tests first. Make them run against a staging environment upon building. Specifc cnit tests tend to be a burden, and I delete tests that break just because the code is now behaving differently (but still correct) than the test expects.

  • Store your tests in a separate directory, typically test/yourproject/ (as opposed to src/yourproject/). Your build tool is responsible for making them available in the contexts where they are necessary; most templates will do this for you automatically. [link]

  • Name your ns yourproject.something-test, a file which usually lives in test/yourproject/something_test.clj (or .cljc, cljs). [link]

  • When using clojure.test, define your tests with deftest and name them something-test. [link]

    ;; good
    (deftest something-test ...)
    
    ;; bad
    (deftest something-tests ...)
    (deftest test-something ...)
    (deftest something ...)

Library Organization

  • If you are publishing libraries to be used by others, make sure to follow the Central Repository guidelines for choosing your groupId and artifactId. This helps to prevent name conflicts and facilitates the widest possible use. A good example is Component. [link]

  • Avoid unnecessary dependencies. For example, a three-line utility function copied into a project is usually better than a dependency that drags in hundreds of vars you do not plan to use. [link]

  • Deliver core functionality and integration points in separate artifacts. That way, consumers can consume your library without being constrained by your unrelated tooling prefences. For example, Component provides core functionality, and reloaded provides leiningen integration. [link]

Contributing

Nothing written in this guide is set in stone. It's my desire to work together with everyone interested in Clojure coding style, so that we could ultimately create a resource that will be beneficial to the entire Clojure community.

Feel free to open tickets or send pull requests with improvements. Thanks in advance for your help!

You can also support the style guide with financial contributions via gittip.

Support via Gittip

License

Creative Commons License This work is licensed under a Creative Commons Attribution 3.0 Unported License

Spread the Word

A community-driven style guide is of little use to a community that doesn't know about its existence. Tweet about the guide, share it with your friends and colleagues. Every comment, suggestion or opinion we get makes the guide just a little bit better. And we want to have the best possible guide, don't we?

Cheers,
Bozhidar

About

A community coding style guide for the Clojure programming language

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published