Convention Over Configuration
Last week I wrote about so-called “best practices”, and one coding style that’s often promoted as a “best practice” is to “prefer convention over configuration”.
What exactly does this mean? Well the idea is that wherever possible we attempt to remove the need to explicitly configure things, and instead rely on sensible (but overridable) defaults.
A good example of this approach is ASP.NET. If I want to serve a page at /products
then I create a class called ProductsController
that inherits from Controller
and add a method with the signature public ActionResult Index()
. And the ASP.NET framework simply uses reflection to work out that any HTTP GET requests coming into that URL should create an instance of ProductsController
and call the Index
method. I don’t need to add a line of configuration somewhere to explicitly specify that this should happen.
There are a lot of advantages to this convention over configuration approach. First of all, it makes it very easy to add new components, simply by copying examples already present in the code. If I need a new orders controller, I can easily understand who to do it by looking at the other controllers. This can make it very easy for people new to the project to extend it.
This is also an excellent example of the “open closed principle” in action. A convention over configuration approach means that you can add a new feature without having to change any existing code at all. You simply add a new class that implements a certain interface or is named in a particular way. This has the side benefit of eliminating merge conflicts in classes or files that contain a lot of configuration, which usually experience a lot of “code churn”.
Convention over configuration is also used commonly with setting up message or command handlers. Simply implement the IHandle<T>
interface, and some reflection code behind the scenes will discover your handler and wire it up appropriately. Again this makes a developer’s job very easy – need to add a new message or command handler? Just follow the pattern of the other ones.
So it would seem that “convention over configuration” makes a lot of sense. But are there any drawbacks?
One criticism is that this kind of approach can seem like “magic” – making it hard for new starters on a project to understand how it works. Often the IDE support to “find all references” will return nothing when these conventions are being used because reflection is typically used at run-time to discover the methods to be called. It can leave developers wondering “how on earth does this even work”?
Generally speaking, the more ubiquitous a convention is, the easier it will be for developers to learn and understand. The conventions in a framework like ASP.NET make sense because they are used over and over again – meaning the time invested in learning them is well spent. But beware of creating lots of conventions that only get used in one or two places. This introduces unnecessary additional learning for developers with minimal benefit.
A particularly painful point can be the mechanism by which you override the “sensible defaults” of the convention. How is a developer supposed to know how to do that? In ASP.NET there are attributes that can be used to override the route used by an action on a controller, which is fine because ASP.NET is well documented, but if it’s a bespoke convention you’ve invented for your project, you’ll need to make sure all the information about how your convention works is readily available.
Another disadvantage is that conventions can sometimes produce unexpected results. A good example is the fact that because ASP.NET uses reflection to find all classes that inherit from a base Controller class, if someone happens to reference an assembly from another project that also contains some controllers, you might find you’re exposing new endpoints that you weren’t intending to. This happened once on a project I worked on and opened up a serious security hole. This is the reason why some developers prefer the competing “best practice” of “explicit is better than implicit”. So whenever you use conventions, try to build in protections against these types of unintended consequences.
So, should you adopt “convention over configuration” for your own frameworks? Well it comes back to the question of what problem you are trying to avoid. If it’s about eliminating repetitive and redundant configuration code, then it only makes sense to introduce if the convention is going to be applied many times. If its just once or twice, it may not be worth it.
As I said in my “best practices” post, there isn’t one clear right way to approach every problem in software development. Conventions remove some problems, but introduce others. So you need to understand the trade-offs in order to make a decision about what makes sense for you. Used judiciously, conventions can help developers fall into the “pit of success” – it should be easier to get it right than to get it wrong.
Let me know in the comments how you’ve got on with custom conventions in your own frameworks? Did they make things better? Or did every new developer complain that they couldn’t understand how the code works?