Avoid performing expensive computations until they are actually needed, and eliminate redundant work in hot code paths. This includes delaying database pulls, moving expensive object creation outside functions, and removing unnecessary operations.

Key strategies:

Example of delaying expensive operations:

;; Bad - always pulls data even if not used
(let [full-data (d/pull db pattern entity-id)]
  (if error
    (throw (ex-info "Error" {:data full-data}))
    (process full-data)))

;; Good - only pull when needed
(if error
  (let [full-data (d/pull db pattern entity-id)]
    (throw (ex-info "Error" {:data full-data})))
  (process (d/pull db pattern entity-id)))

Example of hoisting expensive computations:

;; Bad - recreates function on every call
(defn remove-nils [nm]
  (into {} (remove (comp nil? second)) nm))

;; Good - create function once
(def remove-nils
  (let [f (comp nil? second)]
    (fn [nm]
      (into {} (remove f) nm))))