Cookpad Summer Internship 2019 10 Day Techコースを開催しました

こんにちは、サマーインターンシップ実行委員長の赤松( @ukstudio )です。

クックパッドでは毎年恒例となっているサマーインターンシップのうち「10 Day Tech コース」を 8月19日から8月30日にかけて開催しました。今年もたくさんの優秀な学生の方が参加し、10日間毎日真剣に取り組んでくれました。本当にありがとうございます。

前半

10 Day Techコースでは前半5日間の講義・実習を行い、後半5日間では更にOJT(実務体験)とPBL(サービス開発実習)の2つに分かれるという構成でした。

まずは前半の講義について簡単にご紹介致します。

1日目: オリエン・Web開発基礎

初日はオリエンテーションとしてクックパッドの取り組みの紹介や自己紹介、貸与PCのセットアップなどを行い、午後から講義に入りました。

講義ではインターンシップ全体を通して必要となるGit/GitHubの入門を行い、Webアプリケーション開発の基礎としてRackアプリケーションの実装、TypeScriptの入門を行いました。

f:id:ukstudio:20190819152258j:plain

ハンズオン資料

2日目: サービス開発講義

2日目はサービス開発講義として、午前中にクックパッドのサービス開発に対する考え方や開発プロセスについて学びました。午後は午前で学んだことをベースにインターン生同士でチームを組み、与えられたテーマを元にユーザーインタビューや価値仮説、アイデア出しからプロトタイプを作るというところまで実践し、最後に講師からの講評を行いました。

2日目は前半の中で唯一コードを書かない日ですが、みなさんかなり頭を使ったようで夜にはへとへとになっているようでした。

f:id:ukstudio:20190820181326j:plain

3日目: API

今年の10 Day Techコースの技術面でのテーマは「クックパッドらしさ」というのが実はあるのですが、3日目から5日目にかけてMicroservices、適材適所な技術選定、クラウド技術などを意識した講義になっています。

3日目〜5日目の講義の中でもこの3日目がテーマが一番色濃くでたのではないかと思います。3日目ではNode.js(TypeScript)を用いてBFF層(Backend-For Frontend)となるGraphQLサーバーの実装を行いました。ボリュームも密度も濃い講義でしたが、みなさん無事に乗り切ることができました。

f:id:ukstudio:20190821140252j:plain

4日目: モバイル

4日目では3日目に実装したBFFのGraphQLサーバーにクエリを投げ、受け取ったデータを表示するためのクライアントアプリを実装しました。今年はiOSとAndroidの二手に分かれ(希望制)、SwiftとKotolinで実装をしてもらいました。

iOSとAndroidの希望を聞いた時に、「経験したことのない方に挑戦してみよう」と伝えていたので未経験の方も多いようでしたが、スムーズに実装を終えることができていました。

f:id:ukstudio:20190822104350j:plain

ハンズオン資料

ハンズオン資料

5日目: インフラ

最終日となる5日目ではクックパッドにおいてSREがどうインフラの問題を解決してきたのか、また3日目ではあまり説明のなかったAPIのインフラレイヤーについて講義を行いました。講義の後は3日目に実装したAPIサーバーにパフォーマンスや可用性において問題が含まれていたので、その問題の解決に取り組みました。

ちょうどこの日にAWSに障害があり、少々ざわついたのですが幸い講義にはあまり影響なく無事終えることができました。講義では簡単に障害の内容についても説明されました。

f:id:ukstudio:20190823101042j:plain

以上が前半に行なった講義・実習となります。今年の講義はどれも密度が濃くかなり大変だったと思いますが、全員無事に乗り切ってくれました。本当におつかれさまでした!!

後半

PBLでは6 Day Design Productコースのデザイナーとペアを組み、サービス開発の実習を行いました。PBLについてはふじけんの Cookpad Summer Internship 2019, 6 Day Product Designコースを開催しました|Cookpad|note をご覧ください。

OJTではクックパッドの様々な部署に配属され、メンターの指導の元サービス開発を実践してもらいました。配属される部署はクックパッドマートを運営・開発する買物事業部、Komercoを運営・開発するKomerco事業部などのサービス系から、モバイル基盤や技術部のSREグループ、クックパッドサービス基盤グループなどの基盤系など様々でした。

最終日には5日間の各自の偉業をメンターやOJTに配属されたインターン生全員の前で発表してもらいました。5日間という短い中で進捗を出すのは大変だったと思いますが、全員無事にやり遂げてくれました。ありがとうございました!

f:id:ukstudio:20190830141025j:plain

まとめ

簡単にですが2019年のサマーインターンシップの10 Day Techコースについて簡単に紹介させて頂きました。今年のインターンシップは実行委員長の自分から見てもかなりキツいインターンシップだったと思います。参加して頂いたみなさん、本当におつかれさまでした!!

f:id:ukstudio:20190830203305j:plain

今年の10 Day Techコースはかなりクックパッドの現場に近い内容となっています。クックパッドの現場に少しでも興味がでた方は新卒・中途問わず、ぜひ遊びに来てください!!

info.cookpad.com

iOSDC Japan 2019 に社員2名がLT枠で登壇&ブース企画のご案内

こんにちは!広報部のとくなり餃子大好き( id:tokunarigyozadaisuki )です。
「今年は梅雨が長いな」なんて思っていたらあっという間に暑くなり、気づけば本当の夏も過ぎ……。夏の終りの気配が見えてきましたね。

さて、iOSと周辺技術を題材としたカンファレンス、iOSDC Japan 2019が今年も9月5日(木)〜9月7日(土)に開催されますね!

クックパッドは、プラチナスポンサーをさせていただいており、ブースを出展いたします。今年は、@giginet@hiragram がLT枠で登壇し、@_sgr_ksmt@natmarkがiOSDCのスタッフとして関わってくれます。カンファレンスには、他にも多くの社員が参加いたしますので、会場でクックパッド社員をお見かけの際には、お声がけいただけますと嬉しいです。

参加予定社員一覧

@kanny, @slightair, @giginet, @hiragram, @_sgr_ksmt@natmark, @ichiko_revjune, @iceman5499, @sagaraya, @to9nariyui

登壇スケジュール

クックパッドの社員2名は、day2 - 9/7(土)に登壇いたします。 以下、スケジュールと登壇内容のご紹介です。

day2 - 9/7(土)

15:45〜 Track A @hiragram俺たちのARKitでめちゃめちゃ表情豊かなVTuber向け表情トラッカーを作るぞ

Animojiにも使われているTrueDepthカメラを使って3Dモデルの表情を動かす表情トラッカーを作りました。webカメラを用いて顔認識する他のシステムよりも精度高く、細かく、感情表現に必要な顔のパラメータを取得できるTrueDepthカメラの本気をお見せします。 表情トラッキングの精度以外にも、ARKitのおかげでバーチャルYouTuberを運用するにあたって地味に嬉しい機能をたくさん獲得しているので、プロデュースの現場の目線から面白おかしく紹介できればと思います。

16:55〜 Track A @giginet令和時代のゲームボーイ開発 👾

1989年に発売したゲームボーイは、今年30周年を迎えました。 そんな今だからこそ、実機で動くゲームボーイ開発をしてみましょう! 30年の時を経て、ゲームボーイが最新の技術で蘇ります。

ブース

今年のクックパッドブースではグッズの配布はもちろんですが、2つの企画を行います! 

f:id:tokunarigyozadaisuki:20190827145810j:plain
昨年のブースの様子

クックパッドのコード全部見せます大質問会

day2 - 9/7(土)の11:00-13:00にアンカンファレンストラックにて「iOS版クックパッドアプリのコード全部見せます大質問会」を開催します!

この会は、実際のプロダクトコードを見せながら、ディスカッション形式で、クックパッドのiOS開発について紹介します。 例えば以下のような質問を歓迎しています。

  • どういう開発体制でやってるの?
  • リリースフローやQAについて見せて
  • アーキテクチャはどうなってるの?
  • Podfile見せてください

なにか見たい部分や、開発上での質問がある方は、day1 - 9/6(金)にクックパッドブースへお越しください!当日はその質問を中心にご紹介します。飛び入りでの質問も歓迎です。

クックパッドエンジニアとの「カジュアルトーク」

エンジニアリングマネージャーや、現場で活躍するエンジニアと直接話せるカジュアルトーク企画を実施します。開発に関する話からオープンな場で聞きにくいキャリアについての話などを1on1のようなスタイルでお話しましょう! あんなことやこんなことまで……どんなことでもお気軽に。 参加メンバーとスケジュールは以下のとおりです。

エンジニアリングマネージャーと話せる枠

クックパッドブースにお越しいただき、希望する時間を選んでいただくと、その時間に VP of Tech 星 北斗 @kani_b、モバイル基盤部 部長 茂呂 智大 @slightairと話せます。

  • day1 - 9/6(金)11:00-18:00:星・茂呂
  • day2 - 9/7(土)11:00-18:00:茂呂

