In a perfect world, each and every feature we build would be lovingly crafted, properly factored, elegantly architected and fully tested... and we'd have enough budget for all of it.

I'm not lucky enough to live in that world. My job is to help clients use their limited budgets in ways that maximize their overall business objectives. Sometimes that means minimizing software maintenance costs, other times it means getting an imperfect feature into production as fast as possible. These types of decisions always involve trade-offs, often sacrificing some sacred agile calves on the altar of "getting it done".

So what's a pragmatic craftsman to do? How can we intentionally leverage technical debt to meet short term goals and still maintain a high bar of general quality in our code?

The principle I use in these situations is that architecture and design are negotiable, but clean code is not. This is best explained by breaking it down into component parts:

Architecture is negotiable. Not every project needs an n-tier separation of concerns. Not every project needs DI/IOC. Same for message buses, impersonation frameworks, 2nd level caching and so on. These things are often valuable and should not be forsaken lightly, but they do have costs. A pragmatic craftsman should be able to articulate those costs and weigh them against their value over time.

Design is negotiable. By "design" I mean the low level feature code. Sometimes you can get away with a switch statement instead of a Strategy, or tight coupling or low cohesion or large method bodies. Same for violating SOLID principles. I'm not saying do these things lightly, but be pragmatic about it. Learn to identify scenarios when their benefits will be realized, and scenarios when they won't.

Clean code is NOT NEGOTIABLE. Sacrificing architecture or design can be forgiven if you make it easy for future programmers to clean up and improve. This means that no matter how "dirty" your architecture is, be damn sure your code easy to read, clearly communicates your intent, and documents WHY you've made the decisions you have.

But aren't architecture and design part of "clean code"?

Absolutely. Clean design trumps comments explaining bad design every day of the week. But you will face scenarios when you have to trade away something in favor of something else (time to market, hitting a budget, risk aversion, etc). This blog post is all about identifying those parts of clean code that you can give up, and which parts you should die on your sword to keep.

Here are some of the things I consider inviolable and strategies for preserving them:

Code should always be easy to read and understand. I don't care how nasty the architecture or design is, I don't care how stripped down the feature is, and I don't care what your budget is. You should always make it easy for the next programmer down the road to understand your intent (what you mean the code to do) and your implementation (what the code actually does).

The messier the architecture or design, the more you should document with comments. Well written clean code doesn't need a lot of "here's what I was thinking" commentary. That commentary is much more valuable when you're taking shortcuts and incurring technical debt, because it can make it much easier for someone to pay back that debt later. My rule of thumb is, do it right yourself or provide clues to help the next dev make it right later. (With a huge preference on the former!)

Keep your eye on the prize. In other words, always have an idea of what the end goal would be. Think about what you wish you could be implementing and try and "lean" the code in that direction. For example, think about what the very first step of refactoring would look like. Can you go ahead and take that first step now?

Closing thoughts

Like many of my posts, I'm talking about edge cases. Most of the time we should be striving for clean code including architecture and design. Too many programmers (and clients!) are far too quick to take shortcuts, and this post is NOT about taking more of them.

But if you've fought the good fight and tried everything else first, and you still need a shortcut, then be sure you do it cleanly and in a way that can be easily fixed later.