Software Development Proverbs

Cédric Van Rompay

January 27, 2024

Bits of wisdom I got from the Internet that accompany me every day when developing software.

Readability counts.

Source code is read more often than it is written. It is not simply an input to the compiler, but also an explanation to the other developers that will have to work with it in the future. It has to make sense, to be helpful and easy to understand.

It's easy to overlook the importance of code readability because it's harder to measure than other qualities like execution speed or memory consumption. But as time goes, I am more and more prone to favor code readability even if it may have a minor impact on other performance metrics. Code that is more readable is easier to improve, to fix, and to collaborate on.

This proverb can probably be found in many places on the Internet, but I am attributing it to the Zen of Python here because I think it is a great text, and it is my main source of inspiration when I think about software development proverbs. It's never a bad idea to run import this when you start an interactive Python session, to get your daily dose of wisdom before you start coding. Readability, and caring about people building software at least as much as the software itself in general, was definitely one of the core factors of the huge success of Python up to now.

Explicit is better than implicit.

Being explicit helps make the code more readable because you don't have to guess things. When writing code, I often think about “how much info you need to make sense of this code” just as another type of dependency of the code, together with the variables, data, connections and other things the code needs in order to run. Just like it's usually better to have less dependencies in your code, it's usually better if your code makes sense without requiring too much context. Having to guess what something means is like having to retrieve a value from a “cultural database”, and I'd rather pass the value directly and avoid this extra step.

I actually tend to push this rule quite far even in the choice of the names I use: I avoid acronyms, jargon, and terms that may only be understood by a small group of people. One simple example: some teams like to make up names for their sprints, for instance to name them after fruits, or cities, or elements from the periodic table. What I prefer is naming them using simply their start and end dates, so that I don't have to remember what we are talking about when we mention “the pineapple sprint” or “the manganese sprint”.

Flat is better than nested.

I like this other one from the Zen of Python because it's unintuitive for me, and probably for a lot of other people as well, yet it helps me a lot. I am always tempted to group things together as soon as I see any sort of linking between them, and this tends to create deep hierarchical trees that are difficult to read. This proverb helps me use flatter hierarchies, be it for files, classes, or most sorts of relationships.

Principle of Least Astonishment

This one is not a proverb but I think about it frequently. It is still about readability, and it says that someone should not be too surprised when reading the code or the design of a system. Surprising things may appear, but then they must be compensated by a clear explanation of why they were necessary. A funny way of thinking about it is that good code should not cause a lot of eyebrow movement when being read.

Make it run, then make it work, then make it fast

Davide was my teacher at EURECOM from 2012 to 2014, and one day he gave us this proverb which has helped me a lot ever since. I don't know whether he came up with it or if he was quoting someone, but I know it is thanks to him that I heard it.

I guess this quote is equivalent to Donald Knuth's famous proverb “premature optimization is the root of all evil”, as here “fast” represents any kind of optimization. I like the one I heard from Davide better, possibly for personal reasons, but also because it tells you what to do instead of what not to do: run the program, or whatever parts of it you already have, as soon as possible. Not only will you see if it compiles at all, but you will also learn a lot by seeing how the program behaves for real.

Finally, aiming at having something that just runs as quickly as possible forces you to have loosely coupled parts in your system, as they must be runnable when the other parts are not there yet, and this usually is a good characteristic leading to simpler, clearer and more agile architectures.

we do these things not because they are easy, but because we thought they were going to be easy

I like this one a lot because it's both very funny and very insightful. It plays on the famous quote from J. F. Kennedy's speech “We choose to go to the Moon”: “We choose to go to the Moon in this decade and do the other things, not because they are easy, but because they are hard”.

This proverb is a reminder that most problems you are tempted to solve will turn out to be more complex than you initially thought. I am not talking about projects that are simply “late”, but projects that seem less and less achievable as you work on them

This is one more reason for aiming at smaller intermediate goals, to increase your chances of delivering anything at all and to make sure that when you are planning the last stages of the project you already have a good understanding of what you are dealing with.

Everything should be made as simple as possible, but no simpler

This quote is originally about science, but it applies to engineering very well too, especially if you apply it to generality instead of simplicity (the two are related anyway): you should make things as general as possible, but not more general than that.

As a software developer, it's always tempting to make your code manage a wide variety of cases, but this almost always makes the code more complex. This is easier to illustrate with mathematics: Pythagoras' theorem only works in a right triangle, but the law of cosines does the same job in any triangle: it is a generalization of Pythagoras' theorem. One could then argue that there is no use knowing Pythagoras' theorem and one should simply learn the law of cosines. Except that the law of cosines is much more complex: it's really hard to remember it without mistakes. And this is why Pythagoras' theorem is still useful: even if it's more limited, when you are dealing with a right triangle you might as well use Pythagoras theorem because it's so much simpler.

Do not break the Web

“Do not break the Web” is a motto that is often cited when talking about the development of Web standards. The Web is the technology where backwards compatibility is taken the most seriously, and we often underrate the merit that this community has for having taken this technology so far while making sure that most old websites still work exactly like they used to.

This is also a proverb that is important to me when I get mad at Web technologies for their oddities and unintuitive aspects: I remember that there is a reason for these, and then I am not so mad anymore.

Accept that everything is a draft. It helps to get it done.

This proverb helps me remember that, no matter how much time I will spend on a project, I will always have this feeling that “it could be improved”. Nowadays, I try to publish things as soon as I think they can be useful, and as soon as I think I can expect people to use them and give me some feedback. I also tell myself that I can always publish a better version later.

There is no editing stage

This proverb can seem contradictory with the previous one: if there is no editing stage, shouldn't you refrain from publishing an early draft, knowing that any mistakes it may contain will likely stay there forever?

Of course this proverb is somehow a warning about not publishing things you may regret later. But I think it also tells something about how to deal with change: publishing a new version of something, especially software, does not make the previous versions disappear. It makes them coexist.

If the previous version really has to go, you can rely on various strategies, like reaching out to users, degrading the service quality, etc... but in general, software tends to have a lifespan much greater than what you had in mind when publishing it.

One practical consequence is the importance of clearly communicating the level of maturity of a project to its users, to reduce the chance that they rely on it more than they should.

Autonomy, mastery, purpose

This is not a proverb, instead it is what Daniel Pink argues are the three main driving forces of one's motivation at work.

I find it extremely true, and I keep it in mind when I think about where I want to drive my career or even what projects I want to work on and how. You want to know why you are doing something, you want to have the freedom to choose how to reach your objectives, and you want to have the time and resources to try doing a very good job at it.

“The Mythical Man-Month” and Why I Like Software Development

I knew I would love software development from the first day I was introduced to it. But when I stumbled upon this text in the first chapter of “The Mythical Man-Month” by Frederick Brooks Jr. I was shocked to read a text that was depicting why I was loving it with so much clarity.

The Joys of the Craft

Why is programming fun? What delights may its practitioner expect as his reward?

First is the sheer joy of making things. As the child delights in his mud pie, so the adult enjoys building things, especially things of his own design. I think this delight must be an image of God's delight in making things, a delight shown in the distinctness and newness of each leaf and each snowflake.

Second is the pleasure of making things that are useful to other people. Deep within, we want others to use our work and to find it helpful. In this respect the programming system is not essentially different from the child's first clay pencil holder “for Daddy's office.”

Third is the fascination of fashioning complex puzzle-like objects of interlocking moving parts and watching them work in subtle cycles, playing out the consequences of principles built in from the beginning. The programmed computer has all the fascination of the pinball machine or the jukebox mechanism, carried to the ultimate.

Fourth is the joy of always learning, which springs from the nonrepeating nature of the task. In one way or another the problem is ever new, and its solver learns something: sometimes practical, sometimes theoretical, and sometimes both.

Finally, there is the delight of working in such a tractable medium. The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination. Few media of creation are so flexible, so easy to polish and rework, so readily capable of realizing grand conceptual structures. (As we shall see later, this very tractability has its own problems.)

Yet the program construct, unlike the poet's words, is real in the sense that it moves and works, producing visible outputs separate from the construct itself. It prints results, draws pictures, produces sounds, moves arms. The magic of myth and legend has come true in our time. One types the correct incantation on a keyboard, and a display screen comes to life, showing things that never were nor could be.

Programming then is fun because it gratifies creative longings built deep within us and delights sensibilities we have in common with all men.

The Woes of the Craft

Not all is delight, however, and knowing the inherent woes makes it easier to bear them when they appear.

First, one must perform perfectly. The computer resembles the magic of legend in this respect, too. If one character, one pause, of the incantation is not strictly in proper form, the magic doesn't work. Human beings are not accustomed to being perfect, and few areas of human activity demand it. Adjusting to the requirement for perfection is, I think, the most difficult part of learning to program.

Next, other people set one's objectives, provide one's resources, and furnish one's information. One rarely controls the circumstances of his work, or even its goal. In management terms, one's authority is not sufficient for his responsibility. It seems that in all fields, however, the jobs where things get done never have formal authority commensurate with responsibility. In practice, actual (as opposed to formal) authority is acquired from the very momentum of accomplishment.

The dependence upon others has a particular case that is especially painful for the system programmer. He depends upon other people's programs. These are often maldesigned, poorly implemented, incompletely delivered (no source code or test cases), and poorly documented. So he must spend hours studying and fixing things that in an ideal world would be complete, available, and usable.

The next woe is that designing grand concepts is fun; finding nitty little bugs is just work. With any creative activity come dreary hours of tedious, painstaking labor, and programming is no exception.

Next, one finds that debugging has a linear convergence, or worse, where one somehow expects a quadratic sort of approach to the end. So testing drags on and on, the last difficult bugs taking more time to find than the first.

The last woe, and sometimes the last straw, is that the product over which one has labored so long appears to be obsolete upon (or before) completion. Already colleagues and competitors are in hot pursuit of new and better ideas. Already the displacement of one's thought-child is not only conceived, but scheduled.

This always seems worse than it really is. The new and better product is generally not available when one completes his own; it is only talked about. It, too, will require months of development. The real tiger is never a match for the paper one, unless actual use is wanted. Then the virtues of reality have a satisfaction all their own.

Of course the technological base on which one builds is always advancing. As soon as one freezes a design, it becomes obsolete in terms of its concepts. But implementation of real products demands phasing and quantizing. The obsolescence of an implementation must be measured against other existing implementations, not against unrealized concepts. The challenge and the mission are to find real solutions to real problems on actual schedules with available resources.

This then is programming, both a tar pit in which many efforts have floundered and a creative activity with joys and woes all its own. For many, the joys far outweigh the woes, and for them the remainder of this book will attempt to lay some boardwalks across the tar.