Chromeでのインターン生活を振り返る

今年の夏にGoogle JapanのChrome(ブラウザ)チームで2ヶ月間インターンをしました。去年の夏も行ったので今年で2回目です。

今年のインターンはどんな感じだったのか、年末なので(?)ゆるく振り返ってみようかと思います。

インターンが決まった経緯

私は去年もGoogle STEPプログラムの一環でMapのチームでインターンをしていましたが、そのときにreturning offerを頂いて再びインターンができることになったので戻りました。今回は普通の(STEPでない)インターンとして参加しました。

returning offerを頂いた後、今年の4月ごろにインターンでの希望分野やチームを聞かれたのですが、Chromeを希望しました。 去年いたMapチームが外国人比率が高めだった一方、Chromeは日本人エンジニアが多めなチームの1つだったので、両方の環境を体験してみたかったことと、 やはりChromiumオープンソースであるのが魅力的だったことが理由です。

その後、ホストマッチングで無事にChromeチームに配属されることになり、ChromeのService Workerを担当するチームでの9週間のインターンが始まりました。

プロジェクトの内容

ChromiumOSSでプロジェクトの内容も自由に話すことができるので、軽く説明してみたいと思います。

TL;DR

ChromeのService Workerのアップデートにおいて、旧来はregisterしたスクリプトに変更があった場合しかアップデートが起こらなかったところを、 そのスクリプトからimportScript()で呼び出されるスクリプトのみに変更があった場合もアップデート処理を呼べるようにするというプロジェクトに取り組みました。

Service Workerについて

Service Workerとは、Webページが開かれていない間もスクリプトを走らせることを可能にするようなJavaScriptの機能で、Chromeでは確か2013年ごろに実装が始まりました。 Webリソースをキャッシュしてオフラインでの閲覧を可能にしたり、タブを開いていない間でもプッシュ通知を送ることができるようにしたりなど、アプリケーションのような機能をWebで使うことができるPWA(Progressive Web Apps)の主要技術として最近注目されています。

Service Workerの動き

register

Service Workerを使うにあたって、Web開発者はその挙動を記述したスクリプト(main scriptと呼びます)を用意し、それをnavigator.serviceWorker.register()というメソッドを使って登録させます。下記の例ではsw.jsというのがmain scriptになります。

<!-- main.htmlとか -->
<script>
navigator.serviceWorker.register('sw.js');
</script>

こうすることによって、ページの初回訪問時にそのページのService Workerがブラウザに登録され、次回以降の訪問時にはそのページを管理するService Workerを使った処理がなされるようになります。

register後のページロード

登録済みのService Workerによって管理されているページへのアクセスが発生したときの挙動をみてみましょう。

f:id:momohatt:20181206144448p:plain
Loading with a Service Worker

アクセスしようとしたページを管理するService Workerが登録されているとき、ブラウザは通常のようにネットワークリクエストを投げるのではなく、そのService Workerに対してfetch eventというものを発行します。fetch eventを受け取ったService Workerの動きはmain scriptでの実装次第ですが、よくある例では、予めブラウザのストレージにページのリソースが保存されるようにしておき、それらを読み込んできて返すということをしています。

このような場合、保存されているリソースをリモートに合わせて適切なタイミングでアップデートする必要があります。Chromeでは、該当ページに対するfetch eventの処理が終わった直後にアップデートの必要があるかどうか(=リモートにあるリソースの内容と手元にあるものの内容が異なっているかどうか)をチェックし、必要があると判断した場合はアップデートのルーチンを引き起こします。

もっと詳しく知りたい方は以下の資料なども参考にしてください :) qiita.com

問題だった点

main scriptは普通のJavaScriptファイルなので、次のように他のスクリプトimportScripts() することが可能です。 

// sw.js (main script)
importScripts('foo.js');

このような場合のアップデートチェックで、 sw.js には何も変更がないが foo.js には変更があった、というような場合、全体としては変更があるわけなので、アップデートをするのが自然に思えます(そしてW3Cのspecificationでも要求されています)。

しかし、ChromeのService Workerの実装には、たとえfoo.js に変更があったとしても、 sw.js に変更がなかった場合はアップデートが起こせないというバグがありました。これを直して、main scriptからimportScripts() で呼ばれる全てのリソースに対してアップデートチェックをする、というのが今回の私のプロジェクトでした。

難しかった点

最初にプロジェクトの内容を聞いた時は別にそんなに難しくないんじゃないかと勝手に思っていましたが、

  • importScripts() で読み込まれるリソースを静的に解析できない
  • アップデートが必要な時(リソースに変更がある時)以外はスクリプトを実行してはいけない

といった理由から、新しい版で必要とされる全てのリソースを知ることが不可能だったこと、 また、それまでのアップデートチェックの実装を利用することができず、一から作る必要があったことから、思いの外難しかったです。

それなりに大きいプロジェクトだったので、実装を始める前にどのようなアプローチで実装するのかを説明するドキュメント(design doc)を書く必要があったのですが、 これも私の担当だったので、メンターの方やチームの方と議論をしながら書きました。 design docが完成して実装の方針が立つまでにも色々な紆余曲折があり、それらは以下のスライドで説明されているので、興味がある方は読んでみてください。

docs.google.com

(※最終プレゼンテーションで使ったスライドのうち、公開できる部分のみを残したものです)

9週間でやったこと

プロジェクトの進捗やその他どのようにして過ごしたかを、時系列で振り返っていこうと思います。半ば自己満足のために書いているだけのいわば日記みたいな内容になっているので興味のない人は読み飛ばしてくださって構いません、、、

第1週(7/30~)

初日にオリエンテーションがありましたが、昨年と同じだったので早々に切り上げて自分の席に行き、環境構築をしていました。 初日にはChromiumをビルドして動かすところまでできていた気がします。去年のチームではGoogle Maps iOSを最初にビルドするまでに2日くらいかかっていた気がするのでChromiumはビルドが簡単で良いなと思いました。

f:id:momohatt:20181206154822j:plain:w500

机の様子はこんな感じでした。 確か16コアのデスクトップマシンに24inchくらいのモニター2枚をいただきました。キーボードは私物です。 Chromiumは大きいのでそれなりにコアを積んだマシンでないとビルドが重すぎて人権がないようです。 gopherくんのぬいぐるみはメンターさんのものでしたが、インターン期間中はずっと私の机の上にいてくれました。

最初の週だったので、まずはChromiumのワークフローに慣れようということで、小さなバグを与えてもらってそれに取り組んでいました。

Googleはしょっちゅう何かしらの社内イベントをやっているのですが、この週も早速金曜日に年一度のChromeチームの遠足的なものがあり、一日がかりで飯能に行きました。 遠かったです。 ビールの製造所が併設しているレストランに行って、クラフトビールの製造過程の見学をしたり中東料理をいただいたりしました。 お酒が苦手なので美味しいビールを美味しく味わうことはできませんでしたが、お料理は堪能できました。

submitしたパッチ

第2週(8/6~)

メインのプロジェクトのためにコードを読んでいました。Service Workerの実装は膨大かつ複雑で理解に時間がかかったので多分この週はこれしかしていないです。

学校の某実験の最終課題(オセロ)の締め切りもあり、少し忙しかったです(いうほど真面目に取り組みませんでしたが)。

submitしたパッチ

なし

第3週(8/13~)

先週コードを読んでいて気づいたバグがあったので、それを直していました。 Chromium本体のコードの修正はたった1行でしたが、テストを追加することになり、それに時間がかかっていました。 元々のコードが結構わかりにくかったので、それを修正するためにたくさんコメントを入れたり、テストの形式を変えたりなどしていました。 そのおかげでパッチを出すのに1週間くらいかかってしまい、readabilityは難しいなぁと思いました。

メインのプロジェクトのdesign doc(新しい機能の詳細な実装計画)も多分この頃に書き始めました。

submitしたパッチ

なし(この週に取り組んでいたものは翌週出しました)

第4週(8/20~)

design docを書いて、チームの他の人に見てもらっていました。 当初の実装方針では、例外的な場合においてW3Cの定めているService Workerのspecificationに違反してしまう可能性があることが指摘され、 それを適切に対処するかどうかでチーム内で意見が分かれたのですが、最終的にはspecificationに違反しないが実装がより複雑になる方針に変えることになりました。

実装も多分この頃から始めていました。

あとは、火曜日にService Workerチームの人たちと西麻布(六本木から近い)にある前衛的なスペイン料理のレストランに連れて行っていただきました。 Googleではインターンが来たとき、チーム全体で外食などをしてWelcome Lunchをする風習がある(費用も会社から負担される)のですが、 これも私と、先週からチームに来ていたもう1人のインターンの方のWelcome Lunchとしての企画でした。 ランチなのにwelcome drinkが出てくるようなとても洗練されたレストランでした。美味しかったです。

eneko.tokyo

submitしたパッチ

第5週(8/27~)

