Monday 28 January 2013

Single method perspective vs single behavior perspective

Hi, today, I'd like to outline few differences between a technique called Programming By Intention and Test-Driven Development. I'm currently halfway through Essential Skills For The Agile Developer and the chapter on Programming By Intention made me revisit this fine technique and think it over again, especially in comparison to Test-Driven Development.

What's Programming By Intention?

On the book's page you can get the chapter on Programming By Intention for free and read it, but for those of you that like summaries - it's an outside-in approach to writing methods, that makes a distinction between so called "Sergeant" methods and "Private" methods. A "Sergeant" is a method that has the single responsibility of describing a high-level workflow and it delegates all the implementation details to "Private" methods. When beginning to write a piece of code, you start with a "Sergeant" and pretend that all the methods it needs to call already exist (although they usually don't). A simplified example of a "sergeant" for logging-in logic would be something like this:

public string LogIn(string userName, string password)
{
  string homePage = GetHomePageAddress();
  string loginPage = GetLoginPageAddress();
  string response = GetDefaultResponse();

  if(AreCredentialsValid(userName, password))
  {
    response = GetResponseWithSuccessfulLoginMessage();
    RedirectTo(homePage);
  }
  else
  {
    response = GetResponseWithLoginErrorsFor(userName);
    RedirectTo(loginPage);
  }
  return response;
}

As you can see, in the above example, we're passing what we can as method parameters instead of relying on fields. Also, there are almost no operators (even "new" and object access operator ".") - they're hidden inside the "private" methods to whom we delegate the work. These methods don't exist yet - we're just imagining how they should look to fit our needs. The next step is to generate skeleton bodies for "privates" (preferably using an IDE) and fill them in one by one (Of course, a "private" for this method might also be a "sergeant" with its own "privates"). A simple implementation example of a private method might look like this:

private string GetResponseWithLoginErrorsFor(string userName)
{
  return userName + " could not login. Invalid credentials";
} 

Programming By Intention produces code that is very readable - every work-flow step has its domain-specific name. Also, this technique is so powerful, because it separates specification perspective from implementation perspective and, by doing so, leads to a very high method cohesion. Thus, for example, it's very simple to refactor our exemplary "sergeant" later into something like this:

public Response LogIn(
  UserCredentials userCredentials,
  Page homePage, 
  Page loginPage)
{
  Response response = Response.Default();

  if(credentials.AreValid())
  {
    response.SetUpWithSuccessfulLoginMessage();
    homePage.RedirectTo();
  }
  else
  {
    response.SetUpWithLoginErrorsFor(userName);
    loginPage.RedirectTo();
  }
  return response;
}

and achieve class-level cohesion, although the decision whether to pay that cost right now or defer it for later is left to us. Thanks to this, Programming By Intention can be at best almost no-cost (in case of small programs that don't need any maintenance in the future), while at worst allowing us to defer the cost for later while introducing minimal technical debt (in case of more serious pieces of software that need cohesion on every level).

How does it relate to Test-Driven Development?

I must confess that this is the second most powerful technique I know for writing good and easy to read code, the only more powerful being Test-Driven Development. Whenever I am in a situation where for some reasons I cannot use TDD, I use Programming by Intention.

Now, let's examine the similarities between the two techniques first:

  1. Both are outside-in approaches (I'm talking here about the outside-in style of TDD, as it can also be used bottom-up, as e.g. Kent Beck prefers to use it), crafting APIs and method names from the perspective of their use
  2. Both are variants of the divide-and-conquer strategy
  3. Both are about specifying intention through code
  4. Both tend to lead to highly cohesive designs, although TDD demands both class-level and method-level cohesion, while Programming By Intention tends to demand method-level cohesion only (although class-level is easily achievable from there, as in the example above).

Now for the differences. The main one in my opinion is that Programming By Intention looks at writing code from a perspective of a method, while Test-Driven Development looks from the perspective of a behavior. This means several things:

  1. When implementing a single method (as in Programming By Intention), one does not see the whole context of its usage - only this one method. On the other hand, in TDD, when writing a unit-level spec, it usually begins with object instantiation and ends with observable result (either returning a result or call collaborating object's method)
  2. The method perspective also means that, when writing it using Programming By Intention, one has to consider the method as a whole - e.g. all required execution paths (ifs and elses) must be considered throughout the whole implementation process, since they're all usually added in one go. On the other hand, in TDD one only worries about the current path he's specifying with a test. Other paths are either already tied to other tests that will remind us when we unintentionally break them (so we don't have to think about them anymore), or on our TODO list (so we don't have to worry about them yet).

The second point is, IMHO, really important. Few days ago, I finally watched a presentation by Gojko Adzic and Dan North from BDD Exchange. Talking about accelerating Agile, Gojko and Dan stressed very heavily the importance of making a "measurable impact". I think that, when scaled down, this idea aligns well with TDD. The "measurability" of the "impact" we make with our executable specifications can be considered in two ways:

  1. For a single specification: watching the RED to GREEN transition is an act of measuring the impact - reaching the GREEN phase means that you now have something that was not present yet during the RED phase.
  2. For a suite of specifications: the comparison of already implemented specs vs the ones left on the TODO list are a way to measure "how much of the impact" was already made vs what's left.

Summary

Personally, I find many similarities between Programming By Intention and TDD, with Programming By Intention being lower cost (at least in the short run) and easier to learn, while TDD being more powerful and advanced. I dare to say that TDD is Programming By Intention on steroids. Also, I believe that learning Programming By Intention helps in in grasping mock-based Test Driven Development. Hence, if you are struggling with outside-in TDD using mocks, it's a good idea to train Programming By Intention first.

That's all for today, I hope you liked it and see you soon!

Sunday 6 January 2013

Test Driven Development - objections, part 3

Welcome to part 3 of my post about TDD objections. Today's gonna be the last part of my comments on common objections to TDD. Again, I'm not an expert on these things - I'm merely writing down my thoughts and experiences, so any comment is appreciated! I'd love to know your objections or which ones you're most often getting into, how you deal (or dealt) with them etc.

Where did we leave the last time?

Ok, let's take on the next set of objections from the list:

  1. Not enough time
  2. TDD will slow us down, because we'll have to create a lot of additional code
  3. TDD will slow us down, because we'll have to maintain a lot of additional code
  4. We don't have the necessary skills or experience
  5. We don't have the necessary tools
  6. (This one is unit testing specific) There are already other kinds of tests, we don't need unit tests?
  7. It's very hard to perform with legacy code
  8. I already know how to write a testable code, TDD will not really improve my design
  9. We've got enough quality and we're doing fine without TDD
  10. My manager won't let me do TDD
  11. There's no "scientific proof" and enough research on whether TDD really provides a return of investment

The ones I'm gonna talk about today are the one marked with strong text. Let's go then!

8. I already know how to write a testable code, TDD will not really improve my design

Personally, I know several engineers who actually learned how to write testable code without TDD. They usually added unit tests after code and, over the time, learned to structure the code in such a way that it allows dependency injection, contains small number of private methods etc. If your mates are on this level, it's really not that that bad! However, I observed few deficiencies of such approach when compared to TDD:

  1. It is actually harder to learn how to write testable code without TDD. Also, many hours are wasted on refactoring before one grasps how to do it. In TDD, testability is built-in. So why choose the hard way?
  2. Test-first is often referred to as an "analysis step". You get the chance to take a step back and analyze what should the work-flow be, which behaviors have to be supported etc. Sure, one can use programming by intention to design classes and methods in an outside-in fashion, but when coding, you always see just the current method, or the current statement. In contrast, the unit spec/test forces you to consider whole class from object creation through invoking triggers up to getting a response. This perspective is more useful for analysis (and design) purpose and it is retained each time a new unit-level spec is written.
  3. The benefits of using TDD as a design-aiding tool are not only that you make testable classes that allow dependency injection. The benefits are also in making you focus on just what you actually need, without bothering you with what you don't. In TDD, you state your expectations one at a time, striving for the simplest solution. This leaves less room for over-generic, "just-in-case", framework-like overdesigned structures. Now, don't get me wrong, generic designs are a great and even necessary where genericity is needed and well justified, however, I believe that overdesign is almost as bad as underdesign.
  4. There are other things I have already written about that tend to happen in test-after approach

9. We've got enough quality and we're doing fine without TDD

This was actually responded to by James Grenning on his blog and the response is so good that I have really nothing to add. Go read it.

10. My manager won't let me do TDD

Putting things this way is usually a mental shortcut that might mean different things depending on the context. The most literal interpretation (like receiving a direct order not to do TDD) is almost never the case.

Sometimes, this is merely an excuse. Putting the blame or responsibility on the supervisor is one of the most popular ways of saying "I want someone to take the responsibility instead of me" and may be an example of Cover Your Ass Engineering.

Let me share with you my experience on the topic: in my first project, I thought my boss won't allow me to refactor. But I did - and in the end, no one questioned. In my second project, I thought my boss won't allow me to use smart pointers. But I did - and I was thanked for this. In my third project, I thought my boss won't allow me to use mock objects. But I did - and it was recognized. What's the moral? It's that often it is you who has the authority to make technical decisions. when you put responsibility on people who don't have the authority, they will hold back - this is natural. So go ahead, make the decision and take the responsibility. If it ends with success, take the gratitude. If it fails, take the blame. That's the best way to achieve something.

Sometimes, such complaint may also mean lack of support. In such situations, you can try out two steps that I know help dealing with this:

  1. Build your position - either internally (within the company) or externally (within the wider community) or both. This can be done by participating in open source projects, creating a blog, taking part in conferences, organizing trainings etc. When you are recognized in the community as an expert, your authority rises and people will get persuaded by you more easily.
  2. Instead of hoping your boss will create a plan and make you a part of it, create your own plan and show your boss how he can be a part of it. Show your boss what you want to achieve and what you need from them in order to be able to achieve this.

This whole section put in short is: "you're the one in charge. Believe it!".

11. There's not scientific proofs and enough research on whether TDD really brings any benefits

Oh really...

Let's put it this way - I could really try to convince somebody by listing the references, trying to analyze them, pick some arguments etc. and wait for the other person to say "Ok, I'm convinced" instead of something like "that doesn't convince me, because my case is special and these things do not apply". I could really try and do this.

But I won't. Why?

I won't, because I think this argument is usually raised in highly unfair ways. Ever wondered why anybody needs scientific proof for TDD while they didn't need any for daily stand-ups or four-week sprints, or retrospectives or any other agile or non-agile practices they've already incorporated? Can they show you the scientific research that led them to adopt all these techniques and methodologies?. If so, THEN let's talk about scientific proof for TDD (and it may be a very fruitful discussion). Otherwise, no.

From my observation, the "need for scientific proof" arises when someone simply doesn't want to adopt TDD and is using this argument as a mean to say "I don't wanna do this, unless you prove me that I have no other choice". There may be some reasons why this person doesn't want to try out TDD and they may be VERY good (and worth discussing), but the "scientific proof" argument itself is usually a facade.

Summary

This article ends the series of my comments on common TDD objections. At least for now. I hope you had as much fun reading this as I had writing.

See ya!