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.
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:
defonce
for static values: For expensive computations that produce static results, use defonce
to avoid recomputationExample 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))))
Enter the URL of a public GitHub repository