In my experience (which I think resonates with RH and most Clojurists) is that for the vast majority of _information-driven_ systems, types are used as the latter. If you have a `Customer` class that gives you guarantees about the availability of, say, an `accountNumber` field, that is useful for correctness as you can be sure the information you need is there. However, if some future downstream coder wants to use your customer in the more general sense of being a human, then (s)he has to worry about sub/super/abstract-classing, may have to make upstream modifications to expose previously hidden data, and similar faffage. In Clojure the idiomatic solution to this problem is to "just use a map"; the real-world downside to this, however, is extremely weak contracts between functions. In this example, what `spec` allows you to do is strengthen those contracts by verifying that the data your function is provided is sufficient for your uses (as in the map contains all the keys you'll need with suitable data types in the fields) _without_ constraining what downstream consumers can also do with this data (extra map keys are ignored).
These checks are only done at runtime and only when enabled, however writing a spec gives you (very-nearly-almost) free generative testing that will run your function a default of 1000 times with random data in the correct shape to make sure it doesn't blow up—this isn't a guarantee of correctness, but it does provide extremely high levels of confidence (most type systems are also not even close to guaranteeing correctness either, only compliance with the type system). Spec also gives you (for free) performant runtime coercion for use in actual real-world code. You get a lot of bang for your buck.
`spec` is definitely not a type system, but it very capably fulfils a similar role in the kinds of programs Clojure was intended to be good at. It gives you all the flexibility and dynamism of Clojure with most of the confidence of static typing, without constraining either.