この週の月曜日に、Googleのエンジニア全体で江ノ島にバーベキューに行く一日がかりのイベントがありました。 去年も参加して、折角海の近くに来たのに暑すぎて砂浜には出ずひたすら海の家で過ごすということをしていましたが、今年も同様でした。

プロジェクトの方は実装がある程度進んで、部分的に動くものもできてきたので、ようやくパッチをレビューに出すことを考えるようになりました。 このときすでに手元での変更が600行くらいになっており、1つのパッチにまとめるには大きすぎたので、細分化を試みようとしつつも結構苦戦していました。

思うにChromiumのような大きなプロジェクトでは、単純なことをするのにも様々な箇所への変更を加えなければならないので、 実装をしていて普段の感覚でキリが良いと思えるポイントまで中々到達できず、あとでパッチを分けるという事態になってしまったのだと思います。 そういった箇所で普段の開発との感覚のずれみたいなのがあり、少し難しいなと思いました。

前週からマウンテンビューのオフィスから東京にやってきている社員さんがいて、よくlld(リンカ)などについてのお喋りをしたりしていました。

submitしたパッチ

第6週(9/3~), 第7週(9/10~)

細分化して綺麗にしたパッチをようやくレビューに出し、いくつかmergeさせるなどしていました。 インターン期間の終わりも近づいてきたのに残された実装が山のようにあったので結構焦り始めていました。

9/13には、Chromeが今年で10周年を迎えることを記念して、Chromeに初期から関わっているエンジニアのお話を聞いたりご飯を食べたりする、少し大きめのイベントがありました。 正直仕事のほうをやっていきたいという気持ちでしたが...

submitしたパッチ

第8週(9/17~)

前週の終わりに作り始めたプロジェクトの中で最も重要で大きなパッチ(crrev.com/c/1224610)を少しずつ見てもらいながら、ユニットテストを書き始めました。 新しい機能に対するテストなのでテスト用のクラスを一から書こうとしていたわけでしたが、自分の実装のデザインがユニットテストがしにくいデザインになっていたことに書き始めてから 気づくようになりました。テストがしにくいデザインというのはつまり大概の場合はつまり使い方が直感的でないクラスやメソッドを作っているということな気がするので、 テストを書くとこういう良いことがあるんだなぁという気づきを得たような気がします(ほんまか)。

ただ、今回のプロジェクトではたまたま「テストが書きやすくなるデザイン」と「意味的にわかりやすいデザイン」が食い違うような箇所があって、 どちらを取るべきなのかかなり迷ったようなこともあったので、一概には言えない難しさも感じました。

f:id:momohatt:20181206154952j:plain:w500

水曜日にはChromeのVersion 69のリリースを祝ってケーキを食べるイベントがあり、Chromeの非公式キャラクターであるどーもくんのケーキを食べました。 かわいいですね。

金曜日には学科に新しく入ってくる2年生の歓迎会があり、次の週には学校が始まる予定だったので、もう夏休みも終わるんだなぁと一人でしみじみしていました。

submitしたパッチ

なし

第9週(9/24~)

インターンもついに最終週だったので、木曜日にある最終プレゼンに向けてのスライド作りを隙間時間にしつつも、先週から続けているパッチの修正に時間を割いていました。 プロジェクトがキリの良いところで終わるか終わらないかの瀬戸際だったのでかなり焦っていて、本来ならプレゼンの準備にもっと専念していても良い時期でしたが、 ギリギリまでパッチのsubmitのために時間を使おうと粘っていました。

学校が火曜日から始まりましたが、この週だけは授業を欠席してインターンに行っていました(最初からその予定でした)。 Chrome内の隣のチームの同じ大学のSTEPインターンの方々は先週にインターンを終えていなくなっていたので、少し寂しい感じがしました。

木曜日にインターンの最終成果発表があり、Chromeチームの方々ほぼ全員に集まっていただいた前で30分間の英語でのプレゼンを行いました。 発表の数分前までスライドを直していましたし、喋る練習をする時間が直前の2時間くらいしか取れなかったりしたので、かなりバタバタしたプレゼンでしたが、 複雑なプロジェクトの内容を長々と説明していたらいつの間にか時間をほぼ使い切っていたという感じで終わりました。

submitしたパッチ

おわりに

気の向くままに書いていたら中途半端な日記のようになってしまいました。人に見せる内容でもないのでAdvent Calendarとかには貼らずに静かに公開したいと思います。

インターン期間中お世話になったみなさま、ありがとうございました。