Skip to content

Instantly share code, notes, and snippets.

What would you like to do?


Stuart talking about them as part of the Day of Datomic videos:

Simple data structure

Simple data structure for anomalies.

(s/def ::category #{::unavailable

(s/def ::message string?)

(s/def ::anomaly (s/keys :req [::category]
                         :opt [::message]))


Fixed set of anomaly categories

Part of the secret sauce is that there is a finite set so you can confidently cover all cases in your code.

Table of categories and what they mean.

category retry fix song
:unavailable yes make sure callee healthy Out of Touch
:interrupted yes stop interrupting It Doesn't Matter Anymore
:incorrect no fix caller bug You'll Never Learn
:forbidden no fix caller creds I Can't Go For That
:unsupported no fix caller verb Your Imagination
:not-found no fix caller noun She's Gone
:conflict no coordinate with callee Give It Up
:fault no fix callee bug Falling
:busy yes backoff and retry Wait For Me



Useful helpers for creating anomalies and testing for them...

(def ^:dynamic *default-category* ::fault)

(defn valid-category?
  "Checks if given category exists in the list of categories"
  {:added "0.1.0"}
  (s/valid? ::category cat))

(defn anomaly?
  "Checks if given value is anomaly"
  {:added "0.1.0"}
  (s/valid? ::anomaly x))

(defn anomaly
  "Creates new anomaly with given category(defaults to ::fault) message(optional) and data(optional)"
  {:added "0.1.0"}
  ([] (anomaly *default-category* nil nil))
     (valid-category? cat-msg-data) (anomaly cat-msg-data nil nil)
     (string? cat-msg-data) (anomaly *default-category* cat-msg-data nil)
     :else (anomaly *default-category* nil cat-msg-data)))
  ([cat-msg msg-data]
     (and (valid-category? cat-msg) (string? msg-data)) (anomaly cat-msg msg-data nil)
     (valid-category? cat-msg) (anomaly cat-msg nil msg-data)
     (string? cat-msg) (anomaly *default-category* cat-msg msg-data)
     :else (anomaly *default-category* cat-msg msg-data)))
  ([cat msg data]
   {:pre [(valid-category? cat)
          (or (nil? msg) (string? msg))]}
   (cond-> {::category cat}
     (some? msg) (assoc ::message msg)
     (some? data) (assoc ::data data))))

(def busy (partial anomaly ::busy))
(def busy? #(= (::category %) ::busy))
(def conflict (partial anomaly ::conflict))
(def conflict? #(= (::category %) ::conflict))
(def fault (partial anomaly ::fault))
(def fault? #(= (::category %) ::fault))
(def forbidden (partial anomaly ::forbidden))
(def forbidden? #(= (::category %) ::forbidden))
(def incorrect (partial anomaly ::incorrect))
(def incorrect? #(= (::category %) ::incorrect))
(def interrupted (partial anomaly ::interrupted))
(def interrupted? #(= (::category %) ::interrupted))
(def not-found (partial anomaly ::not-found))
(def not-found? #(= (::category %) ::not-found))
(def unavailable (partial anomaly ::unavailable))
(def unavailable? #(= (::category %) ::unavailable))
(def unsupported (partial anomaly ::unsupported))
(def unsupported? #(= (::category %) ::unsupported))

(def message ::message)
(def data ::data)
(def category ::category)


Translating HTTP status codes

Useful helpers for translating HTTP status codes into anomalies

(def status-codes->anomalies
  {403 ::forbidden
   404 ::not-found
   503 ::busy
   504 ::unavailable})

(defn status-code->anomaly [code]
  (or (get status-codes->anomalies code)
      (if (<= 400 code 499)


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment