初めまして、Yahoo!ニュースアプリ開発を担当している八木下・西原です。Yahoo!ニュースアプリチームでは、1人のエンジニアが複数の技術領域を担当できるスキルアップと体制づくりを目指しました。この記事では、マルチスキルを推進するチーム作りのポイントや、取り組みを通して学んだことを紹介します。
これまでの課題や不安
各技術領域の業務は、そのスキルを持つエンジニアだけで行っていました。結果、このような問題が起きていました。
- 仕事が属人化したり特定の人に集中しやすい
- 最近ではiOSアップデートにともなう対応が多かった
- iOS開発担当のタスクがあふれてしまっていた
- それぞれの開発進捗が噛み合わない
- ユーザーへの価値提供が遅れる
- APIが早く完成しても、iOS/Androidアプリの完成が遅れるとリリースできない
私たちのチームはこのような課題を解決し、開発リソースに流動性を持たせたいと考えていました。また、エンジニアとしては未経験領域に挑戦できずに1つのスキルのみに限られた仕事を続けていると、以下のような不安やデメリットを感じることがあると思います。
- 今後のキャリアへの不安
- 活躍できる環境が限られてしまう
- その技術が廃れたら必要とされなくなってしまうのではないか
- モチベーションの低下
- 同じような仕事が多く、成長を感じにくい
これを解決しようとしても、自分ひとりの力で新領域に挑戦することはハードルが高く、困っている人もいるのではないでしょうか。
マルチスキルとは
そこで、チーム内で流動性をもって、1人のエンジニアが複数領域を担当可能な体制づくりを進めることにしました。この状態をマルチスキルと呼んでいます。Yahoo!ニュースアプリにはiOS, Android, アプリバックエンド(BFF)の3領域があります。この中で、アプリバックエンド開発担当がAndroidに、Android開発担当がiOSに挑戦し、マルチスキル人材を育てることにしました。
例えばサーバーサイドエンジニアがモバイルアプリ開発に挑戦するなど、未経験の領域に挑戦してスキルアップしていくことは重要です。しかし、誰でも簡単にできることでしょうか。
新しい領域には挑戦しにくい
新しいスキルの習得を比較的得意とする人もいると思います。ですが、やはり初めての未経験の領域へ挑戦をするのは、何から始めたらいいのか? うまくできるのだろうか? といった不安もあり、簡単に習得できるものでないと思います。
なぜなら、未経験の領域ではどうしても最初はパフォーマンスが低下してしまうからです。「仕事が間に合わないのではないか」「できる人に任せた方がスピードもあって効率が良いのでは」などと考えてしまい、挑戦をためらってしまうこともあると思います。
もちろん自分で勉強して挑戦できる範囲を広げられたほうが良いです。Yahoo!ニュースアプリでも業務内の学習は推奨されています。しかし、期限のある仕事も多いので業務時間を大量に使って勉強できるわけではありません。家庭の事情などはそれぞれ異なるため勉強に確保できる時間が限られている場合もあり、全員にプライベートの時間を使って勉強することを強制するわけにもいきません。
このような理由から、Yahoo!ニュースアプリチームではシングルスキルの人が多い状態でした。
マルチスキル推進のためのチームづくり
チームの成果をより大きくするため、マルチスキル習得に挑戦するメンバーを増やしていく必要があります。個人のスキルに依存せず、多くのメンバーがマルチスキル化するためには、チームで取り組む必要があります。そこで、ベースとなる技術スキルアップとコミュニケーションの両面で下記のような取り組みを行い、マルチスキル習得への挑戦のハードルを下げることを目指しました。
チームの理解を得る
Yahoo!ニュースアプリのプロダクトチームは技術課題への取り組みや、エンジニアの育成にとても理解があります。マルチスキル化に取り組むことについても中長期的なメリットがあるので後押ししてもらえました。プロダクトオーナーがエンジニア出身なことも理由の1つかもしれません。
理解があるとはいえ、もちろんリリーススケジュールは守らなければならず、このような工夫をしています。
- テスト工数が足りない時:担当にかかわらずテスト工程を分担するなど協力
- 不具合対応時:チケットフォーマットがあり、職種にかかわらず必要な内容を記載、すぐに関係者で優先度判断を行う
プロダクトチームによっては取り組みに理解してもらうことが難しい場合もあるかもしれません。チームで協力してデメリットを抑えつつ、中長期のメリットを丁寧に説明することが大切だと思います。私たちの場合、取り組み開始からおよそ1カ月でエンジニアが柔軟にタスクを担当できるなどのメリットが目立ってきました。
チーム勉強会を開催する
ベースとなる知識・スキルアップのために、お互い教え合ったり質問して効率よく学ぶようにしました。
- 内容
- 「業務内で有用だったこと」「詰まったこと」「シニアメンバーから見て今チームとして学ぶべきこと」などをテーマにチーム全員持ち回りで発表
- 過去のテーマ例:副作用有無とメンテナンス性、BFFの構成、プルリクエストの粒度 など
- 隔週で開催
- ポイント:話を聞いて終わるだけの勉強会にしないこと
- ただ聞くだけではなく、時間の半分は質問・議論を活発に行う
- 少人数で実施し、発表中でもわからない事をチャットで気軽に質問するようにし、必ず全員が発言できる勉強会に
特にレイヤードアーキテクチャについては利点や実装例をテーマとして複数回取り上げていました。学んだことを発揮してiOS/Android/アプリバックエンドいずれもレイヤードアーキテクチャを導入しており、別領域に挑戦する際にも構成を理解しやすかったです。
リモートワークを工夫する
特に新しい領域に挑戦する時は、普段以上に密にコミュニケーションするようにしました。私たちのチームでは、リモートになってからペアプログラミングが盛んになりました。マルチスキル化の取り組みとは、普段とは別の仕事に取り組むことです。
- ペアプログラミング
- 普段の業務でも役に立つ手法だが、マルチスキル化へ取り組む際には普段以上に大きな効果を実感
- どこから着手すれば良いかなど、手順や流れを大きく間違うことがない
- ツール
- コミュニケーション:Slack + Zoom
- タスク管理:GitHubのプロジェクトボード
ペアプログラミングなら、不慣れな開発環境で仕事をする上で困ったことをその場で質問でき、慣れている人から効率の良い操作方法なども知れます。毎日短時間でもペアプログラミングの時間を作っておくと、困ったことを解決できる速度が上がったと感じています。使っているツールはリモート前と後で変わりはありませんが、文章として残すこと・簡潔で正確に伝えることがより意識されています。その意識がチーム全体に浸透してきたので、リモート前よりもむしろ働きやすくなったかもしれません。
マルチスキル習得の挑戦
ここまで紹介したチーム作りを行った上で、マルチスキル習得に挑戦しました。
ハードルは下がりましたが、私たちが挑戦している期間は、一時的に開発スピードが低下したりアウトプットが減ってしまうことは避けられません。
そのため、漠然と学んで終わらずに中長期的にはプラスになると言えるよう、十分な成長・成果を出せるよう事前に目標を立ててから取り組むようにしました。
それぞれ下記のように取り組みました。
- 八木下
- 普段はアプリバックエンドを担当
- 今回はアプリバックエンド + Androidのマルチスキル習得に挑戦
- Android開発の経験はあるが、3年のブランクがあり最新のトレンドはわからない状態だった
- 西原
- 普段はAndroidを担当
- 今回はAndroid + iOSのマルチスキル習得に挑戦
- iOS開発の経験は全くなく、今回初めてSwiftを学んだ
マルチスキル化に取り組んだ結果
チームに還元できたこと
マルチスキル化に取り組んだことで、個人としての学びだけでなくチームの課題を解決したり、得た知識を共有することができました。
- 得た知識をチーム勉強会で発表
- RxJava, KotlinCoroutines, JavaScript async/awaitの比較を共有(※詳細は別項目にて)
- iOS/Android間の仕様違いや不具合防止
- 例えばエラー表示の内容など細かな点でOS間差異が起きることが課題になっていたが、両OSのコードを読めるようになったことで差異に気づきやすくなった
- 気になる点は自分で両OSのコードを読みながら調べられるので、リリース前に不具合を見つけて修正することができた
- コミュニケーション課題の解決
- 同じメンバーで普段の仕事をしていると当たり前になっていて、小さくて簡単な課題であっても、課題と認識されていなかったことがいくつかあった
- 担当する人が変わることで、すぐに課題に気づいて解決することができた
- 例:コードレビュー待ちがたまって負担に → 素早くコードレビューするためにポイントや注意点を補足するセルフレビューコメントをつけるようにした
- 例:UIの相談はテキストコミュニケーションだと時間がかかる → デザイナーなど他職種メンバー間でもすぐにZoom通話・画面共有し、細部の調整を行う文化ができてきた
苦労したこと
やはり、複数の技術領域のスイッチングコストは高かったです。ある程度大きなタスクを担当するためにマルチタスク挑戦期間は1カ月で実施しました。
1カ月の期間中100%新領域の仕事をすることはできないため、一日のうち1~2時間程度はもともと担当していた領域でコードレビューなどの業務を行っていました。言語・ツール・デバッグ方法など異なるため集中してパフォーマンスを発揮するのが難しいこともありました。次回は1カ月よりも短めの1週間程度で取り組み、その間は100%新領域の仕事に集中した方が良さそうでした。
また、不在の期間中の情報・状況キャッチアップ方法には改善の余地がありました。Yahoo!ニュースアプリではiOS/Android/アプリバックエンドに分かれて朝会を行い、毎日口頭でタスクの進捗状況の確認や相談を行っています。
新領域への挑戦期間中は、もともと担当していた領域の朝会には参加していませんでした。代わりにSlackやGitHubのプロジェクトボードを見て情報・状況をキャッチアップしていましたが、この方法はコストが大きい割に効果が低く感じました。次回は朝会に参加して低コストでキャッチアップできるようにしたいです。
メリットまとめ
新しいスキルを業務で発揮できた経験を得られただけでなく、ペアプログラミングなどの工夫をすることで「新しい技術の効率良い学び方」を学ぶことができました。それにより、担当できるスキルが1つであることからくるキャリアへの漠然とした不安感が解消され、今後も新しい挑戦をし続ける自信や安心感につながりました。
私たちが今回の取り組みを通して意識したこと・感じたことをまとめます。
- 複数の技術領域を担当できると、メリットが多い
- 全員が簡単にできるわけではないが、チームで工夫をすることで挑戦のハードルが下がる
- 個人のスキルアップだけではなく、チーム力向上にも効果がある
参考:私たちが担当したこと
私たち2人が実際に立てた目標、担当したタスク、得られたことを紹介します。
Androidに挑戦(八木下)
- 目標
- Androidで採用しているレイヤードアーキテクチャの理解
- 担当タスク
- タイムライン記事の非表示機能追加
- 非表示を選択することでレコメンド精度を高めることができる機能
- 得られたこと
- MVVM+レイヤードアーキテクチャの理解
- MVVMのViewModelは初めて扱ったが、ViewModelで状態管理を行うことでViewのロジックを減らせ、責務が分離されて書きやすく・読みやすいコード設計ができると感じた
- レイヤードアーキテクチャはアプリバックエンドでも採用しているため、Repository層・Domain層を改修するハードルは低く、スピード面に対する不安は解決できた
- 非同期処理を行う箇所では以前はRxJavaをメインで使っていたが、KotlinCoroutinesを今回の挑戦で学ぶことができた
- デバッグツールの習得
- コードを書くだけでなく、不具合や改修のための調査を行うことができるようにデバッグツールについても学んだ
- Charles:HTTP通信の確認、APIのレスポンス編集方法
- AndroidStudioのDatabaseInspector:アプリ内データベースの確認
- MVVM+レイヤードアーキテクチャの理解
iOSに挑戦(西原)
- 目標
- iOS経験ゼロだが、成果>教育コスト になるようにする
- 担当タスク
- そのために、Androidで担当済みの案件をiOSでも担当した(仕様漏れ防止など、技術以外の面でも開発スピードに貢献するため)
- マイページ機能の一部
- おすすめ記事表示
- エラー表示・ブロック機能
- 得られたこと
- iOS/Swift知識ゼロだったが、レイアウトを含む小タスクは開発できるレベルになった
- iOS開発とAndroid開発の特徴理解
- SwiftとKotlinの違いを理解
- レイアウト作成方法の違い
- 成果>教育コストを達成できた
細かい点では違いはありますが、Kotlinを使える人ならSwiftのコードを読むことには困らないと思いました。iOS, Androidどちらかの知識を持っていれば理解できることも多く、もう一方に挑戦するハードルは高くないと感じます。
Androidではコードベースでレイアウトを組み、iOSではGUIでレイアウトを組むという違いがあります(ニュースアプリの場合)。今回の取り組みでiOSの.xibを作成しましたが、細かい設定をGUIで行うので各設定がどこにあるのか、何ができるかを覚えることに苦戦しました。
Androidでは.xmlのコードを読めばレイアウトの想像がつきますが、iOSの.xibはプルリクエスト上では全てコードに変換されているのでレイアウトの想像ができないという点もあり少し不便に感じました。
細かな技術仕様の抜け漏れなくスムーズに開発でき、iOSに取り組んだことで両OSのリリース前のテストなど開発以外の作業も可能になりました。
勉強会での発表:非同期処理のための技術
今回複数領域の開発をしていて特に興味を持ったのは、非同期処理のために使っている技術の違いです。あたりまえですが、iOS/Android/アプリバックエンドのいずれも非同期処理を多用しています。それぞれで主に使っているものは以下の通りです。
- iOS:RxSwift
- Android : Kotlin Coroutines
- アプリバックエンド : async/await(TypeScript)
基本的な使い方やそれぞれの違いなどに興味を持ったので、調べてチーム勉強会で発表しました。ここでは概要を紹介します。
RxSwift
- 概要
- さまざまな言語に提供されているフレームワークReactiveXのSwift版
- 特徴
- もともとはMicrosoftが.NET向けに開発したもので、NetflixがRxJavaを公開して広く利用されるようになった
- 関数型リアクティブプログラミングを実現することが目的で、非同期処理だけでなくさまざまな使い方ができる
- Observerパターンの特徴を生かして疎結合なコード記述を実現できる
- 時系列データに対してIteratorパターンを適用できるので、例えば任意のタイミングで発生するユーザーイベントを配列のように扱うようなことができる
- 関数型プログラミングの要素が取り入れられており、副作用を発生させる箇所を限定してメンテナンス性の高いコーディングができる
- Yahoo!ニュースアプリでの使い方例
- HTTPリクエストやユーザー操作に合わせた処理記述に利用
メニュー内のアイテムが選択された時の処理をViewModelに記述した例
// ユーザー操作によりmenuSelectedが発火する
// subscribe = 購読しているので操作のたびにonNext処理が実行される
// Viewとは分離して宣言的に記述できるのでメンテナンスしやすい
// DisposeBagを使っておけばViewModel破棄と同時に解放されるためメモリリークしない
let bag = DisposeBag()
menuSelected
.subscribe(onNext: { [weak self] item in
switch item {
// 選択されたメニューアイテムごとの処理を実行
// UIを更新する場合はメインスレッドで行う必要がある
}
}).disposed(by: bag)
Kotlin Coroutines
- 概要
- 非同期処理・ノンブロッキング処理を行うためのKotlinの機能
- 特徴
- 1スレッドで多数のコルーチンを実行できる
- 中断が可能。中断中は別のコルーチンが実行されてリソースを効率よく使うことができる
- 特にAndroidではJetpackのサポートも充実しており、画面などのライフサイクルに合わせたスコープが用意されている
- Yahoo!ニュースアプリでの使い方例
- iOSのRxSwiftと同様にHTTPリクエストやユーザー操作に利用している
コメントを取得して表示する処理をViewModelに記述した例
// comment.loadの結果をflow型としてコールバックのように受け取る
// comment.loadメソッド内で flowOn(Dispatcher.IO) を指定し、非同期処理として実行している
// 正常系と異常系を連結して書いていくのはRxとは書き方が異なる
// launchIn(Scope)で解放を管理しているのはRxSwiftと似ているが、専用のViewModelScopeを使えるのでより簡単
// 他のScopeとしてLifecycleScopeなどがある
comment.load(
isLogin = true, userId = userId
).onEach { (comment) ->
// リストにコメント表示する処理を実行
}.catch {
// エラー表示
}.launchIn(viewModelScope)
async/await(TypeScriptにて利用)
- 概要
- Promiseをより快適に記述するためのJavaScriptの構文
- 特徴
- asyncを付与した関数はPromiseを返し、awaitすることで同期処理のようにシンプルに記述できる
- アプリバックエンド = Node.jsで作られたBFFなので非同期処理が多く、シンプルに記述できるメリットが大きい
- Yahoo!ニュースアプリでの使い方例
複数のWeb APIからデータ取得して結果を得る例
// アプリバックエンドは性質上、複数のWeb APIの結果をまとめてクライアントへ返すことが多い
// 同期処理とほとんど変わらずシンプルに記述できるのが良い
public async getResult(): data {
const article = await articleClient.fetch(articleId);
const comment = await commentClient.fetch(article.commentId);
// 記事とコメントを合わせて返す処理
}
複数回のHTTPリクエストを並列実行して結果を得る例
// (素のまま使うとして)並列実行を簡潔に書けていないため、解決したいが今はできていない
const results = await Promise.all(
ids.map(async id => await httpClient.fetch(id))
);
今後に向けて
チーム内で時期によってはiOS/Android/アプリバックエンドの忙しさにばらつきがあります。今回のマルチスキル化の取り組みを継続することで柔軟に複数領域を担当できるようになり、ユーザーに早く価値提供できるようなチームを目指していきたいです。
一度の取り組みで終わらせず、何度か取り組みを繰り返しスキルを深く習得していくことで、もし今後チームや体制が変わったとしても、経験を生かして個人・チームの両方に良い影響を与えられるのではないかと思います。
私たち以外のチームでもマルチスキル化に取り組みたいという声を多く耳にします。今回学んだことを生かし、特にチーム作りの面などでサポートすることでマルチスキルへの取り組みを広げていきたいと考えています。
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました