Historic programming style

While researching one of my recent post I came across a reference to The Elements of Programming Style. It’s a study of programming style and it was first published 50 year ago. My dad recognised the book and he remembers when you had to make programs using punched cards!

Despite it’s age a lot of it’s recommendations still seemed to have merit today. I managed to get hold of a copy and decided to read through to get the context for these rules.

Full disclosure, I did a lot of skimming. It’s examples are in Fortran and PL/1 and… it turns out that I don’t like these languages. Fortran doesn’t have structured programming and all it’s flow control is done using goto. It doesn’t even have normal comparison operators and uses .gt. rather than <. We have progressed since then.

Fortunately it’s the lessons I want to look at and there is a summary.

Be clear

1. Write clearly – don’t be too clever.
2. Say what you mean, simply and directly.
5. Write clearly – don’t sacrifice clarity for efficiency.
8. Parenthesize to avoid ambiguity.
9. Choose variable names that won’t be confused.
52. Use variable names that mean something.
53. Use statement labels that mean something.
54. Format a program to help the reader understand it.

All of these made sense then and they still make sense now. The sample code didn’t follow my naming guidelines but there may well have been identifier length limits at that point.

6. Let the machine do the dirty work.

This sounds weird out of context. In the book it’s encouraging you to let the compiler do conversion and setup work rather than doing it manually. We can now expect our compilers to do far more during the build.

11. If a logical expression is hard to understand, try transforming it.
51. Don’t comment bad code – rewrite it.

I think this is sensible advice to consider today. If you are using expressions that seem fundamentally hard to understand, do something about it. You could add a long comment trying to explain them but can you do something better? Splitting a single expression into multiple expressions assigned to temporary variables might help. Maybe you can look at the expression from a different angle that easier to understand. You should make your code easy to understand to help the next person (who may be future you).

Function usage

3. Use library functions whenever feasible.
7. Replace repetitive expressions by calls to common functions.
14. Modularize. Use procedures and functions.

This is mostly an assumption nowadays. When this book was published some people will have been adjusting to the idea of function calls.

44. Don’t strain to re-use code; reorganize instead.

This is about functions that are trying to do too much. If you refactor so each function concentrates on doing just one thing then each function can be more easily reused.

Variable initialisation

4. Avoid too many temporary variables.
27. Make sure all variables are initialized before use.

Now we can delay the creating a variable until just before it is needed and initialise it immediately.

Flow control

10. Avoid unnecessary branches.
15. Avoid gotos completely if you can keep the program readable.
31. Take care to branch the right way on equality.
32. Be careful if a loop exits to the same place from the middle and the bottom.

These all seem to be associated with the lack of structure programming. I hope you’re not using goto in your code nowadays.

18. Use recursive procedures for recursively-defined data structures.

Maybe this required emphasis because of Fortran’s limitation.

Bugs breed bugs

16. Don’t patch bad code – rewrite it.

This one is interesting. I’ve certainly come across bits of code that seemed cursed. They regularly had bugs, got fixed and then had more bugs later on. That could be code that wasn’t written well in the first place so it has many bugs. That could be code isn’t a good fit for the problem so that bugs keep being made. Getting rid of everything and starting again might be better than patching something with problems. On the other hand you might just end up with new flawed code. I’d say this is good advice to keep in mind but not necessarily to follow every time.

28. Don’t stop at one bug.

Don’t just assume your code must be working now. Make sure it is.

29. Use debugging compilers.

Being able to debug is good.

Testing

17. Write and test a big program in small pieces.

I think most people are working on larger programs nowadays. We wouldn’t try and write the everything and leave all the testing until the end. However we might still try and finish a task in one go and leave the testing until the end. Writing small pieces and testing as you go is often better. You have a better idea of what each piece needs and you’re less likely to miss edge cases. If you’re still prototyping you could be more relaxed about this as you will have another chance to revisit testing later.

33. Make sure your code does “nothing” gracefully.
34. Test programs at their boundary values.

Remember to test code with the different values can accept, not just the ones you want to get.

35. Check some answers by hand.

I don’t think will always be possible. Maybe most programs were just much simpler back then. It is good to check some answers using another method. That might be by hand or it might be using a different, perhaps slower algorithm. I sometimes write my initial code with the slow but obviously correct algorithm. That algorithm then becomes the basis for my testing code when the main algorithm is upgraded to something faster but less certain.

Errors

30. Watch out for off-by-one errors.

As true now as it was then. For-loops were a common source of these. Now we can often re-write these to iterate directly over a collection which avoids any comparison that could go wrong. Writing code that just can’t have that bug is the safer option.

36. 10.0 times 0.1 is hardly ever 1.0.
37. 7/8 is zero while 7.0/8.0 is not zero.
38. Don’t compare floating point numbers solely for equality.

I don’t come across people making these mistakes too often. It’s certainly something I keep in mind for floating point numbers.

Appropriate optimisation

39. Make it right before you make it faster.
40. Make it fail-safe before you make it faster.
41. Make it clear before you make it faster.
42. Don’t sacrifice clarity for small gains in efficiency.

It is interesting to see how much comes before optimisation. Think about how much slower machines were back then. Even with that he was recommending that optimisation comes last.

43. Let your compiler do the simple optimizations.
45. Make sure special cases are truly special.
46. Keep it simple to make it faster.
47. Don’t diddle code to make it faster – find a better algorithm.

We often don’t think about how much our compilers do for us today. With a normal compiler your code is regularly transformed into something much more efficient. Functions are inlined, expressions are pre-calculated, it happens every time we hit build.

You can spend ages worrying over fine details but the overall algorithm often has the biggest effect. If you have the right algorithm your code can be many orders of magnitude faster. First make sure you’re getting the right answers then make sure you have the best algorithm.

48. Instrument your programs. Measure before making efficiency changes.

I’ve posted about optimisation before and how important it is to pick your targets. You may think you know what’s slowing down your code but it’s much better to know. Don’t, say, spend time optimising a calculation if the real problem is doing the same calculation many times.

Think about the data

12. Choose a data representation that makes the program simple.
21. Terminate input by end-of-file marker, not by count.

We have a lot of file standards that can make our lives easier today. There is normal a library that can fairly easily transform your local data for file storage or network traffic.

Comments

13. Write first in easy-to-understand pseudo language; then translate into whatever language you have to use.

I don’t think is so relevant nowadays. Our languages and libraries are able to more things directly now. I do still sometimes rough out the behaviour of a function with comments before writing the real code.

49. Make sure comments and code agree.

While this is obvious it is something to watch for. I’ve discussed code reviews before as a way of catching mistakes. If someone has changed the code make sure the comment has been updated if necessary.

50. Don’t just echo the code with comments – make every comment count.
56. Don’t over-comment.

These two lessons seem to belong together given the details in the book. If your comments are just a slightly different wording of the code then you aren’t adding value. I do like to see comments that tell me the “why” behind the code.

Input and output

19. Test input for plausibility and validity.
20. Make sure input doesn’t violate the limits of the program.
22. Identify bad input; recover if possible.
23. Make input easy to prepare and output self-explanatory.
24. Use uniform input formats.
25. Make input easy to proofread.
26. Use self-identifying input. Allow defaults. Echo both on output.
55. Document your data layouts.

I think this will have been written exclusively with the console and batch processing in mind. It’s always worth considering that there may be problems with incoming data. Just because the data is meant to look a certain way doesn’t mean it will.

I’d highlight a problem with recovery mechanisms. They can end up hiding bugs elsewhere in the system. If something has gone wrong this should be obvious to the developers. You want to know if there’s a problem so you can fix it. If the user thinks they’re using a particular setting it shouldn’t just silently switch to a default.

On balance

I don’t think that was bad for a 50 year old book. While I don’t want to read any more Fortran the lessons are often still relevant. Given how much less powerful their computers they still thought that getting it right and being clear was better than worrying about every clock cycle.


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *