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?
Maybe the solution to the problem is keeping the scope of the framework as focused and limited as possible and then use framework inheritance to widen the scope. JUnit does a great job here: the scope is very confined to unit testing of Java code, but HttpUnit builds on top of this a framework for testing servlets/JSP's. I believe in KISS (Keep It Simple, Stupid!) and there is no reason why a framework should be an exception.
PS: funny failed analogy from my draft post: It is a bit like having children: you have to stop them from going out and getting drunk and wasted on drugs, but lock them inside, limit their view of the world so they can develop in capable and responsible adults who can...
WebWork 1.x suffered from this kind of slow feature creep. As features were added, it became more and more complex to see the core framework.
With WebWork 2 we've refactored this to add Interceptors, which allow us to very easily add functionality without having to make it core to the framework. In fact, most of WebWork2 is implemented as interceptors and a Servlet on top of XWork, which is this simple platform-agnostic command pattern framework. It also allows our framework users to plug in their own interceptors to the command processing stack orthogonally to the Action code itself.
So are we looking at a point somewhere between "traditional" frameworks and patterns? Perhaps at a sort of super- or sub-pattern (depends on your point of view) that delivers just enough of the solution for developers to use, hopefully without constraining them in using it. What would be a good name for that?
> So are we looking at a point somewhere between > "traditional" frameworks and patterns? Perhaps at a sort > of super- or sub-pattern (depends on your point of view) > that delivers just enough of the solution for developers > to use, hopefully without constraining them in using it. > What would be a good name for that?
I was throwing the around the name "seedwork" but I don't know if it has legs. I like what you are saying about patterns. This is like giving someone the source for a pattern and saying "okay, you use this as your base." It's a little different in that design patterns are rather generic. But, I could imagine doing this with something like the three or four classes you need for a testing framework. "Here it is, use this as your base."
> I was throwing the around the name "seedwork" but I don't > know if it has legs. I like what you are saying about > patterns. This is like giving someone the source for a > pattern and saying "okay, you use this as your base." > It's a little different in that design patterns are > rather generic. But, I could imagine doing this with > something like the three or four classes you need for a > testing framework. "Here it is, use this as your base."
yeah, what Mik said -- how about something in between source code and a design pattern! what's the problem with a pattern that describes a very specific, a non-generic solution? the analogy that comes to mind is a fabric pattern -- you can follow the pattern with the fabric of your choosing and completely duplicate something, or you can also add a little flair here and there where it suits you, and no one is going to complain. but I don't see it as a pure project design document, because it's more of a plan, a recipe, where you can adjust things to your needs or taste.
fwiw, I agree about frameworks. they're great, as long as you've written them and brought them along. and I think it takes until version 3 when they finally start paying off. learning some one else's framework can be painful. and a lot of the time you might be thinking that there are better ways of accomplishing that generic task, and better ways of letting you accomplish your specific task. so you can spend your time adapting the framework to your project or the other way around, and hope it does not impact the deadline.
This stuff is too interesting to not have any practical use :-)
For me it adresses a very real problem that developers face who are coding beyond the framework or who are (re)defining one. The most successfull frameworks offer to the user a very limited amount of classes that encapsulate a technical domain model of the framework. And even then, we could take this idea even further:
Imagine that the guys at Jakarta would have started Struts with building/defining a seperated, very simple, MVC model, and then adding the rest of the functionalities as extension-frameworks...
There is a difference with normal object inheritance and "pattern level" loose coupling: the Action and FormBean classes are not a real seedwork as they already imply and structure the rest of the framework. They also don't offer themselves as interface to the framework, but rather as templates. It is probably most similar to using components, but on a sub-framework level...
In the end it all has to do with interfacing with the user, even if he is a developer: we have to find a good way in between being too simplistic and too technical, to explain our framework-API to him: a seedwork may be a good way to do this and can at the same time keep our development efforts focused.
I'm kinda taking an argumentative stance here: I would say if you've reached the point where you need to create a descriptive language to describe the framework API, then there's a good chance you've wasted time and effort. This is not to say that the documentation is wasted, but that maybe too much time was spent solving the meta-problems instead of the problems at hand. I do think frameworks are useful, but when they're really useful, they're either really generic or really specific. but at that point, I think those are products in and of themselves, and have then crossed into another (higher?) realm of existence. And for those products, such documentation would be helpful.
But I think those big frameworks are actually the exceptions -- most frameworks, I would wager, end up in proprietary projects, or used by a small number of people. And probably most don't need the extensibility of a full-blown component plug-in model and such. And I think there are a lot of framework-type patterns, which can be described by a set of classes, a seedwork. But can't we rip off the GoF and compile a set of framework patterns and describe them with pidgin URL and the seedworks? And while I think there are a lot of different frameworks, I think there's a lot of similarities between them.
> "seedwork" is a pretty good name. It should be quite > interesting to go into a bit more detail in this subject. > How could we define a seedwork, and how does it relate to > a framework and a pattern?
Here's an attempt at a definition...
A seedwork is a framework that: 1. Has tests for each class 2. Consists primarily of concrete classes 3. Can be explained in an hour 4. Comes with a promise that it will not be expanded or factored further.
A note about (2). We are getting to the point in the Java community that you can show someone a method that uses 'new' internally in a class and say "Okay, if you extract a method around that, you can override it in subclasses, the tests will support you. Or, "when you start to have too many classes dependent on this class, extract an interface, the tests will support you."
It is interesting how the idea of spreading seedworks falls in line with the notion that it is better to integrate smaller changes rather than larger changes.
A pretty nice definition, although we should maybe add something on the problem field it is meant to be applied to...
What about some action?
We could try to define one seedwork, and derive from that some definitions / best practices / usage patterns to find others. For that we would need a project place (what about http://www.java.net ?), a nice example problem space (hmm not completely sure about that one) and some man days (:-))
Well, let me know if anybody is interested, then I'll do the initial kickoff / setup.
There are two forces pulling on a framework. From above there is feature creep pulling the framework into a larger and larger scope. From below there are portability issues pulling the framework into creating infrastructure.
C++ suffers badly from the latter because of its poor support for basic types and non-existant support for basic things such as threads. The result is that each framework has to define their own fixed-sized integers, string objects, and thread classes. Using two or three such frameworks together is a nightmare.
Perhaps if the underlying runtime is rich enough and stable enough (AWT vs Swing!), then frameworks can work. That's the theory at least. I would be interested in hearing examples of framework troubles in Java. Surely it's runtime is rich enough to avoid the infrastructure hassles of C++?
I love this. It is a thought that always occurs to me when looking at a framework (actually the thought is "Oh s**t, how the hell will ever get to grips with this!).
I think it parallels a move among some agile developers from inheritence to encapsulation. The latter allowing dependencies and tests to be managed more effectively.
I think your checks for a seedwork could be extended with (something like):
"On deployment it can carry out its designed function"
i.e. it can run all its tests but it can also carry out its function (at least in simple example form). I put together a very simple model-2 servlet framework using a command pattern to handle actions. On deployment it had a couple of jsps and handler objects. That way everyone who got it could see it in action, which boosted their confidence and saved quite a lot of explanation.
Cameron & Tracy Hughes in their book Object-Oriented Multithreading Using C++, mention that the lack of support for threads in C++ is benificial. This way you are not limited to the architecture created by the language designers.
In the book they develop OO wrappers for thread & syncronization primitives. These wrapper classes can be used as your seedwork for multithreaded applications. You might want to extend this using some of the patterns documented at: http://www.cs.wustl.edu/~schmidt/patterns-ace.html by Douglas Schmidt.
My frameworks within frameworks idea that I mentioned on the Wiki, would have the problem with becomming more complex with time. Changing this idea to include the seedworks idea, I would now have libraries of independent seedworks. At the end of each project you would then extract any useful ideas from your libraries and distil them into seedworks.
This seems to be the same conclusion that drove me to write http://www.sf.net/projects/megg (some documentation at http://www.javanicus.com/blog2/items/41-index.html [not much as it is so simple to use :)] ), with the basic drive of doing almost the same thing in each project, and wanting to accelerate that initial coding step. It is using Velocity for code generation of what I guess is a seedwork. (And it even generates it's own tests)
Flat View: This topic has 15 replies
on 2 pages