Kotlin best practices for large teams

We’re looking at introducing Kotlin into our server-side project which has many teams. I’m looking for your suggestions in order to increase the chance of success and avoid some of the problems that are typically only realized with experience.

I’m especially interested in hearing from others that have introduced Kotlin to larger teams (as well as internal Kotlin usage at JetBrains) to see what conventions and best practices you have in place.

I’m looking for suggestions for the following areas:

  1. What language features do you try to avoid? (eg. “!!” not null assertions, do you avoid operator overloading for custom classes?)
  2. What language features do you limit the scope of ? (eg. How about using DSLs to enforce patterns? Do you limit the complexity that you allow for DSLs?)
  3. Do you have any conventions for how to organize the code? I’m especially curious about utility functions that would have typically been exposed as static functions. I’m also curious about extension functions. Do you prefer top-level functions or functions defined in companion objects. I’m also curious about namespace issues so I’m hoping that some conventions for organizing these should help. Do you mix & match Java & Kotlin files in the same directory or do you try to separate them?
  4. Since this is currently a Java project, it’s important for existing Java classes to be able to use the new Kotlin classes in a non-surprising / user-friendly way. What best practices help with this? Do you stick with the Java conventions of 1 class per file and classes being stored in the same package?
  5. What’s a good roll out plan to control the risk?

Although the answers to some of these questions may disagree with other opinions, I am genuinely interested in what practices you have in place for larger teams as it’s easier for us to relax the restrictions later as needed instead of the reverse.

We want Kotlin to be a success so I would really appreciate any comments and suggestions as well as any links to more information about conventions / best practices for Kotlin.

1 Like

For now I found only one pitfall specific for kotlin (not existing in java). It is global functions. One can declare any number of global functions, and in small programs it is useful to declare all utility functions as global. But the problem is that in large code base, the number of these functions tends to be astronomic. It produces some problems with auto-complete and possible name clashes. I think that the general solution is to keep the number of global functions minimum, or make them package internal.

2 Likes

There’s no package internal visibility. Top-level declarations can be public, module-internal or file-private. Module is the entire jar file, not a package. As I understand, one should avoid big monolith Kotlin projects and structure them to modules.

Yes, I meant module.

For our teams, we are very careful of introducing extension functions. As powerful as they can be, they can be easily confused. When they are used beyond places where their use is plain, simple, and straightforward… we had to be watchful for people from different teams adding a same/similar extension. We had to control so there is no proliferation for extension function etc.

I think that good practice is to declare global extension functions only in separate file and call them `SomethingExtensions’ or something similar. It somehow limits programmers freedom, but it makes it simple to check everything at once.

Also I think it is better to declare local extensions instead of global extensions like

class MyClass{
  fun String.doFunnyThing(){}
}

instead of

class MyClass{
}
fun String.doFunnyThing(){}
1 Like

I like:

class MyClass{
}
private fun String.doFunnyThing(){}
1 Like

It is even better, but in this case function won’t be accessible outside the class.The idea is to have a global extension function but the one which will work only in specific context and therefore not interfere with other extension functions.

It would be cool if IntelliJ plugin has all linting hints/warnings in built.

About the non OO functions, I would rather tend to avoid them and try as much as I can choose extension functions instead.

your usual utils package would contain these extension functions (if you wish, you could now call the package “extensions”) and I would tend to create Kotlin files for each class you are going to extend. You have a function that takes a Calendar and spits out a formatted date String based on the locale of the device? Create a CalendarExtension file under that package with your fun Calendar.format().

And here comes my second suggestion. Due to the fact that Kotlin ignores extension functions that have the same signature of member functions, an update on a library could implement a new function in the class with the same name as your own function and weird stuff will start to happen and relatively high chances you will only notice it after release… I wish Kotlin would consider this an error, but since it doesn’t, to minimize the risk, you could start all of your extension functions with “ef”. So instead, you would have a fun Calendar.efFormat(), for example.