Michael Feathers thinks about frameworks. They're supposed to be nice and reusable, but more often than not they're hard to use and the abstraction that was supposed to get you reuse gets you headaches instead. Sad examples include the Java IO system, and e.g. the Xerces parser - and these are only small, local, limited libraries. The really big, hairy ones (e.g. J2EE) are much worse than that and basically inaccessible without framework specific tooling to handle the heavy lifting.
Of the things I have used and worked on, a couple of rules apply
- Keep simple things simple. Generality must never get in the way of the simplest possible gain from the framework. If you cannot begin to use the framework in less than 10 lines of code the entire thing is sunk. (First draft said 30 but even 10 is actually too much).
- Reuse first - generalize later. There's an engineering discipline that is sadly nonexistent: Source code usability. I mean actual usability studies, where developers are given an API cold and studied as they struggle to apply the ideas of the API. In lieu of that, we'll have to make do with actual use in actual situations. The only abstractions I find I keep using are the ones that have arisen out of actual uses, the ones that just keep on coming up and proving themselves. Abstractions for rare problems are more trouble than they're worth.
- Spoil your users with examples and facades. While good frameworks are crisp and terse and reuse a few core abstractions a great deal, nothing is more damaging for reuse than a steep learning curve. This advice is sort of an intersection of the first two points: Once you've identified the crispy concept that really drives your framework, that should be the only thing you leave in use, but by supplying samples usable as solution templates or by making facade interfaces for common problems, one gets an easy way into the world of the framework. What I have in mind is something like the best module synopses on CPAN. Most of the time the module doc is simply 'that thing everybody wants to know how to do' as an example. So for HTTP::Daemon it is 'the worlds simplest daemon' - SOAP::Lite is admirable in this respect as well.
Actually, point no. 2 comes with a caveat which probably says more about my particular style of thought than about the Principles of Good Software: When faced with a bulky but simple one-off development task I find that I prefer to 'design' my way out of the problem rather than just doing the work in a straightforward but possibly tedious fashion. I prefer to write a use-once framework that solves the problem indirectly. This has advantages and disadvantages. The main advantages are a) that I'm not bored - and bored people tend to write worse software and b) when my solution fails it does so in an 'interesting' easily spottable way. I find that I can have greater confidence in the final solution once there are no obvious flaws.
The main disadvantage is when my intuition on the cost of the use-once framework is wrong and I find myself heavily delayed, doing work of little immediate value and no lasting value.