Archive for the 'programming' Category
Since I know none of you could possibly know about any software companies in this area who might have long-awaited product releases forthcoming, I won’t bore you with the details of my current project at work. Suffice it to say that it’s a little bit of a twist – I’m providing research for a writer, and together we’re producing the developer-oriented online training materials for the afore(non)mentioned product. (I’m positive you wouldn’t have heard of it. Nope. Not at all.) (more…)
Your shit done broke!
Seriously: when confronted with a text box labeled “Search for:” to the left of a button labeled ‘Search’, the convention is that you can type your search string into the text box, hit “Tab” to move focus away from the text box and onto the button, then hit “Enter” to commit the search. Every search engine I’ve ever used works this way.
Every. Single. One. Except yours.
If, after typing my search string into the input box, should I hit “Tab” then “Enter”, what happens? Why, I go back to the YouTube homepage! “Tab” doesn’t move focus to the “Search” button – it moves it to the big “YouTube: Broadcast Yourself” graphic in the upper left corner of the site. Since the graphic has a hyperlink back to the main page, that’s where I’m redirected.
C’mon guys. That’s just… lame. That’s a mistake I’d expect from an entry-level programmer.
(And yes, I filed a bug report… Dunno how their first level customer service people will deal with it…)
Update: Oy, it’s even worse that I thought…
YouTube’s UI is pretty conventional, following the standards for useability that Amazon pioneered: a top bar consisting of a static header and a tab control for navigation, then the dynamic content in the body and sidebars, then a static footer.
If focus is on the graphic and you start hitting “Tab” to navigate from element to element, guess what the progression is? You tab through each of the 5 top bar links, then on to the “Search” button, then on to the tab control, its links, then its subcategories, then all the links in the dynamic content and sidebar. Then you tab into the links of the footer, the RSS button, the address bar of the browser, then the member login text boxes and button (you hit the “Login” button before the user name and password text boxes), and then, finally… the “Search for:” text box.
That’s right – it’s the very last element you can tab into.
Hey guys, I know you have testers… And this is Web UI 101 stuff.
So at work today, I was greeted by an email containing possibly disastrous news: our software (the version rushed out for the agricultural study, remember?) looked like it might be eating data. After seeing it go live, the client had some (quite reasonable) changes they wanted implemented and gave us a week and a half to get them done. Most of them were content related, not logic, so it wasn’t a big deal to implement and test the changes, then turn the app around for another go in the field.
So they schlepped out to East Buttcrack, WA, app in hand, for another round of (we mutually agreed) pilot data collection. The primary investigator (or one of his minions) then had the good sense to actually import the data into a stats package they were familiar with and take a preliminary look at the data to see if they had everything they expected – for instance, did the subject IDs in the database match up with those in the hand-written log books?
(IMO, it’s always a Good Thing when the client is saavy enough to tell if what they’re getting is crap or not. They know what they should be getting, while we’re pretty much guessing. We try to make as informed a guess as possible, but we aren’t the domain experts in what we’re trying to model in the software. But I digress…)
The client threw up a red flag: one of the interviews that they conducted was missing from the database. Gone. Poof. Vanished into the digital aether.
That’s not good.
That’s really not good.
Still, for debugging a client-reported defect, we had a lot more data than we typically start with. Again, this is a Good Thing. They brought in the offending machine, and we went to work on it.
Sure enough, there in the event log was error after error: SQL transaction failed, again and again and again. We were logging the exceptions, but we weren’t alerting the user to them (a design decision I personally disagreed with, but hey…). We probably couldn’t have told them to do anything more intelligent than exit the application and start over, but that probably would have fixed the problem.
So off we go – what causes this error? Debuggers are fired up, process explorers are going… and they give us nothing. Nada. Bupkus.
We can’t reproduce the error.
Oh, we found the cause of a similar exception that was being logged whenever the app exited (the code worked properly despite itself), but a wholesale failure of a given data-collection session? Nope.
That suxors. Lots. And not in a good way.
Finally, my coworker (he’s a dev, but he’s also more of a jack-of-all-IT-trades kind of guy) is free-associating, and wonders if somehow threads were sharing data. No, I tell him – while you can do that with relative ease, we aren’t doing anything like that with this app. All the components run inline, in the same thread. We spin off child threads for load balancing, but we don’t exchange data between threads. We don’t need to, it’s a disaster waiting to happen, and, done poorly, it can quickly become the programming version of the Witch House, bending reality beyond anything that Should Be.
But maybe he was on to something…
He mentioned that it was a fairly common occurence for the field staff to launch more than one instance of the app – the app runs on a Tablet PC, and the ‘double-click’ with the stylus breaks the rules that Windows follow in a mouse-driven environment. Not much, but it’s enough of a deviation that it takes a little while to get used to.
Second, with .NET applications, there’s almost always going to be lag the first time you run the app. Like Java, the app is compiled on-the-fly; those pieces that get accessed the most often are then pre-compiled for subsequent accesses. This improves performance eventually, but initially it can seem like nothing’s happening. And what do users do when nothing’s happening?
They click again.
Third, these tablets are running fairly puny mobile CPUs. This increases the lag time, relative to the PCs upon which these apps are developed. And what does greater lag time mean?
Yup, more clicking.
So, three or four instances of the app open results in each creating their own database record – but if the lag hits just right, more than one instance of the app will ‘share’ the record ID of the most recent record. The staffer, realizing that they’ve got more than one instance running, will toggle around and close out those ‘extra’ instances. The app, being fairly polite and well-intentioned, will clean up after itself and delete whatever unused database record it is holding on to.
If the instance they leave running shares the record ID with one of the instances that got closed, the closing app sees that the database record hasn’t been used yet and deletes it. The still-functioning app no longer has a valid record to write to. It’s, well, gone. Poof. Vanished into the digital aether. Since we aren’t throwing the resulting exception in the user’s face, they proceed apace, blissfully unaware that none of their data is being stored.
And you can’t reproduce the error if your machine is beefy enough. Can’t. Won’t happen. You can launch as many instances of the app as you want, and each and every one of them will have the correct record ID in memory. If our client hadn’t been willing to turn the machine in to us, we wouldn’t have ever found the bug. They would have continued to lose data, we would have continued to be unable to reproduce it, and things would have gone south from there.
I lose sleep over bugs like this: intermittent and (apparently) unreproducable bugs are a nightmare, and our user base isn’t broad enough (and our hardware base is too monocultural) for us to see any kind of hardware-related (or third-party software-related) patterns of errors.
In this case, the fix was easy – on startup, see if there’s another instance running. If there is, quit quietly and gracefully and bring the other instance to the fore. If there isn’t launch away. But all too often, programming depends upon gut feelings and hunches to identify the sources of problems, and when the fixes aren’t quite so easy, you run a tremendous risk of ending up with more problems than you started with.
Today we were lucky. We won’t always have that luxury.
Sorry for the light posting – work’s been somewhat crazy (good crazy, not bad crazy), and I’ve been working on a new version of the Mystery of the Haunted Vampire site. And while I’m still somewhat cranky about some stuff that went down at work last week, I’m now a little more sympathetic to our client’s position.
You see, the product that we’re working on is to be used for a data-collection project related to agricultural pesticides, which means that our client is working on an agricultural schedule, ie. entirely at the whims of Mother Nature. Evidently, the weather in Western Washington has changed such that the apple growers are about to conduct their first major aerial spraying. Data collection will need to begin Right Now, or we run the risk of missing a significant phase of agricultural pesticide use in WA.
Fortunately, our client has agreed to use this first round as pilot data. We’re probably 95+% code-complete, so everything but uncommon use cases should be accounted for, but still… Personally I’d feel better with another couple of weeks to work on it, but evidently the client is happy with the software as it is now.
And who am I to argue with a happy client?
As for the MotHV adventures, let’s just say that I’m learning lots about MySQL and PHP. The MySQL part is proving surprisingly frustrating – I haven’t needed to write a whole lot of SQL code for a while, certainly not nearly as much as I was writing even 3 years ago, and MySQL’s dialect of SQL is just different enough from Transact SQL to throw up more stumbling blocks than I’d have thought. It’s kind of like having to speak a language that’s closely related to but not exactly the same as a second language you haven’t spoken in a while… Sort of like comparing your high school or college Parisien French to québécois (or in some cases, kreyòl ayisyen…) – it’s almost what you know, but not quite.
But it is a perverse kind of geeky fun, so I’ll take it.
One of the blogs in my RSS aggregator is Builder UK. Why the UK version of the site? Well, first off I think it’s a better site than its American counterpart, which appears to do little more for programming-related content than regurgitate articles from the faux-Libertarian TechRepublic.com. Second, seeing as how I live and work with Microsoft technologies in the middle of the Microsoft heartland – and as I’ve mentioned previously, MSFT is one of our clients – I thought it’d be good to get some perspectives from outside the immediate sphere of influence of the Borg. Third, I like an international perspective on tech news just as much as I like getting an international slant on non-tech news.
But they’ve recently endangered their position with an amazingly dumb article, VB6 Tip: Make the most of Variants.
Some quick background: the ‘Variant’ data type in Visual Basic is an all-purpose data type. It can hold anything without choking: strings, numbers, object references. And it’s a memory pig: since it doesn’t know what the maximum amount of memory is that it’ll need to hold its contents, it grabs a largish chunk and waits for the data to come in.
True/False, requiring 1 bit of memory? Sure, you could use it for that, and eventually, at some point in the future, the variable will release all that extra memory. But not right away – you see, it could still be something else. And we don’t want to give up that buffer, just in case. Now, there are some valid reasons why you might want to use such a data type – but you better have a bloody well-thought out reason for it.
The Variant data type is one reason among many why so many programmers look down their nose at Visual Basic – you don’t have to think as hard about what you’re doing, because VB’ll make guesses for you. Worse, I’ve seen code that declared variables as variants, and then used one or two variables as catch-alls. Need a number? No problem. A string? Sure! A Word document? You betcha! All with one variable!
Whenever I helped review resumes for VB6 programmers, there were 3 things in a code sample that would instantly get a resume tossed on the No Fucking Way pile:
Remember how I said VB will make guesses for you? It’ll do even better than that – if you don’t require variables to be explicitly declared, it’ll create them for you at run-time. A typo in a variable name all of a sudden becomes a new, legitimate variable. I’ve seen very expensive pieces of custom software all of a sudden break when ‘Option Explicit’ was turned on.
In some languages, you can declare a variable like this: “int foo, bar;” – there, you’ve just declared that you’ll need 2 integer values named foo and bar. It therefore isn’t uncommon to see this in VB6 code: “Dim foo, bar As Integer”. Guess what? It doesn’t work that way, but it doesn’t throw an exception of any kind. bar is an integer, sure, but guess what foo is? It’s a (wait for it)… Variant!
If you haven’t bothered to think through what types of data you were working with, how can I be sure that you’ve thought through your code? Toss, toss, toss… NFW.
By itself, this article isn’t enough to get me to delete my RSS feed – but they’re definitely on probation.