Monday, November 24, 2008

Software Development Bloopers

Throughout my career, I have either directly experienced and/or witnessed developers around me lose significant development time because of little things that might be referred to as software development bloopers. I like the term blooper for these because the class of actions that lead to this time lost are the kinds of things that even highly intelligent and skilled developers can make from time to time. Just as bloopers at the end of films gives the audience a chance to see some of the silly errors made during production that were (usually) intentionally hidden for the actual delivered film, we also tend to not want to highlight our software development bloopers during development, but can laugh at them afterward.

While I like to think that I personally encounter these bloopers less frequently and that they cost significantly less time when I do make them then when I started my career, the truth is that I still do commit them from time to time. This should not be surprising when considering that even the best of actors and actresses also commit bloopers. I was planning originally to title this blog entry "Boneheaded Software Development Maneuvers." However, the definition of boneheaded implies incompetence and lack of intelligence. Most of these bloopers that I will be describing were made by very bright and highly skilled software developers. This is a key differentiator between bloopers and boneheaded maneuvers.

I begin describing software development blooper by what, in my mind, a software development blooper is not. If I am having trouble using a product or library because of a bug in that product or library or lack of support for standardization in that product, I don't classify this as a software development blooper. Rather this is a serious problem inherent in the product being used. Likewise, poor designs, poorly implemented code, lack of testing, lack of experience in a given programming language, and so forth are better classified as bad development practices or lack of training rather than as software development bloopers. Software development bloopers tend to be smaller in nature than these previously mentioned items, but can still cause significant loss of development time.

Examples of Software Development Bloopers

My focus now moves to discussing some concrete examples of software development bloopers that I have seen and/or directly experienced. I'll use these examples to form some conclusions about the characteristics of software development bloopers, to outline some specific methods for reducing the frequency and cost of these software development bloopers, and to identify some general areas where software development bloopers are most like to occur. I also hope that anyone reading this might be able to laugh or at least smile at some of their own personal software development bloopers.

Not Running the Latest Built Software

It is difficult to think of anything more frustrating than when the software you are running does not reflect the changes you know you just made. This may occur when one thinks that the JAR, WAR, EAR, .exe, .swf, or other file to be executed is being built in a certain location. No matter how the developer changes the source code, the running application does not seem to reflect those changes.

This situation most often occurs in very large projects or in cases where the developer has been away from a project for a while. In such situations, build scripts can change without the developer's knowledge.

A remedy for this particular blooper is to look closely at the dates associated with generated JAR, WAR, EAR, and other executable files.

Even when making sure that the WAR or EAR is generated as new, this issue can still arise if that newly created file is not deployed. For example, if the newly generated WAR file is not deployed to the web server or the newly generated EAR file is not deployed to the application server, the changes won't be seen. This situation can arise when a developer gets interrupted and forgets that the latest changes were not deployed. It is even more insidious when redeployment does not succeed because of an error during deployment, but the developer does not realize that it is still the old version that is deployed. This can happen when undeployment of the previous version fails or redeployment fails and the only significant warning is buried in the logs.

There are several other things that can prevent the develop from seeing changes made to the their application. These include web browser caching and inadvertent building and running from local file systems on different machines.

Most of these issues can be easily dealt with by paying close attention to what is happening in one's development environment. This sounds easy, but can be problematic when one is in a hurry and/or has several different things going on at once that distract the developer and detract from the ability to focus.

In addition to examining dates of files more closely, another obvious tactic used to narrow down whether the latest software is being executed is to add logging or standard output messages to the areas of code one expects to be run. The debugger can also be used to detect exactly what is happening, but use of these approaches is time-consuming and thus contributes to the time cost associated with running different versions of software than most recently constructed.

An approach that can be used to prevent or reduce the chances of having executable software become out of sync with the latest software additions or modifications is to automate as much as possible. Development effort placed into comprehensive Ant or Maven or other types of scripts pays off handsomely in preventing or reduces this type of software development blooper.


Editing the Wrong Code

Related to the software development blooper above, this one has to do with editing the wrong source code file (without realizing it obviously) and then wondering why the changes don't show up. There are several situations in which this might occur. An obvious case is in conjunction with version control systems. If the branching system is complex, it can be very easy to edit the wrong version of a file.

