Recommendations To Write (Slightly More) Readable And (Thus) Robust Code - a
:

0. Don't write the damn code in the first place.
1. Use descriptive variable and function names. You are not charged per characters, and vowels don't cost extra.
2. Don't squish your code as tight as possible. Use spaces to increase readability, newlines to group small logical blocks.
3. Don't mix spaces and tabs for indentation. Pick either one* and then be consistent in their use.
*Just kidding. Use tabs.
*Just kidding. Use tabs.
4. Exit early to avoid waterfall code. Especially for longer code blocks, this minimizes cognitive overhead.
6. Use a sensible terminal window size (< 100x50 max) as a guide to refactor code:
- if your code doesn't fit into < two pages, you likely can't keep it in your head — refactor
- if your code is indented such that it wraps, you're in too deep -- refactor
- if your code doesn't fit into < two pages, you likely can't keep it in your head — refactor
- if your code is indented such that it wraps, you're in too deep -- refactor
7. Using < 4 space indentation is cheating and makes your code too dense to read easily.
(Again: use tabs. And yes, 8 space tabstops. :-)
(Again: use tabs. And yes, 8 space tabstops. :-)
8. Declare all variables at function start; that way, you can use introduction of a new variable as a hint to create a subroutine.
Similarly, declare variables only in the scope they are needed.
Similarly, declare variables only in the scope they are needed.
9. Allocate and release resources within the same indentation level/scope to avoid e.g., fd- or memory leaks, use-after-free, etc.
10. Don't repeat the same or almost the same code; abstract the difference and create a function.
11. Comments should explain why you're doing something, not what you're doing -- your code should be readable enough all by itself. See #1 above.
(Previously: https://twitter.com/jschauma/status/1044581775983276034)
(Previously: https://twitter.com/jschauma/status/1044581775983276034)
12. Be able to explain your code.
There's a lot of code snippets on StackOverflow that are incomplete, make assumptions, or are simply incorrect -- never copy anything without understanding exactly what it does.
There's a lot of code snippets on StackOverflow that are incomplete, make assumptions, or are simply incorrect -- never copy anything without understanding exactly what it does.
13. Write boring code.
print foo.join(map(lambda x: str(f(x)), g(y)))
looks leet, but requires a lot of cognitive overhead compared to a simple 'for' loop.
(See also: Murder your darlings. https://www.bartleby.com/190/12.html )
print foo.join(map(lambda x: str(f(x)), g(y)))
looks leet, but requires a lot of cognitive overhead compared to a simple 'for' loop.
(See also: Murder your darlings. https://www.bartleby.com/190/12.html )
14. Don't use arbitrary limits and avoid magic numbers. Define meaningful constants with commentary explaining the chosen value.
15. Check the return value of every function that may fail. (Yes, that includes malloc(3).)
17. Write your functions / modules such that there are no side effects. Have each do only one specific thing.
That makes them independently testable and forces you to clarify your data models.
That makes them independently testable and forces you to clarify your data models.
18. Handle I/O in chunks: don't try to read all data into a single buffer. Somebody* will give you input larger than you can handle.
*Ok, I.
*Ok, I.
19. Assume hostile input and usage. Always validate all input from outside your program. (Goes hand in hand with #15.)
'perl -T' is a great mechanism that I miss in many other languages.
'perl -T' is a great mechanism that I miss in many other languages.
20. When dealing with external input, an allowlist beats a denylist any day. You'll never think of all the weird things your users* will throw at you.
*Ok, that's me again.
*Ok, that's me again.
21. Don't shell out; use exec(3) or similar.
Things like popen(3) or system(3) can easily lead to arbitrary code execution via shell command injection. Parametrized execution of commands avoids that.
Still combine with #19 for good measure.
Things like popen(3) or system(3) can easily lead to arbitrary code execution via shell command injection. Parametrized execution of commands avoids that.
Still combine with #19 for good measure.
22. Don't assume you can write to the current working directory.
Don't assume your program is run from any specific directory.
Don't assume your program is run from any specific directory.
23. Avoid interactive usage; prefer command-line flags, environment variables, or config files instead.
(Although avoid the complexity of parsing a config file if you can.)
(Although avoid the complexity of parsing a config file if you can.)
24. Avoid temporary files wherever possible. If you must use a temporary file, set a umask, use mktemp(3), and unlink via an exit handler.
(Previously: https://www.netmeister.org/blog/mktemp.html)
(Previously: https://www.netmeister.org/blog/mktemp.html)
25. Separate stdout and stderr. Don't print any output that's not required or requested. Favor syslog(3) over handling your own logging I/O logic.
26. Ensure your error messages are meaningful and include a useful diagnostic message, preferably via strerror(errno), perror(3), err(3), ...
27. Enable warnings in your compiler/interpreter, turn warnings into errors.
Go does this by default, for C use "-Wall -Werror -Wextra" at a minimum), perl 'use warnings; use strict;', shell 'set -eu' etc.
Go does this by default, for C use "-Wall -Werror -Wextra" at a minimum), perl 'use warnings; use strict;', shell 'set -eu' etc.