Written on 2019-05-27
Working at an institute for computational biology, my colleagues and I deal with computer code almost every day. Yet none of us is a trained software developer as such – we are biologists, physicists, and mathematicians who happen to have learnt a bit of programming on the side. Some of us (like myself) got into it as a hobby, most picked it up along the way. So over the past few weeks, I started to ask myself a question: “How can we become better at developing software?” The next question came naturally: “Well, what is good software development?”
I've been reading and thinking a lot about that since then. By now, I have seven years of programming experience to go on, but I have never thought through my craft in any systematic manner. So this is a bit of a voyage of discovery: drawing together shreds of experience and combining them with what I have learnt from others or read in blogs and books. It's fun, it's exciting, it's educational. And because writing is an excellent form of learning, I thought I would document my journey in a series of posts here on Terra Nostra.
So let's get started with the first part: “What, in essence, are the most important principles of software development?”
Elegance is something both artists and engineers aspire to. Since software development is both an art and a science, elegance often crops up when we talk about good code. Elegance has the connotation of efficient simplicity, of beauty paired with power. Describing something as “elegant code” is a high praise.
Nonetheless, it remains an elusive concept. It is hard to describe and even harder to quantify. Therefore, I prefer to talk about three qualities that are somewhat more concrete. You might call this the golden aim of software development: software that is reliable, understandable, and extendable.
Reliability. Software has to work, and it has to work correctly. Having some bugs is virtually unavoidable, but a single bad bug can render an entire program worthless. This is especially true in scientific computing, where much depends on the validity of our calculations. A bug in the wrong place can set research back by 15 years. Beyond “mere” correctness, reliability also means that a software holds up well under stress and can deal with unexpected input, high computational loads, or a different system setup.
Understandability. “Programs must be written for people to read, and only incidentally for machines to execute”, wrote Abelson & Sussman in the introduction to SICP (see below). If you've ever come back to one of your projects after having left it alone for a couple of months, you'll know how grateful one is for cleanly-written code. If you've ever had to come to grips with somebody else's codebase, that goes double. Regardless of whether you're debugging or extending a given piece of software – a good understanding of it is a necessary prerequisite to whatever you plan to do. When writing code, be kind to the person coming after you. (It might be you.)
Extensibility. A useful piece of software will frequently find itself having to adapt to new requirements. Whether it is support for new hardware in some imaging software, or a new factor to be included in a simulation model, living software grows over time. A well thought-out architecture is decisive in determining how hard or easy the implementation of new additions will be. Apart from making life easier for the original authors, software that is easily extendable is also more likely to be taken up and improved by others.
There's many different aspects to achieving the three goals outlined above. Many of them play together, there is no one way to achieve perfection. Here, however, are two principles that crop up again and again in various forms. I'll be writing more about each of these some other time, but I did want to briefly mention them up front:
Reducing complexity. “Reducing complexity is arguably the most important key to being an effective programmer”, says Steve McConnell (see below). Programming is complicated business – the bigger the program, the more complicated it becomes. Keeping this complexity at a level manageable by the human brain is a serious skill. But it pays off: less complex programs are more reliable, easier to understand, and easier to extend. In fact, one could call this the golden way of software development.
Using the right tools. Over the past seven decades, generations of computer scientists have invented countless tools to help us write better programs. From languages and libraries to paradigms and data structures, knowing the tools at your disposal and when to use which will make you faster, more efficient, and more effective.
Thus far with a very brief overview of some of the core principles of software development. In the coming weeks (and perhaps months) I will go more into detail on the things I have touched on above. Don't ask me yet what I'm going to write, I'm still figuring that out 😄 But I look forward to it.
One last thought before I close. Becoming a better programmer is exactly that: a becoming, a process, a journey without end. The path, though, is simple: read, write, repeat. Read what other people have to say; in discussions, in books, in their source code. Write plenty of code of your own. And then start over 😉
– – –
(I will link the future parts of this article series here once they are published.)
In case you're interested, the two books I am relying on the most for this project are:
Structure and Interpretation of Computer Programs, Harold Abelson and Gerald Sussman, with Julie Sussman (MIT Press 1996, 2nd edition). The classic textbook developed for and used for years in the MIT introductory programming class. Simply known as SICP, it is an elegant introduction to many important concepts of computer science.
Code Complete 2, Steve McConnell (Microsoft Press, 2004). A 900-page magnum opus on all the practical aspects of software development, covering everything from indentation to debugging and team management.