Robust Clojure: The best way to handle nil
Nil-checking is a form of input validation. Validation features often leak into helper functions lower down which don&#x27;t have the context or control to solve the source of a problem, where they don&#x27;t belong. Separation of concerns means assigning management of risk to the bearer of profit (the calling code), where it can &quot;fail&#x2F;retry&#x2F;failover&quot; fast, before leaking validation code into functions that could be pure.<p>Therefore, I prefer to hoist missing value handling up to the caller, akin to &quot;smart vs dumb components&quot;, where the caller has sufficient context and control to recover from errors. For example,<p>Instead of:<p><pre><code>    (defonce bus (ZeroMQ. &quot;some-url&quot;))  ;; this might be null, but it&#x27;ll leak into helpers that should only care about values

    (defn transmit! [bus payload]
      (if bus ;; why???
        (if payload) ;; gross
          (.send bus (zmq&#x2F;encode-string payload))
          (log&#x2F;error &quot;nil payload whoops&quot;)
        (log&#x2F;error &quot;something bad happens that the caller doesn&#x27;t understand and can&#x27;t do anything about&quot;)

    (defn tick! [ms]
      ;; athe wrong place to handle initialization problems
      (if-let [input (.read bus buffer-length)]  ;; multiple failure modes
        (transmit! bus [:some (inc input)])
        (comment &quot;do nothing&quot;)))
</code></pre>
Instead, handle initialization and input validation nearer the source and pass in system components explicitly to simplify mocking and testing:<p><pre><code>    (defn transmit! [^ZeroMQ bus payload] ;; type hint
      {:pre [bus payload]} ;; basic guards
      (-&gt;&gt; payload  ;; notice lack of nil-checking. you have to enforce contracts somewhere
           (zmq&#x2F;encode-string)
           (.send bus)))

    (defn init! [{:as config :keys [zmq-url]}]
      {:bus (ZeroMq. zmq-url)
       :redis ...
       :logger ...})

    (defn tick! [{:as system :keys [bus]} ms]
      (if-let [parsed (parse-input (.read bus buffer-length))]
        (transmit! bus [:some (inc parsed)]) ;; parse-input always returns valid data
        (log&#x2F;error &quot;it&#x27;s fine to handle runtime parsing errors here&quot;))

    (defonce !running (atom true))

    (defn -main []
      (let [system (init! {:some &quot;config-url&quot;})]  ;; attempt bus failover here
        (if-let [bus (:bus system)]
          (go-loop []
             (if @!running?)
               (do
                 (tick! (time&#x2F;now))
                 (Thread&#x2F;sleep 30)
                 (recur))
               (log&#x2F;info &quot;Shutting down...&quot;))
          (log&#x2F;error &quot;Retrying serial bus connection... .&quot;))))</code></pre>