The Woodcutter's Craft

The woodcutter does not choose where to cut, but aligns himself to the grain. The woodcutter does not swing the axe to break the wood, but lets the axe fall to pass through it, along the grain. By making no choice and no effort, the woodcutter makes no mistakes in choosing how to split the wood.

Can the software developer learn from the woodcutter? A developer may invent nothing new, but simply align himself to existing needs. Code may not be made to act for the user, but only written to help the user act as the user desires. How does the software developer avoid making mistakes in satisfying the user's needs? This can only be accomplished when one is not driven by the arrogance of believing one knows, better than the user, what needs must be met.

There is no accurate prediction, and no way for the developer to solve the user's problem. Only the user knows what the user wants. The developer's craft is not to do things for the user, to predict desires, or to replace the user. The developer's craft is to see where something is hard, and align the tools along the easy path. The developer then gets out of the way, and lets the user's needs, like the axe blade, fall where they may.

Otherwise, in a year -- when the developer is the user -- an old project that solved an immediate problem may not solve the new problem. This is because the problem is not the same as the need. When one has a problem that requires a solution, it is because one has not aligned a tool along the easy path, the path of the underlying need, like the woodcutter who thinks of the problem that the pieces of wood are too large when his true need is for the wood to split. Do not hack at the wood until the pieces are small enough; align yourself to your needs, and let them fall into place.

As software developers, we think of hallmarks of good programming practice such as generalized code and modular design. These are techniques, but they are not solutions.

We assemble modules of generalized code into directed bundles of automated action, which we aim at our immediate problems. These are the solutions, but once the most immediate problems are solved we end up finding that the next few problems are subtly different. When we aim our solutions at those problems, we find myriad smaller problems that also need to be solved, to address the details in the current big problem that differ from those of the previous big problem.

We can start over and create a whole new piece of software, which results in things like the Linux sound subsystem mess where we need myriad differing solutions all wired together in a complicated overlapping field of solutions for substantially similar problems. (Lest we forget, MS Windows offers its own tangle.) Alternatively, we can start writing less-generalized, special-case code that we attach to the edges of the existing code that add more steps and more predicted pathways behind the scenes to route around the different characteristics of different problems until the result is a huge, overly complex, tightly coupled bundle of nominally modularized and generalized code slathered in thick, sticky glue.

Refactoring sometimes helps us find the underlying need, teasing its signal out of the noise of problems so that we can address it, but if we still think of it all as solving known problems (where we simply need to know the problems that we must solve to more successfully predict how best to replace the user) we will always leave some part of the underlying need unaddressed. Sometimes, one piece of software tries to be many pieces of software because it tries to solve all problems that somehow look alike, but over time more and more of the world's problems look alike as the problem field the software tries to solve grows. The result, then, is not a solution, or a bundle of solutions; it becomes a bundle of new problems. Netscape grows and eventually turns into the Mozilla browser, which tries to do everything, but then it is replaced by Firefox and Thunderbird and other tools. Over time, Firefox grows and becomes the same thing that the Mozilla browser once was -- an attempt to be everything to everyone, all the time and in every circumstance.

What we do as developers should not, generally, be to create one big solution to everything. What we should do instead is see where something looks hard and find the easy path, then align our work along that path, and finally let the user's need fall where it may. Do not try to predict what the user wants to find, then provide a narrow set of results, while making the broader set of possibilities much more difficult to discover, like Google now does; let the user choose what to find, then find it and let the user choose what is desired, like the command line tool find. Do not try to predict how the network should be configured based on statistical models of users and connect to something that seems to fit those models, sometimes changing connections even while the user is trying to visit a webpage, with predictably unwelcome results, like NetworkManager; give the user the ability to choose what to do, then just do what the user chooses, like the BSD Unix implementations of ifconfig. Do not build every single secretarial task in the world into a single piece of software, then guess at which subtasks are most likely to be useful at a given time and show only those while hiding the remainder away where they can only be found with difficulty, like the Microsoft Office "ribbon"; provide a text editing tool with basic functionality in the form of features that can be combined to accomplish more sophisticated tasks, like vi, and provide a file management tool with basic functionality in the form of features designed to facilitate the use of additional small utilities that can be combined to perform more sophisticated tasks, like the shell.

When something more sophisticated or approachable is required, create it around these simple utilities. Combine these new tools to produce something else even more sophisticated or approachable when required. Do not rewrite all those simple utilities as integrated, inseparable parts of a larger, less comprehensible, more failure-prone and frustrating whole that can never fulfill all needs in some misguided attempt to solve all problems. Problems are only fleeting conditions standing in the way of more generalized needs. Needs are the lack of generalized capabilities that can be employed to solve problems.

Clearly, the world changes, and new approaches to old tasks will be needed. We can build something helpful around ifconfig that does not get in ifconfig's way -- or we can build something alongside ifconfig that gets in ifconfig's way, and in the user's way, when it works at all. We can build a hydraulic woodsplitting guillotine that splits a few pieces of wood very quickly, but just as quickly destroys at least as many pieces of wood that were not positioned properly before the blade came down, splinters pieces of wood into useless mashed-up hunks when it crosses the grain too badly (thus requiring yet more wood to be gathered), and occasionally runs the risk of removing a finger while trying to position wood for the falling blade -- or we can give the woodcutter a few minutes' instruction on how to let the axe blade fall along the grain of the wood, and give the woodcutter a pair of gloves, so that the job suddenly becomes much easier on both the muscles of the arm and the skin of the hand that holds the axe handle.

Perhaps some day we can replace the axe with something better, but a better approach will not look like a smoky, grinding, dangerous, brute-force machine that wastes more than it makes, nor will it look like the Eclipse IDE.