Sunday, December 13, 2015

Lazy vs. Careless

I am a lazy developer. I have no trouble admitting that because laziness is what drives me to automate tedious processes. It's what compels me to let my tools do as much work as possible. It's what pushes me to use unit testing for easy regressions.

I try to avoid being careless. Carelessness is what tempts us to cut corners when we know we should be doing something differently. And carelessness often comes back to haunt us.

I made a careless decision 6 years ago that is still causing me problems. Let's see where things went wrong, and hopefully others can learn from my mistake.

But before we do that, let's take a closer look at the differences between lazy programming and careless programming.

Lazy Programming
Laziness can be a virtue as a developer. Over the years, I've learned lots of shortcuts in Visual Studio (my primary development environment). This includes keyboard shortcuts as well as code snippets and code completion.

Visual Studio Helpers
In Visual Studio 2013 (and earlier), it wasn't always obvious where Visual Studio could offer us help. I always tell people to look for the little purple rectangle:


Here we have a class that declares that it implements an interface (IPersonRepository), but it doesn't yet have any of the required methods. The little purple rectangle under the "I" tells us that Visual Studio wants to help us out. (This does have a name, but I always forget what it's called -- and it's very difficult to search online for that.)

Whenever we see this, we can press "Ctrl" + "." to get a popup:


Then if we choose "Implement interface 'IPersonRepository'", it does all the hard work for us:


Now all 6 methods are stubbed out for us. This saves us the hassle of having to copy/paste method signatures whenever we implement an interface.

In Visual Studio 2015, the purple underline has been replaced with a more prominent light bulb:


When we click on the light bulb (or press "Ctrl" + "."), we get a similar popup that allows us to implement the interface. Plus, we get a bit of a preview to show what Visual Studio plans to do for us.


This is just one example of how Visual Studio fills in code for us that would otherwise be tedious to type in.

Keyboard Shortcuts
Another thing I love are keyboard shortcuts. Unfortunately (or fortunately), there are tons of them. I've picked a few that have been most useful in my coding:
  • Ctrl+K, Ctrl+C : Comment current line/selected block
  • Ctrl+K, Ctrl+U : Uncomment current line/selected block
  • Ctrl+K, Ctrl+D : Reformat document
  • Ctrl+K, Ctrl+S : "Surround with" - wraps a selected block in a code snippet
  • Ctrl+Shift+B : Build project
  • Ctrl+Space : Show code completion options
  • Ctrl+Shift+Space : When inside method parens, this pops up the list of parameters
These are the default key mappings when using Visual Studio with the C# settings. Resharper has its own set of keyboard shortcuts.

The point is that we can get a productivity boost when we learn how our tools can make development faster.

Snippets and Completion
I love code snippets. I use them to quickly create properties and "for" loops. I also love the "Surround with" keyboard shortcut in conjunction with the "try" snippet to wrap a block of code in a try/catch block (or the "tryf" snipped to wrap a block of code in a try/finally block).

And when hooking up event handlers, let's let Visual Studio create the methods for us. After typing an event and then "+=", we get some help:


When we press tab, we get a chance to rename the method:


And when we hit tab again, the event handler is stubbed out for us:


This example is from Visual Studio 2013. Visual Studio 2015 handles things a little bit differently; it stubs out the method first and then uses the new rename functionality to give us a chance to pick a new name.

Lazy is Good
So when we look at laziness in this sense, it's good. It gives us motivation to learn our tools so that we can write code quickly and easily. In addition, it's changed the way that I test code. I'm heavily into unit testing, and that's primarily because it's faster and easier for me -- especially when it comes to regression testing.

Probably the best thing about laziness is that it drives us to automate tedious tasks. Let's write a script rather than checking values manually. Let's put together a small application so we don't have to verify values every day. Let's automate deployment -- 1 click to build, test, deploy.

