Guiding Principles 101

As a answer-in-part to my own Call To Blog More, I’d like to make an entry.  I’d like to talk about some of the guiding principles of design we should all be thinking about and re-enforcing in each other.  These principles require discipline and it’s VERY easy to not use them and still achieve decent results (at the cost of looming, expensive problems down the road).  You owe it to yourself and your fellow co-worker developer to be familiar with these, at least, and to make an attempt to practice them on a daily basis.

Rather than me repeating documentation on various principles which is already abundantly available on the web, I’m going discuss two things:  1.) The ObjectMentor site which has a collection of well-written documents (in PDF, *ugh*) and 2.) Discuss a somewhat newer principle which I call ‘Framework Ignorance Principle’ and which Ayende recently dubbed ‘Infrastructure Ignorance [Principle].’

Wealth of OO Design Principle Knowledge

This has a breakdown of each and every principle (plus more) that I was planning on talking about here.  That kinda stole my thunder, actually.  It’s hard to top ‘Uncle’ Bob Martin when it comes to OO design principles, so I’m not going to attempt it.

It is definitely worth your time to spend a few nights browsing through the PDF’s on that page.

NOTE: If you ever plan on interviewing with me for anything more than a junior level developer position, you better be able to give a sentence or two on at least 5 or 6 of these principles and discuss, at length at least one of them.

I’d like to call out one principle, in particular that I think would probably benefit everyone the most. It’s one of the simplest concepts, though hardest in practice to maintain and requires a good team discipline to maintain:

Single Responsibility Principle

“There should never be more than one reason for a class to change”

- Robert ‘Uncle Bob’ Martin

If you have a communications class that has methods like ‘Open()’ and ‘Close()’ as well as ‘Send()’ and ‘Receive()’, you’re violating SRP.  Connection management and data transfer are separate concerns and should be isolated.

Framework Ignorance Principle

This one isn’t really a new concept and is instead really an amalgam of several other principles. It’s worth pointing out and promoting to the level of a ‘virtual’ first-class principle, in my opinion.

When working with various frameworks, there is a propensity for that framework to become pervasive at all levels of the code.  Without rigid discipline, the temptations and weakening arguments such as ‘Well, we’re joined at the hip to FrameworkX, might as well make my life easier and give up the pretense that I’m NOT going to use it’ will gain in frequency and intensity until there is a collapse of discipline.  Depending on what your framework does, this line of reasoning will trigger a set of events that will lead to a quick or gradual decline in your projects maintainability, testability, and overall agility. It’s a self fulfilling prophecy. If you give up the discipline of keeping the framework segregated by saying that you’re already in for a penny, you will quickly be in for a pound.  While it’s probably true that you may not be migrating from NHibernate any time soon, you will most likely be migrating from NHibernate 1.0 to 1.2, or better yet, 1.2 to 2.0 which could be a much larger issue. At that point, you’re very close to adopting a new framework anyhow. Sure, the API is very similar, but the guts could be very different and there is tons of new functionality that may make you rethink certain design decisions — forcing rewrites of part of your app.  Maintaining the ignorance to the last possible moment will greatly ease this refactoring or rewrite.  If you made the mistake of allowing ICriteria or ISession references bleed up into your UI or Web layer, making the switch from 1.2 to 2.0 will likely be very painful.

Another case study here is log4net.  Over the years, I had grown so accustomed and trusting of log4net and relied on it’s snail-like release schedule (or lack thereof), that I allowed log4net to bleed into all parts of my app. “It’s a logging framework, how much could it possibly change?” I found myself saying.  Well, then log4net 1.2.9 came out. Sure, the API was exactly the same, but they changed the key signature for the DLL which broke assembly linkage all over the place and was irremediable due to the fact that you cannot do assembly versioning redirection in .NET when the key signature changes.  Yet another time that a lack of framework ignorance discipline bit me hard in the rear.

I’m not advocating you go out and write abstraction layers for all your frameworks (*ahem* I’ve tried that, it’s not pretty). You may have to in certain circumstances, but avoid it as much as possible. You will create a potentially brittle change resistance layer that can slow down your refactoring in the future and also mask useful features in the underlying framework (the ‘least common denominator’ problem).  I would, however, advocate that you try to leverage the framework to it’s fullest, but do it in a way that works around and among your primary code and not in the middle of it.  Attempt to make use of IoC containers for injecting or wrapping functionality. Look

A perfect example of this technique is how NHibernate mapping files are separate from the domain entity objects themselves. The mapping files explain to NHibernate how to persist and retrieve these objects from a relational data store. There are no mapper objects, no adapters, no attributes on the entities themselves, no code-generated partial classes, etc.  To stop using NHibernate or to switch to a new version, you simply stop using it or start using the other version. There will be a few places in your code that will have a reference to NHibernate, but these will be minimal and self-contained, allowing for quick refactor.

About Chad Myers

