Let's Reconsider That

Stunting a Framework

by Michael Feathers

July 31, 2003



Summary

Ever download a framework and feel overwhelmed? Maybe there is a way around that.


I ended up creating a little framework the other day. I was working on a set of classes to solve a problem and then bang! All of a sudden I saw how I could extend them to solve a whole set of problems. "Wow," I thought, "if I package these classes nicely and put them someplace conspicious I'll be famous. People will download the framework and extend it. They'll sing my praises far and wide... and.. and.. I'll get real email again!"

I sat at my computer with a beatific grin. Then, the hammer fell. "Yes," I thought, "I will be known as a great framework developer... a framework developer... let's think about great frameworks... erm.. there aren't many are there? In fact, even the good ones are a pain in the ass, aren't they? There was that time when we downloaded framework X and it took quite a bit of time to learn how to use it, and that other time when we thought it would be useful if only it did that, but we spent the next week trying to force it and.."

I'm sure many of you have been in the same situations. Framework development is hard, framework extension is hard, and framework use is hard, yet we keep trying. Why do we do it? Mainly because we want to create leveragable work and leverage the work of others. If we use someone's else framework, we may save some time. Moreover, we've benefited from someone's "crystalized thought," thought in the form of working code. The code shows a proven way of doing things and if it's well-designed it can accomodate our thoughts and we can roll them back to the community.

Sounds good, eh? How does it go wrong?

Many frameworks start out simple. Someone has an idea, they code up a few classes and then tell their friends about it. People download the code and say "hmmm... if only this method was factored this way, I'd be able to subclass and do X." Emails fly to the framework developer and invariably, the code becomes more complicated. What started out as three or four classes becomes a dozen or two. New users have to scramble over a couple dozen classes to see what makes them tick. When you unfocus your eyes a bit, you can still see those simple classes, but only if you remember them from the first version.

The task for the framework developer is no easier. He has to make sure the changes he makes don't impact the users. By necessity, he'll be conservative; there will be directions that the code can go easily and directions it can never go because it will break too many clients. What we're left with is a lot of frameworks which extend into a particular set of problems very well, but then drop off like a continental shelf when we wade away from that set.

The sad part about these framework problems is that we cause them by trying to make frameworks more useful.

"Can we get the framework to do this? Yes, but we have to separate these concerns, break this class in two.. there it's done. Uh-oh, it's a little harder to understand now, but it is useful, let's roll the change in."

Compare that to...

"Can we get the framework to do this? No, we'd have to put in a template method here and generalize this... and now the method names are all wrong, but if we change them we break all clients so no, we're never going to let the framework do that."

It's hard to win. Framework developers try to write classes that don't have to change when new features are added, and often that works, but each time that it doesn't, the framework gets a little more baroque and a little harder to understand. Over time frameworks show their age, but maybe there is a way around that.

A few years ago, I ported JUnit to C++. It was a port with high allegiance to the original design, it treated C++ as if it were Java and it used the full arsenal of C++ features: templates, exceptions, the "standard" library, etc. I released it and the email started coming. "We like your CppUnit code but we have a problem, we can not use exceptions in our environment." Another: "CppUnit is great, but we are running in an embedded system and we can not use new and delete." And another: "On our team, we can't use the standard library, we use string and collection classes from vendor X."

From that experience, I learned a couple of things. I learned that the largest cross platform compatible subset of C++ is C. I also learned that it was better in many cases to tell someone how to solve a problem in their local copy of the framework than it was to try to roll the solution into everyone's code. Sure, as soon as they modified their local copy, it became "their" framework, but the problem was solved. And, in fact maybe it was better that it was "their" framework. It occurred to me that if I made a promise that I would never again update the framework, users could really own the framework. It's growth would be stunted, but since less is more, it would remain highly flexible and above all, easy to understand. Instead of sharing full sets of framework source, people can get together and share tips on refactoring and extending their copies of the original simple seed. I've tried this out with a brutally stunted framework called CppUnitLite and it seems to be working fine.

Does all of this sound drastic? Maybe it is, but I think that the idea is applicable more often than it would seem. Take a second to think about all of the frameworks you've used. How many would have been better off if they were stunted at, say, the second release? If you still had the second release and you knew there wasn't going to be a third would you be happy growing that framework? Would you feel better downloading that version and trying to understand it than you would the current release? And, would you feel better knowing that the framework you get is a gift, something you can own, refactor and use without having your code stepped upon by later versions?

There are some frameworks that I always want the latest and greatest version of, and there are some that I just want the core of; I take the latter and grow them as I need to. Right now, I don't know how I decide to take each tack, but I do know that there aren't many of these officially stunted frameworks around. If I had to write down some criteria for them, I'd say that they should be small enough to explain in an hour and after that hour you should feel comfortable changing them. When a framework is that small and understandable it is easier to adopt. For teams that need leverage the most, a very short learning curve can make all of the difference in the world.

It's something to think about.. The next time you are tempted to write and distribute a framework, run a little experiment. Imagine the smallest useful set of classes you can create. Not a framework, just a small seed, a seedwork. Design it so that it is easy to refactor. Code it and then stop. Can you explain it to someone in an hour? Good. Now, can you let it go? Can you really let it go?

Talk Back!

Have an opinion? Readers have already posted 15 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Michael Feathers adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Michael has been active in the XP community for the past five years, balancing his time between working with, training, and coaching various teams around the world. Prior to joining Object Mentor, Michael designed a proprietary programming language and wrote a compiler for it, he also designed a large multi-platform class library and a framework for instrumentation control. When he isn't engaged with a team, he spends most of this time investigating ways of altering design over time in codebases.

This weblog entry is Copyright © 2003 Michael Feathers. All rights reserved.