Fork me on GitHub

I’m working on a spec for an asynchronous message format and am curious about versioning. My instinct is to declare a versioned namespace for the message envelope - e.g. :my.message.v1/schema, declare all my envelope fields in that same namespace, and then just make a new one if and when I realize the need to make an incompatible change to the envelope. It occurred to my colleague that multi-spec was another way to accomplish this by including the version as a value, e.g. :my.message.schema/version 1 and using multi-spec to choose the correct spec. Are both of these viable options and is there anything to recommend one over the other?

Alex Miller (Clojure team)03:09:31

I’d recommend not making incompatible changes

Alex Miller (Clojure team)03:09:43

then you don’t need versions :)

👍 12
kappa 8
Alex Miller (Clojure team)03:09:24

if the attribute needs to change, give the attribute a new name

Alex Miller (Clojure team)03:09:27

if you’re talking about adding attributes, that’s compatible. if you need to remove attributes from a map, then you should have a new name for the container, which I guess version is one kind of new name


Alas, my foresight is sufficiently imperfect that I don’t believe I can develop the correct message format for all time. Incompatible changes I believe might be reasonably desired include (and are probably limited to?): adding and removed required keys.

Alex Miller (Clojure team)14:09:17

well, I’ll refer you to Rich’s Spec-ulation keynote for the long form argument


what is an example of adding keys that is a breaking change @donaldball?


adding new required keys would be breaking


important word simple_smile


but yeah I think the gist is "don't make breaking changes" which I generally take to mean "if you need to break something, make a new something, and leave the old something alone"


if you need to add a new required key to some map, it's no longer the same thing. Make a new map spec with a different name that requires the new key


if it's for a message on something like Kafka, the new name might be an entirely new Topic


I agree that’s very much the route spec wants you to go down and I’m on board the train, but I am curious what makes the multi-spec idea a poor solution. It seems like it does constrain you forever to having a message be a map, and for the map to require the multi-spec keyword, but otherwise the map contents could vary as required by the versions.


how do I put a bound on how many times clojure.spec.test.alpha/check generates a test?


the documentation is very nebulous



(st/check `foo {:clojure.spec.test.check/opts {:num-tests 10}})

❤️ 4

>The opts map includes the following optional keys, where stc aliases clojure.spec.test.check >The ::stc/opts include :num-tests in addition to the keys documented by test.check.


it confused me at first too b/c test.check/quick-check takes the number of tests as its first argument


yeah it was completely meaningless to me


hm. even specifying {:num-tests 5}, my generative testing is taking minutes


in fact, specifying 0 takes minutes? I'm not even sure this is working


this works for me:

(defn foo [x] (str x))
(s/fdef foo :args (s/cat :x any?) :ret string?)
(st/check `foo {:clojure.spec.test.check/opts {:num-tests 1}})
(st/check `foo {:clojure.spec.test.check/opts {:num-tests 100}})


and the :num-tests is accurately reflected in the check result


I think some of my specs must be really expensive. is there a way for me to selectively narrow some of the child specs?


any recursive specs involved?


hmm hard to say w/o seeing all the relevant code

Alex Miller (Clojure team)15:09:55

Expensive to check or to gen?


I'm not sure 😞


I apologize, it's quite long. but I'm not confident what might be what's causing the slowness


definitely seems to be in the generation of :torch/fields


I'm going through and removing everything and slowly adding back in. adding back in

seems to slow it down, but not astronomically

Alex Miller (Clojure team)16:09:59

I meant what operation are you invoking when it’s slow?

Alex Miller (Clojure team)16:09:18

If you have coll-of, it’s good to set :gen-max to something small like 3


that did help! thanks!

Alex Miller (Clojure team)17:09:08

The default is 20 and that can get a little out of hand


how do I selectively narrow the values generated by stest/check?


e.g. if my spec has a named child-spec somewhere that is defined as keyword?, but I don't really care about testing 1000s of keywords


I think you can pass in an override map in check opts :gen


>:gen map from spec names to generator overrides