code-standards.md
- The Official XX Network Kotlin Style Guide (in progress)
- Goals
- Inspiration
- Table of Contents
- Naming
- Packages
- Classes, Objects & Interfaces
- Methods
- Properties
- Variables
- Other
- Resources Naming
- General Rule
- Main Types
- Icons
- Selector States
- Strings
- Dimensions
- Layouts
- Formatting
- Source File
- Indentation
- Line Wrapping
- Line Break
- Break at operators
- Observation:
- Assignment Operator
- Method Calls in Chain
- Long parameters case
- Vertical Indentation
- Horizontal Indentation
- Horizontal Operators Indentation
- Modifiers
- Public Modifier
- Other Visibility Modifiers
- Classes, Variables & Objects
- Variable Declaration
- Classes
- Data Classes
- Enum Classes
- Getters & Setters
- Companion Objects
- Conditionals & Braces
- Braces
- If Statements
- When Statements
- Types
- Immutable Types
- Type Inference
- Nullable Types
- Misc
- Comments
- Language
- Copyright Statement
- Credits
The Official XX Network Kotlin Style Guide (in progress)
Goals
Use of this style guide ensures:
- Maintainability
- Conciseness
- Readability
- Simplicity
Inspiration
Most of this guide statements are drawn or adapted from one of the following sources:
Google Java Style Guide - Old school can still be contemporary
Android Kotlin Style Guide - Official Android style guide
Kotlin Coding Conventions - Official Kotlin guidelines for coding
Android Contributors Style Guide - Contribution insights on open sourcing
Google Swift Guide - General Formatting and Rules that can be applied to Kotlin, as they share many syntax similarities
Table of Contents
Click to expand
Naming
Kotlin is a JVM-compatible language like Java, and naming should follow Kotlin standards.
Packages
- Package names are all lower-case
- Multiple words are discouraged
- If multiple words are needed, they can be used concatenated together, without hyphens or underscores
BAD
io.Elixxir.some_widget
GOOD
io.elixxir.somewidget
Classes, Objects & Interfaces
- Written in UpperCamelCase
BAD
open class myExampleClass { /*...*/ }
object emptyObject : myExampleClass() { /*...*/ }
interface myInterface { /*...*/}
GOOD
open class MyExampleClass { /*...*/ }
object EmptyExampleClass : MyExampleClass() { /*...*/ }
interface MyInterface { /*...*/}
Methods
- Written in lowerCamelCase, without hyphens or underscores
BAD
fun ProcessRequests() { /*...*/ }
var finishedCount = 1
GOOD
fun processRequests() { /*...*/ }
var finishedCount = 1
Properties
- Generally, written in lowerCamelCase
- Properties should not be named with Hungarian notation, as it is as it is not recommended by Google
- Constant values in the companion object should be written all uppercase using underscores in cases with multiple words
Example property names
BAD
class MyClass {
var publicfield: Int = 0
val Person = Person()
private var mPrivateField: Int?
}
GOOD
class MyClass {
var publicField: Int = 0
val person = Person()
private var privateField: Int?
}
Example constant names
BAD
companion object {
const val theAnswer = 42
}
GOOD
companion object {
const val THE_ANSWER = 42
}
Exception #1
val
properties with no custom get
function that hold deeply immutable data) should use uppercase underscore-separated names.
BAD
val keyName = "KeyValue" //Immutable
GOOD
val KEY_NAME = "KeyValue" //Immutable
Variables
- Written in lowerCamelCase.
BAD
val HTTPSAddress = "https://xx.network"
GOOD
val httpsAddress = "https://xx.network"
- Except for temporary looping variables, single character variables are not encouraged.
for (x in 0..10) { /*...*/ }
Other
- Acronyms should be treated as words
Good ![]() |
Bad ![]() |
---|---|
XmlHttpRequest |
XMLHTTPRequest |
customerId |
customerID |
url |
URL |
id |
ID |
Resources Naming
General Rule
One drawable must have the same file name for all screen densities (mdpi, hdpi etc.) and the file must be added to the corresponding drawable density folder
(drawable-mdpi, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi).
All resources names have the following convention depending on the need:
<LOCATION>_<TYPE>_<WHAT>_<STATE>_<COLOR>_<SIZE>
<LOCATION>
- If the resources belongs to an specific screen only, it must start with its name, e.g.:
onboard_feature_1.xml
<TYPE>
- The resource type, as specified in the next topics, e.g.:
btn_gradient_big_corner.xml
- If not can specified, it can be described with a long form name, e.g.:
shape_circle_filled_white.xml
<WHAT>
- Describes what that resource is representing, e.g.:
main_selector_tab_indicator.xml
<STATE>
- If the resource represents a state, it should be named accordingly, e.g.:
btn_success_pressed.xml
<COLOR>
- If the resource has more than one color, it should be specified, e.g.:
selector_btn_light.xml
andselector_btn_dark.xml
<SIZE>
- Most used for drawables and dimensions, to represent the resource in different sizes, e.g.:
ic_error_sign_red_24dp.xml
andic_error_sign_red_32dp.xml
Main Types
Resource Type | Prefix | Example |
---|---|---|
Action bar | ab_ |
ab_stacked.png |
Button | btn_ |
btn_send_pressed.png |
Dialog | dialog_ |
dialog_top.png |
Divider | divider_ |
divider_horizontal.png |
Icon | ic_ |
ic_star.png |
Menu | menu_ |
menu_submenu_bg.png |
Notification | notification_ |
notification_bg.png |
Tabs | tab_ |
tab_pressed.png |
Selector | selector_ |
selector_btn_color.xml |
Shape | shape_ |
shape_rectangle_big.xml |
Icons
Resource Type | Prefix | Example |
---|---|---|
Icons | ic_ |
ic_star.xml |
Launcher icons | ic_launcher |
ic_launcher_calendar.xml |
Menu icons and Action Bar icons | ic_menu |
ic_menu_archive.xml |
Status bar icons | ic_stat_notify |
ic_stat_notify_msg.xml |
Tab icons | ic_tab |
ic_tab_recent.xml |
Dialog icons | ic_dialog |
ic_dialog_info.xml |
Selector States
State | Suffix | Example |
---|---|---|
Normal | _normal |
btn_order_normal.xml |
Pressed | _pressed |
btn_order_pressed.xml |
Focused | _focused |
btn_order_focused.xml |
Disabled | _disabled |
btn_order_disabled.xml |
Selected | _selected |
btn_order_selected.xml |
Strings
String names should start with a prefix that identifies the section they belong to and its description:
Section | String name |
---|---|
Login | login_username_hint |
Registration | registration_help_text |
Main | main_title |
User Profile | user_profile_picture |
If a string is not associated to an specific screen, you should follow the rules below:
Prefix | Description |
---|---|
error_ |
An error message |
msg_ |
A regular information message |
title_ |
A title, i.e. a dialog title |
action_ |
An action such as "Save" or "Create" |
Dimensions
Apps should only define dimensions for values which are constantly reused.
Dimension Type | Dimension Name |
---|---|
typical spacing | <dimen name="spacing_xsmall">8dp</dimen> |
font | <dimen name="font_larger">22sp</dimen> |
padding | <dimen name="padding_large">24dp</dimen> |
Layouts
Layout files should match the name of the Android locations that they are intended for, using that as a prefix:
Component | Class Name | Layout Name |
---|---|---|
Activity | LoginActivity |
activity_login.xml |
Fragment | SignUpFragment |
fragment_sign_up.xml |
Dialog | WebViewDialog |
dialog_web_view.xml |
Adapter | RecyclerView |
--- |
Holder item | --- | item_contact.xml |
Other layout | --- | component_menu_bar.xml |
Formatting
Spacing is important for code readability
Source File
All source files must be encoded as UTF-8.
Indentation
- Indentation is using 4 spaces, never tabs
- If using tabs, consider using soft tabs (tabs converted to spaces) only. Tabs (hard tabs) are highly problematic since they correspond to different spaces depending on the text editor/IDE
BAD
val test: Int = 12
for (i in 10..42) {
println(
when {
i < test -> -1
i > test -> 1
else -> 0
}
)
}
GOOD
val test: Int = 12
for (i in 10..42) {
println(
when {
i < test -> -1
i > test -> 1
else -> 0
}
)
}
Line Wrapping
This should follow Google's Kotlin Style Guide.
- One statement per line, no semicolon (
;
) - Except for
package
,import
, shell command lines in comments or long URLs, lines should be no longer than 100 characters long - Indentation for line wraps should use 4 spaces as well
BAD
val foo: FunkyWidget =
someReallyLongExpression("that would not fit in a single line")
GOOD
val foo: FunkyWidget =
someReallyLongExpression("that would not fit in a single line")
Line Break
Should follow some rules of Google's Java Guide in addition to Google's Kotlin Style Guide with some adaptations:
Break at operators
- When a line is broken at a non-assignment operator the break comes before the symbol
- This also applies to the following "operator-like" symbols:
- The dot separator (
.
) - The two colons of a member reference (
::
)
BAD
val longVariable = anotherVeryLongVariable + anotherLongerOne - thisRidiculousLongOne +
theFinalOne
GOOD
val longVariable = anotherVeryLongVariable + anotherLongerOne
- thisRidiculousLongOne + theFinalOne
Observation:
According to Kotlin Grammar, as there is no symbol for line continuation in Kotlin, if the first line of the statement is valid, the second line won't work. To avoid that behavior, we can use parenthesis:
BAD
val text = "This is" + "a valid" + "statement"
+ "this " + "line " + "won\'t" + "work" // syntax error
GOOD
val text = ("This is" + "a valid" + "statement"
+ "this " + "line " + "will" + "work") // no syntax error
Assignment Operator
An exception to the previous rule is the assignment operator =
, where the line break should happen after the operator.
- When a line is broken at an assignment operator the break typically comes after the symbol.
- A method or constructor name stays attached to the open parenthesis
(()
that follows it. - A comma (
,
) stays attached to the token that precedes it. - A lambda arrow (
->
) stays attached to the argument list that precedes it.
BAD
val foo: FunkyWidget =
someReallyLongExpression("that would not fit in a single line")
GOOD
val foo: FunkyWidget =
someReallyLongExpression("that would not fit in a single line")
Method Calls in Chain
When multiple calls are chained in the same line, the Assignment Operator Rule
is applied.
BAD
Picasso.with(context).load("https://elixxir.io/_nuxt/img/phone.d5cc20a.png").into(imageView)
GOOD
Picasso.with(context)
.load("https://elixxir.io/_nuxt/img/phone.d5cc20a.png")
.into(imageView)
Long parameters case
When a method has many parameters or its parameters are very long, we also apply Assignment Operator Rule
BAD
loadImage(context, "https://elixxir.io/_nuxt/img/phone.d5cc20a.png", profilePictureImageView, clickListener, "XX Messenger for Smartphones")
GOOD
loadImage(
context,
"https://elixxir.io/_nuxt/img/phone.d5cc20a.png",
profilePictureImageView,
clickListener,
"XX Messenger for Smartphones"
)
//or
loadImage(context,"https://elixxir.io/_nuxt/img/phone.d5cc20a.png",
profilePictureImageView, clickListener,"XX Messenger for Smartphones"
)
Vertical Indentation
Single line spacing between method. If a method has many "sections" that often means it should be split into several methods.
Horizontal Indentation
In general, you should add a single space separation in the following cases:
-
if
,for
, orcatch
before an open parenthesis ((
) that follows it on the same line - Before any open curly brace (
{
) - For any reserved word such as
else
orcatch
, separating it from a closing curly brace (}
) that precedes it
BAD
if(list.isEmpty()){
doSomething()
}else{
doSomethingElse()
}
GOOD
if (list.isEmpty()) {
doSomething()
} else {
doSomethingElse()
}
Horizontal Operators Indentation
Operators other than two colons (::
), dot (.
) and range (..
) should be separated on both sides:
BAD
val c = a+b //binary operator
list.map { item->item.toString() } //arrow operator
val toString = Any :: toString //two colons
it . toString() //dot
for (i in 1 .. 10) print(i) //range operator
GOOD
val c = a + b //binary operator
list.map { item -> item.toString() } //arrow operator
val toString = Any::toString //two colons
it.toString() //dot
for (i in 1..10) print(i) //range operator
Exceptions:
- Spacing must be added after a (
,
) or (;
) - Spacing should be added before a colon (
:
) only if that is used for baseclass
orinterface
declarations
BAD
class Foo: Bar
fun <T> max(a: T, b: T) where T: Comparable<T> // last T is not declaration
GOOD
class Foo : Bar
fun <T> max(a: T, b: T) where T : Comparable<T> // last T is not declaration
Modifiers
Public Modifier
Just like Java, the default modifier for Kotlin is public
. As that's redundant, it should be omitted.
BAD
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name) { /*...*/ }
GOOD
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name) { /*...*/ }
Other Visibility Modifiers
Modifiers should be explicitly used for classes, methods and member variables. According to Kotlin Coding Conventions, if the declaration has multiple modifiers, they should be put in the following order:
public / protected / private / internal
expect / actual
final / open / abstract / sealed / const
external
override
lateinit
tailrec
vararg
suspend
inner
enum / annotation
companion
inline
infix
operator
data
Classes, Variables & Objects
Variable Declaration
Shall have single declaration per line.
BAD
val username: String; val password: String
GOOD
val username: String
val password: String
Classes
- A single class per source file
- Inner classes only when appropriate.
Data Classes
For holding simple data, data classes
are preferred.
BAD
class Person(val name: String, val age: Int) {
override fun toString() : String {
return "Person(name = $name, age = $age)"
}
}
GOOD
data class Person(
val name: String = "",
val age: Int = 0
)
Enum Classes
Enum classes may be formatted as follows:
private enum class Color {
RED, GREEN, BLUE, YELLOW
}
//or
private enum class Color {
RED,
GREEN,
BLUE,
YELLOW
}
Getters & Setters
Unlike Java, getters and setters in Kotlin are auto-generated, and they should be only declared if needed following Kotlin conventions:
class Example {
var name: String = "Elixxir"
}
Companion Objects
Companion objects are similar to Java's static
modifier, and it should only be used while declaring a function or a property to be tied to a class rather than to instances of it.
Conditionals & Braces
Braces
For curly braces, the opening braces will be placed in the same line that opens a scope block and the closing brace on a separate line aligned horizontally with the opening construct:
BAD
class MyExample
{
fun doSomething()
{
if (someTest)
{
/* some logic */
}
else
{
/* some logic */
}
}
}
GOOD
class MyExample {
fun doSomething() {
if (someTest) {
/* some logic */
} else {
/* some other logic */
}
}
}
If Statements
If statements are preferable to be used for binary conditions, and must be always enclosed with braces for both readability and code correctness, as it makes it easier to avoid mistakes:
BAD
if (someCondition)
doSomething()
if (someCondition)
doSomething()
doSomethingElse() //This is not part of the if scope
GOOD
if (someCondition) {
doSomething()
}
//Or
if (someCondition) {
doSomething()
doSomethingElse() //This is part of the scope
}
if
braces can be omitted if the entire expression fits a single line:
BAD
var v = if (condition)
a //that's not ok
else
b //that's not ok
if (condition)
callSomeFunc() //bad
GOOD
var v = if (condition) a else b //that's ok
if (condition) {
doSomething() //this is acceptable
}
if (condition) callSomeFunc() //this is also acceptable
When Statements
- Preferably used when comparing one expression against many expressions.
- Separate cases using commas if they should be handled the same way.
- You must always include
else
case.
BAD
when (input) {
'a' -> doSomething()
'b' -> doSomething()
'c' -> doSomethingElse()
}
GOOD
when (input) {
'a', 'b' -> doSomethingForCaseOneOrTwo()
'c' -> doSomethingForCaseThree()
else -> println("No case was satisfied")
}
Types
Immutable Types
Only use var
for values that will change over time. Otherwise, use val
Type Inference
A local value initialized by an expression should preferably use type inference instead of declaring its type, where possible:
BAD
fun main() {
val manufacturer: String = myCarObj.getManufacturer() //Type inference
println("Car manufacturer: $manufacturer")
}
GOOD
fun main() {
val manufacturer = myCarObj.getManufacturer() //Type inference
println("Car manufacturer: $manufacturer")
}
Nullable Types
As Kotlin compiler by default doesn’t allow any null
values at compile-time, only declare a type as nullable if required using ?
after type declaration.
BAD
var example: String = "Elixxir Style Guide"
str = null // compilation error
GOOD
var example: String? = "Elixxir Style Guide"
str = null // no error
Use implicitly unwrapped types (!!
) only if the variable will be initialized before use, or the safe operator (?
) if it is not certain the variable will be initialized:
BAD
var item: Example? = null
/* other calls */
item.setMsg("example")
item.msg
GOOD
var item: Example? = null
/* other calls */
item = Example()
/* some more calls */
item?.setMsg("example") //If unsure the value is initialized
item!!.msg //If sure the value is initialized
Misc
Comments
In most cases, code should be self explanatory. In exceptionally clever or complicated instances, comments can be used for ease of readability and understanding. Comments must be kept up-to-date or deleted.
Exception: This does not apply to those comments used to generate documentation.
Language
Use en-US
english spelling.
BAD
val centreAlignment = 24f
GOOD
val centerAlignment = 24f
Copyright Statement
The following copyright statement should be included at the top of every source file:
Copyright here
Credits
This style guide is a collaborative effort from: