Software development is always about acquiring knowledge. How we get that knowledge can vary, but it is never enhanced by being lazy in thought or deed and there are capabilities in modern systems development that can encourage laziness. It was not so years ago...
When Small Was Big
When I started programming professionally in the early 1970s, the only available computer was a large mainframe. When I say large, I mean physically; in memory size it was miniscule.a The computer's primary function was running a steelworks and any software development task requiring computer resources had to wait until the machine was not engaged in more important tasks. This would generally be around 2:00 A.M.
Any computer operation: compile, assemble, or test, could only take place overnight and we had to wait until the next morning to find out if it worked. So every software development activity had to be very, very carefully thought out. Machine runs were terribly time-consuming and extremely expensivemuch more than the cost of programmers. So to get the best use out of the scarce machine time we did a lot of desk-checking. We reviewed and inspected everything: documentation, code, tests. We even developed an inspection technique wherein programmers would "play computer" by manually walking through assembly language programs with people taking the roles of registers, accumulators, and memory locations. Test data was written on slips of paper that were passed across the table from memory to accumulators or registers and back again. The program stack was just that: a stack of these pieces of paper. This process was very tedious, it was very slow, and it required a lot of time and a lot of effort. But it made you think.
Fast-forward 40 years and things are different. Most of us carry, in our pockets or in our purses, more computing power than existed in the whole North of England in 1972. Chances are you could not find even one chip as small as 32k in any laptop or desktop you would buy today. There are libraries full of books on languages, methodologies, and development lifecycle models. Modern systems development environments are exactly that: environments. Integrated development environments (IDEs) contain programming language syntax checkers and compilers, editors, functional code and code snippet libraries, object browsers, code navigation and coverage aids, environment support and virtual machine interfaces, authoring tools, configuration management and build functions, even embedded project management capabilities. Critically, IDEs usually contain interactive testing and debugging environments and this can be where problems may arise.
These powerful tools allow us to create something that "works" and create it very quickly. From a standing start, IDEs allow developers to build and test complex systems in days if not hours.
This is very seductive.
Low Energy State
Humans, like most dynamic systems, often attempt to operate at the lowest energy state possible. When the perceived job of a software engineer is to "build something," and the environment provides a very quick way to build something, it is difficult not to follow that road to its end. Fast compilers and interactive debug environments can encourage shortcut approaches that compromise the learning essential to effective systems development. We may build something when we really need to build the thing.
There is instant gratification in coding and immediately seeing the code run. It can encourage programmers to throw something together and then play with it to see how (or even if) it works. At its best this can be considered learning by experimentation; at its worst it can be mindless hacking.
Thinking and Experimenting
Don't experiment when you should think; don't think when you should experiment.
Jon Bentley, computer scientist
There are two ways to approach understanding something. We can think carefully about the required logic to solve a problem before trying to produce something that will show how our understanding holds up. Or we can experiment by running a program in different states of development against different inputs and see what happens. Both are legitimate approaches when used carefully and thoughtfully. But we have entered an era where computing power and speed are so readily available and so cheap that the experimenting approach may be causing us problems; it may encourage programmers to be lazy and that is never a good thing.
Modern systems development environments are just that: environments.
When modern IDEs allow us to build something that works very quickly and modern debuggers allow us to execute it before our eyes in real time there is a strong temptation to design and program by trial and error. Programs can be patched together as a sequence of ad hoc amendments to an initially weak and ill-considered design. Once the program seems to work, it may be bundled with other programs similarly designed. Systems built this way do not work very well, are difficult to understand, and are hard to maintain.
Experimenting and Thinking are Different. Experimenting tends to be situational and instance-driven. It is highly influenced by our initial (and necessarily imperfect) understanding or simply by how the program was first thrown together. Experimenting also encourages understanding-in-the-small since the focus is usually to more and more detail to try to somehow make this program work.
Thinking tends to be more contextual. Using it, we build mental models that allow understanding of the instance in a larger framework. So Thinking is more aligned with higher-level abstractions and global knowledge. Thinking encourages understanding-in-the-large and this usually makes for better systems design.
Faster = Slower?
As well as encouraging unplanned experimenting, faster development tools may paradoxically increase the time to complete programming tasks. They may do this by encouraging a large number of small iterations where the learning increment is minimal, instead of a small number of thoughtful iterations where we learn a lot. So if faster might be slower, would slower speed things up?
There are two forces at work here. One is the cost of experimenting, the other is the cost of all those iterations. As development tool speed increases, clearly it costs less and less to play with a problem. This is shown in the accompanying figure by the blue bars. When the turnaround time is very long, it is usually too expensive to consider experimenting unless Thinking has come to a complete dead end. As turnaround time drops it is likely there is a threshold where Experimenting becomes much more attractive. The threshold is probably related to the perceived effort required using the two approaches.
At the same time, all the iterations generated by Experimenting have a cost that is rising gradually (shown by the red bars). With 100% Thinking, there may only be a single iteration that proves the thinking is correct. As Experimenting becomes less costly and more impromptu many of the experimental iterations may either eliminate specious dead-ends or may not expose any new knowledge. So while the iterations might not cost much, they may also be low-value in terms of learning. And we tend to do a lot of them.
Faster development tools may paradoxically increase the time to complete programming tasks.
When Thinking departs the scene, Experimenting turns into hacking. Here the programmer may not even have an idea of what to look for (or even what he or she is looking at), not having thought through the problem at all. In this case, provided the system does not actually crash, an iteration might be considered "successful"after all, it did "work." But we do not learn much from it.
This hypothesis infers there might be a "sweet spot" where development tools are fast, but not too fastnot so fast that they encourage laziness. Not so fast they encourage writing programs rather than designing programs and running programs rather than thinking about programs. This raises some questions:
- Should we consider deliberately introducing delays? Should the delays be on the order of seconds? Minutes? Hours?
- Would there be different kinds of delays for syntax issues versus operational or logical issues?
- Should the delay be based on the experience level of the programmer? If so, how does experience affect the delay?
There is only one criterion that should determine how we should experiment and how much we should think: Which approach will allow us to learn most efficiently?
Effort and Learning
The putative goal of systems development is the production of code that "works" just as the putative goal of a college education is to obtain a degree. These obvious targets mask the true purpose of both activities which is to learn. We can print up a degree certificate in minutes and we can create code pretty much as quickly as we can type. Creating code that does what is needed requires first learning what is needed and learning is always an active and effortful endeavor. If powerful tools allow us to coast at a low energy state they can shortcut the learning process altogether. This is true of programmers, of college students, and of college students studying programming.
I have sometimes thought the manual computer simulation we played with pieces of paper, pretending to be a PLAN assembly language program, was so interesting and informative that it could be made into a fun board game.
The idea for this column came from a series of discussions with Dave Berque ([email protected]), professor and chair of computer science at DePauw University, to whom I would like to express my thanks.
The Digital Library is published by the Association for Computing Machinery. Copyright © 2013 ACM, Inc.