Showing posts with label Debugging. Show all posts
Showing posts with label Debugging. Show all posts

Wednesday, March 9, 2016

More TDD Videos

I've recently published two more videos that explore Test-Driven Development a bit more. If you're new to TDD, then you might want to start with TDD Basics in C#.

The latest videos use the rules for Conway's Game of Life as the problem to be solved. I ran across Conway's Game of Life many years ago when I was first getting involved with computers, and the patterns have intrigued me ever since.
TDD: Don't Turn Off Your Brain
Test-Driven Development (TDD) lets our code develop out of our tests.  But this doesn't mean that we turn off our brain. We still need to make decisions on our design as we write our tests. In this video, we'll take some "bigger steps" with TDD to implement the rules for Conway's Game of Life. Along the way, we'll see the types of design decisions we need to keep in mind.
Watch the video on YouTube: TDD: Don't Turn Off Your Brain

Or watch it here:

To continue on with the code from Conway's Game of Life, we fix some bugs and test for exceptions.
TDD Debugging & Testing Exceptions
Test-Driven Development (TDD) lets our code develop out of our tests. But it is also extremely useful when we have to debug existing code. When we have a bug, we can first write a failing unit test, then write the code to get that test to pass. In addition to debugging, in this video, we'll see how we can test for exceptions in our tests. There are several approaches with different advantages.
Watch the video on YouTube: TDD Debugging & Testing Exceptions

Or watch it here:


For more articles on Conway's Game of Life and unit testing, take a look here: Coding Practice with Conway's Game of Life.

More videos on unit testing are on the way. Future topics will include using TDD with real-world applications, mocking, and testing asynchronous methods.

Happy Coding!


Thursday, September 25, 2014

Programming by Trial-and-Error

I guess I must be getting old because I keep looking back at my history as a programmer to see what I can learn about myself and what works and what doesn't work. This week's realization is that I've been a programmer for a lot longer than I thought.

When I've written about my history, I usually mention that I wrote my first program on a knockoff Apple ][ way back in 1985. But that's not accurate. I wrote my first program long before that.

Christmas 1979
Way back in 1979, I got an awesome toy for Christmas: Big Trak. This was not a remote-control vehicle; it was a programmable one.

Merry Christmas!

It had a keypad on the back that you would use to punch in instructions (up to 16 of them). Then you press "Go", and it follows them (sort of) exactly.

It could move forward or backward in the very precise unit of "vehicle length".

It also could turn left and right. The units for this were a little easier: "15" was a 90 degree turn. It doesn't sound very intuitive, but there were 60 units in a full circle, which meant that it was similar to a clock face (we used analog clocks back then).

Finally, there was a "Fire" button that would fire off the blue "laser" (with appropriate sounds) at the front. This was kind of cool if you could get the dog to sit still long enough.

Reality Kind of Sucks
Programming this thing was frustrating (to say the least). There was no screen, which meant that you could never see the complete "program". This also meant that there was no way to fix a wrong step in the program. You had to start from the beginning each time. For complex routes, this was tedious.

Here's a sample that was in the instruction manual:

Sample Living Room

(The manual also had blank pages where you could draw in your own living room.) This sounds easy enough: Go forward 4, turn right 15, go forward 6, turn left 15, go forward 5, turn left 15, go forward 6, and then Fire!

The problem:
Physical reality doesn't match theoretical reality.
The tires on the Big Trak were sort of a rubbery plastic. This meant that the grip was unreliable on carpeting (or on tile). The program to move forward "5" may result in really moving "4-1/2". And turning "15" may result in turning "12".

Lather - Rinse - Repeat
To program this thing, I would always start with theoretical reality. I would map things out, write out the steps on a piece of paper, and then set out entering the program.

Then I would hit "Go".

And the Big Trak would promptly not turn quite far enough and end up running into the sofa.

Then I would adjust the first turn a bit, update my steps, and then enter the program again.

Then I would hit "Go".

And watch as the Big Trak makes the first turn okay, but doesn't go forward quite far enough and ends up crashing into the coffee table.

And this would continue. And after many iterations (and with a very mellow dog), you may be able to get it to drive around the coffee table, not hit the sofa, and then fire the laser at the dog on the other side of the room.

I Must be a Programmer
I really must be a programmer because this process did not frustrate me. It was a challenge. And there was something rewarding about getting the machine to finally do what you wanted it to do. It's hard not to get a bit nostalgic thinking about the afternoons I would spend "programming". (And if any of you share that nostalgia, Big Trak was resurrected a few years ago, and you can buy one on Amazon.)

I've always put the start of my programming experience in 1985 (when I was 14), but I really need to move it to 1979 (when I was 8). But I didn't actually get paid for programming until I was 29 -- which is a bit later than most professionals.

Debugging by trial-and-error is frustrating. Especially when you have to start over each time something goes wrong. I guess this isn't really that much different from people who entered computer programs by flipping switches on the front of a console.

Better Ways of Programming
It's much less frustrating working with tools today. I still do some programming by trial and error. For an example of this, you can look at "Coding Practice: Displaying Bitmaps from Pixel Data". But this is not my preferred way of working. Since I was using pixel data and creating bitmaps (which I didn't know in advance what they were supposed to look like), I really didn't have much choice. But this doesn't make up the bulk of what I do.

The bulk of what I do is implementing business rules, manipulating data, validating requests, and so forth. This is much closer to what I was doing last night: working on an implementation of Conway's Game of Life (more info if you're not familiar with it -- and something cool: if you Google "Conway's Game of Life", it shows a live demo of it).

I didn't get very far in my implementation. I started out by implementing the rules on whether a cell lives or dies. There are only 4 rules, so it's not very difficult. I used TDD for this -- creating tests for each rule and implementing them one at a time. This turned into more than 4 tests, though. I ended up with 14 tests to cover various states. And to simplify the tests, I'll probably move from MSTest to NUnit so that I can do parameterized tests for the various scenarios. (Why does MSTest only support parameterized tests for "Metro" applications?). This would reduce things down to 4 tests: (1) live cell stays live, (2) live cell dies, (3) dead cell come to life, (4) dead cell stays dead.

I'm still working on how to store the cell data. I'd like to do it in such a way that I can run the live/die method in parallel. I'm not quite sure how I'm going to do that at this point.

Wrap Up
Programming by trial-and-error is sometimes the only option -- like when programming a Big Trak. But more often than not, there are better options which are less frustrating and more productive.

It was eye-opening to realize that my first experience with programming had nothing to do with a "computer" (although there was a TMS1000 microcontroller inside). And it's also interesting to realize that I'm still programming by trial-and-error 35 years later.

But it's also good to know that this is not my preferred method of programming, and that I'm taking advantage of other options in the majority of my work.

I think that's enough nostalgia for now (or not, I may just have to order a Big Trak for myself).

Happy Coding!

Thursday, September 18, 2014

Observation: A Key Debugging Tool

When I was writing about attraction turnstiles yesterday, one of my favorite troubleshooting stories came to mind. And this is one situation where the data and system logs didn't give us any insight. It was only when I went out to observe with my own eyes that the problem came to light.

Different Technologies
As I mentioned last time, I worked at a major theme park and was part of the project to convert from manual turnstiles to automated turnstiles (more of the story). As another part of the project, we were moving away from mechanical turnstiles (the familiar 3-bar clicky things like in the picture) to optical turnstiles. Optical turnstiles use infrared light beams to count people walking by. These are usually invisible to the people walking by (unless you're someone like me who specifically looks for them).

Optical turnstiles are a bit less accurate than the mechanical ones primarily because they don't act as a barrier, and so people walk through them differently. As expected, there was a concern by the operating area that the optical turnstiles would not be accurate enough for their needs. But after much research, implementation tests, and on-site trials, it was determined that the accuracy was adequate for the business needs. And of course, one of the big benefits was that they were invisible and provided a better experience for our customers.

Data Discrepancies
At most locations, the optical turnstiles performed as expected. But at one location, they had regular inconsistencies. This particular location happened to be a playground for kids -- lots of cargo nets to climb, caves, towers, bridges, rock climbing walls. And the entrance and exit were combined.

Because of the nature of the location, the turnstiles counted in both directions, meaning they counted the people coming in as well as the people going out. This allowed us to keep a "running total" of how many people were in the location at any one time.

But there was a problem. This running total never went down to zero. It would gradually creep higher and higher during the day. At the end of the day, it would show several hundred people who went in but never came out. Now this obviously wasn't the case (unless there was a portal to another dimension somewhere in the caves -- but we didn't have any complaints about missing persons (no, not that Missing Persons (I still miss the 80s))).

Initial Troubleshooting
This sounded like a technical problem, so we got right on it. I checked the raw data from the location for a several week period. There were 4 different "lanes" (separate turnstiles). I looked for gaps in the data that would indicate that a sensor went offline. I looked to make sure we got counts in both directions (both entry and exit counts) from all of the lanes.

There wasn't anything obvious in the data, so it was time to move on to Stage 2: verifying the counts manually.

Observation
I grabbed by trusty person counter (like the one pictured), synchronized my watch with the server, got my clipboard, and headed out to the location.

I would not have been surprised to find a problem with the optical sensors. Since the sensors worked on infrared light, they could be "blinded" by the sun. If they were hit by direct sunlight, they wouldn't be able to make any counts at all. Since this was an outdoor location, I expected I might see some of that.

I was also there to observe behavior of both the customers and employees. If it turned out that an employee would stand in front of one of the sensors, it could affect the counts. We had seen locations were ropes were put up that blocked the sensors or swinging flags would cause the sensors to count even when no people were passing through.

Since there were 4 lanes at the location, I prepared myself to be there for a while. I need to count at least one 15-minute interval for each lane, and I also wanted to count the entry and exit separately. When my watch indicated the beginning of an interval, I started manually clicking off the people using the first lane to enter.

Realization
It was only by standing out there for 2 hours that my brain started to register what the real problem was. This was a kid's play area. It was a place where parents would take their children to expend their excess energy. Kids were climbing over everything, running (even though it wasn't allowed), jumping, swinging, and generally tiring themselves out.

And what happens when kids get tired? They want their parents to carry them. And that's exactly what was happening. Children were walking in to the location (triggering an entry count) and being carried out (*not* triggering an exit count).

While I was watching this (and still clicking away), I did some quick calculations in my head:
Assuming 50% children and 50% adults, if 10% of the children walked in and were carried out, it would account for the discrepancy that we were seeing in the numbers.
So, I had the answer even before verifying my manual numbers. I completed my counts and went back to my desk. And I found exactly what I expected to find: the manual counts that I took matched the automated counts on the server.

I was only able to identify the problem by making observations with my own eyes. I didn't find what I was looking for. But by being at the location and seeing the normal behavior, I was able to figure out what was happening.

Wrap Up
Not all problems with our systems are technical. Sometimes there are human elements involved. Only by watching how people actually use the system can we determine these types of issues. Sometimes it's an easy fix. If I see that one of my users wants to click on "Step 2" before completing "Step 1", then that tells me I need to hide some things until Step 1 is completed -- guiding the user to success in every part of the application.

In the case of the turnstile discrepancy, there wasn't much we could do to alter the customer behavior. We did add a "Minus 1" button at the location. If the employee noticed kids being carried out, she could press the button to trigger an exit count. This wasn't a great solution since the employees are usually doing other things like talking to the customers and answering questions. Other technical solutions would be overly complex and expensive for this particular implementation.

But having an answer to the question was really key in this case -- this made the operations folks confident that the optical sensors were dependable from a technical perspective, and they could rely on the system as a whole.

We can't always find problems from looking in logs and checking data. Sometimes we need to go out and see things with our own eyes.

Happy Coding!