I am a software developer, amongst other things. Software development can be a lonely activity. Sometimes it can be painful. Selling software over the Internet for a few dollars at a time is as potentially rewarding as it can be soul-destroying.
On 30 July 2009, the second person to purchase a licence for ShutOff 2000 this year made his payment over PayPal. As you can probably work out, the registration key business is a manual process, because with two to three purchases a year, I’m not killing myself to automate it with a fancy web service. $10 to $15 a year (less 10% for PayPal) doesn’t warrant the effort.
Not a few minutes after sending the registration key, the customer declared that the software didn’t work. I’ve had ShutOff 2000 for sale in its various incarnations since 1999. My company’s name has changed several times, the bugs have been ironed out, and apart from a stupid hack that limited the product’s life to 31 December 2007, it was rock-solid. After all, it has been on the market for almost 10 years.
I told the customer that he was mistaken, because this is software that has been around since Windows 98 and NT 4.0. It runs on Windows 9x, NT and everything after that (yes, even on Millennium Edition). I’ve been given superb ratings by advert-driven software portals. I’ve had it reviewed in magazines that no longer exist, it’s that old. But in the interest of Doing The Right Thing (TM), I told Bob (for that is his name) to send me a screen shot, and that it was a problem with his regional settings. Yes, I was that arrogant.
Bob sent me a screen shot. He was telling the truth.
I immediately started up my Windows XP virtual machine. You see, I’m running on Windows 7 Ultimate (64-bit) and Visual Basic *does* work, but I wouldn’t advise it.
I then looked for the source code for version 2.8.3, being the last version I’d looked at, in December 2007.
After finding out that Sun’s VirtualBox and my bridged network corrupted the files when I tried to copy them to the VM, I tried zipping the files first.
The reason I’m telling you all of this is to point out how utterly silly I thought Bob was being, and how much effort I was going to, to prove him wrong. Yes, I was that arrogant.
I eventually got Visual Basic to open the project. My idea of version control is copy the folder, replace the path names in certain files, and search and replace the version number in the VB code. No, I’ll never learn.
Then I started rooting around in the code. Keep in mind the version history now: lots of activity between 1999 and 2001. We went from 1.0 to 2.5 in that period. Then over the next SIX YEARS I only managed a couple of point releases and minor bug fixes. I didn’t touch it in 2006. I looked at it once in 2007, and once more in 2008. It’s as though someone else wrote it. Heck, I get upset with my code after a three-week period, so you can imagine how I feel about ten years. Also, I managed to LOSE the source code for 2.8.2, so I literally had to re-engineer the bug fixes that were introduced in that version.
Now it gets interesting. More than eleven months after fixing a date-related bug to do with regional settings in January of 2007, I decided to redo those fixes on 2.8.1 code, plus a couple of new features, to make it 2.8.3. You see, I had hard-coded a time limit into the code to expire ShutOff 2000 from active duty on 31 December 2007. The .NET version was supposed to be out by then. In that 2.8.3 release, I also decided to change the timer to include seconds in the time-based calculations, because it would be more accurate, and computers are faster now.
So I added this new functionality, as well as retro-fitting a bug fix I don’t remember doing in the first place, on code I hadn’t looked at in over two years (2.8.1 to 2.8.3). What could possibly go wrong with refactoring all that Date-related code in Visual Basic 6?
Bob found out.
Bob was upset with me. He had spent $5.00 on a product that didn’t work. So while emailing him updates on 30 July 2009, I released an update, version 2.8.4, which included a tiny bit of code that took seconds into account on a date string. Problem solved! The tests ran successfully. The Timer events were firing correctly, and all was well with the world.
Unfortunately, Bob wasn’t around to confirm my tests, so I updated the website, released the new build, and promptly forgot about it.
Last night, Bob got back from wherever work had taken him. He wrote to say that he couldn’t get it installed, and was being told to pay for the software again. I didn’t understand, so I sent him a 6-point list of how to remove the old version and install the new one.
He wrote back saying “it doesn’t work”. I got annoyed, because he wasn’t being helpful, and I told him as much. He wrote back to ask for his money back. I replied saying his seven day return policy had expired. His terse reply made it clear he wasn’t happy. I told him to take it up with PayPal, and find tech support in the meantime. He lodged a claim on PayPal, and I went to close the call so that it could be escalated. I wasn’t having any of this nonsense.
And then this morning, he wrote back. He told me he had managed to get the new version installed, and that it wasn’t fixed. Now I was convinced he was lying, because I tested it, for goodness sake. To be sure, I downloaded 2.8.4 off the website, like a normal person would, installed it, filled in my registration details, and performed a timed shutdown. It worked. I tried a timed logoff. That also worked. I tried a timed lock workstation. That worked too.
I told Bob that it was working on my side, and something else must be wrong. Then it was home time. I came home tonight, deciding to work on the .NET version. I got a heck of a lot done (truth be told, I’m very close to finishing, but there are rough edges that can hurt you, so it’s not ready). Then Bob wrote to me again and sent me a screen shot.
Bob was having the SAME problem as before. His screen shot clearly shows the real-time clock over a minute past the shutdown timer. Even in worst-case scenario, it will take 30 seconds to shut down. This is because the polling timer checks the clock every ten seconds, and then there’s an extra 20 seconds where the “Are you sure?” message box stays before it goes away if you don’t click anything. But Bob’s screen shot was showing over a minute.
I then decided to fire up the old Windows XP virtual machine again, and take a poke around. I implemented my version control system, changed the version number, folder names and all that, and went in. I figured the only difference between Bob (not working) and my (working) 2.8.4 was the regional settings. I changed my XP’s regional settings to US and got to work.
The first thing I did was to change the way the Date Picker DISPLAYS the date:
> DTPicker.Format = dtpCustom
> DTPicker.CustomFormat = “yyyy/MM/dd”
Now at least it would LOOK the same regardless of what country you’re in.
Then I went to the Timer method to see what that was doing. Don’t laugh.
Old Code (see the & “:00” at the end I put in to fix 2.8.4?):
> ValDate = Date & ” ” & NewHour & “:” & NewMin & “:00”
New Code (that made Bob happy again):
> ValDate = Format(Date, “yyyy/MM/dd”) & ” ” & NewHour & “:” & NewMin & “:00”
See what I did there? I made it the same. You know, for when you want to compare apples with apples.
Sorry Bob. You were right. I’m glad I didn’t refund you, because I wouldn’t have found this problem otherwise.
The moral of the story: my first instinct on 30 July was to refund him and ignore the problem. Although he put me through a lot of effort (ok, two hours tops), it was worth it. He got his $5.00 worth of software. I fixed two bugs. I had a beta tester who was as in-your-face as me, and we both came out wiser.
I’ve offered Bob a second licence (free of charge) for a friend or colleague. Hopefully he takes me up on it. And as we .NET developers know, the Date format problem is a non-issue now, especially if you store everything as UTC. I am doing that in the new version, I promise!