10

Migrating to Kotlin

A language that doesn’t affect the way you think about programming is not worth knowing.

Alan J. Perlis

Migrating to a new programming language can be daunting. This chapter gives practices that have helped other companies successfully migrate. But because a migration is not just a technical matter, this chapter also covers the nontechnical aspects involved, such as communication, getting buy-in, and risks to consider.

On Software Migrations

Changing any tool, technology, or technique involved in the software development process is not just a technical decision because it also affects business concerns, such as deployment cycles, user satisfaction, and estimated project effort. For these reasons, a migration requires buy-in from the whole team as well as the responsible manager(s).

Also, as a technical person, it is important to keep in mind that, from a business perspective, many more aspects come together in a project that affect its chance to succeed. Therefore, when pitching the adoption of a new tool like a programming language to your manager, you should have a realistic view of what impact this tool can have, taking into consideration all that makes up the project.

Risks and Benefits

Every migration or tool change has inherent risks. This means that there is always a certain barrier to introducing change, and a certain “activation energy” necessary to trigger change. Risks exist on the technical and the business level. On the technical level, risks include problems integrating with the toolchain or the existing technology stack, unexpected roadblocks, leaving behind experience with the previous tool or technology, and transferring knowledge. On a business level, risks include negative effects on productivity, team and user satisfaction, and keeping deadlines even during the transition phase.

Each of these risks can also be turned into a benefit if the effects of the migration are predominantly positive. Namely, increasing satisfaction and motivation, fewer roadblocks and smoother development, better interoperability with the current technology stack, and so forth. As a rule of thumb, migrating to a new tool or technology will likely slow you down in the short term, but this initial slowdown should repay in the medium or long term. Of course, this can make it hard to struggle through the migration and justify its necessity—which is why buy-in is crucial before attempting to migrate to any new technology or tool.

Note

Think about each of these general points in terms of migrating from Java to Kotlin to contemplate benefits and risks it may have in your opinion.

Generally, a migration must have a defined purpose—and adopting a new technology because it is becoming popular is not a purpose. This is not to imply that adopting Kotlin doesn’t have a purpose—it can have many benefits as you will see. Good questions to ask before any migration include the following.

In the case of Kotlin, it has been proven mature enough to be incorporated into the technology stack; many companies have done so successfully. These also contribute to Kotlin’s very active community. The other questions depend on your company and team, but can also be affected by you—you can be the expert facilitating the change, and convince your team of the benefits Kotlin can have to spark interest in the language and the motivation to learn it.

In terms of tooling for Kotlin, there are certainly obstacles to overcome. For instance, compiler plugins and static analysis tools are not as abundant for this young language as they are for Java. Additionally, while Gradle is pushing forward with its excellent Kotlin support, other build tools do not offer special Kotlin support and may not play well with language features that don’t exist in Java. The same can be said for several libraries such as Gson (a JSON mapper), which cannot handle concepts like primary constructors. While Gson can be replaced by Moshi when using Kotlin, not all libraries may have such a direct counterpart. Thus, the best way to evaluate Kotlin is to test it with your tech and tool stack to explore possible issues.

Leading the Change

Bringing about a tool change is a task that requires leadership, communication, and ultimately convincing. Especially when it comes to programming languages, developers tend to have strong opinions and preferences because it’s one of the most immediate tools they constantly work with—just like the IDE and the version control system. Thus, adopting a new language may be one of the hardest changes if your team doesn’t like the new language—or one of the easiest if you can get them excited about the new language. This section provides guidance and actionable tips to lead the change.

Getting Buy-In

Adopting a new programming language requires buy-in from everyone involved—but how can you get that buy-in? This depends heavily on whom you want to convince (a technical versus business person) and at what kind of company you work (a startup versus a large corporation). Ultimately, with buy-in, developers are more likely to push through the obstacles that will occur during migration.

Here, I’ll loosely differentiate between “technical people” and “business people” to give examples for arguments addressing both perspectives. Mostly, it is about framing the same underlying benefit in the correct context. Arguments you can use to entice the use of Kotlin include the following.

These are by no means all the benefits. You can pour out all that you’ve learned in this book to give an accurate picture of Kotlin’s advantages and potential drawbacks, then team up with others excited about Kotlin to lead the change. With enough buy-in from your team members, you can pitch the idea to everyone involved, for instance, by composing a brief document that pitches the language—like Jake Wharton did to lead the adoption at Square.9

9. https://docs.google.com/document/d/1ReS3ep-hjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/

Although you want to focus on the benefits here, you must manage expectations. Obviously, you should paint an accurate picture of the language and communicate clearly that adoption comes with a learning curve that will initially slow down the team but should be amortized later. Also, the decision to migrate must be evaluated thoroughly; keep in mind that not migrating may be the better choice for you or your company.

Sharing Knowledge

Sharing knowledge is particularly essential before and when starting the migration in order to inform people about the technology and what it can do. This should be planned for ahead of time as it will take time and effort across the team. Approaches to share knowledge successfully include:

Partial or Full Migration

Let’s assume you have successfully pitched Kotlin adoption, or maybe you just decided to migrate a pet project of yours. You need a migration plan. This section discusses advantages and disadvantages of the two main types of migration—partial migration versus full migration. As you will see, these have quite different consequences.

Partial Migration

Partial migration means that you mix Kotlin and Java in your project. Even so, you get several benefits:

These are on top of all of Kotlin’s benefits that you already know of. Unfortunately, partial migration and a polyglot code base with mixed languages comes at a cost. The most important drawbacks to keep in mind include the following:

A process diagram shows a compilation process of mixed language project.
Figure 10.1  Compilation process of mixed-language project. Kotlin compiler uses both Java and Kotlin source files to link them properly but only emits class files for Kotlin source files

In short, a polyglot code base introduces several drawbacks that may outweigh the benefits of introducing Kotlin in your project. Therefore, it is important to realistically evaluate the pros and cons before doing a partial migration (which may be the only feasible option of migration). In large projects, even if your goal is to migrate entirely, you may have to live with these issues for a considerable amount of time. In smaller projects, where it can be done in a reasonable amount of time, I would recommend focusing on migrating the entire code base quickly (or not migrating at all).

Full Migration

All of the benefits of partial migration above also apply to full migration, as well as some of the drawbacks.

However, dedicating to a full migration brings great benefits. On top of all the benefits listed for partial migration, these include:

In summary, aim for a full migration if you do decide to adopt Kotlin at your company. For small projects, this can be done in a relatively short time. In larger projects, it may well be that a full migration is not feasible due to third-party dependencies, internal restrictions, or simply the effort involved. In these cases, you can still follow a migration plan as outlined below.

In any case, you should have an agreed-upon migration plan that everyone involved follows, and you should introduce Kotlin ideally module by module and for each module package by package to minimize build time, the number of integration points, and context switches.

Where to Start

If you decide to adopt Kotlin, whether partially or fully, the next question becomes: “Where do I start?” This section covers three ways to start integrating Kotlin into your code base, along with the respective advantages and disadvantages.

Test Code

The first possibility is to start writing or migrating test cases in Kotlin. At the time of writing, this is proposed on the Android Developers website12 and many companies have successfully used this approach. Test code is rather simple, so this is an easy place to start trying out Kotlin. It’s also relatively easy to migrate back to Java.

12. https://developer.android.com/kotlin/get-started#kotlin

However, there are many arguments against this approach. There are two basic scenarios: You are adding new test cases or migrating existing ones. In the first scenario, you’re adding new tests in Kotlin for existing functionality—so you’re testing after the fact and not in a test-first manner. Also, this only works if you had gaps in your tests in the first place. In the other scenario, you’re migrating existing Java test cases to Kotlin. This incurs several risks.

Listing 10.1 Subtle Difference

val person: Person? = getPersonOrNull()

if (person != null) {
  person.getSpouseOrNull()             // Let's say this returns null
} else {
  println("No person found (if/else)") // Not printed
}

person?.let {
  person.getSpouseOrNull()             // Let's say this returns null
} ?: println("No person found (let)")  // Printed (because left-hand side is null)!

All in all, although test code has become a popular starting point to introduce Kotlin, I personally consider the following two approaches preferable.

Production Code

I’ll assume here that your production code is thoroughly tested so that changes can be made with confidence, and if a bug is introduced during migration, one of the test cases should fail. This greatly supports migration because you know if you did something wrong. Other benefits include the following.

In summary, don’t assume that migrating test code is the safest way to start migrating. If you have a strong test suite, making changes to your production code is a lot safer because you get direct feedback in case you introduce a bug.

