← Back to list

Android Lint and Kotlin Warnings

Catch real platform problems early, without making CI miserable

Rituraj Sambherao · 2026-02-05 18:36 · 0 claps · 4.2 min read
#linter #android #ci-cd-pipeline #clean-code
Open on Medium ↗
Wiki topics: GEN · Genomics & Sequencing 📱 · Mobile Development ☁️ · DevOps & Cloud

Android Lint and Kotlin Warnings

Catch real platform problems early, without making CI miserable

← Part 2: Detekt Done Right | Part 3 | Part 4: Tests That Scale →

Detekt is great at code smells and complexity. Android Lint is different. It’s closer to a platform safety net.

Lint catches problems that static Kotlin analysis often can’t, like:

  • Incorrect resource usage
  • Broken accessibility patterns
  • Lifecycle mistakes
  • Manifest and permission issues
  • API level pitfalls
  • Subtle performance traps

If you want a modern Android CI/CD setup, Lint is not optional. The key is to run it in a way that stays fast, stays relevant, and produces reports people will actually read.

Where Lint sits in the blueprint

From Part 0, Lint belongs in the PR pipeline gates.

  • PR pipeline: run a fast, practical lint check (usually debug)
  • Main pipeline: run a deeper lint check, often including release
  • Release pipeline: lint should be boring by then

The goal is predictable signal early. You don’t want “lint surprises” the week you’re trying to ship.

Which Lint task should you run?

Android Gradle Plugin gives you multiple tasks. The two that matter most:

lintDebug

Faster feedback, closer to dev builds, good for PRs.

lintRelease

More strict in some setups, closer to what you ship, good for main and release readiness.

If you’re starting out, the simplest approach is:

  • PR: lintDebug
  • Main: lintDebug and lintRelease (if it's not too slow)

As your project scales, you can refine this by module, by variant, or by feature.

The report problem: make output usable

Lint is only helpful if developers can see the result quickly.

In CI, always publish:

  • HTML report for humans
  • XML report for tooling
  • SARIF report if you want results visible in GitHub UI

A typical lint report ends up under something like:

**/build/reports/lint-results-*.html

Even if your exact path differs, the principle stays the same: upload build reports as artifacts so nobody is digging through CI logs.

A clean Lint configuration baseline

You don’t need a huge Lint setup to start getting value. The goal is consistency and a stable pipeline.

In your module build.gradle.kts:

android {
    lint {
        abortOnError = true
        warningsAsErrors = false

        htmlReport = true
        xmlReport = true
        sarifReport = true

        // Keep it focused
        checkGeneratedSources = false
        checkDependencies = false
    }
}

Notes on the defaults:

  • abortOnError = true makes lint a real quality gate
  • warningsAsErrors should be introduced carefully (more on that below)
  • checkDependencies can be expensive in large builds—turn it on later if needed

Warnings as errors, without chaos

Turning all warnings into errors sounds noble. In practice, it can create a noisy rollout and slow teams down.

A better progression looks like this:

Stage 1: Errors block, warnings inform

  • Treat lint errors as merge blockers
  • Keep warnings visible, but don’t fail CI on them yet

Stage 2: Tighten the screws slowly

  • Pick a small set of warning categories that you care about
  • Promote those to errors
  • Keep the rest as warnings

Stage 3: Enforce stricter policies on new code

If your build supports it, you can create policies that prevent new warnings without forcing the team to fix the entire backlog in one week.

The goal is improvement with momentum, not a stalled pipeline.

Baselines, but don’t abuse them

Lint supports baseline files. They exist for the same reason Detekt baselines exist: large existing codebases.

A baseline can be useful when:

  • You’re adopting lint rules in an older project
  • You want to prevent new issues first
  • You want to clean old issues gradually

A baseline becomes harmful when:

  • It keeps growing forever
  • It becomes a way to avoid fixing issues
  • It gets regenerated casually to silence CI

Treat baselines as temporary scaffolding, not permanent architecture.

Kotlin compiler warnings are part of quality too

Lint catches Android-specific issues. Kotlin warnings catch language-level issues, including:

  • Unchecked casts
  • Deprecated API usage
  • Experimental API usage leaks
  • Null safety warnings

Two main approaches teams use:

Approach A: Compiler warnings visible, but not blocking

This keeps momentum. Warnings show up in logs and get handled over time.

Approach B: Warnings as errors, scoped

This is stricter, but must be introduced carefully, or it will break builds constantly.

In a modern Android project, a practical stance is:

  • Make warnings highly visible
  • Enforce warnings as errors in critical modules first (domain, data)
  • Loosen in UI modules if needed, then tighten later

You’re aiming for correctness, not punishment.

Compose and Lint: pay attention to accessibility

Compose moves a lot of UI logic into Kotlin. That makes Lint and accessibility checks even more important.

Good targets for early enforcement:

  • Content descriptions for meaningful icons
  • Touch target sizes
  • Semantics where needed
  • Avoiding clickable items without proper labels

If your app is heading toward “real user utility,” accessibility is not optional polish. It’s product quality.

CI commands that work well

For PR checks, keep it lean:

./gradlew spotlessCheck detekt lintDebug test

For main checks, add stronger coverage:

./gradlew spotlessCheck detekt lintDebug test

If lint starts taking too long, split jobs and run them in parallel. Don’t drop lint. Speed should be solved with caching and concurrency, not by removing guardrails.

Performance tips that keep Lint sane

Lint can become slow in big Android repos. A few practical moves help:

  • Run lintDebug on PR, not full release lint every time
  • Avoid scanning generated sources unless you really need it
  • Keep Gradle caching enabled in CI
  • Avoid running lint multiple times across workflows
  • Consider limiting lint scope by module in very large builds

Lint should remain a credible PR gate — fast enough that people respect it.

Definition of done for Part 3

You’re done when:

lintDebug runs on every PR and blocks merge on lint errors ✓ Lint reports are uploaded as CI artifacts and easy to open ✓ Main runs deeper lint checks, including release if feasible ✓ Lint baselines are used only as a temporary bridge ✓ Kotlin warnings are visible and trending down over time

Once Lint is stable, you have a serious quality gate stack. Next we move into the part that makes pipelines feel “real” to engineers.

Next: Part 4 — Tests that scale (without flakiness)

Part 4 will cover:

  • Unit tests as the core PR gate
  • How to add instrumented tests without making CI unreliable
  • Gradle Managed Devices and parallelization
  • Strategies for flaky tests and quarantining

Because the only thing worse than no tests is tests that fail randomly and teach your team to ignore red builds.

Series Navigation:

← Part 2: Detekt Done Right | Part 3: Android Lint and Kotlin Warnings | Part 4: Tests That Scale →


메타데이터
post_id
5771fce3e6ce
slug
part-3-android-lint-and-kotlin-warnings-5771fce3e6ce
url
https://medium.com/@androidmeda/part-3-android-lint-and-kotlin-warnings-5771fce3e6ce
canonical_url
https://medium.com/@androidmeda/part-3-android-lint-and-kotlin-warnings-5771fce3e6ce
author_url
https://medium.com/@androidmeda
status
ok
fetched_at
2026-06-09 14:34:10