One step too many, or how to mess up your library
Say, you’re writing an open-source package which is going to do some pile of common tasks in applications of many developers (probably including you). For example, something for REST servers, just because my last frustration on the subject was due to a REST framework. Okay, so you’re writing these functions useful for REST: handling serialization, authentication, routing, headers, versions, et cetera. There really are lots of things you have to care about in REST, because REST is easy to get wrong, so your collection of functions and classes would come handy.
Now you have some modules: one with a couple of classes for Authentication, and another with functions to serialize everything to any format out there and back, and another with whatever else REST needs to be proper. The library really seems comprehensive. You’re writing some docs, and the library slowly transforms from comprehensive to awesome. You’re enjoying pleasant thoughts about how everyone will like the library after it gets released.
Finally, you’re taking a good look on your library as a whole and think: hey, it handles, like, everything you need in REST! With this thought in mind, you’re writing a function which (as you think) will save everyone a day or two. This function calls everything in your library in some order, like this:
def totally_handle_a_request(request): authenticate_request(request) route_request(request) unserialize_stuff(request) do_something_what_should_be_done(request) serialize_stuff(request)
And I dare say that this is the worst decision you can make as a library developer.
Because somebody is going to think that this function is the only right way to use your library. And somebody is going to see how everything in the library plays together and think that it has to play together this way and can’t be used separately. And somebody is going to write a patch for you, or a plugin, or something else with such harmful thought in mind, and now all the nice components are co-dependent and co-required and the idea that this opressor function is the ruler suddenly becomes real. And from this point one can’t just use a serializer only, or replace a router, or get rid of all your authentication module. And one is going to want to do it, because authentication is so hard to get right from the first time, but, you know, all depends on everything in your library so noone can’t bend it his way.
In other words, your nice library becomes a framework. So much harm from one little function!
The point is that the person who’s going to assume all these assumptions and turn them into reality might as well be you. It turns out what it’s usually you, I mean the prospective creator of a nice library, who turns library into the framework just because he thinks it can save him a day.
I’m not a framework hater. I happily use a couple of them. But it’s really hard to write a proper framework: extensibility hurts. It’s comparatively easier to write a library: just throw all the functions into it, and for God’s sake let them be used by themselves. It’s pretty easy in most cases. For example, if you don’t ask your users to subclass things, they have one fewer problem with interoperability to worry about1.
You don’t have to have a deep reason to write library. You’re just writing library to make everyone happy! But you have to have an excuse for transforming a library into the framework.
Not only frameworks
Frameworks aren’t actually the only bad thing you can transform your library into. One of the popular ideas for killing a library is making a wizard.
The problem is exactly same: you’ve written a lot of functions for doing useful stuff, and now you think that you know exactly the right way to use it. It’s easy to think so, huh: it’s you who have written all these fine functions in the first place!
So you go ahead and instead of releasing it as a library make a web interface for it, so one can, step by step, do something useful. For example, I don’t know, setup a server! Yeah, setting up a server with a wizard. Ouch.
Your users are smart guys, and they can write some code to rule them all.
def setup_me_some_nice_server(): install_nginx() install_also_a_postgres() dont_forget_backups()
Really, if your library is good everyone will be happy to write a function like this. Why steal all the fun? Write better docs instead.
Subclassing is great for extensibility. But not subclass-as-a-configuration. Really.↩
Please send your comments, ideas, rants and job offers at firstname.lastname@example.org.
Made with Nginx, Jekyll, Git, EC2 and Emacs.