Linting, global or local exclusion?

Source code linting is a lightweight form of static program analysis which is designed to warn programmers about undesirable features, problematic constructions, and formatting inconsistencies which detract from delivering high quality, correct code.

Poorly and inconsistently formatted source code just scrapes my soul. Part of my cognition depends on the geometrical shape of code as it’s presented on screen. Consistent formatting is an aid to understanding for me. Probably for others whether they realize it or not.

Linting is one way to help build and maintain programming code consistency. This short article starts with the assumption that linting is underway.

Once a linting configuration file is operational (e.g., .rubocop.yml), there soon comes the question: “What do I do with new warnings?” In general, there are two choices:

  1. Preferred: Explicitly disable the offense at the point of occurrence within the file.
  2. Problematic: Add the new offense to the configuration file as an exclusion. However, adding exclusions for new offenses defeats the point of linting entirely.

Let’s examine the pros and cons of each case in more detail.

We’ll use the Ruby Rubocop linter for exposition here, but the discussion will be general to any linting tool with similar capabilities.

Disabling at point of occurrence

Once an initial configuration is defined, further exceptions should be made when possible at the individual file, method, or line level. Adding such exceptions “uglifies” the code at the point of the exception, which can be used as motivation to schedule future remediation. Most importantly, adding at the point of offense does not exclude similar offenses from being fired in the future. Here’s what an exception for line length looks like, wrapped on the method.

class Sketchy
  # rubocop:disable Layout/LineLength
  def science
    'Step 1: Form a hypothesis.  Step 2: Do an experiment to test your hypothesis.  Step 3: Measure the results.  Step 4: Make a cardboard poster.'
  end
  # rubocop:enable Layout/LineLength
end

Adding to configuration

The configuration file serves to define the application standard, and once generated, changes should be rare, and lean towards shortening the configuration rather than adding to it. Rubocop allows for excluding on a file-by-file basis, which is excellent for an initial configuration.

There is a profound difference between configuring for a brand new application, and configuring to retrofit linting to existing application, particularly large, legacy applications.

When exclusions are added to the configuration file, these exclusions typically promote themselves beyond the existing scope. For example, suppose a long method violation is triggered in some file. Adding this file to configuration to exclude long methods results in suppressing warnings for all future long methods in that file. For “God” classes like the typical User class in a web application, the global exclusion actively suppresses remediation.

Layout/LineLength:
  Exclude:
    - sketchy.rb

The problem here is that this excludes all long lines in sketchy.rb, past, present, and future.

More importantly, global exclusion removes the behavior from further discussion, out of sight, out of mind. If that’s the desired, then global exclusion is the correct choice. If the topic needs to be revisited in the future, disable it at the point of offense.