The Benefits of Positive Logic


This article discusses a coding practice that I discovered while an undergrad a while back, maybe 1994. After doing some code reviews recently, I am reminded that this particular practice still needs more publicity. When writing code, a developer typically likes to worry about edge cases first, then handles the normal case. This practice has some benefits: the cases you worry about most get handled early. And, this practice creates maintenance nightmares. This practice has a some detrimental effects:

  • Code readability is reduced
  • Precious cycles are wasted on the uncommon cases
  • Code complexity frequently increases

People remember and read positives easily. They have more issues with negatives. Consider the following C# code:

    1 FileStream fs = null;

    2 const string filename = "someFile.txt";

    3 if (!File.Exists(filename))

    4 {

    5     fs = new FileStream(filename, FileMode.CreateNew, FileAccess.ReadWrite);

    6 }

    7 else

    8 {

    9     File.Delete(filename);

   10    fs = new FileStream(filename, FileMode.CreateNew, FileAccess.ReadWrite);

   11 }

Consider what happened when a developer wrote the code above. They thought, "OK, if this file does not exist, then I can create it. However, if the file does exist, I want to make sure I start with a clean slate, so I’ll delete the file, then create it." The code above is confusing. The thoughts are equally confusing. While I wish this code were an anomaly, it isn’t. This is something I see on a regular basis: in support forums, in code that didn’t have time for a proper review, and elsewhere. Consider what would have happened if the developer had chosen to use positive logic instead of negative:

    1 FileStream fs = null;

    2 const string filename = "someFile.txt";

    3 if (File.Exists(filename))

    4 {

    5     File.Delete(filename);

    6 }

    7 fs = new FileStream(filename, FileMode.CreateNew, FileAccess.ReadWrite);

Here, the code is more readable and the intent is clearer: if the file exists, delete it. Then, open the file for read/write. Note that the code is now smaller and maintenance is easier too.

Here is another example, this one a PowerShell script from TechNet magazine. Here, we have an example where readability is diminished thanks to negative logic. Line 15 just bugs me. Read the code:

1. function Get-WmiInventory {
2.  param (
3.  $wmiclass = "Win32_OperatingSystem"
4.  )
5.  PROCESS {
6.   $ErrorActionPreference = "SilentlyContinue"
7.   $computer = $_
8.   trap {
9.    $computer | out-file c:errors.txt -append
10.    set-variable skip ($true) -scope 1
11.    continue
12.   }
13.   $skip = $false
14.   $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop
15.   if (-not $skip) {
16.    foreach ($obj in $wmi) {
17.     $obj | Add-Member NoteProperty ComputerName $computer
18.     write $obj
19.    }
20.   }
21.  }
22. } 

OK: If I should not skip then I should do work. With a little better naming, we could make this all more readable. How about this instead:

1. function Get-WmiInventory {
2.  param (
3.  $wmiclass = "Win32_OperatingSystem"
4.  )
5.  PROCESS {
6.   $ErrorActionPreference = "SilentlyContinue"
7.   $computer = $_
8.   trap {
9.    $computer | out-file c:errors.txt -append
10.    set-variable shouldWriteComputerInfo ($false) -scope 1
11.    continue
12.   }
13.   $shouldWriteComputerInfo = $true
14.   $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop
15.   if ($shouldWriteComputerInfo) {
16.    foreach ($obj in $wmi) {
17.     $obj | Add-Member NoteProperty ComputerName $computer
18.     write $obj
19.    }
20.   }
21.  }
22. } 

Here, readability goes up because I can now see exactly why we DO go into the loop instead of seeing why we do NOT.

If you write negative logic, do a pass after writing things to fix up the code. Do that pass immediately. I frequently write code like:

if (not X){

  &#
160; Do A

}

else {

    Do B

}

I immediately turn around and rewrite this code as:

if (X){

    Do B

}

else {

    Do A

}

I have found that I can read this kind of code long after writing it and remember what I meant. I have difficulty doing the same thing with negative logic paths.

%d bloggers like this: