Hi all, what do you use for linting Kotlin project...
# server
o
Hi all, what do you use for linting Kotlin projects?
t
we use detekt + the ktlint plugin for it. Other than when we first added it to the project we don't use the auto format from ktlint cli. Only did that initially then just configured the IDE format settings to match what the linter expects so you can spam autoformat as you work and come out matching the spec. some of the few appropriate to commit files from .idea imo.
👍 1
j
s
Previous gig I started us on Qodana. It was nice to have things running on server and the integration with GH made PR reviews so much better.
I learned we cant trust developers to be addressing lint rules/"problems" on their own. Some of them completely turned off warning UI elements rather than addressing. mind blown
😄 1
e
Maven Ktlint plugin with default settings to: • locally run
./mvnw ktlint:format
as a pre-commit hook • robot for creating PRs/ MRs with
./mvnw ktlint:format
autoformatted full codebase. For working in team The plugin setup is in pom.xml in my pet project https://github.com/Sedose/codecrafters-interpreter-kotlin as an example. I'm sure there is one for Gradle as well
1
To create such a pre-commit hook all you need is: • Run
mkdir -p .githooks
• Run
git config core.hooksPath .githooks
• Create similar file
your_project_root/.githooks/pre-commit
Copy code
#!/bin/bash
# Run ktlint:format before commit

echo "Running ktlint formatter..."
./mvnw ktlint:format

# Check for any unstaged changes (i.e., formatting changes)
if ! git diff --quiet
then
  echo "Ktlint made formatting changes. Please review and stage them."
  git diff
  exit 1
fi
• Run
chmod +x .githooks/pre-commit
With this setup you get autoformatting each time before you commit. And add this file to git to track it for your team so that everyone can use it
e
We use
Detekt
for all Kotlin code in our project and
Spotless
for all other code. If you are interested you can check our Gradle build file: https://github.com/infonl/dimpact-zaakafhandelcomponent/blob/main/build.gradle.kts
a
We use ktlint, which is great and very configurable, but out of the box it is EXTREMELY opinionated and they are very up front about it. We have to disable quite a few rules and some we have to live with until we create a custom rule that suits our style better. The standard indent rule for example has a lot of built in opinions about how chained methods and function paramters should be formatted that we aren't big fans of.
👍 1
e
Just to add. Golang gofmt is intentionally opinionated, and its creator argues that having a non-configurable formatter promotes consistency across all Go codebases. The underlying idea is that the benefits of universal formatting—such as improved readability, easier code reviews, and reduced bike-shedding—outweigh the drawbacks of not being able to customize formatting rules. Here is the original thread regarding this https://github.com/golang/go/issues/40028
👍 2
j
@Anonymike - you might be interested in this (if you haven't seen it)
👍 1
a
The spirit of that is great, but its a lot easier for the creator to say because its their opinion that gets coded lol. I'm certainly not against consistency, but there are definitely issues where developers tend to choose structural formatting over formatting for readability and the rules could be 95% the same (as we do with a lot of ktlint rules).
👍 1
t
hell didn't ktlint intentionally not support customization in the beginning in an attempt to be similar to gofmt?
👍 2
a
Yes, and we actually submitted pull requests to the project to help fix a bug or two. So its not that we're against ktlint by any means and still use it on every project.
We were completely on that boat at first, until we learned the lessons of that ourselves haha
t
it is a ton of fun to tell noisy devs to "go fmt yourself"
🤣 2
e
I know it's too late 🤣. But today for a new project I used Husky to automate formatting on pre commit. Just instaled Husky. And then modified
.husky/pre-commit
to add
Copy code
echo "▶ Running ktlint format..."
./mvnw ktlint:format

FORMAT_STATUS=$?
if [ $FORMAT_STATUS -ne 0 ]; then
  echo "✘ ktlint format failed. Aborting commit."
  exit 1
fi

echo "✔ Format OK. Running tests..."
Maybe will be interesting for someone. Yep, we need Node.js and other things from JS. But hey, JS is everywhere. I wrote article here about how I set up https://medium.com/@avuzia/pre-commit-git-hooks-in-a-kotlin-maven-project-with-husky-3181a79af13b
j
@Edgar Avuzi If you don't want to install node and husky for this, you can simply use
.git/hooks/pre-commit
- using your code inside that file
👍 1
gratitude thank you 1
e
yep, thanks
j
I recommend
ktfmt
(with
--kotlinlang-style
). We avoid custom configuration because it’s a pain to sync them across different projects/repositories. The biggest advantage of it is this imo:
ktfmt
ignores most existing formatting. It respects existing newlines in some places, but in general, its output is deterministic and is independent of the input code.
https://github.com/facebook/ktfmt?tab=readme-ov-file#ktfmt-vs-ktlint-vs-intellij It ended all style discussions for us because there’s only one valid formatting version.
💡 1
e
yep, that's very smart approach to make formatter not consume any configuration. in the spirit of Google with their Go gofmt. so developers not spend time on meaningless discussions around their own preferences and thus focus on more important things like delivering features
a
While I completely agree with the idea of reducing meaningless discussions there are some standards and rules that our team unanimously disapproves of. The way most formatters handle chained math expressions, chained nullability handling, and fun name() = { } method blocks is counter intuitive at best in most cases. Our team has standards that must cross multiple languages and we unanimously agree that continuation lines for math, concatenation, and nullability should start with the operator on the continuation line, making it clear the line is in fact a continuation. Most formatters in Kotlin want the operator on the end of the line being continued but that makes the continuation line look like the beginning of a new line, lacking the context from the previous line (that we are concatenating, adding, subtracting, null handling) making that line have little cognitive value on its own. I get there are plenty of discussions that are strictly opinion and preference but some of us do make decisions with reasoning and discussions around the making code easier to understand, using concrete arguments. This is why we use ktlint for now with a significant chunk of disabled rules unfortunately, and we've had to rework or make custom rules for it as well. Its unfortunate but necessary as there are some pretty glaring flaws in the "official" code style in our opinion.