RSS

Category Archives: Uncategorized

Code Review in Three Words

I was recently at a networking event chatting with a non-technical acquaintance.  I mentioned that I was mentoring a new member of my team who was going to start reviewing some of my code soon.  I analogized that no one would consider publishing a book without an editor, and code needed a second set of eyes as much or more than prose did.  It got me thinking about how publishers have style guides that can be used as references when editing and I needed some way to explain to this young developer what to look for as he began to review my work.  Some teams have rigorous style guides and certain languages have favored idioms, but I was looking for something more generic and ended with these three broad guidelines.

Is it Correct?

If an article about Abraham Lincoln mentioned “the president’s wife, Martha” an editor might recognize the wrong president’s wife was named and easily make the correction.  Unless the article was about Presidents’ Day or some other topic that discussed the first and sixteenth presidents, then the editor might have to go back to the author and ask for a clarification.  Similarly, some code is clearly wrong on its face and some less obviously so, but in both cases careful reading by a fresh set of eyes can reveal many — but likely not all — the places that the code is incorrect.

Is it Consistent?

Sports writers have a special talent for finding hundreds of different ways to say “won” or “beat.”  Writing software isn’t — shouldn’t be — nearly as creative an endeavor.  If you create one object you don’t add another, unless that difference in term indicates a difference in use.  If “close” is the opposite of “open,” it is always the opposite of open, never “shut” or “seal” or “drop.”

Is it Complete?

When the anchor of the nightly news tells you that a stock market index is up 200 points today, what do you make of it?  Nothing, really; you have no information to go on.  Numbers with units — points in this case — are meaningless.  A 200-point swing in the Dow is very different than the same change in the NASDAQ.  Without a basis for comparison — like the value of the index — the story is incomplete.  If you were told it was up or down 5% or 15% then you have reason to cheer or worry.

What if your favorite sports team’s rival played yesterday and you wonder how the game turned out.  The sports section may be full of scores and commentary, but if the game you are interested it isn’t covered, the section is incomplete.

Putting it All Together

Software can be complex and subtle and reviewing it is not trivial and must be done on several levels.  As an analogy, consider the recent sales listed in the real estate section of my Sunday paper.  There is one item after another like:

Alice and Bob Smith bought property at 704 Main Street from Ted and Carol Jones for $195,800.

There are hundreds of these items county by county (in alphabetical order) and town by town within county (also alphabetical).  The items themselves seem to be unordered, perhaps they are by transaction date.

Is it correct?

The names of the parties, the address, and the price in each item are verifiable.  There is a record of sale somewhere that can be consulted to be sure that the facts are correct.  If a few letters in the middle of the listing were in italic or bold or a different typeface, you could reasonably argue that is incorrect, too, though at a different level.

For software, the source of truth about the software’s intent is the design, sometimes reflected in a test it is expected to pass.  When revising software to address a bug, you must reference the ticket where the bug is described to know what the wrong and expected behaviors are in order to assess the correctness of the code.

Is it Consistent?

Occasionally the real estate sales include an item like

Betty and Barney Rubble bought property at 1 Gravel Lane from Acme Relocation Services for $1,234,567.

This is noteworthy because the seller is a company.  More subtly, you might also have noticed in the first example that one couple is listed woman first and the other man first.  Is that intentional?  Perhaps the listing just echoes the names as they appear on the deed.  Or perhaps they are intended to always be in alphabetical order (in which case the Ted and Carol are in the wrong order).  Once you are accustomed to the standard format, exceptions stick out.

This happens with software, too.  If two pieces of code do very similar things, they should differ only in the ways that are necessary.  Unexplained or unnecessary differences should cause the reviewer to pause and wonder if they are also unjustified.

Is it Complete?

On a rare weekend, the listing of sales omits my town.  It is hard for me as a reader to tell if single transactions are missed with more regularity.  If I were tasked with editing or reviewing that section I would need a checklist of all the communities covered and their counties.  That list could be used week after week.  I’d also need at least a count of transactions per town and overall as a first check of completeness.  And if accuracy was paramount, I’d want some form of the sales records to reconcile the listings with.

Even a simple software system may be built around several different classes.  For classes that may be stored as records in a database, each needs code to create, read, update, and delete instances.  Your classes have to cover all those operations for all the classes (or your comments need to explain why, say, one object is immutable after creation).

