Monday, May 12, 2014

The Debugging Mindset

A good programmer can write clean, understandable, code. A great programmer is also a great debugger. What do I mean?  In every software project I've been a part of there have been one or two issues that are almost impossible to solve. There's no apparent root cause, they happen sporadically, they are difficult to detect, and cause all sorts of problems that require significant cleanup. In my experience most people like to blame these issues on parts of the system they don't understand. Developers will say it's network issues, DBAs will say it's the application code, etc. It takes a great programmer to take the chaos of one of these bugs and figure out the true root cause.

This programmer follows a logical path to determine the root cause, every time. There are no guesses, there are no assumptions. Everything must be proven or dis-proven and checked off the list of possibilities. This programmer has the debugging mindset - the understanding that inside of all systems there are a series of causes and effects. Even this simple fact is sometimes disregarded when IT shops are dealing with the hard problems. When it comes to computers, nothing just happens. There is a cause. And it's your job to figure out why.

You must think - scenario A causes scenario Z. At this point the debugging mindset is simple. 1) figure out all the causes and effects between A and Z, and 2) figure out which ones are working and which ones aren't.

Let me provide an example.

Let's say you are working on a mobile app that plays a song from a server once the user presses a play button.  Sometimes, certain users are reporting that they can't play a song once the button is pressed.

The first step is to create a high level list of causes and effects.

For instance, when a user touches the screen, the app should call a certain method.  The method then calls a service which downloads the song. Then, the method will start playing the song.

You have three high level causes and effects. The second step is to determine which one of these is not working - for which you need the appropriate tools, and for different problems you need different tools. If you are trying to determine if the method is called, you could use a process debugger in an IDE.  If you have access to the code, you could change the first line of the method to write to a file so that you can verify the method was called. If not either of those, are there any actions changed in the method body that you could use?  Is there a setting to allow the operating system to log method calls?  You get the idea.

Once you have determined the part that isn't working, you dig deeper. If you determine that the button isn't working, then what causes and effects are you missing?  Again at a high level, the screen picks up an electrostatic charge and send the coordinates to the CPU. The CPU then notifies the operating system. Is the screen not picking up the user's touch?  Maybe the user is wearing gloves or the screen is wet?  You could go deeper than that if you wanted. Usually you don't have to, but for the really hard problems you'll likely have to go deeper than you thought.

But what if you verify each cause and effect and you can't determine what's wrong? Then your assumptions are incorrect. Either you do not know all the causes and effects, your tests were not correct, or you interpreted the results incorrectly. This is where hours upon hours have been spent in development time. Question everything. Every file name, every class, every link in the chain.

To summarize:
  1. Figure out the chain of causes and effects
  2. Use a tool to figure out which link is not working
  3. Break the piece that is not working into smaller pieces, and start back at 1 until the root cause is determined.
So how do you get better at debugging?  I can't speak for everyone, but I've started a log of every debugging task that takes me more than 5 minutes to solve.  It started paying divdends after just a few days.  In particular, I learned that I wasn't reading the entire stack trace of exceptions and was missing crucial details.  I was missing Step 1; I didn't know all the links in the chain.

Unfortunately there is no silver bullet to becoming a great debugger.  It is always a learning process, and as you learn new technologies you will find new links in the chain you didn't know existed and new tools to inspect each link.  Keep with it, keep challenging your assumptions, log your 10,000 hours, and become a great programmer.

Sunday, May 11, 2014

Code Lasts Forever

Once you write a piece of code, expect it to be in production forever. This doesn't mean that it won't be modified, but it does mean that the architectural decisions you make early on in a project can determine to a huge degree the amount of work that will need to go into making modifications in the future.

I once was asked to build a "simple" application that would calculate productivity bonuses, just as a stop gap until IT could build something robust. The platform: Microsoft Access. Arg.  I can't harp on Access to much, though.  It provides a good place for people inexperienced with enterprise systems to create business applications.  The problem is that its not production-ready.  It's when Access is used in production worthy, business critical operations that things get hairy.  After it was in use for a few months management decided that bonuses would now be based on a percentage of each person's salary instead of using an old methodology. Which meant that the "simple" Access database residing on my laptop would now contain a list of everyone's salary! This was way before the massive data breaches we have seen recently, but that aside, it would only take a hard drive crash, spilled beverage, or power surge to affect the take home pay of all the employees at the company. Oh, and to make things worse, I was an intern at the time...

The point is that once something is built that gets the job done, it makes it that much harder to justify the cost of upgrading due to "technical reasons".  The guys in charge of the budget don't care if it's in Access, .NET, or on an mainframe. If it works it works. Unfortunately it sometimes takes a data loss or data breach for management to invest in solid IT hardware and software.

So any time you write a piece of code, think - this could be around forever.  It will change the assumptions you make early on and will help to avoid costly mistakes.

Friday, May 9, 2014

The Two Irrefutable Laws of Understandable Code

The developer community has no lack of good advice on effective coding, but the shear volume can be overwhelming.  The Two Irrefutable Laws are a great starting point to keep developers from writing nasty code.  Or if you've been coding for awhile you might still be breaking these laws to increase your productivity.  The truth is, breaking these laws will allow you to be more productive in the short run, but not your team.  Any enterprise-worthy application will be modified by many people, and needs to be quickly understood by those tasked to make changes.

The affects of this lawlessness are contained in code bases in practically every company.  You all have seen what I'm talking about - the code that's hundreds of lines long with if-then-else logic and loops scattered throughout.  It's even worse if large blocks are commented out.  The following rules put a "cap" on this kind of code, so that the results are understandable and maintainable.

Irrefutable Law 1: Understandable code contains short methods


CS nerds will call this cyclomatic complexity. I can't tell you how often I've looked at a code base and seen methods that are hundreds of lines long.  This just doesn't make sense.  In this day and age, every good IDE has a "go deeper" shortcut (F12 in Visual Studio and F3 in Eclipse, for example), making it extremely simple make complex logic understandable if it's moved into several methods.
As a rule of thumb, I try to put one piece of logic in each method.  For instance, let's say we have the following code.
SubmitOrder(Order order)
{
 //if order is not to an international location
 if(order.Country == 'US' && order.State != 'PR')
 {
  doSubmitOrder(order);
 }
}
For those questioning why 'PR' is part of the logic - USPS has deemed Puerto Rico an international location, meaning that all packages much go through customs. Just by asking that question you've added one more thing that your mind has to keep track of. A better way to write this code would be:
SubmitOrder(Order order)
{
 if(isNotInternationalShipping(order))
 {
  doSubmitOrder(order);
 }
}

bool isNotInternationalShipping(order)
{
 return order.Country == 'US' && order.State != 'PR';
}

This way someone can quickly see that submitting the order only happens when the order does not require international shipping. They don't have to know that international shipping includes Puerto Rico. Also note that the comment is gone. Since the method is named well, the comment is not needed. Which brings me to the second law.

Irrefutable Law 2: Understandable code is well named


If we are using Law #1 to break up our code into smaller parts, the smaller parts need to be named well.

Irrefutable Law 2A - Typing is cheap
Stay away from abbreviations. Most developers countering this argument would say that they don't want to spend time typing out long object names. But how many of these developers aren't using code complete? Nowadays you just have to type a few letters of any keyword and up pops the full name. And let's face it - one of the biggest time wasters in the modern IT department is trying to understand bad code. For management this translates into a money pit. So in the end it's much cheaper to write expressive, unambiguous names that self document the code, helping out other developers trying to figure out what the ?!$@ your code is doing.

Irrefutable Law 2B- It's OK if it takes awhile to figure out a good name
My rule of thumb on this is to time box 5 minutes or so. If I can't figure out a good name in that amount of time, I'll move on but make a note to come back to it to refactor. Many times I've had a word that kind of describes the object, but not well, so I'll pull up thesaurus.com to search for exactly what I'm looking for. Also try talking through your options with someone. Sometimes it just takes vocalizing the problem to come up with the right answer.

Hopefully these simple rules will point you in the right direction for writing understandable code. Your fellow developers will thank you for it.