Careless Programming
What we really need to watch out for is carelessness in our programming. This is where we cut corners when we're putting things together. It could be global variable that we put in because it's faster to write than trying to figure out a more appropriate way to handle the functionality.

A global variable doesn't sound like it could cause a ton of problems, but it can. I worked on an application where I used a global variable to hold some shared data. The problems came about when reliance on the global variable meant that a single class kept getting bigger and bigger (because it needed that variable) rather than things getting split up into more appropriate chunks. That made things harder to support as time went on.

But that's not the carelessness that is still causing me problems. For that, we'll need to look at my sample applications.

A Simple Data Set
Way back at the end of 2009, I was working on some sample applications for my first outing as a speaker. Those first presentations were in January 2010, and I put together some simple data to use with them. Here's the output from a Silverlight example on how to use data templates and value converters:

from Intro to Data Templates and Value Converters in Silverlight

This shows a set of 7 records. (And in case you're curious, they are "television outer space commanders".) For this Silverlight application, I needed a web service, and ended up with a method that hard-coded the data:


This looks harmless enough, but I made a careless decision to cut a corner. And that was getting the dates into the data:


I decided to use a DateTime.Parse because I was copying the dates in from somewhere else, and it was a lot easier than splitting the components up with a proper call.

Carelessness
At the time I did this, I knew that I was not doing it the "right" way. But it was a way that would work fine. I justified this by telling myself that the data wasn't the point of the demo (the point was to show data templates and value converters). I told myself that no one who downloaded the code would care about this.

But I wasn't thinking ahead.

Carelessness Compounded by Laziness
Over the next 6 years, I would write many more demo applications. And because I'm a lazy developer, I used exactly the same data set for all of them. This was great for me because regardless of the presentation I was giving, the data would look familiar.

from I'll Get Back to You: Task, Await, and Asynchronous Methods

The look of my applications has changed, but the data has not.

The laziness is good. That means that I didn't need to come up with a new data set for each new demo. I could use what I already had. The bad part was that I was re-using careless code, and that would come back to bite me.

Going International
I think another reason I didn't worry about my careless code is because I never dreamed that my demos would leave the US. But those demos did start to go international -- primarily through Pluralsight and my YouTube channel. That's when things started breaking down.

My code:


Only works if someone is using the US date/time format: MM/dd/yyyy. And that's a problem because most of the world uses a format that makes much more sense: dd/MM/yyyy or yyyy/MM/dd.

The code above will throw an exception in either of these scenarios because it will be treating "17" as the month -- and there is no 17th month.

Some options to fix this include setting a specific culture or date format. But it was better to get rid of the ambiguity by using the constructor for DateTime:


This will work regardless of culture because the constructor parameters are clearly defined as year, month, day.

The updated GetPeople method looks like this:


Tendrils Everywhere
So the fix is pretty easy, but by this time, the bad code was *everywhere* -- in my samples for generics, delegates, lambda expressions, interfaces, design patterns, dependency injection, ugh. When I put my code samples together, I have to goal of someone being able to unzip the download and then build and run the code without having to do anything special.
This means that a copy of the careless code is included in pretty much every single downloadable project that I have.
I have been slowly updating the projects as I can. And I make sure that any new projects have the good code. But the fixes are tedious, and I'm still working on getting code updated.

Most of my code on GitHub is good, but there are still a lot of zip files on my website that are not. I'll get there eventually.

Watch Out for Carelessness
I cut a corner 6 years ago. I knew I was cutting a corner. I knew that there was a better way of doing things. But I made a careless decision that was short-sighted. I wish I could go back and fix it. It would save me a lot of work if I could. But that's how we learn.

I embrace my laziness as a developer. I let my tools do most of the work for me. I embrace unit testing. I look for ways to automate tedious work. These are all ultimately good results.

But I need to watch out for carelessness. I don't want to cut corners that will cause me pain in the future. If the solution feels "wrong", it probably is.

Embrace laziness, but watch out for carelessness.

Happy Coding!

No comments:

Post a Comment