FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
Architecture Blog: Overdesign vs. Too Simplistic Design
Architecture & Design
PATTERN LANGUAGE

Modeling, Managing, Making it Right.

by Jonathan Erickson
IF YOU BUILD IT

... Will they Come?

by Arnon Rotem-Gal-Oz
September 04, 2006

Overdesign vs. Too Simplistic Design

I recently read David Hyden's blog "Over-Architecting Via Pure Fabrication and Indirection to Reduce Coupling". David explains why he thinks I have learned to see the low coupling principle as an "evil" principle which causes you to add excess levels of indirection in the name of low coupling you rarely need.


I agree you don't want to over engineer for some unknown future (which may never come). That is what YAGNI is all about. However as I said in that and in the JEDUF post, you don't have to look at any problem as if you've never seen anything like it. Your past experience can tell you when some "for future use" feature is more likely than not to be needed. This can even happen (and usually does) within the same project; for example, you have two data entry modules that implement some UI behavior. I would think you would want to use a similar design for both.

Also you need to consider that software--at least successful software--spends most of its time in maintenance and not in development. you want to consider (again, experience can play an important part here) extension points that are likely to be used (Unless, maybe you are going to make a fortune handling all those change requests)

As far as low coupling goes, it is more important at the component or service level (versus the objects level). Nevertheless it doesn't always cost you that much more to add it. Even more important is the fact the lower coupling increases testability. Objects that only depend on abstractions and use dependency injection can be tested in isolation (using mock objects, for example); also see my blog on DI and TDD.

I also think that sometimes the costs of refactoring a solution down the road when you have a lot of behavior to preserve can cost you more that it would to take care of that something at the beginning.

One thing we used successfully in one project is to think out the design for future needs, document it somewhere (e.g. a software architecture document), and then only implement part of the solution to be able to answer the current needs. The advantage here is the the code remains simple for as long as needed but you already know how enhance it when it will be needed. This is especially useful when you have a set of things which are similar in concept but have varying levels of complexity. For example, on one project we have one service that has a very high call load and another that is only invoked occasionally. We designed one pattern for the maximal case and only implemented a partial solution (e.g. we don't have a queue between the edge and the service) for the low-intensity service.

Following the OO principles, adding levels of indirection, and deciding when to design ahead are not always easy--far from it. There's a lot of grey area where principles sometimes pull in different directions (i.e., contradict each other). I don't have some magic formula to say what is right. Experience helps and so does having more than one person look at a design. Nevertheless, the thing that probably helps most is applying common sense.

Posted by Arnon Rotem-Gal-Oz at 08:54 AM  Permalink




 
INFO-LINK