Because we software developers are also inevitably consumers of others' software applications, we are undoubtedly influenced in the creation of our own software by the software we use. For example, our opinions of what makes an effective interface for users are influenced by our experiences "on the other side" using someone else's software interface. This is particularly true for web and mobile development as web application and mobile applications have become pervasive in our lives.
We are prone to adopt idioms and styles that appeal to us and shun idioms and styles that we don't like. The degree of this influence may vary widely based on the type of software we are developing versus the type of software we use as a consumer (the more alike they are, the stronger the influence). There are times when the influence may be more subconscious and other times when the influence of others' software may be obvious. In this post, I describe a recent experience with an online site that reminded me of some important software development practices to keep in mind when creating software (particularly web applications).
I was recently creating a photobook on a popular photography-related web site. I began by uploading numerous photographs for the book. The web application reported that all but one of the photographs uploaded successfully. It reported that it failed to uploaded one photograph and recommended that I verify my Internet connection. After verifying my Internet connection, I tried uploading the single photograph several more times without any success. I tried changing the name of the file and changing its file location, but still had no success. I was able to upload other photographs after those failures, but could still not upload the one particular photograph.
I decided to work on the photobook with the photographs that did upload and spent a couple hours arranging the photographs exactly the way I wanted them. When I tried to save my photobook project, however, the application would not allow me to save because it said it could not save until it finished uploading the photograph that it kept failing to upload. I clicked on the link to save the project several times without success. I could not remove the reference to the offending photograph and even the Save-As option did not allow me to save because the application thought the photograph was still uploading. Ultimately, I gave up and closed the browser, knowing that my two hours' worth of work was lost.
When I looked carefully at the characteristics of the problematic photograph, I noticed that it was exactly 1 MB (1024 KB) in size. I used some image manipulation software to make a minor change to it that affected its size (made it a bit larger) and it uploaded without incident. I had to start over, but at that point I was able to arrange (again) the photographs where I wanted them and able to save the project as desired.
As a consumer of this software, I learned a few lessons. One lesson is the need to explicitly save often when using that application because it does not have an implicit save feature and because the act of saving a project seems to be the only way to find out that the software is in an inconsistent state in which no more future work on the project will be savable. I also learned to avoid the rare case of attempting to upload an image that is exactly 1 MB to that application.
I was reminded of even more important lessons as a software developer. First, I was reminded of the importance of unit testing, especially boundary conditions. I speculate that the code used by this application looked something like this pseudo code:
if (imageSize < 1024000) { // upload as-is } else if (imageSize > 1024000) { // compress and then upload }
In my speculative pseudo code shown above, the case of an image that is exactly 1024000 bytes leads to an image that is not explicitly uploaded. It's an easy error to make, especially if in a hurry and if no code review is performed or is rushed. Effective unit tests are perfect for driving out this type of bug and unit tests that test boundary conditions like 1024000 in this case are easy to implement. Often, just the writing of the unit test to test this boundary condition will cause the developer to realize the error of his or her ways.
Being an irritated and frustrated consumer of this software also reminded me of the importance of planning for "unhappy paths" in the software I develop. Use of this online photobook-creating software would have been less frustrating in this case if one of several options had been implemented. Had the application supported an auto-save feature that reported when it couldn't save, I'd have known that what I was working on wasn't savable. Blogger, which I used for this blog, has such a feature and when it reports to me that it cannot save, I know to stop adding new content to my post until it can save and I have a chance to copy and paste what I have typed into a file on my local hard drive.
Another option that could have saved me significant frustration would have been a Save-As feature that allowed me to save my project as a different project. I can understand the software being written to not allow me to save while it thinks it's in an inconsistent state (it thinks it's still trying to save), but it should still be able to save it as a different project. I have seen this allowed on several desktop applications that think the currently loaded document is corrupt or inconsistent but allow me to save that document anyway as a separate document (and not overwriting the previous document).
My frustration with the online photobook creation software reminded me of the importance of keeping the users' experience in mind when writing software. We can write all of the clean, readable, and maintainable software we want, but if it provides a poor user experience, that effort is for naught. This experience also was a good reminder of the importance of thorough testing, especially of "unhappy paths" and boundary conditions.