Monday, May 21, 2012

SOLID Code for SOLID Reasons

We should write good code because good code is easy to maintain, not because it makes the code easier to unit test. However, it just so happens that well written code is easy to unit test; and testing our code, especially test-driving our code, helps us to write good code.  But ease of unit-testing is not the only reason for writing good code, in fact it is one of the very last reasons.

So how do we define good code? I think a great starting point, at least for OO code, are the SOLID principles of object oriented design defined by Bob Martin and the concepts of bounded context and anti-corruption layers defined by Eric Evans.

Wikipedia has a great overview of SOLID, or if you prefer, here it is from the horses mouth. SOLID is an acronym for the following 5 principles: Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle.  If you are not familiar with SOLID, I'd recommend you read that now.

Eric Evans recommends in his book, Domain Driven Design, that we define the various Bounded Contexts that exist in our code.  Typically when we work on a project, we have a code base that we work on most often and then there are the external systems with which our code communicates.  That is an example of bounded context -- with our traditional code base being one bounded context and each external system  being considered a separate bounded context.  Evans suggests that when we identify a context boundary we should use anti-corruption layers to prevent the contexts from leaking through to one another.  An anti-corruption layer often takes the form of an adapter that translates and isolates the external context from our code.  This has two very valuable advantages; first, it prevents our code from having to speak the language of the other context (except within the adapter itself); and second, it isolates our code from changes to the other context.  If the other context makes dramatic changes to it's interface, we have no need to panic because that code is not sprinkled throughout our bounded context, it is isolated to the anti-corruption layer.

With that understanding, I would like to address some questions that come up frequently when doing TDD (test-driven development):

Why do we create small classes and break up complex tasks among multiple objects?  It is NOT because it makes it easier to write isolated unit tests and it is NOT because it solves the "how do I test private methods?" question. We do it because classes who do too much work cause spaghetti-like coupling, and maintaining the large class and all the classes that interact with it, becomes tiresome and error-prone.  (Single Responsibility, Interface Segregation and Open/Closed Principles)

Why do we inject dependencies and depend on abstractions (interfaces), rather than concretions (classes)?  It is NOT because it makes our code easier to unit test even though it is nearly impossible to unit test code that depends on concretions.  We depend on abstractions because it makes our code much more loosely coupled and therefore much easier to change without negative consequences. (Dependency Inversion Principle)

Why do we wrap third-party dependencies in adapters?  It is NOT because it's hard to isolate the third-party dependencies from our tests.  We do it because third party dependencies, especially those over which we have little or no control, are subject to change and we want to isolate our code from that change.  We also do it because sometimes those third parties do not believe in the same principles, such as SOLID, that we do, and creating an adapter allows us to adhere to the principles we hold dear. (Anti-Corruption Layer)

When we see code that does not adhere to our principles, we need to stop arguing that the reason to change them is for the sake of unit testing -- because, frankly, that's a tough argument to defend.  We first need to understand the reasons why we believe what we do, and "because it makes it unit testable" shouldn't be a primary reason.  Furthermore, when we believe in a core principle like those above, it should guide everything we do and arguments like "It's too hard in this case" or "It doesn't really apply here because I have a simpler way to do it even though it violates my principles" should rarely if ever be valid arguments.  When we find ourselves making arguments like those, and we really think about the reasons why we believe in these principles, we will almost always find that the principle still applies even in complex (or very simple) cases.

The good news is that TDD, done right, helps us to realize when we are violating our own principles.  In my experience when I've run into a hard-to-unit-test piece of code, it has always been because of a violation of one of the above principles.  The failure, then, is not a failure of our testing tools, it is our failure to adhere to these good coding principles.

And this is why I so dislike moles as test doubles; and it is my biggest beef with the VS11 testing framework which includes moles (they're called shims in the VS11 framework).  When you use moles (or shims) in your unit-testing framework, it keeps you from recognizing that you have poorly-written code that does not adhere to principles like those above.

A classic example of how the VS11 testing framework is going to be widely misused can be found in Rich Czyzewski's blog post, Noninvasive Unit Testing in ASP.NET MVC4 - A Microsoft Fakes Deep Dive.  I understand the temptation to want to solve problems like those Rich discusses using moles and I think his post was very clear and well thought out.  Using moles to test (or test-drive) poorly designed code can be easier (up front) than writing the code correctly, especially when working with legacy code.  But, I fundamentally disagree with his assertion that KISS (keep it simple, stupid) and YAGNI (you ain't gonna need it) should be used to dismiss such fundamental principles as SOLID and context boundaries.  Even Peter Provost, a Visual Studio program manager lead at Microsoft, disagrees with using shims in this manner as he lays out in his post on shims.

Our industry is young and evolving.  We need to be sure, as we evolve, that we are basing our evolution on sound principles, not just on what makes our daily jobs less frustrating up-front, especially, when it causes long-term maintainability consequences.  What do you think?

Monday, April 30, 2012

The Err of Business Analysts

First, let me say, I'm sorry to the Business Analysts (BAs) who may stumble upon this post, I'm sure you will have lots of disagreements with my assertions.

I read this article on InfoQ this morning and it made me start thinking...again...about business analysts. I'm not particularly interested in the argument over the terminology of business analyst vs. business architect, but, if we're going to accept Malik's definition of those two roles, I'll take a business architect over a business analyst any day. Personally, the term business analyst makes me shudder.

I've been a developer for about 18 years and I have worked in a number of different IT organizations including: small and large shops both with and without BAs and with BAs who held different types of roles. In my many adventures, I have come to very much dislike the role of business analysts, especially IT-based business analysts. Again, sorry to some of my friends who fit in this category--it's not you I dislike, it's the role. And the same holds true for business-based analysts depending on what their true role is. The real problem that I have seen with BAs comes down to Customer Affinity. You really do want your developers to be interested in and excited about "the business, it's processes and rules". The more involved and educated your developers are about the business domain and vision, the better prepared they will be to design software that fits that vision. I thought Eric Evans said it well, when he stated in Domain Driven Design, "...if programmers are not interested in the domain, they learn only what the application should do, not the principles behind it. Useful software can be built that way, but the project will never arrive at a point where powerful new features unfold as corollaries to older features."

The problem with introducing BAs is that they are roadblocks to customer affinity for the developers. The typical reason for creating a business analyst role is at the root of the very problem. Typically a business analyst is used to do a lot of the research work to understand the business and then to document the requirements so that the developers can focus on development -- essentially the same as saying, "let's not waste the developers' time learning the business vision so they can focus on developing the requirements." Unfortunately, there is intrinsic value in the work required to understand the business and the best way--perhaps the only way--to find that value is in the back-and-forth discussion that has to occur to translate business vision and needs into software. Unfortunately that value gets lost in communication, even by the most meticulous and skilled BAs. Even the use of BAs in an agile environment (where face-to-face communication is valued over meticulous documentation) leads to failure. The in depth discussions that result in the back-and-forth questions and answers yields value to the BAs but gets lost in translation to the developers. No amount of communication with the BA will ever make up for the lost opportunity to learn the business vision from the business experts.

This is because of the communication gaps that we all must jump. If you're not familiar with the term "communication gaps", I'd recommend taking a look at that link and in fact the whole section on communication in Alistair Cockburn's book Agile Software Development. Communication is difficult, we all git it wrong, all the time. We can never fully be sure that someone else truly understands our point of view or that we have fully understood another's. When you inject a business analyst between a business expert and a developer you now have to jump two communication gaps, business to BA and BA to developer, greatly increasing the risk of loss of fidelity and almost ensuring a loss of vision. Further more, rather than shortening those communication gaps, over time, between the business and the developers you end up widening them in some cases as miscommunication is increased.

The problem with BAs is that they are responsible for understanding the vision for the business from the domain experts rather than being responsible for the vision itself. This is why IT-based BAs are the worst type of BA -- in my opinion. They have IT responsibilities, not business responsibilities. They fall squarely into this pitfall of the double communication gap. Only slightly better than the IT-based BAs are business-based BAs (BAs who report through the business management, not through IT) whose primary responsibility is to communicate with IT. This is because their primary responsibility is still going to be trying to understand the vision from the domain experts rather than being a domain expert themselves.

Of course, the best scenario would be a scenario where the business owner and the software developer are the same person. Then you have zero communication gaps. But scenarios like that are not very common. And this leads me to believe the best scenario is to not have BAs at all. Unless BA stands for Business Architect and you accept Malik's definition of that term. I don't care what term we use, but the optimal scenario occurs when business departments hire individuals who are responsible for driving the success of that department (or parts of that department)--which inevitably means developing a vision, current and future, of what success means for that department. And then, secondarily, this person is also responsible for helping IT to understand that vision. Helping IT understand that vision should be the smaller portion of their job (although a very important one). It is requisite that those filling this role are very capable communicators, but that value is lost if they are not primarily responsible for the success of their department.

Customer affinity is a tremendous value, and in my experience business analysts, even great ones, are an obstacle to customer affinity.

On a side note, this is one of many reasons why I love working at Pluralsight. The developers work directly with the Partners who hold the vision, current and future, of Pluralsight. Much of that is funneled through one of the partners, Keith Brown, who has been involved in the code base. And so, not only does he have the business vision, he also understands the code -- in some cases better than the developers. It is a unique and beneficial blend, and further, if we have any questions we are free to speak with anyone in the company, including the partners, for any clarification. It allows me to get excited about the business and enjoy my work more and it gives me greater confidence that I am writing the software the business wants.

Thursday, April 26, 2012

Customer Affinity

I liked Martin Fowler's article on Customer Affinity that he recently retreaded. I've worked places before where the developers were not allowed to talk to the customers and were forced to work through BAs. I learned then that business analysts in general are an obstruction to Customer Affinity unless those analysts are embedded within the department they represent (i.e. they are domain experts, not IT liaisons). Really liked his point that developers should not be measured by the frameworks and algorithms they know, but in their ability to understand and get excited about the business domain.

Friday, April 13, 2012

Simplicity by Edgsger Dijkstra

I love this quote from Edgsger Dijkstra (1972 winner of the Turing Award):
"Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better."


Friday, March 16, 2012

VS11 Fakes Framework

It seems to me that Microsoft still doesn't quite understand the serious TDD culture.  I recently saw an InfoQ article entitled VS11 Gets Better Unit Testing Tools, Fakes Framework.  My assumption is that the word "better" in that title applies only to the unit testing tools, not the fakes framework.

Although, I like that Microsoft has, for a while now, recognized the existence of the testing community and that they are trying to improve their tools to fit that need, it seems that they often miss important details.  Admittedly,  this is beta, and I only know as much about their fakes framework as I've read in that article and links from that article, however, it seems from what I read that they are not including mocks in their fakes framework.  They have stubs, and what they are calling "shims".  Shims essentially allow you to provide delegates to operate in place of the actual method calls on the object you are faking, so kinda like stubs but more like a fake since you can actually provide a method that executes so you can do more stuff than just specify a return value. This would be great, except that it is completely unnecessary and I think it is going to lead to some awful, spaghetti-like test code.

The other problem I see with shims, if I am understanding how they are implemented, is that you use Visual Studio to generate your shim classes.  On a side note, I was very happy to see they are deprecating private accessors, but it seems that shims are going to have a lot of the same problems. For example, if I am coding against a shim and I decide I don't like the name of a method on the object I am shimming, I'll want to refactor it right there.  I fear that, just like private accessors, if I refactor a shim, I am only refactoring the shim, not the real class, and so shims are going to be an obstruction to refactoring, just like private accessors are.

If Microsoft wants to include a fakes framework in VS, I don't understand why they don't embrace what is already working well in the TDD community.  Fakes frameworks such as RhinoMocks and Moq already give you what you need and they both embrace the Arrange-Act-Assert approach which leads to much cleaner tests.  With Arrange-Act-Assert you setup your test at the top of your test (arrange), then make the call to the system under test (act), and then you do all your assertions.  By not including mocks, the VS11 fakes framework will cause people to have to do assertions as part of the arrange phase of their tests as suggested in the InfoQ article when the author stated, "Mocks are missing, but you can do assertions within the stub method implementations."


Perhaps the most frustrating thing about this new fakes framework is that we will have lots of people, who are earnestly trying to learn how to do TDD, and they will learn that shims are the right thing to do and they will have no understanding of a true mock.   That means that, even though I can just ignore the existence of the new fakes framework myself,  developers everywhere will start doing things the Microsoft way and someday I'll have to work on some code somewhere that uses shims instead of something more reasonable because they have embraced the new framework.


In my opinion, this is another failed Microsoft attempt to emulate what the rest of the development world has already figured out.

Tuesday, March 13, 2012

Code Simply

I've been coding for a quite a while.  Unfortunately, I spent a lot of my career not realizing I was writing bad code.  A few years ago, I think I started to learn what it really means to write good code (of course, I thought that at the beginning of my career too).  This happened when I began working on my first real agile project.  The methodology was XP.  I started learning about things like loose coupling, dependency injection, test-driven-development, etc...things that began to help me write more simplified code.  Having said that, I think I've always had a propensity to try write the simplest code that could possibly work; I just didn't always know how.


Of course, I've written code that started out simple and when I was done I looked back and thought it was a mess.  And, admittedly, I still do that.  It seems inevitable that today's pride will always be tomorrow's embarrassment.  But I do feel I'm doing better.


All around me I see examples of things I could do better, or at least, when I open my eyes that's what I see.  There are some things that I believe in that have become guiding principles, things like loosely coupled classes, test-driven development, preferring composition over inheritance, the simplest design that could possibly work, etc.  And there are other things that seem like a good idea and that I hope are right, things like Domain Driven Design, for example.  And of course, there are more concrete things that I recognize I could learn more about, like when Mike Clement (@mdclement) tried to melt my mind recently in his session "Linq (From the Inside)" at Utah Code Camp.


The more I learn, the more I realize how much more there is to learn. Some things, when you hear them, seem obvious and simple, others can be quite complex and difficult to understand at first. The interesting thing is, learning complex and difficult things about writing good software doesn't lead to more complex code, it leads to simpler code.  The more we learn, the more we are empowered to create clean, simple code.  The measure of a good developer is not his (or her) ability to create impressive frameworks and complex code; it is instead his ability to identify the most simple solution to a given problem that truly delivers all the desired features to the user.  I hope to be able to learn to do that better.


I've found that as I learn, I want to share, and that has led me to create this blog.  


It can be my chronicle of today's proud moments turned into tomorrow's embarrassments published to forever live in infamy on the interwebs.