▼ RubyKaigi 2019 実施時の様子

特定の技術に詳しいエンジニアと話せる枠

以下の時間は、特定の技術について豊富な知見を持っている社員がブースにおりますのでぜひ情報交換しましょう!

day1 - 9/6(金)12:50-14:20 fastlane / Carthage:@giginet

fastlaneCarthageのコミッターやってるのでチョットできます。 その他にもSwiftPMやCocoaPods, XcodeGenなどの開発ツール全般についても知見あります。上記の話題にかかわらず、興味のある技術トピックがあればわいわいしましょう 🙌

day1 - 9/6(金)14:20-15:10 モバイルテスト自動化/QA @ichiko_revjune

参考:https://techlife.cookpad.com/entry/2018/12/12/120000

day1 - 9/6(金)15:10-16:00 SwiftUI @iceman5499

参考:https://techlife.cookpad.com/entry/2019/06/25/120000

おわりに

クックパッドはiOSエンジニアを募集しています。クックパッドで働くことに少しでもご興味をお持ちの方は、お気軽にブースまでお越しください! みなさまにお会いできることを楽しみにしております。

Android cookpadLiveで採用してる技術 2019夏

メディアプロダクト開発部の安部(@STAR_ZERO)です。

Android cookpadLiveで採用してる技術について紹介したいと思います。

cookpadLiveとは

cookpadLive は、料理上手な有名人や料理家がクッキングLiveを生配信しています。一緒に、Live配信でリアルタイムに料理が楽しめるアプリです。

ダウンロード: Android アプリ iOS アプリ

ぜひ、ダウンロードしてLive配信を見てください!

基本環境

基本となる環境です

  • Kotlin
  • minSdkVersion 21
  • targetSdkVersion 28
  • AndroidX

特別な箇所はないですが、最新に追随するように努めています。

比較的新しいアプリなので、最初からすべてKotlinで記述されています。

targetSdkVersionについてはそろそろ29に対応する予定です。29にすることでの影響を調査している状況です。

Android Studio 3.5

Android Studio 3.5はbetaの段階から導入しています。理由としてはIncremental annotation processingを使いたかったためです。

cookpadLiveでは全面的にDataBindingを採用しているため、これの恩恵は非常に大きいものになります。 これまでは、レイアウトファイルを変更してビルドし直さなければコードが生成されず効率がよくありませんでした。3.5からはレイアウトファイルを変更すると同時にコード生成も行われるのでビルドによる待ち時間が減り効率よく開発できるようになりました。

Jetpack

現在、cookpadLiveではJetpackを積極的に採用しています。

DataBinding、LiveData、ViewModelについては、最初は使用されていなかったのですが、徐々に導入を進めて今では全面的に使用しています。

意外と便利だったのが、ViewModelをActivityに関連付けることでActivityとFragment間、それぞれのFragment間でのデータやイベントのやりとりが可能になる機能です。この機能のおかげ実装がだいぶ楽になったこともありました。 例えば、以下のようにActivityのイベントをFragmentでも受け取ることが簡単にできます。

class HogeActivity: AppCompatActivity() {
    private val viewModel by lazy {
        ViewModelProviders.of(this).get(HogeViewModel::class.java)
    }
    fun someEvent() {
        // イベント発行
        viewModel.somaEvent.value = someValue // someEventはLiveData
    }
}

class HogeFragment: Fragment() {
    private val activityViewModel by lazy {
        // thisではなく、Activityを指定することで共通のViewModelを使用できる
       ViewModelProviders.of(requireActivity()).get(HogeViewModel::class.java)
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        activityViewModel.somaEvent.observe(viewLifecycleOwner) {
            // Activityのイベントを受信
        }
    }
}

Pagingはだいぶ癖があるライブラリですが、ちゃんと理解して使う非常に便利です。これも早い段階から導入しています。現状ではネットワークにはライブラリ側で対応はされてないですが、今後される予定らしいので、楽しみにしています。

Navigationについてはそこまで活用できないですが、部分的に使っています。全面的にSingle Activityにすることは考えてないですが、出来る箇所はFragmentに移行していきたいと考えています。また、SafeArgsは画面間の値の受け渡しが便利になるので積極的に使っていこうと思います。

RoomはDBまわりの処理には欠かせないくらい便利です。RoomはLivaData、RxJavaと簡単に連携することができるため、既存の実装に組み合わせることが簡単にできました。SQLも補完とシンタックスハイライトが効くので非常に助かります。

