Imagine a pie-chart that represented every programming tool you’ve ever used according to the amount that it has improved the quality of your code. I don’t have any idea how I would go about producing such a chart, but I reckon mine would be dominated by code linters.
Two categories of tools come to mind. There are tools that help you write bad code faster (giving you more time to refactor and iterate), and there are tools which help ensure that the code you’re writing is good. My adoption of Vim and later Emacs (with Evil) fall squarely in the first category, while my configuration of various code linters fall into the second. A linter, according to Wikipedia, is “a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs”. ‘This sounds great’, you might be thinking, ‘but what does that actually look like?’.
I’ll use RuboCop here to demonstrate the function of a code linter, because it’s the one with which I am most familiar. It describes itself as “a Ruby static code analyzer (a.k.a. linter) and code formatter”. Let’s begin with a concrete example:
def ExampleMethod(a, b) return b + 1 end
When I write the above code in my editor, I am given a total of six warnings (see below). In my setup, the presence of a warning is indicated by orange text or a green squiggly underline (helpfully provided by FlyCheck), with the description of the warning appearing at the bottom of my editor when I place my cursor on the culprit code. The exact presentation of these warnings will change depending on the editor and plugins you use, but RuboCop offers integrations with a vast number of editors and can even hook into tools like git and rake, so there will definitely be a place for it in your environment somewhere.
- Use snake_case for method names. [Naming/MethodName]
- Unused method argument -
useless. If it’s necessary, use
_uselessas an argument name to indicate that it won’t be used. [Lint/UnusedMethodArgument]
- Method parameter must be at least 3 characters long. [Naming/UncommunicativeMethodParamName]
- Use 2 (not 0) spaces for indentation. [Layout/IndentationWidth]
- Extra empty line detected at method body end. [Layout/LinesAroundMethodBody]
As you can see, some warnings mention specific variables (number 2 in the list above), and all warnings show the relevant rule (enclosed in square brackets with hyperlinks added by me) so that you can find more information (by searching the RuboCop docs), or disable/reconfigure the rule to avoid those warnings in the future. For example, if I include the string
"double-quoted" in my code, the default behaviour of RuboCop will produce the following warning: “Prefer single-quoted strings when you don’t need string interpolation or special symbols. [Style/StringLiterals]”. However, if, after reading Brandon Weiss’ article Always Use Double-quoted Strings in Ruby, I decided to always use double-quoted strings, I simply need to edit (or create) my
~/.rubocop.yml file to include…
Style/StringLiterals: EnforcedStyle: double_quotes
…and I’m good to go! I’m now free to write double-quoted strings all over the place without RuboCop telling me off. If I now included a single-quoted string like
'single-quoted' in my code, however, RuboCop would give me the warning “Prefer single-quoted strings when you don’t need string interpolation or special symbols. [Style/StringLiterals]”. One of the best things about RuboCop is the simplicity and flexibility of its configuration. By default, it prefers single-quoted strings, but it’s perfectly happy to swing the other way. Indeed, if we visit the community style guide from which many of its rules originate, we find the following: “Adopt a consistent string literal quoting style. There are two popular styles in the Ruby community, both of which are considered good — single quotes by default and double quotes by default”.
In the examples above, RuboCop wasn’t catching anything that stopped the code from running. It’s more interested in good ruby code than it is in valid ruby code. That said, these often go hand in hand. For example, although unused variables don’t cause errors by themselves, they increase the likelihood of coming up against errors like
wrong number of arguments (given 1, expected 2) (ArgumentError)
when you try to call
Finally, clean code is easier to both read and understand. If methods are indented correctly, it’ll be easier to see where methods begin and end. If you properly declare unused method arguments (or avoid them altogether), then you won’t be struck with ArgumentErrors when you forget to include them when you call the method later on. If you only use return in places where it wouldn’t happen automatically then you’ll know that when you do see an explicit return statement, it’s because you’re trying to do something clever (like a guard clause). It can also be faster to write! After all, if you’re cutting out any code that isn’t there for a good reason (e.g. a redundant
return), you’ll be developing good habits resulting in many fewer keystokes overall.