Things to watch out for with logging is that you can easily go too far and your

service will be like my 4 year old telling me every time she is going to go

potty. Here though, most logging frameworks provide an easy solution where you assign priority to the log statement — warn info debug trace etc. — and at

run time you can decide which level of logging you want to see. Most logging

frameworks even go further by allowing you to set the level of logging message you are interested in on a per-module basis, allowing you to say that I want to see trace logs from one part of your code base and only warning or error logs from another section. Configuring these logging levels can ideally be done at runtime, so in the event that you have a production issue you can lower the logging level in the section of code where you suspect a problem. In this way, you can get visibility into your code in production of the sort that would normally require attaching a debugger. These advantages are pretty easy to realize, so next time you find yourself writing a comment explaining what the next step in the code is about to do, consider if it could be simply converted into a log message.

Testing — “Show, don’t tell”

In writing, “Show, don’t tell” is good advice and I think it applies to tests

in programming. Sometimes, I start to feel uneasy trying to explain what a

function is doing and not quite getting it right. Here, reaching for a test is

a great solution. Rather than say, this functions takes some stuff structured

exactly this way and puts out some other stuff structured a different way

according roughly to these rules, using a test, we can show direct examples of

what goes in and what comes out of your function. Tests of course have a big

advantage over comments — they are tested. Your testing framework will not

allow you to pretend a failing test is passing, but your compiler will be happy

to compile your code with factual errors in the comments. Usually, I find it

easier to write a test to explain what some tricky code is doing than to try

and explain it in English. If it is hard to understand in code, it will often

be harder to understand in English.

Good Naming

Why does it seem like a certain section of code needs a comment? Maybe the

first thing to do is make sure the name of the function or variable is the best

that you can do. In an ideal world, all names would perfectly capture the

purpose of the thing that is named, but we don’t live in an ideal world and

sometimes a better name or a refactor to tease apart the conflicting

functionality that is defying a clear name is out of the question. Still it is

usually worth a minute or so to wait and see if a better name pops into your

head before jumping in with a comment to explain what a certain poorly named entity actually represents.

Separation of Concerns

Related to good naming, does your code need comments because it is trying to do too many things? Before explaining what it is trying to do, see if it is easy

to split out the competing functionality. The same caveats apply here as for

above with good naming, we don’t live in an ideal world where all code will

have a single purpose and sometimes we just won’t see the obvious refactor.

Still, improving the clarity of the code is always worth striving for and

certainly worth a minute or so of thinking before trying to paste over the

issue with a comment.

Specification

Here I’m delving into territory that will depend a lot on what language you are writing in. At AppsFlyer, we write a lot of our code in Clojure, so some of

what I will discuss will be specific to Clojure, but I’ll try to be as general

as possible here.

By “specification”, I’m obviously thinking of Clojure Spec, but I’m also

thinking of many other features that are common to languages other than

Clojure. For instance, are you using the data type that most clearly expresses

your intent. A good example here is a Set vs a Vector. If you care about the

order of items in the collection then Vector is a good choice. If you don’t

care about the order and you don’t want any duplicates, then Set is a good choice. What if you care about the order and you do not want any

duplicates? Many languages offer a sorted set that might serve your use case.

Along the same lines, if you are working in a strongly typed language, there is

no need for you to add documentation saying that the first argument needs to be a string and the second one a number. This sort of thing is clear directly

from the function signature. If you are not in strongly typed language what

are your options? A straight forward option that should work almost anywhere is to express in code the requirements or contract of a function. Contracts style programming can be as simple as writing code at the beginning or end of a function to check that the arguments and returns are correct, however many programming languages including Clojure have some native support for this style of programming. Using a contract style, instead of need to write in a comment that the first item must be a string, this requirement can be spelled out directly in code.

Clojure Spec takes these ideas and builds on them. Using a rich and open system, you can specify the allowed arguments to your functions, including constraints between the arguments. For instance you can say that the first and second arguments both need to be dates and that the second date needs to be after the first one. Furthermore, you can also express constraints between the input arguments and the outputs of your functions. Once you’ve done this specification work — which is not exactly easy — in many cases you get automated testing for free. Since Clojure now knows what input types are

allowed, it can create random inputs for your function and can check that the

function outputs adhere to the specification you wrote. Many of the

specifications themselves are quite easy to read — think of predicates like

`number?` or `string?` and you can write your own predicates for application specific entities. Once you have a specification for your function, constraining what type of arguments your function takes and how those arguments relate to the outputs of the function, there is not likely to be very much left to explain in a comment. Clojure Spec is currently a work in progress, but it shows a path for rigour and safety in an untyped language.