Unfortunately I only made it half way through. The message seemed to be:
1. Package dependencies well.
2. Use contracts to define what is in a package.
Was there another point to the first half?
Were there additional points in the second half?
There was a fair amount about this transitive dependency problem: Suppose you have symbols b1 and b2 in package B. And b2 depends on some symbol in package C.
Then application A uses symbol b1 but NOT b2, so it must include package B, and thus package C, because dependencies are static and coarse grained. The application doesn’t actually USE anything in C – it’s just a bunch of dead code.
I’ve hit this problem in various contexts, and thought about it a fair bit. And it pretty much went away when I started structuring applications with dependency injection (which I’m not sure is idiomatic in Clojure, and maybe that’s part of the problem.)
With dependency injection, the rule is: libraries don’t depend on other libraries. Applications depend on libraries. Your main() function has 100 imports, and 100 instantiations, and wires everything together dynamically.
Now this is a caricature, because sometimes you genuinely have free functions like math.sqrt() or whatever… in that case, libraries can use libraries. But those kinds of basic libraries usually have few dependencies themselves. There should be a limit to the depth of the hierarchy
So my philosophy is not to organize your application as a pyramid of libraries. That is an anti-pattern – the higher the stack is, the more fragile it is. Instead, use more of a flat structure, where the ACTUAL dynamic dependencies are specified in main().
God this was rambling :(
Talks a bit about variance and how it impacts library ecosystems. Charges the Clojure community to solve it. Sounds shortsighted in a few different ways.