1. 11
  1.  

  2. 8

    Most linters have very strict for no reason rules by default. For example, rubocop requires to add documentation comment before each class or module. Should I write comment for each Rails controller, something like # Controller for blog posts? It requires to use %r{} for regex literals if regex contains / and requires to use // if it does not contain /, which is bad for consistency if there are lots of regexes nearby (I can’t use %r{} for each of them). It forces to introduce false abstractions by requiring method bodies to be no larger than 10 (!) lines by default. And despite it is one of the most well-designed linters.

    csslint (or css-lint, or css_lint, these are different and I confuse them all the time) requires CSS attributes inside selector to be sorted in specified order (i.e. first color, then font, then background), I don’t understand how it can improve maintainability but it improves frustration and anger.

    And mostly these tools can’t catch any bugs or architectural problems, only cosmetic conventions.

    I think they should have more forgiving defaults.

    1. 7

      This is why when adding a linter to a project it’s important to go through the list of rules as a team and decide which rules should be ignored.

      There are many kinds of issues that linters can catch that might be missed during code review. There’s no reason to have these bugs at all. Not testing for them just because you don’t agree with the default rules is a bad idea.

      A common example in python is making a mutable the default value of a keyword argument, like:

      class MyClass(object):
      
          def my_function(self, foo={}):
              pass
      

      It turns out that since the dictionary on the right hand side of the foo={} expression is mutable, it’s associated with the class, not an instance, so all instances share that dictionary. If it ever gets mutated, then it’s mutated for all instances.

      This is easily detected by e.g. pyflakes. There’s no reason to not have a check for this in every python codebase, as part of the test suite and integrated into developer’s editors.

      1. 1

        It turns out that since the dictionary on the right hand side of the foo={} expression is mutable, it’s associated with the class, not an instance, so all instances share that dictionary. If it ever gets mutated, then it’s mutated for all instances.

        Slight nit-pick, but one which hopefully makes things a little clearer: default arguments get ‘associated’ with the function itself (remember that functions are perfectly valid first-class objects in Python). Classes are also first-class objects, and methods are basically function objects stored inside a class object (plus a little “binding” magic).

        That’s why all of the instances share the same value: they all invoke the same method object, by virtue of referencing the same class object. Also, it’s not just mutable objects, but those are the only times we actually notice!

        IMHO remembering rules like ‘default method args associate with the class’ seem (a) very arbitrary and (b) like a bad idea, hence it’s difficult to understand or justify why it’s there.

        If we think about the methods and classes as objects, then it seems (to me) to make more sense: the way default arguments are handled seems reasonable (although I don’t like it); and it has unfortunate interactions with the class/instance mechanism, like this one you mention.

        One possible problem with this explanation is that it may be an unfamilar way of thinking, e.g. for those used to Java-like languages where things like methods and classes aren’t objects (I’ve argued many times when I’ve heard the phrase “everything’s an object” in reference to Java ;) )

      2. 2

        Rubocop has aggressive defaults so you hit a lot of things, fix what is worth fixing, and then auto generate your config once you’ve decided you’re at tolerable levels for your project. That way requirements can stay tight for a wide variety of projects. And rubocop.yml lets you exclude directories, so just ignore comments for app/controllers/**/*. However, plenty of non-trivial apps have controllers that are worth documenting. Why it deserves to exist, it’s intended scope, and so on.

        1. 1

          This is because Ruby doesn’t actually have the ability to be more strict. Eslint is one of the best tools in modern software development because the language allows for intelligent linting.