If you do not yet have a (good) test suite, then adding new tests in Kotlin first is a reasonable alternative. Adding them after the fact is still better than not testing at all, and you can combine this with migrating the corresponding production code.

Pet Projects

Pet projects are probably the best way to gain experience with Kotlin once you’re familiar with the language—which you certainly are after working through this book. You should work on pet projects by yourself to further familiarize yourself with the language before pitching it to your company. If other team members are interested in evaluating Kotlin, it is the perfect chance to work on a pet project together. If you’re considering adopting Kotlin at your company, work on the pet project with the team that would be affected by the adoption—ideally also using the same technology stack to encounter possible problems ahead of time. For instance, Kotlin does not play well with Lombok. So if you have a large project using Lombok where you cannot easily migrate all Lombok uses, you’ll have to think about how to deal with this incompatibility beforehand.

On the downside, this approach costs effort and time without direct progress on company products. But there are many benefits to pet projects that can make them well worth the investment.

Pet projects are incredibly effective to evaluate Kotlin before even considering a migration. It not only gives you the opportunity to research build tool integrations, third-party libraries, and other integration points; it also allows you to start developing internal libraries that encapsulate common use cases in well-defined APIs and would be useful in future projects. What’s more, it gives you a chance to evaluate testing best practices and test infrastructure.

Generally, I’d recommend starting off with pet projects in the team and, if Kotlin should be adopted, start with simple and well-tested functionality in a non-business-critical app.

Make a Plan

The previous sections already outlined general practices that can all be part of a migration plan. Here, we summarize and extend upon them again as an overview.

These general rules help guide the process. SoundCloud18 and Udacity,19 for example, both followed the last three points when adopting Kotlin.20,21 Agree on a clear set of rules with your team, and work out a concrete migration plan that follows the above ideas.

18. https://soundcloud.com/

19. https://udacity.com/

20. https://fernandocejas.com/2017/10/20/smooth-your-migration-to-kotlin/

21. https://engineering.udacity.com/adopting-kotlin-c12f10fd85d1

Tool Support

The Java-to-Kotlin converter is a useful tool to speed up migration. This section covers how to use it, what to do after using it, what to take heed of, and general tips to facilitate migration.

Java-to-Kotlin Converter

The converter is bundled into the Kotlin plugin so it’s accessible in Android Studio and IntelliJ by default. It is useful not only to make quick progress when integrating Kotlin but also to learn the ropes for beginners by comparing the generated code to the original Java code.

You can trigger the converter in different ways. First, you can invoke it under Code and then Convert Java File to Kotlin File in Android Studio’s menu to convert the current file. Second, whenever you paste code from a Java file into a Kotlin file, Android Studio will automatically prompt you to convert the code. Third, although this action is currently named Convert Java File to Kotlin File, it can convert whole packages, modules, or even projects. So you can right-click on any directory in the project view and trigger the action from there to recursively convert all its Java files.

Note

Don’t autoconvert large parts of your code base without a plan and the time to go through and refactor all converted files. Even then, I’d still recommend doing the conversion file by file to migrate a package or module to have better control over the process.

Adjusting Autoconverted Code

Irrespective of how you decide to use the converter, you will have to adjust most converted code to follow best practices, to use idiomatic patterns, and to improve readability. After all, there is only so much an automated tool can do. Here, we provide a checklist of common changes you’ll have to make.

More high-level questions to ask include:

Not all these changes are trivial; some can require substantial refactoring. But all are important considerations to make on the way to a high-quality code base—after all, this is why you would want to migrate to Kotlin in the first place. My hope is that this checklist helps guide you to a code base that all developers agree was worth the work for the migration.

Note

Converting any file will delete the original .java file and add a new .kt file. Thus, version control history for the Java file quickly becomes useless when modifying the Kotlin code.

Summary

This chapter covered the technical and nontechnical aspects of migrating to Kotlin (or a new tool in general), from implications on build time and code base quality, to getting buy-in and pitching adoption at your company. This summary recaps the primary steps involved, roughly in a chronological order.

This is the bird’s-eye view of the main steps involved in the adoption of Kotlin. Keep in mind that not every developer will be eager to switch to a new programming language, that it introduces risks on the technical and business level, and that it may in fact not be the best choice for your company. However, Kotlin can substantially improve developer experience, productivity, code quality, and eventually product quality. The recommendations in this chapter aim to help you evaluate which are true in your case.