DI

DIについて Dagger を使用しています。こちらも最初は使用されてなかったのですが、徐々に導入をすすめました。

Daggerについては非常に難しい印象がある人が多いと思いますが、一度使うと便利すぎて手放せません。 DaggerでRepositoryクラスなど生成するようにして、あとは使いた箇所でInjectするだけです。ViewModelなどで必要なRepositoryが増えた場合も、生成するコードを意識せずパラメータに追加するだけ済みます。 最初の設定さえうまくやってしまえば、あとは楽になるはずです。

以前、部内でやったDagger勉強会のチュートリアルコードのリンクを貼っておきます。(まだ@Component.Factoryには対応してないです…)

STAR-ZERO/dagger-tutorial

AppSync

cookpadLiveではライブ中のコメントやハートなどのリアルタイム通信にAWSの AppSync を使用しています。

f:id:STAR_ZERO:20190826153800p:plain:w300

この部分が一番特徴的かもしれません。

AppSyncはAWSのマネージド型GraphQLサービスです。 ユーザーが送信したコメントやハートをAppSync(GraphQL)のSubscriptionの機能を使い受信するようにしています。

ライブによって非常に多くのコメントやハートを受信することになりますが、受信のたびに画面に描画するのではなくRxJavaのbufferを使ってある程度まとめて画面に描画するようにしています。このあたりはうまくRxJavaと組み合わせて実装しています。

AppSyncの話は以下の記事の後半部分を見ていただければと思います。

CookpadTVのCTOが語る、料理動画サービス開発の課題と実装 - ログミーTech

その他ライブラリ

このあたりよく使われてるライブラリですね。これらももちろん活用しています。

設計

MVVM + Repositoryパターンを採用しています。Googleが公開してる Guide to app architecture とほぼ同じです。

元々はVIPERだったのですが、DataBindingやLiveDataとViewModelを導入していくと同時にMVVMに移行していきました。今ではすべてMVVMで実装されています。 私個人の経験からもJetpackを導入することで、開発効率と品質に大きく貢献することは明確だったので、これらを導入しました。

VIPERはAndroidではあまりの馴染みがないかもしれませんが、MVPパターンのようにInterfaceを使って各レイヤー間の処理を呼び出します。 すごく簡単な例ですが、以下のような感じです。(例ではViewとPresenterしか登場してないです)

// HogeView.kt
interface HogeView {
    fun show()
    fun hide()
}

// HogeFragment.kt
class HogeFragment: Fragment(), HogeView {

    private val presenter by lazy { HogePresenter(this) }

    override fun onCreateView(/** */): View? {
        // ...
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        presenter.fetch()
    }
    override fun show() {
        // View.VISIBLEにする
    }
    override fun hide() {
        // View.GONEにする
    }
}

// HogePresenter.kt
class HogePresenter(private val view: HogeView) {
    fun fetch() {
        val data == // ...
        if (data != null) {
            view.show()
        } else {
            view.hide()
        }
    }
}

このようにViewへの処理を呼び出すのにInterfaceを使ってPresenterからViewへの処理を呼び出しています。

一見、問題がなさそうですが、この時点で既に問題があります。例えば、Presenterでデータ取得中にActiivty/Fragmentが破棄された場合はどうなるでしょう。破棄されてるオブジェクトにアクセスすることになり、場合によってはクラッシュします。これはPresenterがActivity/FragmentのLifecycleについて何も知らないからです。これを解決するにはPresenter側に破棄されたことを教えてあげる必要があります。

では、これを今の実装で書き換えた場合です。

// HogeFragment.kt
class HogeFragment: Fragment() {

    private val viewModel by lazy {
        ViewModelProviders.of(this).get(HogeViewModel::class.java)
    }

    private lateinit var binding: FragmentHogeBinding

    override fun onCreateView(/** */): View? {
        // ...
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        binding.viewModel = viewModel
        binding.lifecycleOwner = viewLifecycleOwner
        viewModel.fetch()
    }
}

// HogeViewModel.kt
class HogeViewModel: ViewModel() {
    val isShow = MutableLiveData<Boolean>()

    fun fetch() {
        val data == // ...
        isShow.value = data != null
    }
}
<!-- fragment_hoge.xml -->
<layout>
    <data>
        <variable name="viewModel" type="...HogeViewModel" />
        <import type="android.view.View" />
    </data>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="..."
        android:visibility="@{viewModel.isShow ? View.VISIBLE : View.GONE}" />
</layout>

ここではDataBindingを使用していて、ViewModelが保持しているLiveDataとバインドしています。このLiveDataの値を変更すると自動的にView側にも反映される仕組みになっています。 ここで重要なのは、LiveDataはLifecycleのことを知っているので、Activity/Fragmentがアクティブな状態のときしかデータを流しません。そのため、さきほど説明したActivity/Fragmentが破棄されたときの対応を特別にせずとも問題が起こることがありません。またViewModelおいては回転でActivityが再生成されたときもViewModelは状態をもっているため、データ取得を中断することなく処理を継続することができます。

この設計にすることでActivity/FragmentはViewModelの状態を反映すれば良いだけになり、責務もしっかり分かれて見通しが良くなりました。また単純にVIPERはファイル数が多くなるため、コードを追う時にコードジャンプであちこち飛ばなければならず、個人的にはコードが追いにくい感じでした。

他にも様々な面で効率・品質を向上させるのに貢献してくれています。その他便利なJetpackライブラリも簡単に導入できるようになっています。

今ではすべての画面が同じような感じになってるので、どういう処理をしてるのかを理解しやすくなっています。

この設計変更ですが、新規画面については新しい設計でやり、既存については少しずつ進めていました。またUIを大きく変更するタイミングもあったので、その時に一気に直した箇所もありました。出来ることからコツコツやってこともあり、大きくコストをかけることなく変更できました。

CI環境

CIに関しては、Jenkinsを使っています。やってることは以下になります。

  • Pull Request
    • テスト、lintを実行
    • 社内テスト環境にAPKをアップロード
  • masterマージ後
    • 社内テスト環境にAPKをアップロード
    • Google Play内部テストへアップロード

可能な限り早い段階でリリース版をビルドして触ることで、問題があったときに早めに気づくことができるようにしています。特にProguardまわりは見落としがちになるを防げます。

リリースするときは内部テスト版を製品版へ昇格するだけになっています。今ここは手作業でやってるのですが、ChatOps等で出来るようにしたいと考えています。

マルチモジュール

現状では、スマホ、AndroidTV、FireTVで共有するようなモジュールと、featureモジュールをいくつか分割しています。

図にすると以下のような感じです。

f:id:STAR_ZERO:20190826154302p:plain:w300

  • core
    • 全モジュールで共通処理
    • APIアクセス、Repository、データモデルなど
  • appcore
    • スマホアプリ共通処理
    • 共通View、ログ、リソースなど
  • feature
    • 各機能を分割したモジュール
  • app
    • スマホアプリメイン
  • smarttv
    • TVアプリ共通処理
    • 共通View、ログ、リソースなど
  • androidtv
    • AndroidTVメイン
  • firetv
    • FireTVメイン

まだfetureモジュールは分割できる箇所があるので、少しずつでも進めていきたいと思います。

課題と今後

テスト

正直、まだそこまでうまく書けてる状況ではないので、なんとかしていきたいと思っています。 せっかくなので、ライブラリの選定から考えようとも思っています。Truth 良さそうですね。

StyleとTheme

StyleとThemeについては結構ちらかってる状態なので、整理したい思っています。画面数もそこそこあるので、だいぶ大変な作業になる気配がしています。まずは、どういうふうに整理するかを検討してから少しずつやっていく感じになりそうです。

Navigation

前に書きましたが、まだまだ活用できる箇所があります。すべてSingleActivityとは考えてないですが、Fragmentでの遷移で良い箇所もあるので、そういった箇所に対応していきたいと考えています。

Coroutines

Coroutinesについては、どうするかを検討している段階です。現状でCoroutinesじゃないと困るような場面は出てきていませんが、JetpackもCoroutinesの対応が進んでいて実装するのに困ることはないと考えています。また、今後Coroutinesによって実装コストが下がるような機能なんかも出てくる可能性ありそうです。 メンバーと会話して、導入する気持ちはありますが、進め方やどこから導入するのかを考えています。

まとめ

cookpadLiveでは積極的にJetpackを使っていき、Googleが推奨しているやり方にどんどん乗っかっていっています。 今後もJetpackも改善されていくと思うので、それにいつでも追随できるような状態を保つようにしています。

これからもcookpadLiveでは新しい技術を積極的に取り入れていきますし、やりたいこともまだまだたくさんあります。

興味がある方いらっしゃいましたら、気軽にお声がけください。一緒に色々チャレンジしていきましょう。

info.cookpad.com