We developed our own thing inspired by dry-schema and activemodel validations at Shopify, however decided to prefer Sorbet in the end mostly because of runtime performance concerns.
The drastically shortened feedback loop enabled by static analysis was another reason.
There was a bit of pushback against sorbet (from me, amongst others) because it is very verbose (virtually no inference) and restricts the language (no duck typing), but in the end it was, and I think still is, by far the most mature static type checker for Ruby. And that in itself is worth a lot.
We also used, on a few smaller (but still large) apps, a gem that would parse yard annotations and generate runtime type checks from them. That approach too has been mostly abandoned for the same reasons mentioned above.
because sorbet doesn’t have type inference across methods, its utility is greatly reduced if signatures are far apart in the call stack. So you kind of need signatures on most methods. That’s my main gripe with Sorbet. I would have loved to just use type signatures for all package APIs and then sprinkled over the rest of the code where they make sense.
e.g. if a method‘s signature specifies only that a parameter is to satisfy a certain interface, methods outside of that interface can’t be called on that parameter within that method. But then if that value is passed on to a method that doesn’t have a signature, we lose that type safety and all methods can be called on it.