AndroidアプリのKotlin化をやり切るための腕力

この記事は、ドワンゴ Advent Calendar 2020の4日目の記事です。

N予備校Androidチームでは、およそ2年かけて、Javaで書かれたコードのほとんどをKotlinに書き換えました。この記事では、コードをKotlinに書き換える上で必要だったことをまとめていきます。

Kotlin化に技術はそこまで必要ではない

そもそもKotlin化をすることにそこまでの技術力は必要ないです。JavaとKotlinの互換性が高いので、Kotlin化によってコードが壊れることは少ないですし、以下のように文法にも大きな変化はないので、読めないコードが生まれることもありません。

Javaで作成したFragment

public class SimpleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       return inflater.inflate(R.layout.simple_fragment, container, false);
    }
}

Kotlinで作成したFargment

class SimpleFragment: Fragment  {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.simple_fragment, container, false)
    }
}

そして、このような変換であれば、Android Studioの機能で、自動的にKotlinに変換してくれます。

developer.android.com

たまに、nullableの処理が変わってエラーを起こしたり、getter, setterの名前空間がぶつかってエラーが起きたりしますが、それらの解決も難しくないです。むしろKotlinになったことでエラーの検知がしやすくなり、今まで見逃していた不具合を見つけることもあります。

つまり、Kotlin化をやり切るために必要なのは技術力ではありません。必要なのは、エンジニアとしての腕力です。

Kotlin化をやり切るための腕力

「エンジニアの腕力」というのは、実装をやりきる力としてよく使われる言葉です。ただし、かなり抽象的な言葉であるので、ここではエンジニアの腕力を、「コードを書く速度」と「コードを書く時間」の相乗とします。

コードを書くのが速くて、コードを書く時間が長ければ、当然の結果として実装が素早く完了します。この2つの要素を改善することで、Kotlin化をやり切る力も生まれます。それぞれを具体的に見ていきましょう。

コードを書く速度を上げる

コードを書く速度とは、もちろんタイピングの速さではありません。適切にコードを書いて、マージする速度のことを意味しています。

コードを書く速度を上げるには、Pull Requestの回転数を上げる必要があります。コードがマージされなければ、そのコードは何の価値も持ちません。在庫を抱えすぎた企業が身動きが取れなくなってしまうように、マージ前のコードを抱えすぎたチームも動けなくなっていきます。

なので、Pull Requestの差分を最小にして、コーディングとマージの回転数を上げることで、コードを書く速度は向上していきます。
(Pull Requestの差分を小さくする話の詳細はこちらをご覧ください。)

blog.nnn.dev

Kotlin化を進めていく上で、Pull Requestの差分を最小にするために気をつけるべきことは、参照構造を把握することです。Kotlin化するべきJavaファイルは、参照構造によって以下の4タイプに分類できます。

f:id:hiraike32:20201130180717p:plain
参照構造の分類

図の中の「結合量」とは、そのJavaファイルが持つ実装の密結合の量を示していて、「被参照」とは、そのJavaファイルの実装がどれだけ他のファイルから参照されているか、を示しています。そして、顔文字の雰囲気でKotlin化の進めやすさを示しています。以下で、それぞれのパターンの対応をまとめます。

結合量が少なくて、参照も少ないファイル😃

おそらく、1箇所でしか使われていないようなdata classのファイルや、interfaceのファイルでしょう。これらは、Kotlin化しても影響範囲が限定されているので、すぐにPRを出してマージすることができます。Kotlin化を進めていくには、まずこのタイプのファイルを倒しておくと気が楽になります。

結合量は少ないが、参照が多いファイル🙁

commonディレクトリやutilityディレクトリに入っているような、シンプルで便利な機能を保持しているファイルでしょう。Kotlin化をすることで様々な部分に影響を与えるものの、テストが通っていればJavaのときと同じ機能を提供していることは保証されるので、特に心配する必要はありません。コンフリクトに気をつけつつ、早めに倒しておきたいファイルです。

参照は少ないが、結合量が多いファイル😩

このあたりから、Kotlin化が面倒になってきます。分割せずに機能を盛り込みすぎて巨大になってしまったActivity, Fragment, Presenter, ViewModelなどがここに当てはまるでしょう。このファイルを一気にKotlin化するのは、それなりのリスクを含みます。nullableの扱いが変わったことで、何らかの機能が動かなくなる可能性があります(私たちはこれで機能を壊したままリリースをしてしまったことがありました)。
そのため、一気にKotlin化するのは危険を含むので、まずは外側のKotlinファイルを作成して、そこに少しずつ実装を移していくのが安全です。

// SimplePresenter.Javaを移行していくためのKotlinファイル
class KotlinSimplePresenter: SimplePresenterInterface {
    // JavaファイルのPresenterを内部で持っておく
    private val simplePresenter = SimplePresenter()

    override fun showSomething() {
        // 最初は既存Javaファイルの実装を返すだけ
        simplePresenter.showSomething()
    }
    
    override fun hideSomething() {
        // 少しずつ実装をKotlinに移していく
        someView.isVisible = View.GONE
    }
}

参照が多くて、多くの実装が密結合しているファイル🤮🤮🤮

これはアーキテクチャが敗北しています。存在してはいけないファイルです。このファイルを一気にKotlin化するのは無謀です。レビュイーもレビュワーも疲弊するPull Requestが生まれるでしょう。機能を少しずつ分割して密結合を解消して、その機能ごとにファイルを区切ってKotlin化してくのが良いです。これによって、一時的なKotlin化だけでなく、将来的な技術的負債の解消も行うことができます。

まとめると、以下のようになります。

f:id:hiraike32:20201130184518p:plain
参照構造のまとめ

このように気をつけながらコードを書いていくことで、Pull Requestの回転数が速くなり、Kotlin化も素早く進んでいくはずです。とはいえ、コードを書く時間がなければ何も進まないので、業務時間にKotlin化を進める時間を確保することも必要です。

コードを書く時間を確保する

コードを書く時間を確保するための方法は、英単語を覚えるための時間を確保するのと同じように、スキマ時間を活用することです。そのためには、何度も言うようにPull Requestを小さくしなければいけません。

  • レビューを待っている間の15分で、結合量も参照量も少ないファイルを1つKotlin化してPull Requestを出してみる
  • MTG前の5分で、1ファイルだけKotlin化されているシンプルなPull Requestのレビューをする

など、Pull Requestの実装・レビューがどちらも短い時間でできれば、案件の実装をしながらもKotlin化を並行で進めることができます。大事なのは、一気に進めようとしないことです。

「Kotlin化のために2ヶ月欲しい」と言っても、マネージャーの快諾を得ることは難しいですが、「Kotlin化のために1日30分欲しい」と言えば、OKを出してくれるマネージャーも多いのではないでしょうか。そして、成果としてKotlin化できたファイル数を毎月集計していけば、マネージャーもメンバーもモチベーションを保ってKotlin化を進めていけると思います。

このように、地道にPull Requestを重ねることでKotlin化は達成できますし、それを後から振り返った時に、「エンジニアの腕力」というものを感じることができるはずです。

Kotlin化で得られるメリット

最後に、私たちのチームがKotlin化によって得られたメリットを簡単にまとめます。

JavaとKotlinの頭の切り替えが不要になった
これが意外とストレスだった。言語が統一されたことで、コードの読みやすさが上がって、少し幸せになれた。

ktxライブラリを導入できた
Kotlinの拡張関数が用意されているgoogleのktxライブラリを導入することができて、用意されている便利な関数を使えるようになった。

新機能実装までの時間が減った
今までは、新機能を実装するためにJavaファイルがあると、そのKotlin化もしていたので、余計に時間がかかっていた(Javaにコードを書いて、後からKotlin化するのは面倒なため)。現在はKotlin化されているので、すぐに機能の実装に着手することができるようになった。

最新の開発環境に追従できている安心感
コードにJavaが含まれていると、最新環境に追いつけていないコンプレックスみたいなものがあった。開発者である以上、やはり環境は最新のものに追従したいので、Kotlin化できたことの安心感は大きかった。また、採用を進めていく上で、コードがKotlinで書かれていることは1つの強みになると思っている。


コードのKotlin化に限らず、Androidプロジェクトの様々なリファクタリングについても、この方法で進めていけば実現できるはずです。

小さな改善を重ねながら、より良い開発環境へと改善していきましょう。

N予備校Androidチームでは、採用活動を積極的に進めています。もしご興味をもっていただけましたら、是非こちらからお気軽にご連絡ください。

progres05.jposting.net