Docs, tests, design, and RFCs
I’ve noticed a trend lately of a lot of languages or ideologies following the line of thought that if something is in your code, it should affect how your program runs, or it’s not important. Sometimes it affects the way your program compiles. For instance, in Go you can’t compile if you have an unused import. But you can compile with an unused constant.
This sort of absolutism leads to a narrow definition of programming, or maybe even code. By limiting how expressive you can be with a program, sure, you’re limiting errors and bugs, but you’re also limiting what you can do with a program, and, more importantly, you’re eliminating what you can communicate to another human being with a program.
That’s one of the things I rather miss about Java. Keywords like “final”, “const”, “static”, and “void” all help me more specifically convey not just how a program will run, but how the next programmer understands how it should run. I can write code without these, but I loose something in the process.
Is code just the runnable part of the program? If so, why not just what gets compiled into the binary? Is it anything that changes the outcome or process of you program? Or is it maybe broader – the RFCs, design docs, documentation, comments, tests, and logic that goes into making a program. I think it’s the broader – and arguably more fun – one.
In many organizations, the empiricists have at least partly won; if it doesn’t change the output of how the program run (i.e. if it doesn’t change how much money the company makes, or how fast the program runs, or how few bytes are sent) it doesn’t seem to matter.
But I think this ignores the softer aspects of programming. RFCs, design documents, tests, and final documentation of the code are all things should be done, even if they don’t change the output of the program as it runs, because they do change what the program should do, and are a part of the source material that makes computing meaningful.
Is this just about doing a full and complete job? A sense of a job done right? Partly. And it’s easy to chase that instead of moving on and doing harder work. This is one of the reasons a lot of organizations writing code push for KPIs, or something like it. The process pushes people to at least move the needle that is most likely associated with material improvements in whatever software is being building.
But specificity and fullness of expression is why I like programming languages that have compiler-aware syntax, particularly Rust. For some things – just some! – controlling memory very specifically, and controlling references helps me write better code. I might not care about that for all projects or domains, but I certainly don’t not care about it for all domains.
If I’m writing code that isn’t clear, documentation can make it clearer. If I’m writing code that doesn’t work, tests can help me find how it should work. Tests, RFCs, and design docs can make it clearer and work better as well. This is one of the reasons I’m more than a little skeptical of people that claim code is self documenting, or clean code needs no documentation. I can re-write something to work the same way, but be cleaner, all while still increasing complexity. Or I can just document the first thing, and have it be cleaner, simpler, and above all, include of the non-functional information that goes with code.
RFCs, designs, tests, and documentation can tell me when I’m getting it right, and can tell the next programmer how to keep it right.