Thursday, July 24, 2008

Wow – What a trip. F#, C#, TDD, COBOL and enlightenment

Life truly has a fantastic humbling ability. A few months ago I was in the midst of a deep dive, swimming in the goodness of F#. I had all sorts of wonderful ideas on future blog posts. I was treading in a veritable pool of functions, records, pattern matching, currying; all while not getting bogged down in semi-colons and missing curly brackets! I was on top of the world.

Then life reminds you what is truly important, my wife and I were blessed with our 3rd child at the end of May. What a wonderful addition to our family. So after one month of anxiety waiting for the baby to arrive and then another two months of interrupted sleep, burping, changing diapers and worrying about poop, (those of you with kids understand!) my thoughts never strayed too far the pool. So here I go, with a great big cannonball, I splash back into the blog waters. It was during those few months out of the pool, when my only concentration on coding was in C#-ville from 9 to 5, several statements of a profound nature crept into my head. I have been long since spoiled on the benefits of Test Driven Development (TDD); write a test, write some code, make the test pass, refactor, run tests - beautiful simplicity.

As simple as that is, one of my co-workers would always write extra code; code that was not needed to make the tests pass. He could not bring himself to just write the simple code that made the test pass. I would always ask the question, why did you write that? The test would still pass if that block was not there.

His reply was always, "We'll need it soon." Rather than argue, because he would be right, and delete the code he had written, we would go on our way and complete the task at hand. (By the way, when teaching TDD to someone new, deleting code that has no coverage is a great way to enforce the rule that you shouldn't write any code that is not a result of a test!)

So one day, our group was having a small discussion while taking a break from coding and talking about past jobs and different languages we knew. This guy mentioned he programmed COBOL for several years prior to migrating to java and C#. While I snickered and mentioned my own experience with COBOL; a mere two semesters in college and a professor that couldn't code, he laughed and said, "you had to be careful about your code, if you had a bug, you could crash the entire system for everyone". So after my casual chuckling, I repeated his statement and added something to the effect of, "wow, I would have surly pissed of a lot of other developers, my code is always buggy, I rely on tests to find them, not the final run." As soon as I said that, the little light switch in my head clicked and I then realized why our coding styles were so different, why he just couldn't write the simple code and trust the tests.

Being faced with the situation of crashing an entire mainframe, COBOL developers, at least this one, had to make sure the code was sound before submitting your code to the queue. Comparatively speaking, if I crash my system, 1 person is down for a little while, but if you take down an entire mainframe region, there's a little more impact. (I am not picking on COBOL developers here, it's just part of the story.)

Compare that to TDD, where my mindset is, "hey, if I refactor this chunk of code into a method, these two other block are the same, and could be made a delegate. Let's try that!" Or, things like "I don't like that switch statement, lets make it a dictionary". Compile, Run Tests…. oops, the entire test fixture fails, oh well, ctl-z.

This tantalizing story leads me up to these few points:

Profound statement #1: TDD is an incredible gift. Having a series of unit tests gives you the confidence and comfort to try really crazy things with your code. It also gives you the ability to enforce some of the higher level design patterns to make your code truly awesome; open closed principle, single responsibility, Liskov substitution principle, to name a few.

Profound statement #2: TDD has made me lazy. I let my tests find my bugs.

Profound statement #3: TDD has made me confident. I realize that this statement might conflict with statement #2, but mixing of these traits just makes me smile. If my QA tester finds a bug, I can write a test to re-produce the issue and can guarantee that bug will not surface again.

Profound statement #4: You need productivity tools like ReSharper and TestDriven .Net. Not to sound like a commercial for ReSharper, but man, that stuff is like crack! When you combine those tool suites, with your IDE, you can do really fun things when pairing like "mouse free Thursday's". Once you learn all the keyboard shortcuts within an IDE, you realize the mouse is a distraction. The speed, fluency and pace you can write, and refactor code triples when you are not fumbling around with that little pointy arrow. You find that your thoughts are focused on your end goal and not scrolling through your code looking for those pesky syntax problems like missing curly bracket or missing semi-colon. And yes, it is a bad habit to rely on the compiler to tell you were that missing semi-colon is!

So now here's my disclaimers.

  1. I have respect for COBOL programmers, my intent here was to not poke fun at them.
  2. I am not pushing ReSharper or TestDriven.Net, those are the tools we use. There are productivity tools out there. ( If Dustin Campbell reads this, I'll have already heard the groans when I mention ReSharper!)
  3. Mouse-Free Thursdays is an awesome experience. While pairing, each developer unplugs their mouse, only for Thursday and only while coding. After a few MFT's, you find you are a much more efficient coder.

3 comments:

Jim Holmes said...

Nice article, Nate!

I agree that TDD can in some respects make us a bit lazy, relying soley on the larger-scope tests which validate behavior or prove a feature's correctness.

The part where we have to utterly force ourselves to rely on solid software engineering skills comes with things like trapping potentially bad return values from calls, or ensuring that we're not missing a stupid off-by-one error somewhere. These sorts of bugs aren't generally covered in a spec, ergo we have to remind ourselves that TDD isn't a silver bullet set of training wheels -- we still need to do good dev.

Sorry for the blathering response. Excellent post!

Nate Hoellein said...

Hi Jim - Thanks for the comments! Regarding the tenacity we need to have as developers - I couldn't agree more.

You can have hundreds of unit tests, with 100% code coverage, but those one off errors, unhandled exceptions, and those bugs where you find youself saying "I can't believe that happened, that's impossible!" somehow still creep into your code.

Anonymous said...

Profound statement 2 may need to be tweaked. Your production code is only as good as your tests. How can you say you let your tests find bugs in the code. What if you do not write the test that finds the bug in the code.

Writing more code than necessary to pass the test is a question of discipline. People new to TDD might exhibit it. It has nothing to do with COBOL programmers and crashing environments.

You may have a lot of tests, still can you write code that can run out of control ? My answer would be yes.

Ask yourself this question, have you not written applications without using TDD, that worked/still work in production.

Finally, I am the Cobol programmer you refer. I am amazed by your ability to come up with all these profound statements in the
short time we have pair programmed.

Congratulations on your extraordinary power to take a random statement and construct a blog post with multiple profound statements.

Anyways, wish you all the best.