I’m using spec to validate updates before saving to a DB - basically updated/created entities have to match the schema for their type before I attempt the actual db write. I found that this validation stage was slow, and made some changes to the specs and saw huge perf gains. I’m wondering whether there’s any guidance online for writing efficient specs, or whether anyone has any tips.
As an example, here’s the change I made, which in hindsight is pretty obvious.
;; Domain entities have an :entity/type key indicating their type.
;; It's a survey-building app, so the types are :entity.type/question,
;; :entity.type/survey, etc.
(defn constrain-type [t]
(fn [x] (= (:entity/type x) t)))
;; Initial version
(s/def ::question
(s/and
(s/keys :req [...])
(constrain-type :entity.type/question)))
(s/def ::survey-content
(s/or :q ::question
:p ::page))
;; Way faster version
(s/def ::question
(s/and
;; next two lines are swapped from initial version
(constrain-type :entity.type/question)
(s/keys :req [...])))
It makes sense that the second version is faster; ::question and ::page have similar specs, so putting constrain-type first makes it so we can rule out non-matches faster.
Thank you for the tip. This is pretty close to what I want, with the exception that there doesn’t appear to be any built-in way to check that a map matches the schema of a particular “subtype”. For example, here’s the schema for an art museum:
All of the above is good, but how do I check that a map describes not just an artwork, but a painting? As far as I can tell, we still have to resort to something similar to the example in my first post: