A few Python blog entries I’ve happened across of late have brought up the topic of frameworks versus libraries. This article by Guido van Rossum questioned differences between the design of libraries and frameworks. ‘Why Frameworks Suck’ by Andy Smith is an outright rant (who’ve guessed from that title? ;-) against frameworks.
While reading both of these, I was hit by a cognitive dissonance: I’ve both written and used frameworks that have been fantastic tools. Yet, having grappled with J2EE (cough), I certainly understand Andy’s pain. This raises a question: why do some frameworks suck, while others are a blessing?
Sins of Framework Design
For my purposes, I’ll define a framework as utility code that calls your code. This stands in contrast to a library, which typically presents a self-contained collection functions that your code calls. The differences between a library and a framework are a matter of what kind of abstraction is provided and what kind of problems they each solve.
In my experience, the biggest sin in framework design is overreaching: a framework that simply tries to do too much. Stuffing one framework full of too many use cases is an easy recipe for disaster. The whole thing rapidly becomes too brittle and too big for developers to easily assimilate.
Here’s where I get to take one of my own lessons learned as an example. I wrote a framework a few years back that facilitated load balancing of compute tasks in a multi-processor embedded system. The need for this framework became apparent when the developers writing the to-be-distributed applications ended up creating different ad-hoc synchronization code for each new task. Each implementation ended up having different subtle bugs in it, and it quickly became apparent that this approach was unmaintainable. I was able to refactor the whole mess and provide an interface which the dev teams could write to without explicit synchronization concerns in their code. All the sync code was lifted into the framework, where it was written and debugged once for all of these applications — a huge win.
Alas, this framework was written at a startup under the influence of one too many Death Marches. The result of this was that the synchronization tools were too strongly tied to a larger framework with other concerns (e.g. state machines for the most common problem of our early implementations).
A Way to Better Frameworks
I’m increasingly of the opinion that minimalism is a sound rule of thumb for framework design. Don’t let frameworks grow into big monolithic entities. Instead, think in terms of a collaboration of micro-frameworks and libraries — work in the smallest and least-intrusive units that solve the larger problems at hand.
My hypothesis is that by keeping your frameworks small, they will provide fewer unnecessary constraints to their use (and to developers), they’ll be easier to refactor, and will have a higher likelihood of being reusable. This view provides an interesting answer to this complaint against the definition of a framework as “code that calls your code” :
The problem with this definition is that “map” counts as a framework.
Not as useful in a language where functions can be passed around like data.
I’d argue that this only indicates that higher-order programming languages (where functions can be passed like data) make it much easier to create tiny, reusable framework-like code. While I wouldn’t call “map” a framework either, we’re used to equating “framework” with “large” or “bloated”, which need not be the case.
I’m a firm believer from experience that frameworks are an indispensable tool of software design. That said, it’s far too easy for a software designer to make the scope of a framework encompass the entire the problem domain du jour. In this way, we end up with overgrown, tightly coupled frameworks for enterprise web platforms, embedded whatsits, and so forth. Remember: when thinking framework, think small.