I was holding off on posting this, but read some other good blog posts which sort of went along similar lines to this so decided to post it now…
What is a framework?
Whenever you write a reasonably-sized application, you’ll probably end up writing some sort of bespoke “framework” in your application. When you start writing it, the idea sounds great up-front – you’ll write something that will save hours in future! No-one will have to worry about anything, the “framework” will take care of it all! You’ll talk about abstraction and encapsulation etc. etc.. and tell yourself it’s best practice to do it.
There’s just one problem with writing generic, catch-all frameworks – and that’s that more often than you think, it’ll be completely unnecessary. You’ll start by writing some business code. Then you’ll think to yourself, “Hmm, this bit of code seems like it’ll be useful to the team at some point in the future. Let’s write some helper classes and integrate it with our framework so other people can reuse this bit of code.”.
This is what is known as “horizontal development” i.e. developing frameworks and cross cutting concerns rather than developing business functionality.
The biggest problem with horizontal development is that often this code reuse never happens; you end up with abstract factories with just one or two types being returned, or whole class hierarchies being written when it would have been ten times quicker just to manually compose some classes together a few times. Even worse, sometimes you’ll guess wrongly how you’ll end up using the framework and end up boxing yourself into a corner and having rigid and viscous designs i.e. the framework blocks you from doing what you want, and you end up having to work around it.
Developing vertically is the idea of developing features end-to-end that cut across all tiers, and developing just the part of every tier that you need to fulfil that feature; as you develop more features, your framework should naturally and organically evolve. You’ll refactor existing business logic to take advantage of your improved framework to make your code more succinct and easy to understand within the context of the domain. Your unit tests will protect you from breaking changes in your framework as you refactor and enhance it.
How do we go about doing this? I tend to strongly follow the Uncle Bob school of thought here, plus the 80/20 rule. This boils down to the following: –
Do you know for a fact that this bit of code is going to be reused, right now, somewhere else? If the answer is “no” – don’t do it. Wait until you need it the second time and then consider doing it then. You’ll be surprised with how many times you end up leaving things as-is. Obviously, it’s not black and white – if it’s fifty lines of code I’d be more inclined to refactor it into a reusable method / class than if it were one or two lines.
There are many examples of this sort of situation that you might come up against every day: –
Having a constant in a method / class versus having a shared constants class / enum
Hard-coding something versus making some sort of generic / abstract factory
Using explicit / hard-coded values or classes versus abstracting it to the database (do you really need run-time configurability?)
Writing some screens that look similar versus writing a generic “control factory” that generates UI for you etc.
In all these cases I would choose the simplest, cheapest option and only if I could prove through the codebase that we would benefit from abstracting the code away would I do it.
There’s one exception to this rule: when the cost of doing it later will be excessively high and would require a lot of effort to do retrospectively. Obviously this decision requires a judgement call on your part on an instance-by-instance basis as to whether it’s a real risk or not, and what the likelihood of it happening is. In such situations, I would either absorb the cost up front, or try to protect myself against this sort of thing where possible e.g. in the example above relating to a factory, a decent halfway-house might be to make a factory class but hard-code the implementation to always return the same type of object, so that you can easily expand it later without impacting lots of places.
Whenever you are faced with this sort of situation, I always ask myself questions like: –
What’s the cost/benefit of doing this?
Are we ever actually going to use this in the future, or is it just “in case”?
How discoverable will my framework classes be?
Am I going to document the framework and make it easily available to the rest of the team – otherwise there’s little point in having it.
There’s always a cost of having duplicate code everywhere – but there’s also a cost of having a framework that’s unnecessarily complex.