Friday, March 8, 2019

On laziness, productivity and elegance (part 1)

I've been doing this programming gig for a long time now, and perhaps it is a function of my age or maybe something more sinister, but I have come to a belief that most things should be easier to achieve than they often seem.


Some problems (often because I have experience of a similar problem), I instinctively know how to approach through writing a chunk of code.  However, decades on, I am a little less willing to churn out a bazillion lines of code or jump through too many hoops to get that job done. This isn't because I don't like cutting code (believe me, it's still a joy to get paid to do this), but more because if it feels like a problem that should be easy to solve and if my approach seems too much like hard work, then it probably is and there is probably a better or more elegant solution.

This avoidance of code doesn't sadden me. In fact, I've started to embrace it as a positive trait that forces me to spend a little more time thinking and learning. In almost all cases, my refusal to accept the drudgery leads to better solutions and practices.  Let me give you an example.

Unit Testing

It's sad to reveal that in a time where some progressive developers are suggesting that unit testing may be a sign that your application hasn't been broken into small enough pieces, other organisations have not even begun their journey into unit testing and still see it as something that they don't have time to do because somehow their project is different to every other project I've ever worked on.

I'm a big fan of unit testing, or more precisely, automated testing (because I enjoy a good blend of unit, integration and behavior testing), but not just for the reasons that you should automate your testing.

Laziness, impatience and instant gratification all play a big part in my desire to write unit tests, and this leads to many positive side effects. So let's imagine I'm working on a piece of code that is several layers down in the bowels of my NextBigThing web application.

I've changed the widget transmogrifier class, so I fire up the debugger, open a browser, log into the app, username... password... enter a customer number into the search box... click on the 3rd match in the list... blah blah blah blah - by this time I'm losing the will to live.

Eventually, my break point is hit, execution pauses, I step through a few lines of code and realise that I've missed the -1 on that string.Length call. Grrr.  I stop the debugger, fix the OBOB and start the whole sequence again.  This is not a fun way to do development, and it reminds me of my C++ days where the compile and link cycle took 12 minutes.

If I've designed it well enough, the widget transmogrifier class has good separation from most of the other big, ugly concerns (like persistence) in my application and surely, there must be some way to just run the code I've been changing.

Now, at this point, I've seen many developers create a new console application, paste their problematic class into it, surround the whole thing with some exception handling and liberally sprinkle some Console.WriteLine statements here and there. The first time they run it, a console window flickers up and is gone before  they can say "bugger."  They add a Console.ReadLine() at the end of the main function and try again.

Enter unit testing.  Creating a unit test project and test method to run your code is no more difficult than creating that console application - in fact I believe it's a little easier as you don't have to remember the Console.ReadLine() call.

You've now got an execution environment for your code that starts in a couple of seconds, is fully integrated into the IDE and can be run at the drop of a hat or as part of a CI pipeline.

You find a few other cases that could be problematic, so you create test methods for those, and before you know it, you have a suite of tests that are expressing and confirming the intended behavior of your "class under test" and are there to help you (and others in the team) the next time you find a bug or to prove that things are logically unchanged after doing a major refactoring.

All these benefits stem from the fact that you couldn't be arsed to fire up the whole application in your development environment and wade through the actions you have to perform to get to the part where it executes your code.

If the only reason you decide to create a unit test is to reduce your build & debug cycle, then do it. You've taken the first step and it's very hard to go back. You're now on a slippery slope that only gets better, and soon you'll be addicted to those little green ticks that the IDE rewards you with when something works as it should. That little hit of dopamine will drive the next test, and the next ... all the way to TDD.

The time that you used to spend stepping through code in the debugger is now better spent writing tests. You find that the quality of your application gets better and development is no slower and often quicker.  Weird huh?

No comments: