Dark Corners and Social Forces
Last week, I pointed out that bugs hide in dark corners. As a result of this phenomenon, programmers are often well advised to avoid unusual usages. As an example, I cited a C++ implementation I once used in which multiplying two
unsigned integers would yield zero on overflow, even though the C and C++ languages both define
unsigned multiplication as being modulo the word size.
Let's look at this phenomenon from the other side. You're a programmer, and you have a choice of two ways to solve a problem. One stays on familiar ground; you're comfortable with the technique, even though it's a unwieldy. The other alternative is newer, but much simpler. Which do you choose?
There is a lot of pressure — both from yourself and from your colleagues — to stick with the technique you know. The new way may be simpler, but if you're not thoroughly familiar with it, you're less confident that you haven't forgotten something obvious that will cause your program to fail. Moreover, the argument that applies to your programming also applies to the compiler: If you're using new features, you're likely to be among the first to find the bugs in those new features' implementation. Better to let someone else find those bugs.
This attitude translates into a reluctance to use new features, period. In the early days of C++, I worked with people who tried to convince development organizations to start using C++ instead of (or in addition to) C. One major source of resistance was that people did not want to be the first to find bugs in new tools.
This reluctance causes a curious phenomenon: People are more willing to use a totally new tool than they are to use new features in a tool that they already use. You have probably noticed this phenomenon yourself: I am going to guess that most of the readers of this article are using development tools with recent features that they have not bothered to learn how to use, even though they have used older features for some time.
This situation is not entirely discouraging, because even so, new facilities are sometimes adopted quickly. Perhaps the most important such circumstance is when end users do not need to use those facilities in order to benefit from them. In C++, for example, this phenomenon shows up in the use of move constructors in the standard library. A program fragment such as
vector<string> data; string line; while (cin >> line) data.push_back(line);
will run dramatically faster in C++11 than it will in C++03 because
string has a move constructor and
push_back uses that move constructor. As a result, pushing a new
string onto the back of
data does not need to make copies of all the
strings that are already in
data. This is an example of a new feature that has an immediate benefit because people don’t have to use it directly.
However, in cases in which users have to write code differently — or, worse, change existing code — in order to take advantage of new features, it can be surprising how long it takes those features to be adopted. This slow adoption is not because people are lazy, but rather because of the several factors we have seen that act to push people in the direction of greater caution.