Monday, January 10, 2011

Stardate 2011.10.1: Revenge of the Commons

In my day, I've seen a lot of shoddy architectures. From a COM system that read data as strings from the user-interface, converted it to its inherent datatypes, then turned it back into a string and sent it from the client to the server, then read it as a string and turned it into its datatypes, did work on it and sent it back, the entire program of which was written in about 200 3,000-line switch cases (copy-pasted to the server and the client), to a project that only had a few successful continuous-integration builds for the entirety of its lifespan due to such a low-quality codebase, and such immature developers.

Today, I'd like to discuss the Commons; not Boston Commons, but rather Commons projects. These are typically defined to be a series of interfaces, classes, and user controls that didn't seem to have an obvious spot in the domain. They often represent purely technical concerns that have little to do with any real business functionality, such as a combo-box that automatically formats its input in various ways: There's no obvious place that that really fits into the domain, so why not throw it into the junk drawer?

Wait, junk drawer? Is that really all Commons projects are? It seems so. While I understand the utility of having a Big Ball of Mud for things like this, they really do tend to cause significant issues later on down the road. The reason they do this is because they tend to have insanely high afferent coupling. In other words, there are a lot of projects which depend on some or all the classes in the Commons project, and so when another project needs a special version of the class in Commons, and they decide to change that class directly because it fits in the other places that they're aware of, it breaks all the places that work with that class because they didn't realize they had to fix it, and didn't bother to check it (whether because they didn't know they needed to, or because their test-cases weren't well-defined).

A very high afferent coupling doesn't necessarily mean it's a terrible architecture, though - it just means that it's very likely to have problems when those classes need to be changed or fixed, and so the overall design quality is likely lower. The main reason for this is that Commons projects tend to be highly unstable. Consider, for example, Core projects - projects which are decided to be core either to the domain or to the architecture. These projects are usually extremely abstract and tend to be more stable than any Commons projects, because the Core projects define the interfaces and abstract classes which are in-turn implemented specifically in the modules. Since they are abstract, there is a kind of implicit understanding that they should be stable within the domain, i.e. they aren't going to be changing much for any reason that the business people might offer, and at the same time should be relatively stable within the architecture.

Does that mean we should make the Commons projects more stable and abstract? I would say we should certainly make them stabler, but it would be incredibly difficult to make them abstract. Often-times, they actually have abstract components buried in them, but those abstractions are not at all stable. I think better is to avoid, if at all possible, Commons projects altogether, and instead look for ways to embed these pieces into the modules themselves. I would even go so far as to say it's "OK" to have copies of each class that seems to be common distributed throughout the modules. Whoa whoa whoa - what about the DRY principle - don't repeat yourself? Well as it turns out, when DRY could potentially cause you a lot more work due to one class handling several different use-case scenarios, DRY should take a back-seat to the SRP - the single responsibility principle. In that case, the SRP allows each of the different classes to serve their respective modules without breaking the other modules' implementations when bug-fixes/domain changes/architecture changes are required in those particular modules.

I certainly understand that that leaves a kind of gritty after-taste in your brain when you have to do it, but in my experience, it is simply much, much easier to work with/maintain this kind of system than it is one with multiple Commons projects that serve every module individually. "Find and replace" is still better than "Make one class dangerously handle every responsibility."


  1. I still shudder when I hear "duplicated code" and "find and replace"... I'd still go with abstracted reusable methods that are either in the base class or in the junk basket. The key thing here is developers have to pay attention when they make changes to it: a common method needs to be "common" to all users; as soon as one of the users (not all) demands a different responsibility, a separate method should be written to maintain the "single responsibility" principle. Though it sounds like we still need a balance - either extreme is no good, no good I say! :)

  2. Base classes are bad, in general. This is why Google is making a new, "modern" language that doesn't even support inheritance. :)

  3. Google is concurring the world...they are even solving sudoku for you! Human brains need to be exercised! Speaking of which, I did notice my speed of calculating tips in head reduced drastically ever since I started using an App for it.