Friday, September 5, 2008

Lines of Code and Unintended Consequences

One of the most insightful articles that I have read (in terms of both general daily life and a software development career) is Steven Kerr's On the Folly of Rewarding A, While Hoping for B. In this article originally published in 1975, Kerr employs descriptive examples to demonstrate how a reward system can often lead to different and even opposite results than intended. Using lines of code to measure effort and developer productivity often feels like something that would be another good example for this article.

In this blog entry, I have no intention of delving into the details of most of the advantages and disadvantages of measuring and collecting metrics by counting lines of code. These are outlined in many online resources such as the Wikipedia entry, Lines of Code - Dispelling the Myths, Estimation Technique: Lines of Code, and You Can't Manage What You Don't Measure. As these articles point out, there are advantages (such as quantifiable nature and simplicity) and disadvantages (such as language differences, developer efficiency differences, problem complexity differences) to using lines of code as a metric.

For this blog entry, my focus is on how the use of lines of code as a metric may serve as a detrimental motivational force for the software developer. In other words, while using lines of code may be meant primarily to measure productivity and to understand how far along a particular task is, the unintended consequence may be poor decisions that favor larger code counts in shorter periods of time. If we applied Kerr's article directly to this discussion, we might rename the article "On the folly of rewarding potentially worse development practices that happen to lead to more lines of code, while hoping for a measurement and estimation approach that helps developers and managers manage their work loads more effectively." This is not nearly as succinct a title as the original title, but does make the point. To be clear, I am not saying that fewer lines of code is always and necessarily better than more lines of code. What I am writing about here are situations when fewer lines of code is preferable to more lines of code, but the motivation to keep that count up might influence the decision.

So how can using lines of code as a primary metric serve as a detrimental motivational force? I will outline some examples where a developer could make a decision to choose a less efficient software development technique because of pressures of delivering a certain expected number of lines of code per day or week or month. I admit that the realization that I am reducing my expected line of code count has occurred to me more than once as I refactored many lines of code into code that was cleaner, more maintainable, and more readable with much fewer lines of code.


Code Generation

I want to avoid the debate about generated code versus hand-written code here. As with so many other things in life, there are times when each has its advantages and may be more appropriate. However, even in cases where hand-writing code may generate superior code, knowledge that one's performance might be measured by the lines of code produced could lead to one using the generation tools.

For example, I like to only provide "set" (or "mutator") methods on my objects when they are known to be needed. I prefer immutable objects generally. However, "set" methods can add significantly to my overall line of code count if I put them in all classes blindly. This is really easy to do with all modern Java IDEs that will automatically generate these methods for me. In this example, measuring my productivity via lines of code might tempt me to deviate from my hard-earned preference for immutable or carefully crafted mutable objects.

This can also go the other way. For example, one might choose to directly write object-relational mapping code rather than use a third-party object-relational mapping (ORM) mapping tool to increase the line of code count. There might be valid reasons for writing one's own ORM code, but I hope that increasing the lines of code count is not one of them. In this example, I might be tempted to make a decision about ORM handling based on lines of code rather than on more technical criteria.

Code generation can be a highly useful tool and truly can make me more productive. However, it can also be used improperly to "hide" the real efficiency and even lead to unneeded code that still must be tested and maintained.


Use of COTS Products

Several of the blog entries and articles on using lines of code as a measurement tool point out one of the challenges of this approach being how to count libraries. For example, the Spring Framework brings much functionality to an application. This is provided not only by the Spring libraries themselves, but also by all the dependencies libraries it employs. Spring can help one avoid much boilerplate code (as discussed in Add Some Spring to Your Oracle JDBC Access). In extreme cases, one might be tempted to not take advantage of Spring or similar frameworks and libraries because rolling one's own code will lead to a higher count of lines of code. There is also a temptation among many of us to succumb to Not Invented Here Syndrome (even when it is not really appropriate) and the lines of code metric motivation does not make this any easier to avoid.


Redundant Code

Years ago, I saw a well-respected project manager for a major software vendor outline how this person's company's IDE made reuse easy. The person went on to demonstrate this feature, which really boiled down to a fancy CTRL-C, CTRL-V copy-and-paste implementation. This, of course, did not appeal to my ideas of proper reuse. However, such copy-and-paste reuse might be appealing to a developer that knows his or her productivity is being measured simply by lines of code rather than by the maintainability of the code.


Refactoring

While there can be drawbacks to inappropriately chosen refactoring (such as breaking working software or delaying delivery at when implemented with poor timing), I have found that refactoring can often provide significant long-term advantages to the software I develop. Perhaps more surprising is that sometimes it even helps in the short-term because it makes my next task easier and quicker to implement. For example, if I see that I'm doing the same thing I've done before in multiple locations, taking the time to refactor this redundant code into methods often makes even the short-term job easier. However, refactoring like this can be especially egregious in terms of performance measured against lines of code because time is being spent to actually reduce the measured lines of code. It can sometimes be difficult to explain (or difficult for that person to believe) how fewer counted lines of code this week than last actually corresponds to more functional software.


Doing Things the Easy Way Rather than the Better Way

If a developer knows that he or she will be rewarded or punished based on the number of lines of code he or she generates, it is tempting to take the easiest problems with the highest degree of boilerplate coding. Likewise, a developer may choose to implement something in the most wordy manner imaginable rather than taking advantage of polymorphism, reflection, and similar techniques to write more concise code. Note that I am not saying that polymorphism and reflection are always the best approaches. In fact, they can be overused and abused and there are times when the complications of these approaches are overkill. However, I am saying that there are times when they are elegant and the most efficient. Unfortunately, writing code that effectively uses reflection or polymorphism can often be more difficult than writing the code with many more lines. So, as a developer with some degree of motivation based on lines of code, it is tempting to write the lengthier code that is easier and quicker to write rather than take the time to carefully craft the smaller code that requires more time to initially write.


Dead Code (UPDATE: This section added on 11 September 2008)

Most of us have witnessed and understand the lurking dangers of dead code or unreachable code. However, placing too much emphasis on total lines of code without any regard for the quality of the underlying code can be a demotivating factor that influences one's decision to eliminate dead code. Because many of the most common negative side effects of dead code (such as maintainability, extra compilation time, more things to break) are spread out over the long term, the total cost of dead code may not be apparent and so it is easy to think that avoiding confrontation over reduced lines of code is easier and better than removing the dead code (and the lines of code that get credited for it). This is representative of many of the factors discussed here: the negative consequences may be spread out enough or incurred far enough in the future that the idea of reducing negative consequences of reducing code count now outweighs the idea of fixing long-term problems or problems whose negative cost will not be incurred until much later.


How Motivating is the Lines of Code Metric?

This is the most difficult question for me to answer quantitatively. While using lines of code as a quantitative metric for measuring performance theoretically can motivate developers to make poor decisions as discussed above, it is not easy to estimate the degree of motivation this metric has. This difficulty arises from the fact that all individuals are different in terms of their responses to the same motivations. I have also observed that the best software developers often place great importance on the quality of their work and their own craftsmanship. Therefore, it may require a high degree of motivation to sway them from doing what they feel is best. However, if one feels his or her job is on the line or that there might be a nasty confrontation or embarrassing review, even such a person with a strong desire to write the best code can be influenced by such a motivation. The degree that lines of code counting will motivate the developer is also highly dependent on the importance and frequency of its use in the work environment. The closer that the measurement of lines of code is seen to being related to rewards or punishments, the more likely it is that lines of code will lead to poor software development decisions.


Is There a Better Way to Measure Productivity and Progress?

It is generally easy to identify how any reward system motivates unintended and/or undesired results. Unfortunately, it is much more difficult to provide better systems because these alternatives often have their own advantages and disadvantages, including their own side effects or unintended consequences. In other words, it is easier to criticize and find fault with any proposed system than it is to come up with a better system.

I have read about many proposed alternatives to using lines of code as a metric and they all have their own problems and disadvantages. No matter what system we use to measure our progress and our efficiency, we can always keep in mind that these numbers are far from absolute or clear. Instead, focus should be on "orders of magnitude" and the realization that not all lines of code are created equal. Other factors to consider include the difficulty coding a particular problem, languages used, newness of the technology, and the other many inadequacies of this measurement approach. If we don't take lines of code as an absolute measurement and consider lines of code as only one facet in a much larger set of considerations, I believe we can reduce the degree of potential negative motivation that lines of code counting can entail. Most importantly, lines of code count should be only considered with other factors such as recent refactoring, difficulty of work involved, and the relationship of lines of code to actual implemented functionality.


Measuring Affects Performance

Just as measuring performance of software actually impacts the very performance we are measuring, it seems that measuring lines of code may actually impact the number of lines of code generated. When we measure software performance, we try to use tools and techniques that have as little impact on the actual performance of the software as possible. Likewise with measuring code productivity and progress, we should try to use whatever tools and approaches will have the least impact on the generated lines of code. We want code to be generated to best solve a particular problem rather than to meet a certain expected total number of lines of code.


Conclusion

The idiom/proverb "The road to Hell is paved with good intentions" is widely popular because of the many true, real-life instances of it that we have all witnessed. Clients, managers, and even developers themselves want and need methods for estimating project sizes and measuring their progress. Unfortunately, these types of estimations and measurements have never been particularly easy. Measuring lines of code is one method for approaching the measurement of progress of a software development project and does have some obvious advantages. However, measuring lines of code also has its disadvantages. One of the primary disadvantages of measuring progress with lines of code is the potentially adverse motivational effect it can have on developers when they must choose between coding to meet this motivation (more lines of code) or applying tried-and-true software development best practices.






UPDATE (11 October 2008): A somewhat related Geek Hero Webcomic is now available and is called Programmer's Salary Policy.


UPDATE (24 December 2008): The following are some related resources that have been published since my original post or that I have come across since my original post.

"Measuring programming progress by lines of code is like measuring aircraft building progress by weight." (quote commonly attributed to Bill Gates)
- Programming Quotes
- A Double Handful of Programming Quotes

Measuring Productivity of Individual Programmers

Lines of Code as a Measure of Progress

Measuring Programming Progress by Lines of Code

1 comment:

@DustinMarx said...

Edsger W. Dijkstra's quote from On the Cruelty of Really Teaching Computing Science regarding the folly of counting lines of code ("lines spent") is apropos to this post: "My point today is that, if we wish to count lines of code, we should not regard them as 'lines produced' but as 'lines spent': the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger."