By nature or nurture, programmers fall into two camps regarding their general attitude towards abstractions: Believers and Non-Believers.
- Believers tend to trust abstractions, and are comfortable working with abstractions without "opening them up". They are quite happy with having many small methods and classes as long as they are "well" named - well enough that they don't have to read the implementation. They don't mind the fact that in such systems, it can be difficult to see where the real work gets done, as they believe they can always drill down to that place if and when it becomes necessary. Favorites: ExtractMethod and Very High Level Languages. Pet Peeves: long methods and bad naming.
- Non-Believers have a general mistrust of abstractions, as long as they don't know and remember their implementation. After all, a method (for example) might do a little bit more or less than its name implies, or something slightly different, or might have unexpected preconditions, side effects or performance characteristics. For them, the name of an abstraction serves only as a reminder of its implementation anyway, and when the name is good enough as a reminder, it's good enough. They hate being presented RavioliCode, because as they read the code, they feel the need to open up every single method implementation, and will quickly suffer from a personal StackOverflow. They prefer somewhat longer methods that actually do some work, instead of just passing the buck. They like to work in languages with a straight-forward, predictable mapping to assembler. Favorites: InlineMethod and C. Pet Peeves: RavioliCode and snotty UML-and-Pattern types who think "IRQ" must be some kind of chat program.
Another way of seeing the difference:
- Believers think that a name can properly encode the meaning of an abstraction.
- Non-believers think that you can keep the semantics of a program in your head.
Of course, the truth lies in between...
Some people like to take things apart to figure out how they work. It is often part of the "geek gene". Perhaps such people should find jobs closer to the hardware, such as embedded devices.
Actually, taking apart a well-built system of abstractions can be just as fascinating as taking apart any other system. One just has to get used to "Compartmentalizing", treating areas of abstraction that you haven't opened yet as little black boxes that do exactly what they say they do. (Or, alternately, starting from the edges/bottom and working your way out of the little black boxes and into the higher-level abstractions) This technique is, in my admittedly somewhat limited experience, amazingly effective when tracking down weird bugs in software. I have occasionally encountered a personal StackOverflow
when doing this, but that's usually a sign that I'm trying to go too fast or skipping steps.
I'm not sure "belief" is the appropriate way of framing this, as if it's a mere matter of opinion. It strikes me that a) the world is too complex, and even such a simple thing as a computer program easily gets too complex, to be understood properly without a skill at abstraction; but b) AllAbstractionsLie
, so you still need a skill at drilling down into details.
I wasn't perfectly happy with "belief/believer/non-believer" for the two approaches myself, just couldn't think of any better alternatives. Any suggestions?
About b): Yes you need a skill at drilling down, the question is just when you do it: immediately, because you mistrust the abstraction, or lazily on a per-need basis. About a): Yes, good programmers especially in the OO or functional world need also be good abstractionists. But let's just say that such skills are varied (try handing your average Lisp or AspectJ program to the average PHP guy...). Also, some people *can* deal with a higher level of abstraction if they have to, but are not comfortable at that level. --FalkBruegmann
I think that the ability to work with abstractions is more a skill than an act of faith.
Of course, when working with poorly-formed abstractions, one's faith in them can quite rightly be shaken.
I like to say "Abstraction Is Power,"
implying that achieving higher levels of abstract understanding of a problem gives one leverage to accomplish more.
However, sometimes you do have to look at implementation characteristics -- for performance optimization, for instance.
I fall into the "believer" category defined above, but I don't think of myself as BelievingAbstractions
. I think of it as being optimistic. If an abstraction says it will do something I hope it does just that (and nothing more) but I feel safer with some tests to verify it. If the tests expose a lie I fix it, rename it, or (if I can't edit the source) wrap it. -- EricHodges
is knowing that something written will DoAsItSays
. It is not about being optimistic - when symbols DoAsItSays
belief is unnecessary. In so much as AllAbstractionsLie
: humans fall short in summing up the world around us with symbols; abstractions afford us Wiggle Room.
It seems to me that the correct way to go is to believe the name if it gives you enough information, and go read the code if it doesn't. Because of that, both clear structuring of code (i.e. no RavioliCode
) and self-describing names are important. I'm more on the non-believer side, but believers still tend to write much longer methods / functions than I. -- PanuKalliokoski
It is partly a political issue. If you use somebody else's abstractions, but you take the heat if they fail, then most would rather reinvent or open them up. On the other hand if you can safely point a finger at the abstraction maker if it fails, then you are more likely to rely on it. -- top
I think I would try a slightly different categorization; general code reading versus program maintenance (two types).
- Some people (I am not one of them, so I can't really comment a lot on this viewpoint) just like to read code. When starting on a project, they will get files out of the version control system and try; to read the code from beginning to end. These people will tend to prefer fewer and longer methods that can be read sequentially and avoid a lot of drill down and walk back.
- Some people only want to look at code to solve a problem. They prefer that the code direct them to the area of interest with minimal side trips into other areas. These people prefer more well named methods so that they can quickly filter out code that is not of direct interest.
- Some people only want to look at code through a debugger. They will single step through every line of code to see what is happening. The partitioning of code into methods is immaterial to them; the debugger will always find the next line of code. These people really don't understand the arguments between the first two types.
The three viewpoints might be summarized as:
- the code structure should show me the details,
- the code structure should help me filter the details, and
- the code structure does not matter.
Any reactions to this breakdown? -- WayneMack
Trust, but verify. If a library/framework is well-designed, I shouldn't have to
read through its source in order to use it, but I find that if I do
have some understanding of how the innards work, it helps me use it more effectively and efficiently. Sometimes a walk through the source code reveals things about the library developer's intent
that don't get adequately communicated in the docs. -- MikeSmith
See Also: AllAbstractionsLie