Linting vs Formatting: A Swift Guide (Part 1)
Note: This is the first part of two posts on the subject of linting and formatting. After being surprised by how much was floating around in my head, I decided Part 1 (this post) should be focused on the “why”, and Part 2 will be on the “what and how” (i.e., more practical including tools and recommended setup).
From a high level, linting and formatting are meta-processes that you apply to your code to improve code quality, consistency, style, and to reduce bikeshedding (even with yourself 😅). The subject of linting and formatting code is an interesting one. These terms tend to be conflated together to mean the same thing, but the distinction is useful to make and understand. Why? As programmers, we are constantly translating our knowledge of the world and the way things work into code and processes. Without knowing differences between things (even if subtle), how can we use them effectively?
Note: Part 2 will include a recommended iOS/Swift linting and formatting setup including Xcode and CI/CD system.
Linting vs Formatting
Linting and formatting are useful to enforce coding style and conventions, so that you and your team can better focus on the more meaningful parts of your code. Below are some Swift style guides and code conventions that linting and formatting tools attempt to codify.
- Swift Style: An Opinionated Guide to an Opinionated Language
- Ray Wenderlich
Why is the difference between the two important?
As mentioned, we as programmers translate our understanding of things into code and processes. In order to make this translation, our definitions (and understanding) need to be the same between us and other sources of information such as other developers, stakeholders, and documentation — we need to come to terms. If our definitions are not the same, then there’s potential that expectations are not aligned and the outcome of some work isn’t what everyone expected (an impedance mismatch).1
A small but interestingly subtle example is if you are asked to send up a user id to your backend for whatever reason. A typical assumption is to think that this refers to your business’ user id, but is that really the case? Maybe this means the user id for the analytics provider or another service? Once you get this definition correct, you go and create a function that takes in a string user id. Down the line, when this definition is forgotten (which happens faster than you might think), you or someone else goes and passes a different string user id to that function…a weird and difficult bug to troubleshoot is introduced.2 This is what makes naming so hard. Naming is the translation of a purpose into code. In this case, user id is probably the wrong name as it is vague.
So what does this have to do with linting and formatting? Getting our definitions and understanding right, coming to terms, applies here too. If we conflate the meaning of these two, when and how should you use one verses the other?
1. The title of this blog post is an intentional example. A Swift Guide can mean different things: Is this post Swift language focused? Is this post a quick guide to linting and formatting?
2. Tagging, aka phantom types, is one possible solution to this problem. For more information check out these resources: Strings Are Evil, swift-tagged.
Linting is the act of analyzing your code to determine if that code is potentially flawed in some way. Another way to think about linting is that the intent is to flag code smells. Therefore, the nature of the insights that linters provide typically requires human intervention. Some linters do provide some automatic fixing functionality, but like mentioned above, we should focus on using a linter for its intended purpose to get the most out of the tool.
Some examples of what a linter can flag: cyclomatic complexity, force unwrapping, and excessively nested types, etc.
With a linting tool in place, developers can have a higher degree of confidence when shipping their code since there is another set of eyes on your code.
Where linting typically focuses on the behavior of your code, formatting focuses on your code’s style. At first glance, formatting may seem unimportant, but formatting helps keep a code base consistent so that the code is more easily read and understood. Think about where you store your utensils. If they were all mixed together, you would have to think slightly harder to find something. Now extend that to two drawers with the same items. Let’s say this time they were organized, but laid out differently, you would have to first reorient yourself when searching for things between drawers before going about finding what you want. This may not amount to a lot of time, but there is still mental overhead required and we have a finite amount of that! Think about all the different areas of a code base. If they were styled differently, you will have a harder time reading and understanding the code, and that would likely take a mental toll on you without you even realizing it.
It is also worth noting that formatting can typically be done automatically since we are only focused on the style of the code.
It’s worth noting that not all linter and formatter tools follow the strict definitions. For example:
- SwiftLint can do some automatic formatting. I usually don’t use that capability since I’ve had issues where SwiftLint thought some code should be formatted one way and the formatting tool I was using thought the same code should be formatted another way 🙃. I could have probably configured the two to agree, but I opt to use these tools for their intended purposes.
- Despite the name, Kotlin’s ktlint is more of a formatter than a linter. So in cases like this, a little more mental overhead is required when evaluating and using the tools.
This post was more philosophical in nature, but I find giving our craft thought on many levels to be enjoyable, insightful, and applicable to more areas than just the subject at hand. Many times, including this discussion on linting and formatting, I find that the concepts can be applied to other areas!
With all this said, Part 2 will be less thinking and more doing! Until then 👋.
Thanks for reading, and feel free to reach out on Twitter — cheers!