こんにちは、Chatwork モバイルアプリケーション開発部マネージャーの福井(@tinpay)です。最近は宮崎辛麺にハマっていて、卵とじ & ネギニラトッピング以外の美味しい食べ方絶賛募集中です。
さて、みなさんが作られているプロダクトには技術的負債ありますか?
Chatwork iOSアプリは2016年春にフルネイティブ(2016年時点ではフルObjective-C)に刷新して、そこから6年が経過しました。その期間の中で様々な理由によって負債がどんどん積み上がっているのですが、チーム一丸となって負債の返済に絶賛取り組み中で、ようやくSwift化などでも成果が出てきています。
ただ、返済にはまだまだパワーが足りてないのが現状なので、仲間を募集する上でも、今回は赤裸々にどんな負債があるのかについて紹介してみようと思います。
技術的負債とは?
調べてみると、技術的負債には大きく2つあることが分かりました。
- 意図的な技術的負債
- 不注意による技術的負債
です。
前者は、リリーススケジュールを優先して実装の近道をとることで発生する負債で、後ほどリファクタリングをして修正する必要があることがわかっているケースです。
後者は、開発手法の不備やコーディング能力不足、判断ミスなどによるもので、意図せず発生するケースです。
今回は主に前者の「意図的な技術的負債」について書いていきます。
iOSアプリの技術的負債と向き合い方
Chatworkアプリには、現在以下の5つの技術的負債があると感じています。
- Objective-Cのコードが残っている
- ビルド時間が長い
- ユニットテストが少ない
- 特定の機能における属人性
- リファクタリングが中途半端な状態で、レガシーなコードが多い
以下、これら負債の具体的な内容について説明し、その負債に対して今後はどのように向き合って返済していく予定か書いていきたいと思います。
1. Objective-Cのコードが残っている
Swift化は以前から進めているのですが、今現在でもObjective-Cのコードがまだ若干残っています。
すべてのコードがSwift化されていないことでSwift Packageに移行できず、Swift Packageを利用したマルチモジュール化の計画の遅延にも繋がります。
また、モバイルエンジニアの中にはObjective-Cを書いたことが無いエンジニアもいるので、今後のためにも早くObjective-CをなくしてSwiftだけのコードにする必要があります。
▶ 向き合い方 : 『優先的にObjective-CからSwiftへの移行をすすめる』
昨年の後半はSwift化の専門チームをつくり、Swift化のスピードを加速させました。その結果、21%あったObjective-Cのコードを2%まで減らす事ができました。
Swiftへの単純な置き換えであればそれほど時間かからなかったかもしれませんが、もともとObjective-CではReactiveCocoaを多くの箇所で使用しており、さらに間違った使い方をしていたり複雑な実装となっている箇所も多くありました。
仕様的に難易度が高い箇所はチーム内でモブプロを行うことで仕様に関する知識が共有ができ、共通認識をもって作業を進めることができました。そうすることで、属人化していた箇所も半数以上のiOSエンジニアが個々で作業を進められるようになり、結果的にはSwift化のスピードを上げることができました。
今年中にObjective-Cのコードを0にすることを目標に掲げています。
2. ビルド時間が長い
現在、依存関係が整理できておらず処理が密結合になっており、少しのコード修正でもフルビルドがかかってしまい、待ち時間が多く発生しています。
▶ 向き合い方 : 『モジュール分割を行いビルド時間を短縮させる』
SwiftPackageを利用したモジュール分割を行うプロジェクト(PJコードネーム: Alpen(アルペン))を7月から開始しています。モジュール分割の際には依存関係も整理する予定で、整理後は必要な最小限のファイルだけがビルドされる状態になり、ビルド時間が短縮される想定です。
ただ、モジュール分割を進める上で色々な壁にもぶち当たっています。embeded frameworkやCocoaPodsの廃止がなかなかうまくいかなかったり、導入済みのサードパーティ製ライブラリがモジュール分割の妨げになったりと。これらの課題に対して、iOSアプリエンジニアのkoherさんに技術支援をしていただきながら少しずつ進んでいます。
3. ユニットテストが少ない
Objective-C時代はそこそこテストは書かれていましたが、Swift化に伴いそれらのテストは破棄して、新たにモデルのテストを書いています。
しかし、テストが書かれているのがごく一部のモデルだけで、まだまだテストが少ない状況です。そのため、リファクタリングがしづらく、さらに多くの負債を抱えていくことになります。
▶ 向き合い方 : 『テスタブルなコードにしてテストを書く習慣付けを行う』
現在、アーキテクチャ自体をユニットテストが書きやすいものにリファクタリングを行っています。
- 一部のApplication Service層をprotocol実装してDIな構成にした
- Quick / NimbleとXCTestが混在していたが、XCTestで統一するというルールを決めた
これらを行うことでユニットテストを書ける状態になるので、あとは開発メンバーがテストを書く習慣をつけるところが重要です。こちらについては、チケットのDoneの定義に「ユニットテストの実装が完了していること」を入れることで忘れることなくユニットテストが書ける状態になると考えています。(チケットの見積もりはユニットテストも含めた見積もりになる)
こちらは影響するメンバーが他部署にもいるので、近々周知してDoneの定義をリニューアルする予定です。
4. 特定の機能における属人性
ネイティブのiOSアプリをリリースしてから6年経過していることもあり、以下のようなたくさんの実装が入っています。
- ユーザー影響が大きい新機能
- アーキテクチャー改修
- 小さなUX / UI改善
- Appleの仕様変更(deprecated対応)
- 行動解析
- 広告
- CI / CDの仕組み構築
これらすべてをモバイルアプリエンジニア全員が把握するのは非常に負荷がかかることです。
とはいえ、ある特定の機能については実装した担当者しか把握できていないという状況では、問題が発生したときに調査コストが発生し、解決までに長い時間がかかってしまいます。
Chatworkでも特定の開発者しか詳細を把握できていない実装が一部分で存在します。
▶ 向き合い方 : 『チームごとに責務をもたせることで属人性を排除する』
前述したとおり、モブプロによって属人性は少しずつ減ってきています。
また、全員がすべての細かい仕様を把握しなくてもいいようにチームごとに責任範囲をきめて、各個人の認知負荷を下げようとしています。
具体的には、チームトポロジーに書かれていることを参考にしながら、機能開発を行うチーム(ストリームアラインドチーム)と機能開発のサポートをするチーム(プラットフォームチーム)の2チームに再編して開発を進めていこうと計画しています。
現在のチーム構成やスクラムの進め方については、先日公開された折田のブログをご覧ください。
5. リファクタリングできておらず、レガシーなコードが多い
もともとReactiveCocoaを利用していましたが、途中からRxSwiftに置き換えて、さらにはCombineに置き換えていきました。ReactiveCocoaはObjective-Cの消滅とともになくなっていっていますが、RxSwiftとCombineが混在しており、リファクタリングが中途半端な状態になっています。
現在Swift化を進めていますが、基本的にはリファクタリングを行わずにObjective-Cの作りをそのままSwiftのコードに置き換えているので、Objective-Cの設計思想に基づくコードが多く存在します。
リファクタリングを進められない原因の一つとしては、ユニットテストが少ないことが挙げられます。
▶ 向き合い方 : 『レガシーなコードを積極的に新しい技術に置き換える』
現在は koherさんに技術支援をしていただきながら Swift Concurrency の導入をすすめており、積極的に新しい技術への置き換えを行っています。
ユニットテストを書くことが習慣づけられることでリファクタリングもやりやすくなり、Swiftの言語思想をとりいれた実装に置き換えやすくなります。
最後に
今回は現在iOSアプリに存在する技術的負債と、その負債への向き合い方について書かせていただきました。
上記した負債以外にも、来年に向けて
- SwiftUI導入
- SwiftUI導入に向けたアーキテクチャの選定
- 職能横断型組織での開発にあわせたアーキテクチャの選定
を考えていく必要があります。
やるべきことは山積みですが、チームで協力しながら、立ち向かっていければと思います。
iOS, iPadOSの課題解決や技術選定、将来の技術ロードマップをご一緒に考えていただける方、少しでも興味を持っていただけた方は、是非カジュアルにお話させていただければと思います。
また、2022年10月7日(金)、Chatworkが主催するカンファレンス「Chatwork Product Day」がオンライン開催されます。奮ってご参加ください〜!