That system should have a test suite that covers creating an instance of each class in each way that is meaningful (using default values, using overridden values, using invalid values, etc.).  It should test retrieving or reading each class, including trying to get one that doesn’t exist.  It should test updating an instance of the class with no changes, with valid changes, with incompatible changes to values, etc.  It should read and attempt to update one that is deleted between the operations.  It should test deleting objects, including trying to remove one that doesn’t exist.

The combinatorics add up quickly — like the sales in towns and the towns in counties — and reviewing the code of the implementation and the tests that verify can be painstakingly detailed work.  But if the system is important enough, it is necessary.

That’s my 1,000-word take on three watch words for code review.  Are they enough?  Probably not.  But I hope they provide a starting place for novice reviewers as they develop their reviewing skills.

 
Leave a comment

Posted by on November 1, 2017 in Uncategorized

 

Tags: ,

A Smart Phone is a Computer

This summer I got a new car, my first with Bluetooth. Since then I’ve been refining the in-car experience with my smart phone and it led me to a conclusion about the state of the market in cell phones.

I had a car mount with a nice mechanism for one-handed load and release. And before I had Bluetooth having to hook up power as well as audio wasn’t that big a deal. But now that my phone audio connects with my car wirelessly, having to connect the USB for power seems a chore. I have a wireless charging case that I use with chargers on my bedside table and desk so I found a car mount by iOttie with a wireless charger built in.

With that dock, I can get in my car, put my phone in the dock, and it starts charging. If I have a podcast or music player paused, the presence of Bluetooth usually causes it to resume. So far so good. My home screen includes Google Maps for navigation but I usually have GPS turned off to save battery so actually navigating means getting to settings, turning on GPS, and accepting — for the 1007th time! — the terms of the location services. I thought there has to be a better way.

I’ve had phones with matching car docks that could tell they were in the car dock and run a car mode app. My current phone does not. I needed some way to make my phone know it was in the car (ideally, knowing if it was still in my pocket or put in the dock) then change settings (e.g., GPS on) and run an app (a third-party “car mode” or “dashboard” program). Bluetooth presence was one option but that couldn’t distinguish between dock and pocket. Another option was NFC tags. I had a packet of WhizTags so I decided to see what I could do with them.

The WhizTags folks recommended NFC Tasks which I found kind of awkward. I’d heard good things about Automate and decided to give it a try. Automate seems like a fairly flexible graphical programming environment. The limitations I run into are in the Android OS.

I want to set up “when the NFC tag in my car dock is tapped, turn on GPS.” There are recipes (or “flows”) available on the Automate community site to do that but they require “superuser” privileges; that is, you need to have “rooted” your phone. At least one dashboard I tried has a setting to enable GPS when it starts up so you’d think I could have Automate start the app when the tag is seen and have the app turn on GPS. But when the app starts up, the OS intercepts the attempt to turn on GPS and prompts me to confirm. The whole point is to have this all be automatic so this confirmation is a show stopper. If the confirmation had a check box for “always do this” I could work around it, but it doesn’t so I can’t.

I’d also like to be able to set up “when my phone is undocked, turn off GPS” but here I run into multiple problems. NFC seems to have no way to detect leaving the proximity of a tag; it’s not designed for that. Looking for options, I realized that taking my phone out of the dock and turning off my car happen pretty much at the same time, and turning off my car turns off its Bluetooth. So if I could react to Bluetooth disconnect, I’d have a reasonable heuristic for “undocked.” While Automate has a “Bluetooth device disconnected” trigger, when I try to use it I’m told it “isn’t officially supported by Android.” And, of course, even if that was supported, turning GPS on or off is a privileged operation.

In some sense, some of these difficulties arise from the fractured nature of the Android market; a monolithic or tightly controlled system like iOS would have fewer of these problems in coordinating software from multiple sources. But the deep issue here is that Google, Motorola, Samsung, LG, etc. don’t treat smart phones as the computers they are. Computers have a way for competent users to perform privileged operations. Windows has User Access Control. In Linux we have sudo. In stock Android the closest we come is being allowed to install programs from locations other than the Google Play store.

To have full access to the computer you own — a computer that happens to make phone calls — you need to “root” it by a method fraught with difficulty; every device has a different method, it doesn’t always work, and it has the potential to make the device unusable. What we need is a setting in Applications or Security for “allow root access” which just works out of the box. Typical users would never check — perhaps never find — that check box and their phones would continue to work as they do now. But “power users” could make full use of their pocket computer without jumping through hoops and something as simple, natural, and obvious as turning GPS on and off wouldn’t be a challenge.

I really believe that the device maker or carrier that realizes this will have a loyal following; it will provide the device of choice to people who want to make full use of their phones. (Perhaps one has tried and no one noticed or there were other reasons the device was not welcomed.) In my wildest dreams, someone at Google makes it a core part of Android N and even goes so far as to not allow it to be disabled in OEM versions. I can dream. In the meantime, I’m just frustrated.

 
Leave a comment

Posted by on January 4, 2016 in Uncategorized

 

Better is Worse

In the early 1990s, Richard P. Gabriel posited that “Worse Is Better” but could never quite decide if he meant it. In his essay, he contrasted what he called the MIT style of software development and the New Jersey style. He argued that the NJ style produced useful but incomplete, even flawed, tools like UNIX and C which grew incrementally — and perhaps sloppily — over time but by getting something into users’ hands early, the NJ style had a chance to develop a loyal following while the MIT crowd was still polishing their first release. Or at least that’s how I interpret it.

I was reminded of this recently in the context of another great divide of the software world. My wife and I have different tablets with different performance problems that I think illustrate a similar difference in design or market philosophy (or maybe an artifact of the Apple uniculture vs. the splintered Android market).

From time to time, I’ve been frustrated with the performance of my Asus tablet and, on inspection, find that there are simply too many applications trying to retrieve data and process it to tell me things my phone tells me perfectly well. (For example, there’s no good reason to have the mail or social media apps on my tablet poll for new data when my phone will alert me and I can manually download to the tablet.) When I uninstall these resource hungry apps or configure them to not poll, my tablet’s performance is restored and I go merrily on my way for a few more months when I need to disable or uninstall a new set of apps.

Whereas Google can’t seem to get its licensees to update devices, Apple seems to almost force users to update; my wife’s iPad is on its third version of iOS. Each new version comes with new software features; some support new hardware in new devices (fingerprint readers, force touch, etc.), but some are purely software, more complex algorithms accomplishing more complex tasks for the user. The problem is that they can’t do that on old hardware with limited resources without being unacceptably slow. This is great for Apple which will sell new hardware to loyal users, but not so good for more conservative users who’d rather get another year or two out of their hardware purchase.

I can see the benefit of having coordinated versions of iOS on your phone and tablet (and your computer, if you go that far), but I’m OK with Lollipop on my phone and Jellybean on my tablet. When a software update makes your device slow down — whether that update comes from Apple or a responsive Android developer — better is worse.

Update, Fall 2017: Android Oreo was purported to have improved performance vs. Nougat on the same hardware and my upgrade experience showed just that.  In a strange coincidence, iOS 11 seems to be doing the same thing for Apple devices in the field. Sometimes it’s nice to be wrong!

 
1 Comment

Posted by on November 21, 2015 in Uncategorized

 

Sleep Considered Harmful

I’m not talking about “I’ll sleep when I’m dead” but, with apologies to Edsger Dijkstra, if you are programming a modern computer and you find yourself using sleep() there is probably a better way to write your program.

Recently, my team has been porting fairly old software to a system with more than ten times the performance of the previous system and sleep() has bitten us several times.

How Did We Get Here?

The first computers were slow and expensive enough that the thought of deliberately slowing them down was ludicrous. But the first personal computers — slow as they were by today’s standards — interacted with people in ways that sometimes meant making them work in human time, slowed down considerably from even the 4.77 MHz that a IBM PC was clocked at. Say you wanted to blink the cursor in a text editor, alternating the current character between light on dark and dark on light every half second. That’s on, wait a half second, off, wait a half second, and repeat. That half second is nearly two and a half million clock cycles! How do you do that?

A common technique for timing things on early, single-user computers was a wait loop or busy loop. Knowing how long it took the CPU to simply increment an integer, you would construct a loop like:

/* Count to 1 million to introduce a small delay */
for (i = 0; i < 1000000; ++i) {
    /* Do nothing */
}

There are several problems with that code in the context I describe. Many or most early PC programs were written in assembly language. Also, it wasn’t possible to represent one million in an 8- or 16-bit number, so you might have nested loops that each counted to one thousand. But the real problem with that is the assumption in the algorithm: that you know how long it takes to increment i and test it against the limit. This is not invariant.

Before long, computers that were mostly compatible with the IBM PC — even other systems from IBM — had processors that ran at 6 MHz or even faster. Blinking a cursor 20% faster isn’t a big deal, but myriad other user interactions — like the repeat rate when you hold down a key — matter a great deal and can frustrate users when they don’t behave as expected. The answer — well, one answer — is for the system to provide a means to wait a specified amount of time. This function is often called sleep() and the loop above might be replaced by:

/* Wait one second */
sleep(1);

It might be that sleep() is implemented by a busy loop, but the system can tune itself to do the right thing — loop the right number of times — to accomplish the delay desired by the programmer.

Sophisticated operating systems might allow other things to happen while the sleeping program was waiting. Early versions of Microsoft Windows did something like this. They implemented “cooperative multitasking,” wherein every program had to periodically call a yield() function to allow other programs to have some system resources.

Cooperative multitasking was an awkward stepping stone to “preemptive multitasking,” wherein you write your program without concern for other programs that may run at the “same time” and the operating system preemptively interrupts your program to let others have system resources. (A consequence of this inversion is the need to mark critical sections of your code so the system doesn’t interrupt them. But that’s a topic for another day.)

Coordination

With truly independent programs, sleep() isn’t a terrible solution. But software often has to wait for hardware and as computers grew faster and applications grew more sophisticated, it became common for two or more programs to cooperate to achieve some goal. Programmers with sleep() in their tool box would write code like:

/* Wait for the data server to start up */
sleep(5);

On some system at some time, five seconds may have been the right answer. But any number of things can make the server take more or less time to start. If it takes less, then the program waiting for it is wasting time and the system is not as responsive as it could be. Worse, if the server takes longer to start, this program proceeds, trying to make use of services from another program that aren’t available. The correct way to handle this is for the server to implement some “I’m ready” indicator that clients like this code can look for. Something like:

// Wait for the data server to start up
serverFound = false;
timeOut = 1;
for (i = 0; i < 30; ++i) {
    if (serverReady(timeOut)) {
        serverFound = true;
        break;
    }
}

The serverReady() function waits timeOut seconds at most for the server to be ready. It might be implemented with a system function like select() or pthread_cond_wait().

Timing

There’s one last scenario where sleep() is really the wrong answer: polling loops. Many data collection applications have core logic like:

// Poll for data every 100 ms
while (true) {
    // Request data
    sendQueries();
    // Handle data
    processResponses();
    // Wait before polling again
    usleep(50000);
}

Here the programmer has relied on experience that requesting and processing data take 50 milliseconds so the microsleep should take another 50 for the whole loop to take the desired 100. But we’re back to the original problem with IBM PC wait loops: assuming the performance of the system. If originally sending queries took 10 ms and processing responses took 40, a performance improvement that let processing run 40 percent faster shortens the overall loop to around 85 ms, changing the program behavior from polling 10 times a second to polling 12 times a second. A better idiom for this is to use a timer.

// now() returns system clock in milliseconds
expiration = now();
setTimer(expiration);
while (true) {
    if (timerExpired())
        sendQueries();
        processResponses();
        expiration += 100;
        setTimer(expiration);
    }
}

This is still not perfect. If sending and processing take more then 100 ms together, the timer is set to a time in the past and will be expired at the top of the next loop. Your application requirements will dictate whether skipping one or more scans is the best fix or if you need some other approach. Still, this algorithm is immune to better performance of the underlying system, a much more likely scenario than worse performance.

Just as high-level languages provided if, for, while and such obviating the dangerous GOTO, so too do modern systems provide semaphores, timers, and such obviating the dangerous sleep(). If you are still using sleep(), wake up!

 
Leave a comment

Posted by on May 11, 2015 in Uncategorized

 

Software is Like Cheese

Hardware may be like produce, most desirable when it’s fresh and gradually declining in appeal as it gets older, but software is like cheese.

At first cheese is really just spoiled milk; it has potential, but it’s not very desirable. Gradually, cheese ages and matures until it reaches a peak of flavor and texture. Eventually, the cheese spoils and you need to throw it out and start over.

Software — great software that runs our lives in banking and communication and industrial automation — is not very good when it’s “fresh.” But it matures as it is refined and refactored, bugs being worked out over time, until it reaches a peak of usability and stability. In the end, it no longer suits its users, or it succumbs to bit rot and neglect.

I am tempted to take this analogy too far — Is Windows more like American Processed Cheese Food or a nice aged Asiago? — but I’ll leave it here for now.

 
Leave a comment

Posted by on April 20, 2015 in Uncategorized