It seems appropriate to assemble a collective answer from everyone's responses, since they now seem stable. Here it is.
Abstraction is always undesirable
Some people actually see abstraction as undesirable--a waste of effort. For example, students being introduced to abstraction by a teacher reviewing their code often respond "but it works". Even people programming for a living often don't see a need; their code is good enough. However, Martin Fowler has written a wonderful book on refactoring that I consider essential reading for all programmers. He does a great job illustrating why refactoring, which is a key step to obtain abstraction, is practical and desirable in nearly all cases. Therefore, to answer the begged question, "abstraction is desirable" is considered an axiom.
Abstraction is undesirable in exceptional cases
On the other hand, nearly every rule has an exception, and some have explained that abstraction can rightly be seen as undesirable in some cases. For example, in accordance with YAGNI, abstractions should be just-in-time (JIT), so a premature abstraction can be worse than no abstraction. Martin Fowler would probably suggest that this sentiment only applies in the face of a "big bang" abstraction, and he would probably recommend smaller refactorings that create the abstraction in smaller chunks. However, also in keeping with YAGNI, some argue that one should create abstractions only when the code begins to smell in certain ways, such as: when a method exceeds one screenful, when you want to copy more than one line for reuse, or when you feel the need to isolate something tricky or sophisticated. Another common exception occurs when one is learning an API, where uncertainty leads to concreteness that should be refactored later.
n-alexander beautifully described the consequences of creating an abstraction, in that it hides details, creates a contract, uses assumptions, and requires context. Therefore, creating an abstraction implies taking on significant responsibility that should not be taken lightly.
Indeed, many people recommend considering the potential audience carefully. They observe that the use of abstraction can make code less readable in the short-term--one may have to hunt through various source files to find definitions for numerous parts and participants in an abstraction. Some readers may even have to learn new (English, technical, etc.) words introduced by the abstraction. Therefore, lower-level abstractions are more accessible and universal, especially the ones delivered with a particular development platform. Failure to consider the audience may result in misuse of an abstraction because users ignore its implications, resulting in them placing the blame for any defects on the writer of the abstraction!
There is a lack of time for creating abstractions
As was mentioned in the question, sometimes we know better but we just don't have the time to create those abstractions or, at least, we don't feel that we do. We intend to refactor and to create those abstractions, and we may even know what they should look like, but we don't do it, yet.
Sometimes we feel the rush towards the next looming release (tight deadline). Perhaps it may be lack of budget, understaffing, the impact of providing support, that leaves us feeling that abstraction must be put off for now. Perhaps it is some fear, or misconception, that makes us think that abstraction is expensive in time and requires special planning and treatment.
Some have proposed that, under that pressure, we tend to fall back on practices that we learned when our experience was zero. We drop all our best practices, we temporarily forget the advanced techniques, and we just grind away at the most concrete level.
In an interesting twist, I have found that the most common occasion for me to experience this phenomenon occurs at the introduction to a new development language/platform/environment. I begin programming and wish that I had all my favorite abstractions, but I am overwhelmed in the face of the effort to recreate them. Of course, my new development landscape may include language features and APIs that already incorporate some of my favorite abstractions or make them unnecessary, but it will take me time to discover that. I usually begin recreating the essential abstractions as I create my first few projects, but I find myself wishing that I had the means to instantly "generate" them in my new development landscape. Indeed, I have repeatedly considered starting a project to do exactly that--express my favorite abstractions in a neutral way that can be "generated" into each new development landscape that I encounter, without a lot of fuss and effort. It could happen, I think...
There is a lack of experience for creating abstractions
Now we get to the heart of the controversy--that many programmers lack experience with creating abstractions, to the point of being unwilling or unable to do so effectively.
Some have argued that our institutionalized educational system is largely to blame, which perhaps focuses on correctness and efficiency to the exclusion of design and structure, resulting perhaps in the common claim that abstraction is generally undesirable and unnecessary. This may particularly reflect the fact that most educational processes consist of writing a series of unconnected, trivial programs that lack the dynamics of the real world that suggest, encourage, or even force the use of abstraction. The artificial environment lacks real users, lacks real use, lacks maintenance, lacks scale, and often even lacks peer review.
Even in the real world of programming, beyond the educational atmosphere, programmers are often isolated. They may be the sole programmer in their organization, or at least on their applications, or they may have peers but no one that is able and willing to serve as a mentor. The organization may simply push too hard and fast to allow for any kind of mentoring, peer review, or learning of any significance.
Maybe a particular programmer has yet to perform any software maintenance, or receive any feedback from someone attempting to maintain their code. Since abstraction is driven largely by the need for readability to support maintenance and reuse, the lack of that particular kind of experience can easily leave one questioning the value of such preventative measures.
Most programmers have only ever worked in-the-small, meaning that the scale of their applications/systems is on the low end of the spectrum. It is a simple fact that programming in-the-large is relatively rare, since there are few organizations that have the need and/or resources to reach that scale. Much of the drive for abstraction, and design patterns, and many other advanced programming approaches comes from the special considerations of programming in-the-large. Simply put, scale changes the dynamics of everything. It seems to me that we should make a point of differentiating our advice based on scale, since most of our advanced techniques are optional or irrelevant when programming in-the-small. However, abstraction seems to be relevant at practically every scale.
Unfortunately, our industry is focused on tools rather than technique. Whether in the educational process, the hiring process, the training process, the book-writing process, even the product evaluation process, everyone seems focused more on the tools than on the application of the tools. It seems that the situation may be improving, though.
As a reflection of this situation, it seems that novices tend to work from the tool (no abstraction) up to the problem (high abstraction, maybe), but experts tend to do the opposite. It also seems that novices want to hold onto power and responsibility by seeing low-level code on the screen, but experts let it go to achieve brevity and simplicity through abstraction.
Another consequence of this situation is that many programmers are never exposed to various abstraction principles that make implementation easier or even possible. Many have never heard of design patterns such as Inversion of Control or modularity approaches such as functional composition. Hence they are unsure or ill-equipped to introduce the abstractions that would help them.
Someone cited the Sapir-Whorf hypothesis which says that a person's thoughts are shaped and constrained by the language that they use, with particularly interesting application to computers. So, perhaps, the lack of abstraction that we see comes in part from a large number of people being exposed only to imperative languages (e.g., C) that encourage low-level, bottom-up, procedural code. Meanwhile, only a few are exposed to functional languages (e.g., Lisp) that encourage high-level, top-down, declarative code.
There is a complete breakdown in creating abstractions
It is apparent, just as should be expected from a decent grasp of human nature, that there are many people who don't care: they are just not willing. They understand the concept of abstraction, maybe they see its value, but they won't invest themselves in doing it.
For some, perhaps it is a lack of discipline. They want to invest themselves, but they can't quite pull it off consistently. Perhaps the organization puts obstacles in the way, probably unknowingly, and the programmer lacks the desire to overcome them. There may be a variety of reasons, but what matters is that it does not happen.
For others, perhaps it is a lack of professionalism. They see the value, have the discipline to execute, but choose not to do so. Maybe they do abstraction for their own projects, but not at work. Maybe they recognize that abstraction is not part of their performance review. Maybe they don't care if it will improve the company's bottom line, since that doesn't benefit them. In whatever manifestation, they put themselves and their own interests before their customer, so abstraction does not happen. Some argue that this is a key difference typically between employees and consultants.
Finally, to complement those that are not willing, it is apparent that some people just don't get
abstraction: they are just not able. As a matter of fact, there are many articles on the 'net and elsewhere that acknowledge the inability of many people to get
programming at all. I am not trying to claim that such people are less valuable (I vehemently don't believe that), but simply that programming, and particularly abstraction, is similar to painting or dancing or singing or any other challenging activity--some people have the talent, some people get
it, and the rest of us simply don't.
Some might find this example pathological, but I once had a development manager that strictly forbade me from creating abstractions because it resulted in "too many files in source control". Someone else on the development team (about a dozen members at that point) had apparently complained that all those files bothered them in some way, although I was never given an explanation. As the team lead and architect, I had spent months alone fleshing out a design in the newly-minted C# on .NET 1.0 (mid-2002). I had created trivially-simple, even obvious, abstractions such as EmailAddress
, PhoneNumber
, CustomerName
, etc. Now that we had ramped up a development team, I was merely continuing to occasionally add new but comparable abstractions as we encountered the need. Each was supported by a solid set of unit tests (over 4,500 in all) that verified their functionality and illustrated their use. Apparently, much of the team did not get
or want abstraction, which was further apparent when reading their code, since that code perfectly illustrated this question.
When I was younger, I had assumptions that someone who didn't get
abstraction, or didn't get
programming, or didn't get
troubleshooting, would recognize that fact and would act accordingly (meaning to seek improvement or seek to do something else). However, after much experience, and particularly after watching TV shows like "American Idol", I have since concluded that many people simply don't get
that they don't get
it. Furthermore, their family, friends, and coworkers often seem to help perpetuate that ignorance (making them accessories).
Thankfully, with singing, dancing, and many other activities, it is painfully obvious to many of us when someone doesn't get
it. But apparently not so with programming (especially abstraction)--the intangible, invisible, generally incomprehensible nature of that activity seems to make detection difficult, and to make description very much more difficult still. This is illustrated by many interesting articles about interviewing programmer candidates, where I have seen estimates that only one in two hundred applicants could actually complete a trivial programming task.
Therefore, I really try to examine my own thoughts, abilities, and actions, so that I might save my fellow humanity from the pain of watching me when I don't get
something. And I promise never to apply to "American Idol".