Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save absolutvodka8888/aa0017cf7ade6c7911e6b210a8da22fe to your computer and use it in GitHub Desktop.
Save absolutvodka8888/aa0017cf7ade6c7911e6b210a8da22fe to your computer and use it in GitHub Desktop.
Kotlin clean code and best practices

Kotlin clean code and best practices

Clean Code Rules

Our main aim is to have a clean code, avoid common silly mistakes and reduce the load on engineers during PR reviews. This is a team effort and If anyone has something that’s generally applicable, let’s talk and decide whether it should be baked into our style guide— so everyone can benefit from it

The bad code creates a lot of distractions. It causes developers to waste time and energy navigating through functions, classes, and files, and pouring over code trying to understand it.

Working on a project where developers care about clean code makes it easy to understand its virtues; the developer can read the code from top to bottom in one go and clearly understand what it does. It makes code manageable and maintainable in the long term, isolating changes and increasing the efficiency of the team.

However, each era will have different ideas and opinions, and different teams will also have different conventions. So best to take this as a rule of thumb, not to follow it strictly.

Also, try not to use too much OOP. Because Kotlin is a more functional-friendly type of programming language. And OOP doesn't do very well in Jetpack compose if we have a plan to migrate in the future

Rules that we should follow

Nomenclature / Naming Convention

Everything in code hides a meaning behind its use; it has an intention and the developer must reflect this meaning using an appropriate name for it. Follow the Name Convention of the project. For more information click here.

  • On the whole, naming should follow Java standards, as Kotlin is a JVM-compatible language.
  • Good naming makes it easier to read and understand code.
  • All naming has to be simple, clear and mnemonic (short and meaningful).
  • Abbreviations and acronyms should use camel case as normal words (e.g., XmlHttpRequest, QrCodeReader, userId).
  • Avoid fully short-form naming unless the name or context is widely known by others. But still please avoid it and write the full name as it gives more clarity of what the variable actually does.
    • For example, val ea = “” // Email Address This can cause confusion as new people wouldn't know what is ea
    • Another example is password. Some people love writing it as short-form pwd. But some people might prefer the full-name password.
    • Also, most of the IDE now comes with auto-complete. It wouldn't hurt to type a few words more on your keyboard

Comments

in general, comments are excuses for bad code. Feels the need to add a comment, which means that the developer probably needs to refactor the code. Add comments for very specific case methods; add a general comment which explains the main purpose for all interfaces and unique classes.

Class naming

Upper CamelCase is used for classes.

  • The same applied to Sealed Classes

Classes names also come with the suffix which defines the type of the class

  • BaseActivity
  • BaseFragment
  • BaseViewModel
  • UserAPI

Enum/Constant naming

  • Enum naming should be ALL_CAPS with an underscore separating words
  • Constants should be ALL_CAPS with an underscore separating words

Function naming

Written in lowerCamelCase

  • For requesting data from the server use load*** prefix: load**Outlet*, load**User
  • For sending the data to the server user send* prefix*: send**Profile, **send**Info
  • For any click events, use onXXXXXClicked. For example, onLoginClicked
⚠️ Please only limit the function to do the operation that it's intended to. Do not add or put other operation logic that is not it's intended to be ⚠️ But if really no choice, then please rename the function to fully reflect what it do. For example `loadUserAndSendProfileToServer()` This is still a bad design but a bare minimum to avoid any confusion

Variable/Field naming

  • Generally, written in lowerCamelCase. Single character values must be avoided, except for temporary looping variables.
  • We also prefer a single declaration per line.
  • Boolean types start with is **prefix. is**ShowTitle
  • Lists come with **List suffix.** news**List**

Package naming

Package names are similar to Java: all lower-case, multiple words concatenated together, without hyphens or underscores

Visibility Modifiers

Only include visibility modifiers if you need something other than the default public.

Access Level Modifiers

Access level modifiers should be explicitly defined for classes, methods and member variables.

Private fields for Kotlin always start with underscore. For example, val _privateFieldName : Int

💡 Since we are migrating to Kotlin. For Java private fields. We should no longer prefix with `m`

Vertical Spacing

There should be exactly one blank line between methods to aid in visual clarity and organisation. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor several methods.

Braces Style

Conditional statements are always required to be enclosed with braces, irrespective of the number of lines required.

When Statements

Unlike switch statements in Java, when statements do not fall through. Separate cases using commas if they should be handled the same way. Always include the else case.

Constants vs. Variables

Constants are defined using the val keyword, and variables with the var keyword. Always use val instead of var if the value of the variable will not change.

Tip: A good technique is to define everything using val and only change it to var if the compiler complains!

Getters & Setters

  • Unlike Java, direct access to fields in Kotlin is preferred.
  • If custom getters and setters are required, they should be declared following Kotlin conventions rather than as separate methods.

Avoid Redundant Java semicolons

  • The Severity level of the Redundant semicolon has been set to ERROR via the Editor Inspections for All Changed Files and Warning for Everywhere else.
  • This inspection will report redundant semicolon (;) tokens which are not required in Kotlin.

Avoid Wildcard () imports*

Avoid wild card imports ;)

Avoid Unused imports

  • The Severity level of the Unused import directive has been set to ERROR via the Editor Inspections for All Changed Files and Warning for Everywhere else.
  • This Inspection will report import statements in Kotlin code that are unused as error

Handle not-null assertion (!!)

  • We should avoid applying the !! operator in our Kotlin code because there is a likelihood that the value will throw an exception if it is null.
  • The Inspector will report any not-null assertion and provide a workaround.
  • Other safer options include using null-safe calls (?) or the Elvis operator (?:).

Proofreading (Recommended)

  • Spellchecker inspections help locate typos and misspellings in our code and fix them in one click
  • This inspection is applied to code, literals and comments.

XML files

  • Always run Optimize Imports and Reformat Code when working with changed XML files.
  • The data element should always be placed top-most of the XML and the root view will follow.
  • All properties that may be used within the layout should be defined and placed in the data element.
  • Layout expressions should be kept small and simple, as they can't be unit tested and have limited IDE support. In order to simplify layout expressions, you can use custom binding adapters or a ViewModel.
  • Hardcoded string usage is strongly discouraged, should use a @string resource. And if its for design purposes should use tools instead.
  • Hardcoded colors are discouraged, should use the colors resource. if the color not exist please create one following the color naming conventions
  • Hardcoded dimens and text sizes are strongly discouraged, should use the dimens resource.
  • The corresponding layouts have to be named with the appropriate prefix: activity_login.xml, fragment_register.xml.

Layout.xml files

File names start with the type and finishes with a suffix which describes the purpose

  • activity_home, fragment_home, viewstepitem

XML View ID naming convention

XML resource IDs have to use snake_case.

IDs should be prefixed with the abbreviation of the element in the lowercase underscore.

  • A TextView that displays a name → tv_name
  • An EditText that inputs an email → et_name
  • A Button that submits → bt_submit
‼️ If QA team have automation tests and require to name every view id that is clickable and actionable. Please make sure to give a resource ID that every view actions that is clickable

Strings

Every string name starts with screen name as prefix

  • **complete_setup**_box_desc, **login**_box_title, **home**_top_section_rewards_label

Colors

Start with a prefix that describes the purpose of colour: **text**_fave, **text**_pink, **border**_black

Menu

Starts with the prefix menu: **menu**_home, **menu_**settings

Menu item

Starts with the prefix action*:* action**_data; **action**_share

Drawables

  • icon: starts with the prefix ic: **ic**_logo, **ic**_share, **ic**_login
  • picture: starts with the prefix img: i**mg**_description
  • background: starts with the prefix bg: **bg**_otp_active
  • selectors: starts with the prefix selector: **selector**_button_login
  • screens: starts with the prefix screen: **screen**_splash

Ktlint

ktlint is an anti-bikeshedding linter with a built-in formatter for the Kotlin language.

ktlint can reformat your code for you when there is an issue. This allows you to focus your time and energy on the more interesting problems of at hand.

Ktlint provides two tools

  • A linter to check for formatting errors
  • A formatter to fix formatting errors

A perfect place to run this is in your local machine before you push your changes to a git repository.

Project Configuration

Intergraiotn is done via a plugin ktlint-gradle. The Gradle plugin that automatically creates check and format tasks for project Kotlin sources, supports different Kotlin plugins and Gradle builds caching.

Add the ktlint gradle plugin to your root-level build.gradle file

buildscript {
    ...
    repositories {
        ...
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        ...
        classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0"
    }
}

Enable ktlint in a specific module

Make sure the module contains .kt files

To apply the ktlint Gradle plugin in a module, add this plugin via the module Gradle file

id 'org.jlleitschuh.gradle.ktlint'

The module will be checked along with other modules once the ktlint check is run.

This can also be added in all Projects Gradle files but is not recommended since our code base is Java and Kotlin.

Screenshot_2021-09-27_at_4.29.25_PM.png

Checking Kotlin code formatting with ktlint

To actually check your code’s formatting, run the following command from the command line:

./gradlew ktlintCheck

This will run through the project and report back any errors via the terminal which are found using the ktlint set configuration.

The ktlint check can also be run on a specific module that has been configured with ktlint. To run a check on a specific module run the following command from the command line:

./gradlew :{some_module}:ktlintCheck

Reformatting Kotlin code with ktlint

To automatically fix any errors which are reported by ktlintCheck, you can run the following command from the command line:

./gradlew ktlintFormat

There could be some errors that can't be fixed automatically by this command line e.g usage of wildcards imports, for this you should manually resolve these.

Confirm Kotlin code is formatted correctly

Once everything is correctly formatted by running ktlinkFormat or manually formatting, re-run ktlintCheck Terminal should report Build as successful.

💡 Using `ktlintCheck` will warn of a missing newline on the classes. This can be ignored but it would require setting a .editorconfig file for the project which has its downsides. The new line is part of the `ktlint` rules so for the time being we can embrace it.

Reference

https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment