CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

On Writing Maintainable Code

A QUICK NOTE:  This was supposed to be a single treatise on the coding and design principles that *I* think are most important for writing maintainable code.  A draft of this has been on my hard drive for a long, long time and it's turning into my own great white whale.  Just to get it out, I'm breaking it into pieces that will follow shortly, depending on ongoing bouts with writer's block.  I'm going to intermix quick discussions of these design concepts with some case studies of systems I've built or worked with that illustrate both the positive outcome of following the principles and the pain incurred by failing to apply the principles.  Some day, I'll gather the pieces back up into a single coherent article.

 

Enable Change or Else!

Change is a constant in an enterprise software system.  A system that is expensive or risky to change is an opportunity cost to your business.  Especially if you're a small company, poor code quality in your flagship product will jeopardize your company's future. 

A couple of months ago I talked about my vision for creating a Maintainable Software Ecosystem in which I claimed that the single most important quality for an enterprise software system is maintainability, i.e. the ease in which a system can be modified or extended.  I spent a lot of screen space talking about topics like source control, test automation, and build automation.  There's a lot of supporting practices that can greatly aid in shipping working code, but I purposely put off the single most important factor -- the Code!  Maintainability will not happen without a commitment to quality code, now, and throughout the lifecycle of a system.  I can always throw code quality to the wind and code faster now, but that slop will catch up to me or the next team in the future, and the future has a funny way of happening sooner than we expect.  Besides, I've been the "next" team, and it wasn't pretty.

I spent much of the past two years at my previous job extending, restructuring, or flat out re-writing legacy code.  I frequently saw my team's efforts hindered because of existing code that was poorly factored or just flat out hard to understand.  We were consistently faster when we were working with the newer code that we wrote and designed inside of an Agile process than we were working with the older legacy code.  Some of the disparity in productivity between new and legacy code was our familiarity with the newer code and the better build and test infrastructure of the newer code, but I'd still place much of the blame on the structural flaws of the legacy code.  Ironically, and certainly not for the first time, I thought some of the biggest impediments to extending our system were directly attributable to well meaning attempts at creating extensibility in the existing code. 

Extensibility yes, but how?

One solution for system maintainability is to build in "extensibility" points or use metadata-driven design approaches.  It's great if the extensibility points match up well with the actual direction of the later change, but the wrong extensibility points can cause a lot of harm by making a design harder to understand or awkward to extend.  David Hayden recently posted some frustrations with this style of design. 

If it's true that most code spends much more time being maintained (changed) than the initial write, then it certainly behooves us to create code that can be changed.  Extensibility points can certainly help, but the wrong extensibility points can also do plenty of harm, so they're not the whole answer.  In My Programming Manifesto, I expressed a strong preference for creating maintainable code throughout the codebase rather than concentrating on specific extensibility points for future needs.  I've always thought that the Simplest Thing That Could Possibly Work is largely true because the percentages say that you simply will not be able to anticipate a great deal of the future change with consistency. Overall, odds are that the best chance for successful maintainability is in creating "malleable" code that can be easily and safely changed in unforeseen ways.  Instead of focusing on future proofing code in a few "strategic" spots, concentrate on making your code easy to change as a simple matter of course.

Coder to Craftsman

When people first learn how to write code they necessarily focus on just making the code work, with little or no thought for style or structure, and certainly no thought for the future.  Any particular piece of code tends to land wherever the coder happened to be working when they realized that they needed that piece of code -- or wherever the RAD wizards felt like dropping the code. 

I think there is an inflection point where a coder mindlessly spewing out code transforms into a thoughtful software craftsman capable of creating maintainable code.  That inflection point happens the day a coder first stops, lifts his/her nose out of the coding window, and says to him/herself "where should this code go?"  That might also lead to questions like "how can I do this with less code?" or "how can I write this to make it easier to understand?" or even "how can I solve one problem at a time?"  The rest of a developer's career is spent pursuing better and better answers to the question "where should this code go?" 

My first "enterprise-y" system was an ASP Classic web application on top of Access to track project auditing for my engineering team.  If you opened up any of the early ASP scripts from that application you'd see SQL statement construction mixed with post form handling, data access and HTML creation all intermixed.  Business logic happening willy-nilly at various points in the ASP page whenever I was coding away and realized I needed some logic.  A lot of functionality was duplicated because each ASP page was self-contained, causing more effort to write and then change the application.  The pages themselves became difficult to understand because all the code was dumped into one bucket with no rhyme or reason.  Troubleshooting business logic meant sifting through a lot of unrelated http handling code. Finding data access problems meant reading through quite a bit of the html templating along the way.  The code stunk and I knew it, even as a coding newbie working solo.  There had to be better ways to build the application.  I improved things just by creating a set of common utility subroutines that could be used to reduce the amount of duplicated code. It wasn't much, but it was a start.

The Maintainable Code Checklist

So exactly how do I know "where this code should go" for maintainability?  To guide your coding and design for maintainability, I've come up with a checklist of the half dozen questions in the table below that I think should be answered in the positive.  There are three major themes running through the checklist -- intention revealing code, getting rapid feedback from the code, and being able to do one thing at a time.  Assuming that maintainability is important to you, it's also time to talk about the design principles that provide guidance to answering this Maintainable Code Checklist.  In the table below I've tried to tie the maintainability questions to some of the design principles that will guide the thoughtful developer to answers.  This certainly isn't a comprehensive list, but it's a start. 

Question Yes comes from...
Can I find the code related to the problem or the requested change? Good Naming, High Cohesion, Single Responsibility Principle, Separation of Concerns
Can I understand the code? Good Naming, Composed Method, The Principle of Least Surprise, Can You See The Flow of the Code?, Encapsulation
Is it easy to change the code? Don't Repeat Yourself, Wormhole Anti-Pattern, Open Closed Principle
Can I quickly verify my changes in isolation? Loose Coupling, High Cohesion, Testability
Can I make the change with a low risk of breaking existing features? Loose Coupling, High Cohesion, Open Closed Principle, Testability
Will I know if and why something is broken? Fail Fast

You can't help but notice that many of the principles are closely related.  I think you could say that many of the principles are simply looking at the exact same problems from a different angle.  Because of this, I'm going to first do a survey of the principles, then I'll try to illustrate the principles in code with some real life examples from my career.

 

It All Starts with Separation of Concerns

Separation of Concerns is the Alpha and Omega of design principles.  No other design principle that I'm going to discuss -- be it loose coupling, high cohesion, encapsulation, minimal duplication, or orthogonality -- is possible without Separation of Concerns.  Simply put, strive to do one thing at a time in your code.  Layering.  Divide and conquer.  A lot of the other principles are about enabling a system to change with minimal effort and risk.  Before you can even think about that, you need to be able to build the system, and then understand that code.  The human mind and eye can only handle so much complexity at any one time.  At a minimum, separating the traditional concerns of user interface, business logic, and data access into "layers" of the application can help minimize the complexity of any single piece of the code.  The system is still as complex as it has to be, but you stand a much better chance of understanding the business rules or data access mapping in isolation than you could if it was all mixed together.

Back to my original ASP application.  Like almost all ASP code circa 1998 there was no separation of concerns.  Business logic, user interface presentation, and data access details were hopelessly intermingled in a single code file.  It was difficult to "see" the business logic flow because it was obscured by all the intermingled html markup and ADO database manipulation.  My first exposure to separation of concerns was the 3- or n-tier architectures for Windows DNA applications on Wrox's old ASPToday website (forget about the physical deployment options and let's just talk about logical layering here).  At a bare minimum, layered systems should be easier to understand because you can look at presentation logic or business logic in isolation.

A couple of months ago at my previous job we were discussing the technical tasks necessary to localize the application with foreign language support.  The company has essentially run out of growth room in the United States and was looking at the European market with great hope.  Localizing the application, and supporting Unicode encoding for that matter, was a major opportunity for the company.  One of the developers was repeating the typical opinion that localization is easy to do upfront and always much more difficult to retrofit.  Sure, but in this particular case the flagship product was going to be extremely tedious to localize because they were building strings to display on the screen very deep within big stored procedures as well as the C# middle tier code.  For that matter, code that created HTML text with string concatenation was intermixed with business logic.  In that particular case, localization after the fact could have been made much less costly simply by a better separation of concerns.  Instead of mucking around with every single area of the code, a good separation of concerns would have enabled us to focus strictly on the presentation layers to make the localization changes.

When you say layering, we probably come up with a knee jerk list:

  • User interface, presentation, controller logic
  • Business logic, rules, domain model
  • Database and data persistence
  • Service layer
  • Logging/auditing
  • Security

That's an awfully good start, but I think the layering metaphor of higher layers talking to lower layers might not fit perfectly anymore.  "Concern" or "Layer" might be interpreted too coarsely.  Even within a single traditional "layer," you may have finer grained areas of concern that should be separated as well.  I'll discuss this more in the section about the Single Responsibility Principle.

Orthogonality

Orthogonality is more a goal than a principle.  In geometry, two or more axis of movement are said to be orthogonal if a change in position on one axis does not effect the position in the other axis.  If I walk north for a mile I've changed my latitude, but not my east-west longitude.  The Pragmatic Programmers applied the term Orthogonality to software as:

The basic idea of orthogonality is that things that are not related conceptually should not be related in the system. Parts of the architecture that really have nothing to do with the other, such as the database and the UI, should not need to be changed together. A change to one should not cause a change to the other. Unfortunately, we've seen systems throughout our careers where that's not the case.

In essence, Orthogonality in software design is the ability to change one thing at a time.  An orthogonal codebase allows different concerns to be changed independently.  Another way to think about Orthogonality is that it's the ability to work in isolation with only one aspect of a system at a time. 

A classic, positive example is the invention of Cascading Style Sheets (CSS) in the late 90's.  When I first learned how to create html websites (in Frontpage 97!), I embedded all of the style properties directly into the html.  Mixing the content and the structure of the content with the formatting of the content often made large scale websites hard to maintain.  Moving presentation rules into a CSS stylesheet greatly simplified the creation of the html content, while also allowing the style of the web page to be more readily changed than before.  CSS started to make the formatting and content of an html page orthogonal.

In a negative example, I inherited a codebase had very poor orthogonality between the user interface and the business logic.  In this case we had a significantly complex piece of code that worked through business logic to build an html response inside code.  One of the significant problems with this code was that the business logic could only be tested and verified through an examination of the resulting html.  Everytime we had to change the user interface, we broke all of the automated tests that were intended to test the business logic.  We weren't able to change business logic easily because we had html concatenation cluttering up the business logic.  The very first mistake was a failure to separate the business logic and user interface concerns (<sarcasm>another tip, when you're rewriting a bad existing system, you might not want to reproduce the existing bad design in the new programming language</sarcasm>). 

 

So how do you make Orthogonality happen?  Tune in next time -- assuming that I can break through my case of writer's block.



Comments

Philip Stevens said:

Why is it when I tell people that I write my code with a bent towards maintenance over performance I get the stink eye or the crook eye?  100% agree, whether you are the maintenance programmer or it is someone else.... bottom line is someday (usually sooner than later) someone has to maintain the code you write, giving them a fighting chance at doing that is ever so important!

# December 6, 2006 7:38 PM

Jeremy D. Miller said:

You know Philip, not that this argument will work for you either, but if the code is maintainable first, shouldn't making the necessary, assuming there are necessary, performance improvements be easier and safer?  The performance improvements that you only determine from profiling after the fact?

With my new job working with financial market software, I think you just nailed a big part of the programming culture I'm hearing about.

# December 6, 2006 7:46 PM

durior said:

typo:

Indeed, "At a bare minimum, systems can become"

;-)

# December 7, 2006 3:38 AM

Philip Stevens said:

Jeremy -

you are correct, that argument doesn't work for me either :)

# December 7, 2006 6:04 AM

Jeremy D. Miller said:

Thanks for pointing that out durior.  I fixed it, but I really don't remember what I meant  to say there;)

# December 7, 2006 6:06 AM

Steve said:

Great post.  I completely agree.  I also like to include documentation somewhere as to the "why" things are done at all, and the "why" for specific functions.  I can look at an old piece of code and tell anyone what it "is" doing, but usually have no idea why it's doing it, or even if that is what it was supposed to do.  This is a huge annoyance for me.

# December 7, 2006 8:09 AM

Jason Haley said:

# December 7, 2006 8:02 PM

Marco said:

I've worked with programmers who intentionally made their code imposible to follow just for the sake of job opportunity. Their mindset was "I'm the only one who can follow this, so they can't afford to let me go.".

Obviously, I moved on rather quickly..

# December 8, 2006 6:56 AM

Marco said:

That should read "Job Security"

# December 8, 2006 6:58 AM

Jeremy D. Miller said:

That's the bad kind of job security though.

# December 8, 2006 7:28 AM

hammett said:

Funny thing, I wrote about orthogonality quoting the same paragraph, almost two years ago.

http://www.jroller.com/page/hammett?entry=castle_and_orthogonality

# December 8, 2006 2:25 PM

Jeremy D. Miller said:

Quoting the "Prag's" is always a safe choice

# December 10, 2006 2:41 PM

James Taylor said:

Great article. I think there is real value in building part of some systems using a business rules approach and posted some links explaining why:

http://www.edmblog.com/weblog/2006/12/using_business__1.html

# December 28, 2006 11:06 AM

Fregas said:

Has anyone noticed that the principle of layering three tiers (presentation, business, data access) causes Orthogonality to be difficult?  If i make a change in the database such as add a column, i will most likely have to change my data access layer, business layer and presentation layer just in order to do the necessary CRUD with the new column.  O/R Mappers automate this process, but the problem is still there: all three layers must be kept in synch to some extent and are not independent by any measn.

# December 29, 2006 1:35 PM

Jeremy D. Miller said:

Two posts down the road Fregas.  You might try taking a look at some of the Naked Objects approaches, or better yet, Rails.

# December 29, 2006 1:43 PM

Ayende Rahien said:

Naked Objects are hard to sell to a customer, and they require some extra training for the users. It is the ultimate manifestation of DDD, though.

They are no usable in the web, though.

Rials doesn't solve the problem of a new column any more than a GridView with AutoGenerateColumns = true does.

# December 31, 2006 4:05 PM

Jeremy D. Miller said:

Ayende,

Not solve, I was thinking that Rails does help a bit from the perspective of the DRY principle.  The number of steps it takes to walk the entire wormhole from new field in the database to showing up in the screen is less because of ActiveRecord's derived mapping from database to domain model class.

Jeremy

# December 31, 2006 6:09 PM

Jeremy D. Miller -- The Shade Tree Developer said:

My employer, Finetix , added my blog to their main feed and my most recent post at the time just happened

# January 8, 2007 11:59 PM

you've been HAACKED said:

Writing Maintainable Code

# January 9, 2007 11:43 AM

Jeremy D. Miller -- The Shade Tree Developer said:

Occasionally, ok often, I'm gently mocked for the length of my posts. I start with good intentions of

# January 22, 2007 7:15 PM

Rhonda Tipton’s WebLog Web Links 12.07.2006 « said:

Pingback from  Rhonda Tipton&#8217;s WebLog Web Links 12.07.2006 &laquo;

# August 19, 2007 5:01 PM

Jeremy D. Miller -- The Shade Tree Developer said:

About a year ago I hit a patch where I wasn&#39;t able to blog much (something about finding a new job

# October 18, 2007 9:20 AM

Maintainable code « Jonne Kats said:

Pingback from  Maintainable code &laquo; Jonne Kats

# October 27, 2007 5:46 AM

portrait painting said:

I’m not sure but a lot of our programmers were able to relate on your article.  I didn’t want to think that they feel like they’re always dropped like hot potatoes by our admin or they’re always working and working and yet they get rejected.  

# November 26, 2007 3:06 AM

Jeremy D. Miller -- The Shade Tree Developer said:

To everybody that attended one of my talks at DevTeach this week. All of the materials are now online

# November 29, 2007 12:03 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP