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.
I should actually qualify that by saying the first version of it is almost released. I blogged about Code First what seems like ages ago, and it’s great to see a version of it getting out there. However, this version won’t ship with several features – some of which are in the current model- and database-first scenarios, and some of which aren’t – for example: -
No SP support
No compiled queries
No enum support (as per the rest of EF)
No user-defined mapping conventions
All are on the roadmap – but in the interests of getting it out there, they’ve been pushed back to version 2. The feedback on one of the announcement pages has been pretty negative – I can’t understand why. Yes, the work isn’t complete yet. Yes, there are features that need to be added before everyone’s happy. In fact, there’ll always be someone who doesn’t like it, but hey….
What is important IMHO is that the ADO .NET team are adopting a more community-based approach – get smaller iterations of code out there and get feedback quickly. EF3.5 was OK but not much more – EF4 went a long way to improving the state of affairs; EF4.1 adds a brand new class-based approach without any EDMX etc. – give them some time and I’m sure it’ll have all the features that we want. Otherwise, they’d never release it. Just look at Firefox 4 – a great browser, but it took so long to get out there due to (IMHO) large scope creep and poor roadmapping.
My personal wish list feature would be to see better integration between EF’s model-first approach and DB projects. Currently, once you go live with a database, you effectively have to move over to database-first modelling since EF offers no integration with DB projects. I’d love to see EF modify tables and such in a database project once you’ve modified your model.
So, EF4.1 might not do everything you want. But at least it’s out there – we can try it out with full support from MS (or as much as MS Connect offers….) and go from there.
A slight off-topic post today. I want to talk about pragmatic software design and development – the idea that when you implement a feature, before you start it ask yourself some questions: -
What are the customers / users expectations for this feature?
What is the bare minimum I need to do to implement this feature?
What impact will this feature have on the existing system?
There’s always a difference between customer and developer expectations of a feature. As developers, we think of user-facing features in terms of not only what the core feature is – say an Add New Customer – but also in terms of other cross-cutting concerns. Will there be validation on the screen? Will there be tooltips to offer the users help? A “please wait” dialog that opens on a background thread whilst communicating with the server? Caching some data on the UI so that you don’t have to make repeated calls to the database for performance reasons?
All of these are things that the customer probably hasn’t thought of when asking to be able to add a new customer to the database. Sometimes, they’ll be expecting them, others not. Sometimes they’ll be expecting features that you won’t have thought of. This is all part of normal requirements gathering. What you need to be aware of though is that every extra feature you add to the customer requirement changes their expectations going forward. These little “extras” all add up – this takes up time for you, and cost for the customer – cost that they might have rather spent on doing any number of other features.
As a developer you might want to deliver the “best” solution for the customer, adding in lots of features that you think should always be delivered. If you are able to deliver them for free to the customer, at a time that suits them, great. On the other hand, if you’re charging them for time that you’re spending on a project, give them the simplest option first. Spend time on writing well written code that adheres to SOLID principles where appropriate. Write unit tests. Then let the customer see it and mention at that point that it doesn’t include validation / tooltips / caching etc. – and give them an idea of how long it would take to deliver that. This doesn’t mean that you don’t want to give a good solution to the customer, or that you don’t have a “can do” attitude. It means that you’re empowering the customer to decide exactly what they want to spend their money on. It also protects you from spending days writing a feature that you’ve misunderstood and wrongly delivered (or protects the customer from not knowing what they wanted initially).
I guarantee you that literally dozens of times you’ll find that the customer doesn’t want those extras. Instead you can spend that time delivering other features that they absolutely need to get up and running as quickly as possible.