Organizing code at scale with EditorConfig
For the first time in my career, I have been put in a supporting position sitting around GitLab, its continuous integration and a lot of legendary (a.k.a legacy) software (mostly Java). It’s quite hard to get into those old pieces of code, to make them build in the new settings, or add the continuous integration configuration. Those things had been needed for years. How to respect the conventions (or lack of) they used back then? EditorConfig comes to the rescue.
What is EditorConfig
Simply put, it’s a configuration file informing the text editor how to manage some files. You need at least one .editorconfig at the root of a project containing the root = true value.
root = true[*]
charset = utf-8
line_ending = lf
indent_style = space
indent_size = 4[{*.js,*.json}]
indent_size = 2[*.go]
indent_style = tab
tab_width = 2
Then, each section describe how the files matching the globbing pattern should be managed by the text editor. In the above example, the default encoding (or charset) is UTF-8 and the indentation size will be different for JSON. The Go files will, as expected, use the tabulation.
This doesn’t remove the need for a proper language-specify code formatter such as gofmt or prettier.
An example of a real use case we faced is with charset. Didn’t I say legendary code? If Java does internally use UTF-16, it lets you encode the files the way you like, but you should tell the compiler what was your choice though. Migration from the old and dangerous cp1252 (the Microsoft’s own interpretation of the iso-8859–1), to UTF-8, we had to keep the _Properties_ file aside. On Java 8, the Properties files are expected to be in ISO rather than Unicode.
[*.java]
charset = utf-8[*.properties]
charset = latin1
We’ll keep this as is until Java 11+ gains more traction.
Why do you might need it
Before today, I didn’t need any of it because: I was working on new projects
where you get to set the rules. Or I was working with a small group of people
where getting to organize yourself is relatively easy. Or I was in a setting where the technology dictates you what’s best for you. E.g. using Python PEP-8 and one of its auto-formatter (black, yapf), or Go’s gofmt.
Go is the perfect example of a problem solved at scale. By not having any
alternative, Google managed to remove all debates about projects organization. Which is only a fertile ground to see new ones emerge, like pkg vs internals.
The EditorConfig could give you the best default configuration for any projects, new or old as well as enforcing those practices via the continuous integration. Even if in practice some cases might get tricky via a language agnostic tools.
My little contribution
As the team grows, a continuous integration tool is much needed. Setting up
linters could feel go, and language-specific tools such as golangci-lint, checkstyle or flake8 are paramount. However, incremental checks on the .editorconfig file might be a good start. For some projects, we are a the tab vs space, even proper charset stage. Setting up a bar and preventing from ever going even lower.
Here is a sample configuration for GitLab CI.
eclint:
image: greut/eclint
script:
- eclint
Or one for GitHub action.
on: [push]jobs:
lint:
runs-on: ubuntu-latest
name: EditorConfig linter
steps:
— name: checkout
uses: actions/checkout@v2
— name: eclint-action
uses: greut/eclint-action@v0
But it already exist
Indeed, there is a JavaScript version of eclint, available as both Docker image and GitHub action. It’s a bit slower and the fast pace development on the NPM front makes it appear obsolete.
EditorConfig is no lint, but hint
To treat the .editorconfig as the editor hints it was meant to be rather than
a strict rule to follow, any rule might be overwritten via the eclint_ prefix.
[*.py]
max_line_length = 80[tests/*.py]
eclint_max_line_length = off