Monday, June 15, 2015

Solutions in Search of Problems: Disadvantages of Modular Designs

In software designs, developers often talk about "modular design," "separating concerns," and "loose couplings" as being good things.  The idea is that software should be built by identifying individual pieces of functionality, isolating each piece of functionality from the rest of the system, and then assembling the system out of pieces like building something with Lego.  Many developers and "architects" believe any other approach is wrong.  Indeed, nowadays tightly-coupled designs are usually wrong-ish, but not always.  Failure to recognize when to break the rules can result in a lot of unnecessary pain.

In the process of isolating individual pieces of functionality, useful features of that functionality are sometimes lost.  Perhaps the best examples of this is seen in DAL (Data Access Layer) abstractions which are intended to isolate the rest of the software from any particular database implementation.  The problem is that no DAL can cover all of the features of any particular DBMS (Data Base Management System).  Most of the time I've seen teams just act as if the the particular DBMS feature doesn't exist, or hope that the DAL vendor includes that feature in its next release.  Or, they create a custom DAL comprised of a hybrid of an off-the-shelf library like Entity Framework and custom code.  There are times when loose coupling makes perfect sense, like when you think it is reasonable that in the next 5 years you could change to a different vendor's database.  But on most projects, this isn't going to be the case.  And yet, a lot of .NET development teams intentionally handcuff themselves by shunning quite a few features offered by DBMSs that aren't abstracted by EF.   In many cases it would be more optimal to tightly bind your software to the DBMS the company has chosen and take full advantage of every single feature the DBMS offers.  How often do companies really drop SQL Server for Oracle or MySql anyway?  If you're migrating to MongoDb, EF is the least of your refactoring problems....

A simple example of this is hand-powered screw drivers.  Most of us have one of those screw driver sets where you can swap out a phillips-head for a flat-head or hex-head.  The software designer would say that the screw head and handle body are "loosely coupled" and that is a good thing.  But it isn't always a perfect thing!  Many of us have learned the hard way that while those modular screw drivers are good for light tightening jobs, they aren't great for heavy jobs where you need a dedicated fixed-head screw driver.  Especially if maybe you also need to use the screw driver for prying, hammering or chiseling, the modular sets are just too fragile, and screwdriver where the handle and bit are "tightly coupled" is the better choice for the application.  Right tool for the right job!

This lesson applies to all areas of software functionality, not just the database.  Sometimes there is a high likelihood of component changes, so abstraction makes sense, even if there is risk of component feature-loss.  But other times the cost in terms of feature loss is not worth it, and your software will perform better and be easier to maintain if some components are more tightly coupled.  It is all relative to your real world needs, not some abstract textbook.

I don't think there is any golden rule on this.  Perhaps a good rule of thumb is that if you can't articulate a reason why a particular component would change in the future, then the argument for abstracting it is less strong.  Every project has a budget, so focus your efforts on compartmentalizing pieces of your software that are the most likely to change in the future. I've seen too many projects where premature compartmentalizing got out of hand and resulted in a design that fixed problems that never came to exist.