Open-source project
saveourtool/diktat avatar
saveourtool/diktat

diktat: a strict Kotlin coding standard that runs as a linter and a fixer

Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues and bugs

570 stars40 forksKotlinMIT

At a glance

What is it?
diktat is a Kotlin linter built on ktlint's engine, where the rules are not arbitrary lint checks but a documented coding standard, with more than a hundred inspections coupled to it, each individually configurable and suppressible. It ships as a standalone binary, a Maven plugin and a Gradle plugin, and it can both report and automatically fix the violations it finds.
Who is it for?
Adopt diktat if your team wants a written Kotlin standard that is enforced in CI rather than a set of lint preferences, and if you are prepared to adopt the rules incrementally, because more than a hundred coupled inspections will otherwise turn adoption into a formatting argument.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 142 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What makes it different from ktlint and detekt

diktat is described as a strict coding standard for Kotlin, consisting of a collection of Kotlin code style rules implemented as Abstract Syntax Tree visitors built on top of ktlint. The stated purpose is detecting and automatically fixing code smells in the CI/CD process.

The relationship to ktlint is architectural rather than competitive. ktlint provides the parsing and rule-running machinery, and diktat supplies the rules. That means diktat inherits ktlint's ecosystem, its configuration conventions and its ability to format as well as lint, and diktat's own README calls it KTlint with diKTat injected when it describes the command line.

The README anticipates the obvious question, asking why diktat is necessary when there are other tools like detekt and ktlint performing static analysis, and gives four reasons.

More inspections, over a hundred, tightly coupled with the codestyle document. Unique inspections that are not found in other linters. Highly configurable, where every inspection can be customised and suppressed. And a strict codestyle, a detailed document that can be adopted and applied in a project.

The third reason is the one that separates diktat from a linter that merely reports. A linter that has one opinion about every aspect of your code is a linter you turn off. A linter where each rule is independently configurable, and where the rules trace back to a written standard, is a linter you adopt piecewise. The fourth reason makes that concrete: the codestyle is a document a team can read, argue about and sign up to, rather than a set of defaults that emerged from a maintainer's preferences.

The project has been around long enough to be listed in the static analysis tools directory, the kotlin-awesome list and the kompar catalog. The primary language is Kotlin, the licence is MIT, the default branch is master, and the homepage is diktat.saveourtool.com.

Running it as a standalone binary

The CLI route is the quickest way to try it, and it takes two steps.

Download the binary from the releases page, or use curl:

shell
   curl -sSLO https://github.com/saveourtool/diktat/releases/download/v2.0.0/diktat && chmod a+x diktat

On Windows only, you download diktat.cmd manually from the same releases page.

Then you run it against your Kotlin sources. The README describes this as running KTlint with diKTat injected to check your kt files in a directory:

console
$ ./diktat "dir/your/dir/**/*.kt"

And the Windows equivalent:

console
> diktat.bat "dir/your/dir/**/*.kt"

The path pattern with a double star is a glob, so the tool is pointed at a file tree rather than a single file, which is what you want for a whole project.

The autofix mode is the other half of the command line. To autofix all code style violations, use the --mode fix option. This is the feature that changes how a linter feels in practice, because most style violations are mechanical and a rule that can only complain is much less useful than one that can repair. The catch is the familiar one: an automatic formatter is a large diff the first time it runs, and a team adopting diktat should expect to spend one review cycle on a reformatting commit before the incremental fixes start to matter.

It is worth noting that the binary is a self-contained executable, so a CI job can download it, or a developer can keep it outside the project entirely. For a Gradle or Maven project the plugins are usually a better fit, but for a quick check on a directory, or for a repository that is not built with either, the standalone binary is the answer.

The Maven plugin, and why execution ids matter

The Maven route is the more configurable of the two build-tool integrations, and the README's example configuration is worth reading closely because it shows a design choice that trips people up.

The plugin is com.saveourtool.diktat:diktat-maven-plugin, and the example block sets two goals, check and fix, inside one execution whose phase is none:

xml
            <plugin>
                <groupId>com.saveourtool.diktat</groupId>
                <artifactId>diktat-maven-plugin</artifactId>
                <version>${diktat.version}</version>
                <executions>
                    <execution>
                        <id>diktat</id>
                        <phase>none</phase>
                        <goals>
                            <goal>check</goal>
                            <goal>fix</goal>
                        </goals>
                        <configuration>
                            <inputs>
                                <input>${project.basedir}/src/main/kotlin</input>
                                <input>${project.basedir}/src/test/kotlin</input>
                            </inputs>
                            <diktatConfigFile>diktat-analysis.yml</diktatConfigFile>
                           <excludes>
                              <exclude>${project.basedir}/src/test/kotlin/excluded</exclude>
                           </excludes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

The phase being none is deliberate. It means the plugin does not run as part of the normal build lifecycle, and you invoke it explicitly instead, which the README does with two commands. To run diktat in only-check mode, use mvn diktat:check@diktat. To run diktat in autocorrect mode, use mvn diktat:fix@diktat.

The trailing diktat in those commands is the execution id, and the README explains that requesting a specific Maven executionId on the command line may be essential in a few cases.

The first case is having multiple executions with different configurations, for example multiple rule sets, where each execution points at a different diktat-analysis.yml file. The second is a YAML file with a non-default name or in a non-default location, in which case the configuration has to name the file explicitly. And there is a third option mentioned in passing: you can omit the diktatConfigFile, or if it points to a non-existent file, diktat runs with the default configuration.

That last one matters more than it looks. A missing configuration file does not fail the build, it silently runs the defaults. If your team intends a specific rule set, a typo in the path gives you a green build that checked something else entirely.

The configuration block also shows the inputs, the main and test source directories, the diktatConfigFile, and an excludes entry for a path, which is how you keep a specific directory out of the checks.

The Gradle plugin and the module layout

The repository is a Gradle multi-module build, and the module list is the clearest description of the architecture.

There is diktat-rules/ which holds the inspections themselves, diktat-ruleset/ which packages them, diktat-api/ for the shared interfaces, diktat-ktlint-engine/ for the adapter that runs the rules on the ktlint engine, and diktat-runner/ for the entry point. Then the three distribution shapes: diktat-cli/, diktat-maven-plugin/ and diktat-gradle-plugin/.

Two modules are worth singling out because they explain the project's ambitions. diktat-common-test/ holds shared test fixtures, and diktat-dev-ksp/ is a Kotlin Symbol Processing module, which is a different mechanism from a ktlint rule entirely.

That distinction matters, because ktlint rules are AST visitors and see only the file they are in. A KSP module operates on the whole program and can therefore see declarations across files. If diktat includes a rule that needs cross-file knowledge, for instance a check that a public function is documented or that a type is not duplicated, it cannot be a plain ktlint rule, and diktat-dev-ksp/ is where that work would live. The presence of the module is a sign that the project is not restricted to what a single-file visitor can express.

The root files follow the same pattern. There is diktat-analysis.yml, the default configuration, and detekt-config.yml, which suggests diktat and detekt are meant to coexist rather than compete. There is a build.gradle.kts and settings.gradle.kts at the root, gradle.properties, the gradle wrapper, a .editorconfig, and a .git-hooks/ directory.

The examples directory shows the Gradle integration in three flavours: gradle-groovy-dsl, gradle-kotlin-dsl, and gradle-kotlin-dsl-multiproject, alongside the maven and maven-multiproject examples. The multi-module examples are the ones to look at if you are integrating into a large build, since rule application across modules is where a linter's behaviour is hardest to predict.

The documentation set, and the points to check before adopting

The README's See first row is a map of the project, and the entries in it tell you what kind of thing this is.

There is the Codestyle document, the available rules and inspections list, a set of examples, a web demo hosted at saveourtool.com, a white paper, and a mapping of groups of inspections. The groups of inspections document is the one that is unusual and useful, because it means rules can be selected in bundles rather than one at a time, which is how a team adopts a style incrementally.

The white paper and the demo both point at a project that wants to be adopted as a standard rather than installed as a tool. The demo, in particular, is the sort of thing that saves an evaluation cycle, because you can see what the rules do to a snippet of code without installing anything.

Four things to check before you commit.

How the rules interact with ktlint's own. diktat runs on the ktlint engine, so if your project already runs ktlint you need to know whether diktat replaces it or sits alongside it. The README's framing of running KTlint with diKTat injected suggests the diktat binary carries the ktlint engine, and the Gradle plugin probably replaces the ktlint plugin rather than adding to it, but the practical consequence is that adopting diktat means adopting ktlint's formatting behaviour as well.

How much of the standard you want. Over a hundred inspections is a lot, and the strictness is the point, but a team that adopts all of them at once will spend a week in reformatting diffs. The groups of inspections document and the per-rule suppression mechanism exist for exactly this, so the sensible path is to enable a group, fix, then add the next.

The release age. The tagged releases are v2.0.0 from 2023-12-18, v2.0.0-rc.8 from 2023-11-10 and v1.2.5 from 2023-03-17, and the last push was on 2026-06-03 with the project not archived. So the code is being worked on, but the published versions are from the end of 2023. Anyone pinning a version is pinning something nearly three years old, and anyone using a version that tracks the branch is running code the releases do not describe. The renovate.json at the root suggests dependency updates are automated, which is at least a sign the project is not abandoned, and the CI workflows for build and test, for detekt static analysis and for the diktat code style run on the master branch.

The community is on Telegram, and the topic list includes hacktoberfest, which is a reasonable indicator that the project recruits contributors through that route.

Editorial conclusion

Adopt diktat if your team wants a written Kotlin standard that is enforced in CI rather than a set of lint preferences, and if you are prepared to adopt the rules incrementally, because more than a hundred coupled inspections will otherwise turn adoption into a formatting argument. Do not adopt it if you already run ktlint and rely on its formatting behaviour, since diktat runs on the ktlint engine and adopting it means adopting that behaviour too, and do not enable every inspection at once. Before you commit, read the codestyle document and the groups of inspections mapping, try the web demo on your own code to see what would change, confirm whether your build uses the Maven plugin or the Gradle plugin and whether you need a named execution id, and check the version you are pinning, since the published releases date from the end of 2023 while the repository itself is still being worked on.

Frequently asked questions

What is diktat?

It is a strict coding standard for Kotlin, consisting of a collection of code style rules implemented as AST visitors built on top of ktlint, with the purpose of detecting and automatically fixing code smells in a CI/CD process. The project is MIT licensed and the homepage is diktat.saveourtool.com.

How is diktat different from ktlint or detekt?

The README gives four reasons: over 100 inspections tightly coupled with its codestyle document, unique inspections not found in other linters, the fact that every inspection is individually configurable and suppressible, and a strict codestyle written as a document a project can adopt. diktat runs on the ktlint engine rather than replacing it.

How do I run diktat from the command line?

Download the binary from the GitHub releases page, or use curl -sSLO to fetch diktat and chmod a+x it, with a separate diktat.cmd download for Windows. Then run ./diktat with a glob pattern over your Kotlin files, and pass --mode fix to autofix the violations it reports.

How do I run diktat with Maven?

Add the com.saveourtool.diktat:diktat-maven-plugin to your pom.xml with the check and fix goals and a phase of none, then invoke it explicitly with mvn diktat:check@diktat for check-only mode or mvn diktat:fix@diktat for autocorrect. The trailing diktat is the execution id, and you need it when you have multiple rule sets or a non-default configuration file.

What happens if the diktat configuration file is missing?

You can omit the diktatConfigFile, and if it points to a non-existent file, diktat runs with the default configuration. That means a mistyped path gives you a check against the defaults rather than a build failure, so it is worth confirming the rule set you intended is the one being applied.

How current is diktat?

The tagged releases are v2.0.0 from 2023-12-18, v2.0.0-rc.8 from 2023-11-10 and v1.2.5 from 2023-03-17, while the last push to the repository was on 2026-06-03. The project is not archived and has CI workflows for the build, for detekt static analysis and for the diktat code style itself, but published versions are from the end of 2023.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. saveourtool/diktat on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/saveourtool-diktat.svg)](https://hysenlabs.com/projects/saveourtool-diktat)