Chad Myers is the Director of Development for Dovetail Software, in Austin, TX, where he leads a premiere software team building complex enterprise software products. Chad is a .NET software developer specializing in enterprise software designs and architectures. He has over 12 years of software development experience and a proven track record of Agile, test-driven project leadership using both Microsoft and open source tools. He is a community leader who speaks at the Austin .NET User's Group, the ADNUG Code Camp, and participates in various development communities and open source projects.
This entry was posted in Principles. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • I’m kinda disappointed there aren’t any other comments on this post yet. I appreciate the link to the Object Mentor papers, thanks. (I even like the PDFs, as they print nicer than web pages.)

    At first I was fearful that the second half of this post was going to rail on me for not knowing every little detail of every framework out there. I thought you were going to say that being ignorant of said frameworks is an anti-pattern and that I should repent. I’m glad you haven’t forced me to face my sins.

    Actually, I think the principle is misnamed. I think it should really be named something like the Framework Agnostic Principle. The more I think of it, “agnostic” might not be the right term, but I don’t know a better one. I want it to mean religious, but not a believer in any particular church or sect. Perhaps anti-disestablishment non-sectarianism is a better term? I dunno. Basically, you don’t need to be ignorant of the framework, just don’t give yourself fully to it.

    That said, and at the risk of repeating myself, I tend to think that the existence of so many “frameworks” is a sign of trouble. In Ruby, we don’t consider ActiveRecord or Sequel or DataMapperto be frameworks; they are simply libraries. The fact that we need, or think we need, a framework to solve our problems gives me pause.

  • @Mike I’m disappointed there aren’t more comments too :)

    I chose Ignorance over Agnostic on purpose because you don’t want your code to be totally dependent (i.e. compiled/interpreted-against) a specific framework.

    (loading standard anti-ActiveRecord argument in 3… 2… 1…)

    I have a domain model. It gets persisted somehow, I don’t care. In a separate assembly/dll, I have all the stuff that cares about how the model gets persisted. I could chose to switch off NHibernate and go with something else in the future, or maybe I want to use the domain model in-memory with persisting anything. Having my classes attributed heavily or having to derive from interfaces or base classes violates this. now, whenever I want to do anything with my model (even if I don’t need to persist or retrieve anything from a database, I now have to lug along NHibernate or Castle ActiveRecord (and probably have to have a bunch of junk in my config file) just to make those two happy when I don’t even care about it.

    Likewise, in your Ruby AR example, you are completely tied to AR. Hopefully you won’t have to ever use your model for anything other than RoR work because you’ll be SOL for the most part.

    Sure, you could code around it, or find a way to host it without a database or something, but now you’re having to worry about it.

    Instead of the persistence mechanism serving (transparently) your model, your model is serving the persistence mechanism.

  • @Chad I totally agree with you about implementing interfaces and deriving from base classes violating the separation of concerns when it comes to long-term maintainability and support of your middle-ware libraries in a statically compiled world. Your point about lugging around NHibernate or Castle ActiveRecord with your domain models is exactly why I don’t use them. My understanding is that it is really hard to make full use of NHibernate or Castle if you don’t let the implementation details of those frameworks peek leak through. This is why I typically don’t use a “framework” to create my domain models and manage their persistence to and from the data store when using C#.

    To your Ruby example, I have alot more flexibility in Ruby because isn’t using static type-checking at compile-time. If I wanted to swap out my ActiveRecord model for a DataMapper model, I just have to retain the behavior. My DM model simply has to quack like my AR model. Because my business logic isn’t dependent on implementation details such as the parent class of the model, I am free to make changes as long as I don’t change the behavior my application uses. I can’t do that in C# because the static type-checking won’t allow me to swap out models without recompiling my application. This is the crucial difference between polymorphism through composition vs. inheritance, and the late-bound nature of dynamic dispatch vs. the early compile-time type-checking of static languages.

    While you make a great point about ActiveRecord models requiring a database to work, I don’t think this is as much of a real world hurdle as you might make it out to be. In the case of AR, the dependence of the database is serving a very specific purpose. If you want to use a model in a different way or for a different purpose, you should use either a different library (like DataMapper) or a completely different approach (like RESTful web services, or a document based database, or memcached, or whatever). Every abstraction leaks, and there is no silver bullet. So rather than depending on a framework to offer infinite flexibility and do all the heavy lifting, we are free to choose the implementation that makes the most sense for our needs. In short, you approach the problem differently.

  • @Mike:

    I guess it depends on whether you’re building a system which happens to have a web interface, or a web application based on RoR.

    If you’re all in to RoR then AR makes perfect sense because you’re not going to use it for anything else.

    But what about when you’re not making web applications, or rather, what about when you’re making your own framework that provides business logic that can be used by a web application, a backend service (web service), or maybe even out in the field disconnected on a field engineer’s laptop?

    I need to build my model to serve the business need. Not serve the database, or a web framework, or any other framework/context in which my model may run.

    My model and associated business logic has no inherent need to be tied to a database, IoC container, or anything else so why should I go about tying myself totally to one of those things?

  • The Object Mentor link is good stuff. I first read a lot of that stuff when it came out in the C++ Report back in the 90′s somewhere. I would say that Mr. Martin is directly responsible for putting me on the path to becoming a half-way decent. After reading his articles, I became obsessed with becoming a good programmer, as opposed to just learning my way around the latest and greatest technology.

    What amazes me is how many people have never heard of OCP, SRP, LSP, etc. When I first read about OCP I want to say my skills as a programmer went up a couple of notches right then and there. This stuff taught me how to think in an object-oriented way. It’s immediately useful, which just fascinated me to no end when I first discovered it.

    The company I work for in Houston runs an internal bootcamp for new hires right out of college. Guess what I am going to teach? Maybe, following your call, I’ll post it all on my lame blog.

  • @J.P.: Please do share with us how your training session goes. I’d like to hear how the students react to these principles.