In our mission to get ourselves and other software developers out of the tarpit, we began considering the complexity of software by first focusing on the complexity of physically writing lines of code. To borrow a definition from Mosely and Marks (who themselves borrowed from Brooks), we think of complexity as fundamentally in one of two categories:

Essential Complexity is inherent in, and the essence of, the problem (as seen by the users). Accidental Complexity is all the rest — complexity with which the development team would not have to deal in the ideal world (e.g. complexity arising from performance issues and from suboptimal language and infrastructure).

When thinking about the work involved in the act of typing lines of code into a text file, we can broadly categorize those keystrokes into different conceptual buckets:

Semantics. First order structural decisions (e.g., “add match statement here”, “make a new function”, “remove a constructor of this type”) form the basis of programming decisions, creating the logic of the program, and serving the will of the programmer. Structure. Things like braces, commas, parentheses (i.e., syntax) are necessary to manifest your semantics and to inform the interpreter/compiler. Names. You have added a function, argument, constructor, etc., and you must name it so that you can refer to it later. References. You have already defined a function, argument, constructor, etc., and you want to reference it. Propagation. Both semantic and naming changes (i.e. “refactoring”) must be propagated to affected parts of the program (e.g., you remove an argument of a function and must go change all calls to that function; you rename a value and must rename references to it).

So within these broad buckets, where is the essential complexity and what is accidental complexity introduced by the text editing paradigm? What is the necessary work of the programmer conveying the logic of the application, and what work is being done to satisfy the machines that will read it?