{"id":14460144,"url":"https://github.com/drawers/abecedary","last_synced_at":"2026-01-17T09:07:08.276Z","repository":{"id":193495933,"uuid":"669164171","full_name":"drawers/abecedary","owner":"drawers","description":"A set of lint rules for ordering Kotlin enums, sealed classes, and more","archived":false,"fork":false,"pushed_at":"2025-12-11T05:51:30.000Z","size":1033,"stargazers_count":8,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-12T00:45:15.483Z","etag":null,"topics":["android","android-lint","android-lint-rule","android-lints","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drawers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-21T13:54:00.000Z","updated_at":"2024-12-01T10:21:49.000Z","dependencies_parsed_at":"2024-02-01T14:49:23.395Z","dependency_job_id":"76f39a74-b630-4b30-ae5f-b41b027821bf","html_url":"https://github.com/drawers/abecedary","commit_stats":null,"previous_names":["drawers/abecedary"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/drawers/abecedary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drawers%2Fabecedary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drawers%2Fabecedary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drawers%2Fabecedary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drawers%2Fabecedary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drawers","download_url":"https://codeload.github.com/drawers/abecedary/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drawers%2Fabecedary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28504701,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-lint","android-lint-rule","android-lints","kotlin"],"created_at":"2024-09-01T21:01:05.639Z","updated_at":"2026-01-17T09:07:08.244Z","avatar_url":"https://github.com/drawers.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"# Abecedary\n\n![An abecedarian form from a Tibetan ritual text](/images/abecedary.png)\n\n_An abecedarian form (ka rtsom) from a Tibetan ritual text. (Image\nfrom [BDRC](http://purl.bdrc.io/resource/MW1NLM718_O1NLM718_011))_\n\n## Introduction\n\nFor some enums, order is meaningful:\n\n```kotlin\nenum class Planet {\n    MERCURY,\n    VENUS,\n    EARTH, // etc.\n}\n\nprintln(\"rock ${Planet.EARTH.ordinal + 1} from the sun\") // rock 3 from the sun\nprintln(\"${Planet.MERCURY \u003c Planet.VENUS}\") // true\n```\n\nIn others, order is arbitrary and we just want to group a set of related constants:\n\n```kotlin\nenum class CatalogFeature(override val id: String) : Feature {\n    LINK_TO_SEARCH(\"link-to-search\"),\n    HOME_CAROUSEL(\"carousel\"),\n    ACCELERATED_BUY_NOW(\"buy-now\")\n}\n```\n\nIn the case where order is meaningless, it's tempting just to add new entries\nas they arrive (chronological order). This can work in small codebases, but in\nlarger codebases it can cause problems:\n\n* Appending to the end of the file can generate merge conflicts when two developers\n  attempt to land their new entry at the end of the file at a similar time.\n* There might be a team who has more of a vested interest in the enum through using it more\n  frequently. In this case, it's much easier for them to read the file if it maintains\n  some other kind of order.\n* Post-hoc re-orderings after the file has reached some tipping point can generate a noisy diff.\n\nLexicographic order (alphabetical order) is the most natural choice where we want to locate\nan entry within a long list. But we can't insist on it everywhere because of cases\nlike `enum class Planet` where the entry order is meaningful. It's especially dangerous\nto reorder when enums are parsed from an ordinal sent over the wire.\n\nAbecedary is a static analysis tool that lets you choose which enums you want to maintain\nalphabetical\norder. It is based on discussions with [Zarah Dominguez](https://github.com/zmdominguez) and is a\nclean room implementation of work started by Michael Ye.\n\n## How to use\n\n### Enums\n\nAbecedary exposes an annotation called `@Alphabetical`. This is metadata you can use to decorate\nan enum where you want the entries to maintain lexicographic order. You'll get the error in the IDE\nif you are using Android Studio:\n\n![An error on an enum where the entries are not in lexicographic order](/images/enum_with_error_in_IDE.png)\n\nApply this annotation directly to an enum:\n\n```kotlin\n@Alphabetical\nenum class Fruit {\n    APPLE,\n    BANANA,\n    CHERRY\n}\n```\n\nAlternatively, you can apply the annotation to an interface.\n\n```kotlin\n@Alphabetical\ninterface Edible {\n    val calories: Int\n}\n```\n\nNow all enum classes that implement the interface will be scanned:\n\n```kotlin\nenum class Fruit(override val calories: Int) : Edible {\n    APPLE(50),\n    BANANA(100),\n    CHERRY(200),\n}\n\nenum class Vegetable(override val calories: Int) : Edible {\n    ASPARAGUS(20),\n    BROCCOLI(10),\n    CARROT(30),\n}\n```\n\nThis is especially useful for enums that implement a common interface from a base module\nthat a feature module would be expected to implement.\n\n### Sealed classes and interfaces\n\nAbecedary handles the case where sealed classes and interfaces are used like enums, with subclasses\ndeclared in the class body:\n\n```kotlin\n@Alphabetical\nsealed class Fruit {\n    object Apple : Fruit()\n    object Banana : Fruit()\n    object Cherry : Fruit()\n}\n\n@Alphabetical\ninterface Edible {\n    val calories: Int\n}\n\nsealed interface Vegetable : Edible {\n    object Asparagus : Vegetable {\n        override val calories = 20\n    }\n    object Broccoli : Vegetable {\n        override val calories = 10\n    }\n}\n```\n\nNote that we don't handle the case where sealed subclasses are declared outside the class body since\nwe want to keep things simple and don't want to enter into more general disputes about declaration\norder of members\nwithin a Kotlin file.\n\n### Calls to functions with vararg parameters\n\nThanks to a suggestion from [Nicola Corti](https://github.com/cortinico), it's possible to target a\ncall expression:\n\n```kotlin\nval fruits = @Alphabetical listOf(\"Apple\", \"Banana\")\n```\n\nWe only check alphabetical order for call expressions where the callee function has\na single parameter marked `vararg`. The intention here is to cover the most common use case,\n`listOf`, `setOf` and so on, without adding too much complexity.\n\nIn a chain, the `@Alphabetical` annotation will target the first such expression:\n\n```kotlin\nclass SpecialList(\n    vararg element: String\n) {\n    fun addAll(vararg element: String)\n}\n\nfun foo() {\n    @Alphabetical SpecialList(\"c\", \"b\", \"a\").addAll(\n        \"f\",\n        \"e\",\n        \"d\"\n    ) // reports only \"c\", \"b\", \"a\" out of order\n}\n```\n\nIf you want a different target, you should be able to decompose the chain into local variables:\n\n```kotlin\nval specialList = SpecialList(\"a\", \"b\", \"c\")\n@Alphabetical specialList.addAll(\"f\", \"e\", \"d\")\n```\n\n### Ordering `.gradle` and `.gradle.kts` files\n\nIf you are interested in keeping build files tidy, the solution is in another castle:\nhttps://github.com/square/gradle-dependencies-sorter\n\n## Installation\n\nJust add the annotation artifact as `compileOnly` and the lint artifact to the `lintChecks`\nconfiguration.\n\n```kotlin\ndependencies {\n    compileOnly(\"io.github.drawers.abecedary.abecedary-annotation:\u003cVERSION\u003e\")\n    lintChecks(\"io.github.drawers.abecedary:abecedary-lint:\u003cVERSION\u003e\")\n}\n```\n\n| Artifact             | Version                                                                                                                                                                          |\n|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| abecedary-annotation | [![Maven Central](https://img.shields.io/maven-central/v/io.github.drawers/abecedary-annotation.svg)](https://mvnrepository.com/artifact/io.github.drawers/abecedary-annotation) |\n| abecedary-lint       | [![Maven Central](https://img.shields.io/maven-central/v/io.github.drawers/abecedary-lint.svg)](https://mvnrepository.com/artifact/io.github.drawers/abecedary-lint)             |\n\nNote for non-android projects, you must apply the `com.android.lint` Gradle plugin to\nuse `lintChecks`.\n\n### Compatibility\n\n| Abecedary version | Lint version  |\n|-------------------|---------------|\n| 0.5.0             | 31.7.1        |\n| 0.4.0             | 31.7.0        |\n| 0.3.0             | 31.5.0        |\n| 0.2.0             | 31.2.0-beta01 |\n\nRemember that lint versions are tied to Android Gradle Plugin (AGP) versions:\n\n```kotlin\nlintVersion = androidGradlePluginVersion + 23.0.0\n```\n\nBut if you're on a lower version of AGP, you can still use a higher version of lint\nby following the\ninstructions [here](https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html)\n\nProblems with the Abecedary checks not showing in the IDE can sometimes be solved by using a newer\nversion of lint or by upgrading to a more recent version of Android Studio.\n\n## Philosophy\n\n### Easy to clone and fork\n\nAbecedary classes should be easy to clone and fork:\n\nhttps://twitter.com/JimSproch/status/1656143262804217860\n\nThere are many projects with their own custom lint rules.\nIt should be easy for these projects to copy/paste Abecedary code into their own rule sets in order\nto avoid an extra dependency on a 3rd party library or incompatibility issues with Kotlin/lint.\n\nThis means that a design where Abecedary abstracts over lint was considered and discarded.\n\n### Severity\n\nThe `@Alphabetical` annotation is for cases where it _really_ is important to keep\ndictionary order. Serious enough that you'd expect a PR to address a comment about ordering before\nmerging.\n\nIf you would prefer Abecedary to give less strict advice, it is possible to configure the rules.\nSee\nthe [Configuring Issues and Severity](http://googlesamples.github.io/android-custom-lint-rules/user-guide.md.html#lintgradleplugindsl/configuringissuesandseverity)\nsection of the lint user guide.\n\n## Inspiration\n\nMuch of the infrastructure and approach in this project is informed by the amazing\nhttps://github.com/slackhq/slack-lints project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrawers%2Fabecedary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrawers%2Fabecedary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrawers%2Fabecedary/lists"}