The Lazy Programmer

"If you have a difficult task, give it to a lazy person - they will find an easier way to do it." -Hlade's Law

Whenever I am asked why something should be done "a certain way," the answer at the front of my mind usually begins, "Laziness. This is the easiest way." Then I spend a minute recomposing the answer in terms of "efficiency" or "best practises." But enough. It is time to stop villifying laziness and start recognising the virtue of the true key to successful programming.

The "lazy" way I describe is not necessarily the easiest path in the short-term. Instead it is the easiest in the long run to understand, reuse, maintain, extend, and often, to explain. Long range laziness. When the history of software is pondered, the lazy, efficient paths will have wasted the least amount of people's time, money and energy.

In the long run, the truly lazy will always seek the most efficient solution. As you begin to see problems the lazy way, it becomes clear that all the great approaches to software development are in fact predicated on laziness. Prime examples are the Gang of Four's Design Patterns, Boyce-Codd Normalization, and Fowler's Refactoring. Reduce, reuse, recyle.

A Case Study

To demonstrate, consider the threat of SQL Injection. This is where a hacker tries to hijack a legitimate SQL query with malicious code. A SQL Injection attack on a login page may be identified by the use of certain characters not found in normal usernames or passwords, like these:

';#&*%"

There are many ways to protect a site against such attacks, and they all reduce to various degrees of laziness.

First Lazy Form: First Order Lazification (1LF)

First Lazy Form is the solution that plugs the hole in the least efficient way. It is the Band-Aid solution that may work today, but probably does not scale, adapt, extend, or read well. In our SQL Injection example, it would be the solution that proposes using the following sort of code in the login processor:

...
  ' 1LF
  ' Prevent SQL Injection attacks: Clean input with Replace()
  Dim QUOTE as String = chr(34)
  Dim strRejectedChars as String = "';#&*%" & QUOTE
  Dim strCurrentCharacter as String
  Dim intX as Integer
  For intX = 1 To 7
    strCurrentCharacter = Mid(strRejectedChars, intX, 1)
    pUserName = Replace( pUserName, strCurrentCharacter, "" )
    pPassword = Replace( pPassword, strCurrentCharacter, "" )
  Next
  
  ' Query credentials against database
...
end sub

This is unnaturally elegant for First Lazy Form, but it qualifies. The problems?
Inefficient code: All input is run through the same 7 iterations of the loop, for a total of 21 function calls.
Hard-coded Dependency: If someone discovers a new dangerous character (like ":" or "|"), it must be added by hand to strRejectedCharacters, and the loop counter adjusted. Even if the loop were from 1 to strRejectedCharacter.Length, this approach still needs programmer intervention to adapt to any new characters. Sounds like work to me.
Not Reusable: Reusing this code block involves cutting and pasting. More work.

Second Lazy Form: Second Order Lazification (2LF)

Second Lazy Form takes a little thought and/or planning, but results in cleaner code. It will address 1LF's problems of efficiency and make an attempt at a general solution.

...
  ' 2LF
  ' Prevent SQL Injection attacks: Clean input with Regular Expressions and Replace()
  Dim options As RegexOptions = RegexOptions.None
  ' Rule: Only numbers, lowercase, and uppercase characters are allowed
  Dim regex As Regex = new Regex("[^0-9a-zA-Z]", options)

  ' Pre-check the data to see if action needs to be taken
  if regex.IsMatch(pUserName) OR regex.IsMatch(pPassword) then
    pUserName = regEx.Replace(pUserName, "")
    pPassword = regEx.Replace(pPassword, "")
  end if

  ' Query credentials against database
...

This time, a conscious effort is made to only execute code if necessary. The slower Replace operations are only executed if the faster IsMatch operation(s) deems it necessary. And since expressions are evaluated right to left, if the pPassword test returns true for invalid characters, .NET never needs to evaluate pUsername to proceed to the Replace operations.

The approach introduces a rule which eliminates Hard Coded Dependency, thus providing a more General Solution. Usernames and passwords must contain only alphanumeric characters. Spaces and symbols are forbidden. Rather than try to guess all the possible harmful characters and manners of expressing them, this solution defines a finite sandbox which is trivial to enforce.

One problem remains. Our code is:
Not Reusable. Using this block elsewhere still requires cutting and pasting. Maintaining two or more copies of the same code is work, and work is the enemy of the lazy. A comfortable solution lies one level deeper.

Third Lazy Form: Third Order Lazification (3LF)

Third Lazy Form takes code which meets requirements for Second Lazy Form and encapsulates it into a reusable package. There is a lazy debate over whether encapsulation should actually be the rule for 2LF, and clean, general-purpose code the rule for 3LF. An argument can be made that making code reusable provides more leisure time than making code maintenance-free. On the other hand, bad code encapsulated is still bad code. And frankly I'm too lazy to rewrite the section on 2LF so let this be a lesson to you: don't sweat over inconsequential details. And something about planning. Every software mistake teaches something about planning.

' 3LF
Public Class StringCleaner
  Public Function StripNonAlphaNumeric( byRef inString as String ) as Boolean
    Dim options As RegexOptions = RegexOptions.None
    Dim regex As Regex = new Regex("[^0-9a-zA-Z]", options)
    if regex.IsMatch(inString) then
      inString = regEx.Replace(inString, "")
      StripNonAlphaNumeric = true ' Something changed
    else
      StripNonAlphaNumeric = false ' Nothing changed
    end if
  End Function
End Class

Note that encapsulation allowed me to get rid of a few comments, a benefit described by Refactoring. Good structure combined with appropriate function and variable names equals less to be explained. In the code that calls this method I might comment that the purpose is to prevent SQL Injection, actually naming the function "StripNonAlphaNumeric" instead of "PreventSQLInjection" is more descriptive of what the method actually does.

Third Lazy Form is good enough for most people. The solution can be understood, reused, maintained, extended, and explained. Why would you ever go further? I mean it's late, people want to go home already. But there is an even lazier solution.

Fourth Lazy Form: Fourth Order Lazification (4LF)

At the beginning it was discussed that the laziest solution takes the longest view, and requires the least work over the life of the app. Fourth Lazy Form meets all the requirements of 3LF, but also requires you to add features to make problems easier to solve in the future. 4LF is benevolent that way. 4LF pays off in the long-run. This may require rethinking the application's purpose, and perhaps entirely restructuring your approach.

Consider our string-cleaning code. It all began as a slap-dash effort to turn dirty input into sparkling, clean input and evolved into a reusable class. But think about it, is the solution to that problem really to clean up a hacker's input and let it run? Not only is the string replacement a waste of time, but allowing any query to run at all is a deeper waste of resources. And what if we later choose to strip the word "UNION" from input, since it too could be part of a SQL Injection attack. How long will it take before you or tech support figures out why user "PaulBunion" can't login, or why Gina's "unionstation" password no longer works?

The issue is that the problem is solved, but the solution is not appropriate and in the worst case introduces new problems. Rather than trying to dress bad input in new clothes, the 4LF solution is to change the approach. The most appropriate and efficient behaviour is to emit an error, stop the query and get on with the next cycle. This calls for a custom error object that we can reuse for any bad input.

Public Class CustomAuthentication
  Private Function IsAlphaNumeric( byRef inString as String ) as Boolean
    Dim options As RegexOptions = RegexOptions.None
    Dim regex As Regex = new Regex("[^0-9a-zA-Z]", options)
    if regex.IsMatch(inString) then
      IsAlphaNumeric = false
    else
      IsAlphaNumeric = true
    end if
  End Function

  Public Function Login( strUsername as String, strPassword as string ) as String
    Dim errLogin as CustomError = New CustomError()
    If IsAlphanumeric(strUsername) AND IsAlphaNumeric(strPassword) then
      ' Check credentials with a stored procedure on the database server
      ' Write to log
      ' Set up the user environment
      ' ...
    Else
      errLogin.ErrorCode = "ERR_NONALPHANUMERIC"
      ' Write to log
      ' Could emit errLogin.Description here
    End If
  End Function

  Public Function IsAuthenticated( ) as Boolean
  ...
  End Function
...

The code has all the benefits of 3LF, plus in solving the problem a new CustomError object was created which simplifies the current solution, and also generically helps the developer define any other errors produced by the app throughout its development life.

You may ask, "What is this CustomError object?" Well, in theory it's a class which contains an ErrorCode property, and a Description method which might be defined in a Resource File or off in a multilingual database. In practise, today, right now, it remains a figment of my lazy imagination. The point is, if the CustomError object exists then you can use it not only to report "Invalid Input," but also to access appropriate messages to the user for "Account not found," "Bad Password," "Password not provided," and so on. Lazy Paradise.

Conclusion

This pseudo-methodology can be described as "half serious and all true." Seeking the lazy path will make you a better programmer. While the specifics are up for discussion, I believe that long-term efficiency will be the crux of any unified theory of the physical laws, and in people is the only characteristic worth attaining. And not only at an individual level, the principle gains strength in numbers. All this document does is apply the principle to programming. Beautiful, efficient, lazy programming.

So while people better suited to the task work out that whole gravity / magnetism / strong and weak-nuclear thing, why not enjoy the benefits of universal truth and make your coding a little lazier. If you would like to contribute to the Lazy History of Programming, or have a story where laziness saved the day, please write me or submit a Comment below.

Published Friday, March 14, 2003 1:07 AM by erobillard

Comments

Friday, March 14, 2003 1:07 AM by TrackBack

# The Lazy Programmer : ScottW's ASP.NET WebLog

The Lazy Programmer : ScottW's ASP.NET WebLog
Friday, March 14, 2003 1:07 AM by TrackBack

# More on Lazy Programming : Chad Osgood's Blog

More on Lazy Programming : Chad Osgood's Blog
Friday, March 14, 2003 1:07 AM by TrackBack

# The Lazy Programmer : Eli Robillard's World of Blog.

The Lazy Programmer : Eli Robillard's World of Blog.
Friday, March 14, 2003 1:07 AM by TrackBack

# More Lazy Programming : Eli Robillard's World of Blog.

More Lazy Programming : Eli Robillard's World of Blog.
Friday, March 14, 2003 1:07 AM by TrackBack

# Lazy Programming III : Eli Robillard's World of Blog.

Lazy Programming III : Eli Robillard's World of Blog.
Friday, March 14, 2003 1:07 AM by TrackBack

# Fifth Lazy Form : Eli Robillard's World of Blog.

Fifth Lazy Form : Eli Robillard's World of Blog.
Friday, March 14, 2003 1:07 AM by TrackBack

# Fifth Lazy Form : Eli Robillard's World of Blog.

Fifth Lazy Form : Eli Robillard's World of Blog.
Friday, March 14, 2003 1:07 AM by TrackBack

# Seeking the lazy path : CSharpener's Blog

Seeking the lazy path : CSharpener's Blog
Friday, March 14, 2003 1:07 AM by TrackBack

# Laziness rules! : Lance's Whiteboard

Laziness rules! : Lance's Whiteboard
Friday, March 14, 2003 1:07 AM by TrackBack

# Laziness is next to Godliness? : Loosely Coupled

Laziness is next to Godliness? : Loosely Coupled
Friday, March 14, 2003 1:07 AM by TrackBack

# More laziness : Loosely Coupled

More laziness : Loosely Coupled
Friday, March 14, 2003 10:02 AM by Ambrose

# re: The Lazy Programmer

You've heard of the four types of people that the Germans used in WWII to determine what military positions a person would be suited for. They're based on combinations of two factors: laziness and intelligence.

If you have someone who is lazy but unintelligent, he would make a good grunt.

If you have someone who is very hard-working and intelligent, he would make a good staff officer.

If you have someone who is very hard-working but stupid, don't let him in because he'll cause more trouble than he's worth.

But if you have someone who is very lazy and very intelligent, he will make the best general because he'll find the most efficient and least expensive (laziest) way of achieving a goal.

It is about time we see laziness in a more positive light. Thanks, Eli!
Monday, September 15, 2003 12:00 AM by TrackBack

# Peter Provost

Peter Provost
Wednesday, January 14, 2004 9:40 PM by TrackBack

# Maintaining SQL Server Databases

Sunday, January 18, 2004 8:03 PM by Peter Torr

# re: The Lazy Programmer

The right solution is to not concatenate usernames and passwords into SQL strings in the first place :-)
Sunday, January 18, 2004 9:10 PM by Eli Robillard

# re: The Lazy Programmer

True, and actually the username and password strings are not concatenated into SQL strings here. In fact, it never quite says exactly how they are used, only that they could carry malicious data.

In general, parameterised SQL queries are the recommended way to build any query with optional or variable input. Aside from providing a level of protection against SQL injection, SQL Server is smart enough to use the same compiled version of a parameterised query repeatedly, it's an easy performance gain.

A feature in ASP.NET 1.1 might be the laziest solution -- Request Validation. Read about it here: http://www.asp.net/faq/RequestValidation.aspx
Note that a patch should be applied: http://weblogs.asp.net/gad/archive/2003/11/12/37219.aspx
Tuesday, February 03, 2004 6:41 PM by TrackBack

# Hardening ASP.NET - avoid SQL injection (ouch !!) - Part 2

Tuesday, February 03, 2004 6:47 PM by stefan demetz

# re: The Lazy Programmer

it would be good if MS built in this input data cleansing in all input controls
Wednesday, February 04, 2004 9:04 AM by Eli Robillard

# re: The Lazy Programmer

> it would be good if MS built in this input data cleansing in all input controls

They have! See the link at the end of the article on the RequestValidation feature. It checks GET or POST data for malicious content.

-e.
Tuesday, February 10, 2004 3:14 AM by stefan demetz

# re: The Lazy Programmer

only for scripts with angle bracktes , not for sql injection, param tampering et all
Saturday, February 14, 2004 7:32 AM by Mark

# re: The Lazy Programmer

Long range lazyness... fantastic turn of phrase!
Thursday, February 19, 2004 9:06 AM by Eli Robillard

# re: The Lazy Programmer

Before anyone else bothers to post refinements to the code in this article, note that the point of the article is an approach to code construction. The point is not to provide best practises to prevent cross-site scripting or SQL injection attacks. The code samples describe progressively lazier solutions to a given problem; they exist only to illustrate the concept.
Tuesday, March 02, 2004 8:38 AM by TrackBack

# re: ASP.NET Request Validation and Cross-Site Scripting

Saturday, October 10, 2009 6:41 AM by Jed Hunsaker

# re: The Lazy Programmer

I've been trying to make this lazy vs. efficient argument forever! Glad to see someone else is on the same page as me :)

Friday, November 20, 2009 9:53 AM by Loosely Coupled

# Laziness is next to Godliness?

Laziness is next to Godliness?

Leave a Comment

(required) 
(required) 
(optional)
(required)