It can also be easy to start editing a generated file without even thinking about it and then have those changes wiped out when the regeneration process is run again. For example, when one generates Java classes from XML via JAXB or similar Java-to-XML binding approach, it can be easy to start looking at the generated code and even start changing it, forgetting that it is generated and likely to be overwritten the next time it is generated. Other examples of this include editing the NetBeans build-impl.xml Ant file and several types of IDE-generated code (such as GUIs with Matisse).

This is another case where this is most often a problem when the developer is distracted by too many things going on at once or is in too much of a hurry to think about the file that is being changed. Many of the popular tools that generate code place many comments and warnings in the generated code about it being generated in an attempt to reduce this happening.

The worst thing about editing generated code unintentionally is that all of the work is often wiped out with the next clean and build. The clean process will typically remove the generated file and then the build process will regenerate files. All changes made to the generated file are then lost. So, in addition to time lost by editing the wrong file, all remnants of the work are lost.


IDE-Assisted Software Development Bloopers

Java IDEs have become very powerful and highly useful in improving developers' productivity. However, there are multiple costs to the widespread use of the IDE. Most of us have decided that the benefits outweigh the costs, but we still must deal with those costs. One cost of using an IDE is that it can be easy to fall into the trap of letting the IDE do the thinking and/or accidentally approving an IDE recommendation that is not appropriate.

For example, when writing a "get" method on a class, the IDE might recommend that very "get" method as a return type because it does return the expected return type. If the developer selects that recommendation because he or she is in a hurry and is assuming that the data member associated with the "get" method will be the default recommendation, the developer will instead get a recursive "get" method that calls itself without end. This may not be discovered until the code is reviewed by someone else, run against a unit test, or actually used in an application.


Failure to Commit

Another really easy software development blooper to fall into relates to relational databases (or any other transaction-based product) and failure to commit when it is desired that another client can see the latest changes. This often manifests itself when a developer is using a database's tool for accessing the database directly (such as SQL*Plus for Oracle) and is also running an application against the same database. Before one realizes this common blooper is in progress, it can be disconcerting to see certain table values in the database tool but to not have those values present in the application using that database.

This situation is often very easy to fix; one simply commits the changes in the first tool and then the second tool or application can now "see" those changes. However, before running into this one the first time, it can be very disheartening to see this seeming discrepancy between views of the database.

In most cases, one should not turn on any autocommit features in an effort to avoid this issue. This is because, in most cases, one wants the ability to have a transaction span multiple statements. The preferable way of dealing with this, especially for people who are new to the potential problem, is to either commit the recent changes or provide a prompt instructing the person running the script to commit the changes if appropriate.


Static State

When one is used to dealing with instance-level objects and data, there can be some unexpected surprises when dealing with class-level (static) state. It is easy to fall into the trap of wondering why a class does not behave how it is supposed to behave from an instance-based point of view. The problem is that an instance-based point of view is not correct in these circumstances.

An example of this might occur when one runs a series of JUnit tests in the same test suite against different instantiations of the same class, but each test assumes that some portion of that class's static state is the same for each test. Because each preceding test in the suite may have changed the static state, the next test's assumption is erroneous and its results will almost certainly be invalid.


Common Characteristics of Software Development Bloopers

The examples of software development bloopers defined above help to draw out some conclusions regarding the nature of these bloopers. Software development bloopers tend to have these characteristics:

* Everyone's Doing It: Experience has certainly helped me to reduce both the frequency and the duration of software development bloopers such as those described above. Almost instinctively, even when talking to someone else about a problem they are having, I immediately consider one of the above situations where the symptoms merit that. However, even with experience, I cannot always control distractions and the temptation to try to do too much at once. This can lead me into bloopers like those identified above. Fortunately, I usually only lose a few seconds to a few minutes on these. However, that is a few minutes lost at a time in which minutes were already precious (hence doing too many things at once). This is much better than when I first started in my career. In those days, some of the bloopers described above could have cost me development time measured in hours rather than in minutes or seconds.

