SwiftLint doing its best to ease conflict

Anyone who has ever worked in a team knows only too well that there is no such thing as one true way of formatting code. Everyone has their own way of formatting and provided it compiles, all of those formatting choices are valid. So when it comes to formatting, it really is a personal choice based on some aesthetic. Often one of the first questions I ask when joining a new team is:

"Can I see the coding style guide?"

However even when there is a style guide and you've read and followed it, you will still get this most frustrating of PR comments:

"That's not how we do things here"

because a style guide, much like comments, is as only good as it is up-to-date. It can feel like playing a game that you can't win as a new joiner, and if the team you join is large enough with multiple developers reviewing your PRs it can really hit your confidence. So as well as insisting on maintaining an up-to-date style guide to reduce stress during PRs, I also insist on having one because of the following benefits:

  • Quicker to review code
  • Easier to spot bugs
  • Improved intrateam communication
  • Improved team moral
  • Quicker on-boarding of new joiners

Getting to those benefits, however, can be a very painful process, potentially involving hours of discussion about mundane formatting points like: "where should the opening { go" or "if there should be a space after (". And once you have the coding style guide agreed you then have to enforce the rules and remember them yourself 😮. In the past I've experimented with automatic formatters but they have always felt "a bit broken" however for the past few months I've been experimenting with SwiftLint and have found it to be a very low impact way of enforcing a coding style guide.

SwiftLint 🏎️

On it's GitHub readme it's described as:

"A tool to enforce Swift style and conventions, loosely based on GitHub's Swift Style Guide."

and can be installed via Homebrew, CocoaPods or directly from source. As a team when we were trying out SwiftLint, there wasn't a pod to be installed so we used Homebrew but have recently switched over to using the pod to ensure that everyone has the same version installed. Following the Xcode integration setup described in the readme we set about disabling the majority of rules in the .swiftlint.yml. Due to the way that SwiftLint integrates into Xcode compiling the project shows the instances were SwiftLint believes you have broken one of the rules - I say believe because it's possible for SwiftLint to make mistakes but the authors of SwiftLint already have those covered and allows you to disable rules directly in the source code using either of the following comment instructions:

// swiftlint:disable:next [insert_rule_name]
// swiftlint:disable:this [insert_rule_name]
// swiftlint:disable:previous [insert_rule_name]

After resolving the non-comformance issues SwiftLint raised in our project we gradually switched on more and more of the rules until we ended up with our .swiftlint.yml file looking like:

excluded: # paths to ignore during linting. Takes precedence over `included`.
  - Pods

  - force_cast
  - type_name
  - force_try
  - function_body_length
  - nesting
  - variable_name
  - control_statement
  - line_length
  - trailing_whitespace
  - statement_position
  - type_body_length
  - todo
  - valid_docs
  - missing_docs
  - file_length
  - function_parameter_count
  - cyclomatic_complexity
  - unused_closure_parameter
  - for_where
  - unused_optional_binding
  - redundant_void_return
  - void_return
  - vertical_parameter_alignment
  - unused_enumerated
  - redundant_discardable_let
  - empty_parentheses_with_trailing_closure

opt_in_rules: # some rules are only opt-in
  - force_https
  - empty_count
  - conditional_binding_cascade
  - explicit_failure_calls
  - explicit_init
  - redundant_nil_coalescing
  - syntactic_sugar

line_length: 120

    name: "Avoid asserting 'false'"
    regex: '((assert|precondition)\(false)'
    message: "Use assertionFailure() or preconditionFailure() instead."
    severity: warning

As you can see with SwiftLint you are even able to define custom rules. In the above snippet, we have decided to discourage the use of assert and precondition so that it shows up as a warning when they are used.

However this post isn't really about the contents of the .swiftlint.yml file but rather about the reduction of conflict among team members once SwiftLint was switched on. When we were enforcing/using a similar coding style guide as described in this earlier post for Objective-C we did so manually by leaving comments about infringements on PRs. In a team dynamic these comments can begin to break down the bonds between team members as some people get annoyed at others nit-picking while those nit-pickers can't understand why the other party doesn't just following the agreed convention and they need to waste their time leaving formatting comments. I find that people can be surprisingly robust when facing criticism that they feel is valid or significant enough; but criticise on something that they think is trivial and this is a cocktail for annoyance and hurt feelings. People and especially (if you allow me to generalise) programmers are not great at talking about what annoys them while that annoyance is small enough to be easily dealt with. Instead they let that annoyance grow and become resentment and before you know it you have people arguing and trying to settle scores. Now there are many ways to overcome this and I found to my surprise that SwiftLint was one of those ways.

Peace among team members

Leaving everyone's ego alone 😈

With SwiftLint it's the tool rather than a person that is providing the initial feedback during compilation rather than a person during a PR. This frees up the PRs to be about the bigger issues in the code rather than the positioning of a bracket. This ability to provide regular feedback throughout the development process without damaging anyone's ego is, I think, the main strength of SwiftLint. As the rules that SwiftLint is enforcing are driven by the Swift community, it results in the conversations about which rules to opt into (or as the case may be - opt out of) much more impersonal. Rather than two developers debating for or against each other's rule/style preference instead each developer is debating a rule/style which no one party "owns". This change in rule/style ownership more easily allows one party to change their mind without their ego being bruised as it's not their idea which is being rejected. While we like to think that as developers we are driven by logic and reason, it's been my experience that a good percentage of what we do is in fact driven by "how it feels" and that instinctive sense that "our way is the best way". All of which is tied to our sense of self-worth/ego - once we are able to reduce the role of our ego in a debate/conversation we are able to more quickly reach a position which most people can accept.

I went into my SwiftLint experiment focused on the different rules it had and how enforcing those rules would produce a better code base. In the end I got that but I also got a more unified team that speak more 🏄.

If you haven't used SwiftLint or are still unconvinced of its benefits, there is a great talk on it here.