Tuesday, September 22, 2020

Spot the Bug - FOR Loops

The next bug in the Spot the Bug series is one that is seen quite often from new programmers. Can you spot the bug?

This code contains a class that adds two 2x2 matrices together. The class also includes a print method for displaying the matrices in the console. The main program passes in two sample matrices to add together. The bug in this code is that the indices in the AddTwoMatrices method are flipped for the additionalResult object.

This bug is common with new programmers. Unfortunately there is no C# language construct to prevent us from creating this kind of bug. I've included it in this series because it presents a great conversation for a mentor to have with a new developer about programming techniques that can be employed to lessen the chances that a bug like this gets released.

The first thing I would highlight is that this code needs unit tests. Math operations are straightforward to test because they should be operations that have no side effects. The code doing the math operation should also be pure, meaning for the same set of inputs, it always produces the same output. These conditions make unit testing math methods straightforward and these methods should always have accompanying unit tests.

The other thing I would highlight here is that better variable naming may make it easier to see the bug when it does get introduced. (Generally speaking, I don't mind simple counter variables like these in FOR loops being single-letter variables.) More descriptive variable names for the loop counters will make it easier to read the code and reason about the code, making it easier for a developer to see a problem as it is being introduced.

Here is the code with the bug corrected and more descriptive variables to make it more readable.

Even though C# doesn't provide mechanisms to prevent this type of bug, there are some good programming techniques that can be taught and discussed when bugs like this arise.

Friday, September 11, 2020

Spot the Bug - File Globbing

The bug in this post comes from the world of scripting. Powershell is used for the code below, but this bug could be introduced in any scripting language that is commonly used for task automation. Can you spot the bug?

The code above is a fairly straightforward script. It gets a list of files from fileDir, iterates over each file moving it to two different locations. After looping through the files, it removes the files from fileDir.

The bug in this script is that the removal of the files is done using a wildcard to locate the files to delete. If the number and size of the files being moved is large, the iteration over each file will take some time. New files can be placed in fileDir and those files will be deleted by the final script line using the wildcard. If that scenario happens, data can be lost!

In the code above, the bug is fixed by deleting files by file name as a part of iterating over the list. This approach ensures that you only affect files discovered in the first line of the script, and newly arriving files can't be accidentally deleted.

This example had to be done in Powershell because the C# libraries for working with files and directories don't allow the use of wildcards, therefore preventing a developer from creating this bug. This is another example of the language construct preventing developers from introducing bugs.

Friday, September 4, 2020

Spot the Bug - Using Statement Part 2

I introduced the "Spot the Bug" exercise previously. This is the next bug in the series.

Below is a class named DataWriter. This class is responsible for opening a file, closing a file, and writing data to a file. Below that class is a console program that exercises the DataWriter class. Can you spot the bug?

In the console program, I forgot to call the method to close the file. This is another example of the using statement in C# protecting us from creating code like this example. In the previous example, we saw a similar bug where it is very easy for the developer to forget to close a connection or file handle, using up precious resources. The using statement isn't a perfect solution, but it does push the developer to keep responsibilities grouped appropriately and it ensures that the garbage collector can do some amount of cleanup in the system when resources are no longer needed.

Below is code that represents a better solution.

The code above uses the using statement to handle the opening of the file. The developer is still responsible for closing the file, but the using statement will make sure that the StreamWriter object is disposed.

The other thing to notice in the better code example is that the DataWriter class was completely eliminated. Because the DataWriter class split up the file writing responsibilities too granularly, once those responsibilities were merged back together, the class was no longer necessary. This example highlights the fact that sometimes our designs aren't quite right, and we shouldn't be afraid to remove classes if they aren't necessary.