* Hindsight is 20/20: When we hear a specific example of a software development blooper as described above, it is easy to think, "How could a developer ever allow that to happen?" This illustrates the simple problems that are often at the root of software development bloopers. This is because software development bloopers are easier to identify and are more obvious the observer is not entrenched in the problem itself. A common reaction for a developer who resolves one of these software development bloopers is a mixture of relief (the time-consuming and perplexing problem is resolved) and of frustration at not identifying the "obvious problem" earlier. Hindsight is 20/20.

* Product of Distraction: Many of these software development bloopers occur because the developer is unusually busy; is unusually new to the language, domain, or environment; has too many things going on at once; or is otherwise distracted. The developer might not fall into this blooper the vast majority of time, but significant distraction increases the chances of running into one of these software development bloopers.

* Embarrassing: These software development bloopers can happen to anyone, especially to anyone new to a particular language or development environment. However, the developer cannot help but feel a little sheepish when the cause of lost productivity is discovered. It is nice if we can personally discover the issue and quietly move on, the lesson hopefully learned.

* Cost/Frequency Reduced with Experience: These software development bloopers can happen to anyone, even with significant experience. However, the more experienced developer is more likely to take steps to avoid these issues in the first place, is likely to identify the potential blooper condition much more quickly, and is more likely to resolve the blooper condition and move on than the less experienced developer. The good news here is that each of these bloopers usually only need be experienced once by the developer for the lesson to be learned. Generally, the more expensive the lesson (the more productivity lost), the more likely the developer is to remember it.

* Advantage of a Second Pair of Eyes: It is often true with these software development bloopers that a second pair of eyes is particularly useful in resolving them. The downside of this is that it opens the developer up to increased embarrassment cited above, but there are several advantages to having another person look at the issue. First, that person has fresh perspective and has not tried a bunch of different things or felt the desperation of grasping at straws. Second, I have found that the person dealing with the blooper actually identifies the problem himself or herself once he or she tries to explain what is happening to the second person. In the act of explaining, the person reasons through the problem and realizes why there is a disconnect.



Common Costs of Software Development Bloopers

There are multiple types of costs to software development bloopers. Fortunately, many of these are reduced on a per-developer basis with experience.

Time Lost Due to Application Not Working: This is the most obvious cost and has to do with the delay in finishing a feature because of the blooper.

Code Refactored for the Worse: This refers to the cost of adding logging and standard output to code or otherwise changing source code or build scripts or other development artifacts in an effort to discover the problem. There is a cost to adding these, a cost to removing them, and the cost associated potentially with junk (or even negative changes) leftover from the process.

Cost to Other Developers: While having a second person look at an issue is an effective method for identifying software development bloopers, it does come at the cost of that second person's time.


Advantages Associated with Software Development Bloops

Software development bloopers can actually produce long-term benefits despite short-term costs. These provide the silver lining for the clouds of software development bloopers.

Hard-earned Experience is Hard to Forget: Developers who have lost significant time to a particular software development blooper will rarely forget the lessons learned from the experience. That particular blooper and other related bloopers are less likely to happen in the future and should be less costly when they do happen.

Code Refactored for the Better: In the struggle to figure out why things aren't working as they should, the developer's dealing with a blooper can actually lead to positive changes to the code baseline. If, in a desperate attempt to identify and resolve a blooper, the developer improves the readability of the code, build scripts, and other artifacts, these changes can be left in place once the blooper is resolved for the overall benefit of the application.

Bloopers Can Teach Software Principles: I definitely learn best through hands-on experience. It is also true that I often learn at least as much from mistakes as I do from success. So, it is no surprise that one benefit of dealing with software development bloopers is that the developer can learn many things through hands-on experience dealing with the blooper. For example, a developer who lacks expertise with transactional databases might benefit from the concrete experience gained dealing with the failure to commit blooper described above. Similarly, the developer who is new to Java and is not clear on the difference between static and instance variables might learn this concept from the static state bloops discussed above than he or she could ever hope to learn just by reading.


Conclusion

Software development bloopers can be frustrating at the time, but hopefully are something we can look back on later and laugh at. While software development bloopers do cost us in several ways, we can turn these into valuable learning activities as well and possibly even improve our code and related artifacts in the process.

No comments: