TL;DR for detekt

If you’d like to just see the implementation and move forward quickly, you can just check my PR. Otherwise bear with me a couple of minutes for some decision points.

The story

Speaking from an Android background, we have a couple of options like detekt, ktlint on top of Android’s lint tool for static code analysis and more (pmd, checkstyle etc…). Code formatters like Spotless work well with ktlint. I’m not gonna compare ktlint and detekt in this article (it has been done many times in the past in different articles like in Medium and in ChatGPT), but both tools can work for Kotlin Multiplatform projects and allow analysis of platform-specific code (JVM, JS, Native). I’m more used to working with detekt and like their suggestion to tweak the rules to work with Google’s Compose API Guidelines.

But the problem is by default, Detekt looks for Kotlin and Java source code within a standard Gradle project as mentioned here. You can probably see these discussions when you Google how to make Detekt work for a KMP project (by the time of writing);

For the first 2 issues, I agree that it could be nice to have a common task with no configuration needed. For the 3rd and 4th issues, it looks like a source set issue to me. For the last one, it looks like a nice suggestion but it usually gave issues for generated sources to me (although I know I can ignore paths). It’s interesting that a small task like implementing Detekt can be such a headache at least by the time of writing (maybe there is a nicer solution but Google search makes me think that community is having the same trouble).

Looking at the KMP samples in jetbrains here, none of them had detekt configured (Maybe I missed some, let me know if there is a nice example please). DroidconKotlin example has a nice setup for ktlint but they only support for iOS and Android atm. Even kotlinconf-app didn’t have any static code analysis tool which surprised me a lot.

Considering all these, I could’ve also chosen to go with separate static code analysis for separate platforms (Swift lint for Swift) like mentioned in this nice article. But considering that I’m planning on writing almost no Swift, I decided to stay with one static analysis tool on top of the Lint. At the end, I decided to include all the source sets myself by using source.setFrom(...) configuration feature of Detekt for each sub-Gradle project. This reports the issues multiple times but so far has looked pretty reliable.

If you’d like to see how I implemented this, you can check the PR.

Little remark

  - name: Check detekt
    run: ./gradlew detekt

  - name: Check lint
    run: ./gradlew lintDebug

I could have just used ./gradlew check task in CI since detekt and lint tasks also run when check task runs, but IMO the check task does a lot. But if you want to follow standard Gradle lifecycle practices, and you’re OK with less fine-grained control, then the check task is not bad. I wanted to optimize CI time and only run certain checks in certain situations like running lint only on debug build type, therefore I separated detekt and lint tasks and run only them which saved me some time. I believe it also gives faster feedback and better visibility into where a failure happened. But that might be just how I eat the yogurt.

One last suggestion, I came across a nice article here which mentions nice tools to have for a KMP project.

We can now tick one/two checks in the list below and rest remains 🎉;

  • Core
    • ✅ Dependency management: Renovate
    • ✅ Lint
    • ✅ Static code analysis: Detekt
    • Logging
      • Error reporting
      • Analytics
      • Tracing
    • Network
    • Benchmarking
    • Build conventions
    • Flavours
    • Mocks
    • Test fixtures
    • Build info
    • Preferences
    • Storage
    • DI
    • Feature flags (local & remote)
    • Deep linking
    • Push notifications
    • TimeProvider
    • Local Formatters
    • Coroutine Dispatchers & Test helper
    • Unit testing
    • Test coverage
    • Obfuscation & Shrinking
    • Pipelines
    • Releasing
    • Force updates
  • UI
    • Design system
    • Gallery App
    • Navigation
    • Baseline profiles
    • Compose compiler metrics
    • Previews
    • Network image loading
    • supportsDynamicTheming
    • Status bar color changing
    • App settings with Resource Environment
      • l10n
      • i18n
    • Testing
      • UI Testing
      • Compose Screenshot testing

Hope to see you in the next episode.

Until then may the force be with you… 🖖