Gumption traps, Part 1 - definition and setbacks
Published on 4 April 2020
"Gumption traps" are a term introduced by Robert M. Pirsig in Chapter 26 of Zen and the Art of Motorcycle Maintenance as a detail of his Metaphysics of Quality. They're probably one of the most approachable topics in that book, and also one that can be acted upon very quickly, even (or maybe especially) by beginners. A newcomer to software engineering, motorcycle maintenance or any other craft job will definitely find something there that they struggle with and that they can work on.
Gumption and gumption traps
"Gumption" is a Scots word similar to English "courage", but its meaning is a little broader. Cambridge Dictionary defines it as:
The ability to decide what is the best thing to do in a particular situation, and to do it with energy and determination. She had the gumption to write directly to the company manager and persuade him to give her a job.
It also has that certain ring to it that, once you say it out loud a couple of times, will give you a better idea what it means.
Gumption in philosophy is not a particularly new idea, Aristotle hinted to something of that sort in Nicomachean Ethics as the difference between potential and result. Gumption is that difference. A doctor, for instance, can have the necessary skills to save the life of a bleeding out patient in front of him, but without gumption - without the drive to actually put those skills to use - the patient will die.
As Pirsig puts it:
Gumption is the psychic gasoline that keeps the whole thing going.
Gumption is essential in any kind of craft - be it woodworking, motorcycle maintenance or programming. Within the context of programming, you will see gumption differently based on whether you're working on a project of your own or a commercial project for a customer. The gumption traps will be the same, just the ways of dealing with them might be slightly different and your approach might require different kinds of adjustments.
What is a gumption trap then? It's a factor, either external or internal, that causes your gumption to "leak out". It's when the relationship between the craftsman and the material he is working on breaks. The result of falling into a gumption trap is the loss of enthusiasm of working on the material and the irresistible desire to put the entire thing away. Since we are talking here primarily about programming, we will be discussing these mostly within that context.
There are, broadly, two groups of gumption traps. External, that is arising from the material (in our case, code) itself, from the system we are working on, from the external parts (libraries) that we use or from something else. Pirsig calls these "setbacks". The second type - internal - is caused by our own poor mindset or failures in performing our craft. These are called "hangups". Hangups are further divided into three groups — "value traps", "truth traps" and "muscle traps".
Setbacks are external gumption traps and most often arise from the material the craftsman is working on or other external conditions. They may also come up as a result of something you have done previously.
Out of sequence reassembly
Does this happen to you, that you work on a complicated feature, but just in the end, as you're about to wire everything together - and maybe connect the UI to the back-end you discover that there's a mismatch on the interfaces and you need to rewrite one of the sides entirely? Or it turns out that the feature is not exactly up to spec because you missed something and now you have to start over?
That's the trap of doing things out of sequence. In more general terms, it's when you realise you did something based on incorrect facts. Sometimes those facts change as you work - it's even more disheartening then. But there are ways to deal with it.
The first one is to be meticulous about your presuppositions. In other words - plan ahead. Not too much - there will be facts that you will fish out once you start working on it - but think about the general, high-level requirements that you will be working with. Read the spec of the feature a few times, note down (either physically or mentally) what jumps out at you as incorrect, under-communicated or what might prove to be more difficult later on. The knowledge of how to do that comes with experience - but the more attention you pay to it, the quicker you will gain that experience.
Secondly, commit often. If you don't use a version control system, start. And then whenever you finish a logical chunk of work, commit. This leaves a paper trail and a quick and easy fallback mechanism to return to in case your preconceptions turn out to be wrong.
And finally, when you do finally end up falling into this particular trap, remember that the subsequent "reassembly" - rewriting of the incorrect modules - will most likely take far shorter than the first run. After all, you already have the necessary knowledge not to make the same mistakes.
...or as they're called more commonly in our field: heisenbugs. These are issues that seemingly disappear when studied. They exist only in production, but when we take the magnifying glass of our debugging tools, they are nowhere to be found.
Unfortunately, if the heisenbugs were not as pernicious as they are, they would not be so feared. The advice Pirsig gives to deal with them in the context of motorcycles - "just ride it out" for a couple hundred kilometres - is also difficult to action within the software context.
Fortunately, the programming languages of today provide us with a host of tools to catch these bugs - but problems start when you do not exactly know how your diagnostic tools work.
One story from my professional experience comes to mind here. My colleague was trying to debug an object that seemingly had incorrect properties appended to it - but whenever he put a
console.log into the function that constructed this object and inspected its nested properties, everything seemed to be fine, even though the result of an Excel file that was later generated by the object was blatantly wrong.
The issue, turns out, arose from a peculiarity in how
console.log works in browser dev tools (which I'm sure my esteemed colleague knew about, but just happened to forget when it was needed — it happens to the best of us). Namely, the nested properties of an object are reevaluated when opened inside the browser dev tools. So, when one logs an object in a given state, if it had mutated by the time one opens one of its nested properties, those nested properties might not be the same as they were at the time of logging the object. (We had to mutate the object for performance reasons.)
The solution in that case was to
console.log(JSON.stringify(object)), however we did spend a good workday trying to figure that out. The bottom line - remember how your diagnostic tools work.
We do not often think about having to replace parts in a program - after all, it's not a physical thing, and the code that we write doesn't experience wear and tear, right?
Wrong. Our code uses (especially in the JS ecosystem) various kinds of libraries and modules. Some of these modules we wrote ourselves, some are third-party. Those third-party modules are often a source of our woes. Sometimes an update is going to break it. Sometimes there will be a bug in third-party code that we can't exactly fix. Sometimes we will simply outgrow the needs of a library. Another time we will have to replace one component with another that does not have the same interface.
The most straightforward way to deal with these issues is to wrap your third-party code in custom interfaces. When you import a React component, do not simply use the provided interface that it ships with and then use it in dozen pages. Instead create a single point of contact between the library and your code. Oftentimes it might just be a rewrite of the module's interface into your own code - but that step is very important. Because when the time comes to replace that component, there will only be one place to change the interfaces rather than a dozen.
Of course this all has to be done within the bounds of reason - if you import React itself, it is fairly pointless to write an entire interface around such a robust framework. If a time comes to replace React, you will have bigger problems than that.
And also, remember to sometimes ask yourself the question "can't I make this myself?". Homegrown components are not in any way worse than third-party ones and the time spent building one of your own can save a lot of headache in maintenance. It's also much more flexible and gives you more experience to approach similar issues in the future. Finally, since we live in the digital world where every piece of code can just be copied or imported indefinitely, you might very well use that module in the future.
Setbacks often feel unfair - they come from things that we do not have much control over. But we do have control over how we approach them.
Check back next time for part two, where we will consider another kind of gumption trap: hang-ups. Or, in other words, the things that are all your own fault.