銀色うつ時間

思い出すたび何か胸につっかえてるだけ

日本経済新聞社を退職しました

f:id:sisidovski:20181209190053j:plain

いわゆる退職エントリ。興味のない人は閉じるボタンを。

11月末で日本経済新聞社を退職した。2年8ヶ月という短い期間だったが、素晴らしい経験をさせてもらった。

やっていたこと

日経に入社して、日経電子版のwebを新しくモダンなアーキテクチャで作り直すプロジェクトの立ち上げから参画した。これは現在r.nikkei.comというドメインから配信されている。

r.nikkei.com

結局退職までこのプロジェクトがメインの仕事になったわけだが、最後まで全く飽きることはなかった。技術的な面で飽きずに働けるということはエンジニアにとって簡単なようでいて難しいことで、それができたのは最初のアーキテクチャの設計が優れていたこと、特定のフレームワークやライブラリに過度にロックインさせないポリシー、新しい取り組みにどんどん挑戦していけるカルチャーや環境(これは単純に人手不足という話もあるかもしれない)があってこその感覚だと思う。

自分はいわばフルスタックで働いており、

  • フロントエンド
  • CDN(Fastly)
  • サーバサイド(Node.js)
  • インフラ(AWS)・DevOps

加えて、一時はPM業なども行っていた。フロントエンドでは、今でこそ当たり前になったレスポンシブデザイン、コンポーネント志向のデザインシステム、Service Workerなどを使ったPWAの実装などを実践で経験することができた。フロントエンド方面のみ取り上げられることも多いが、ハイパフォーマンスなwebアプリケーションを開発するにあたって最も大事なのはキャッシュ戦略とそれを実現する設計であり、このプロダクトを通じてそれが学べたのは大きな財産になった。単なるCDNの役割を超えたFastlyを使ったedge computingをそれなりに大規模なサービスで利用できる機会は貴重だし、利用しているプロダクトは日本を見回しても数少ないと思う。FastlyとElastic Beanstalkを使ったマイクロサービスの設計も、スケールしつつある組織とプロダクトにとってよかったように思う。フロントエンドも画面ごとにリポジトリレベルで別管理されており、分割が容易だ。前職ではCDNAWSも使った経験がなかったが、これを経験豊富な同僚のもと学ぶことができたことは自分にとって僥倖としか言えない。

@triblondon, @cssradar, @boblet, @sugimoto1981 といったとんでもなく優秀なメンバーと仕事ができたことは自分にとって糧になった。新しく入った若いメンバーたちも優秀で、下の世代から学ぶことも多かった。

リリース後には幸いにして多くの反響をもらい、PRという意味でもビジネス的にもポジティブな結果を残せた。特にサイトの速度という点に着目されたのは、エンジニア冥利に尽きるというものだ。これは自分の力というわけではなくて、優秀な同僚たちのお陰であり、更には日本経済新聞という大きな看板を背負わせていただいたからに他ならない。

サービスの大小に貴賤なしとはいえそれなりに大規模なトラフィックを有するサービスは外への影響力も大きく、自分の仕事が社会に与えるインパクトをダイレクトに感じられた。今年のGoogle I/Oやその他の海外カンファレンスで事例として紹介されたことはエンジニアとして大きな自信に繋がったし、それ以上に自分たちのサービスから配信されるニュースが日本に大きな影響を与えたり、市場を揺るがしたりすることに大きなやりがいを覚えた。

逆に言えば、システム障害で電子版が止まるということは社会に大きな影響を与えることであり、それなりの責任感も求められる。

f:id:sisidovski:20180509100434j:plain
同僚に撮ってもらったGoogle I/Oでの一コマ。日経について@addyosmaniが話している。

ニュースメディアは社会的意義のある仕事だ。

英語について

英語に関しては、日経に転職してから初めてちゃんと勉強した。海外グループ会社から来た人間や外国籍のメンバーがいたこともあり、チームでは英語でドキュメントを書いたりMTGをしたりしていた。大学時代は語学の単位を落として留年するほどには外国語が苦手だった自分もそのお陰で、多少は上達したように思う。もちろん手放しで上達したわけではなくそれなりに努力もしたが、入社して2年後には海外の現地企業でなんとか働ける程度にはなった(もちろんまだまだ課題はあるし、全然上手くはない)。日経はイギリスの経済紙フィナンシャル・タイムズをグループ会社に持っており、彼らと一緒に仕事ができたのは他では得られない経験だった。あまり知られていないが、日経では意外にも英語を使うシーンが多い。

そういったパスが整備されていて誰でも機会が得られるわけではないが、自分から主体的に働きかけていけば機会は得られるし、会社はそのためのサポートをしてくれる。海外カンファレンス参加の機会は当たり前として、何なら海外赴任だってチャンスはある。語学に関する投資もしてくれる。

やらざるを得ない環境に身を置くこと、時節訪れる機会に手を挙げることが人の人生においていかに重要かを身を持って学んだ。

日経、あるいは非web系の大企業で働くということ

日経電子版というビジネスは、おそらく大抵の人の想像を超えるほど多くの人間が関わっている。エンジニア、デザイナ、プロダクトマネージャ、マーケター、データサイエンティスト、そして記者や編集者などがステークホルダとなる。想像の通り組織は強烈な縦割り構造が存在し、年功序列、終身雇用といった古き良き日本社会の副作用がそこかしこに内在している。そんな組織の中で仕事を進めるのはかなりのハードワークだし、上を説得するための資料づくりみたいな仕事に躍起になっている人もままいたりして(現段階でこれはある種必要なことだ)、気がつけば社内のことばかりを考えてエンドユーザを蔑ろにしがちだったりする。おそらく多くの日系大企業が同じ病理に陥っているのだろう。しかし、だからこそ。こういった組織を変えていく経験はweb業界では中々できないことだし、その先のキャリアパスも明るいように思う。本当に、ここを変えて行けなければ日本は駄目になってしまう。そのくらい大企業が日本経済に占める比率は高いのだ。前職での似たようなバックグラウンドの人たちに囲まれた会社員生活に時節望郷の念を抱くこともあったが、多種多様な職種の人と仕事をするのもある種の楽しさを覚えるものだ。

その一方で、電子版の開発組織はかなり若い組織でもある。20~30代の若いエンジニアがバリバリに活躍しているし、上層部は若い世代の感覚を理解しており、使うツールなども殆どの場面で制限がない。現状の組織では人数が少ないこともあり、個々人が活躍でき、プレゼンスを発揮しやすい。対外発表にも積極的で、過半数のエンジニアが何かしら外に向けて発信した経験があるのではないか。自分もいくつかのカンファレンスで発表したり、技術書典で記事を執筆したりした。エンジニア向けの新人研修も行うようになったし、社内isuconなども今後やっていく予定だ。

電子版をはじめとする日経のデジタル事業は、いわゆる大企業的な面と非常に若い組織という面を併せ持っている。大企業と聞いて想像するような、色々な環境が用意されている状態を期待する人には向いていない。エンジニア組織もまだまだ未熟だ。自分の理想とする環境や組織を自分の力で作り出していかなければいけない。しかしながらそれができれば、これ以上ない活躍のフィールドを与えてくれる。そういった組織を変えるべく日夜働いている同僚たちもおり、彼らを心から尊敬しているし、遅かれ早かれ古い体質から脱却していくことを確信している。

f:id:sisidovski:20181209231538j:plain
千鳥ヶ淵が近いので花見をしながらランチなどもした

このような変革は度々起きているはず。創業140年を超える会社なのだ。

業界の話

ニュースメディアという産業は今危機的な状況を迎えている。戦後より続く紙の新聞配達という強固なビジネスモデルがいよいよ終わりを迎えつつあり、デジタルで生き残れるモデルを確立できないとその先にあるのは衰退だ。本旨から逸れるのであまりこれに反応してほしくはないが、新聞社や各種メディアが力をなくしたとき、政治の腐敗や企業の粉飾など社会を揺るがす不祥事を、一体誰が監視するのだろう。地方のしがない出来事を誰が世に出すのだろう。美術展やスポーツのパトロンは誰が?この業界は単に新聞を届けるだけの仕事ではない。

更に言うと、人件費を下げればいい話だとも思わない。儲からない仕事に優秀な人間は入ってこない。若者が憧れる仕事にジャーナリストという職種があってほしいし、彼らには高給取りであってほしい。内製によるエンジニアリングは、それに貢献する。

驚くべきことに、日経が新聞製作システムをコンピュータ化したのは1970年代だ。また、データベースビジネスもこの頃から始めている(こういった偉業を成し遂げた圓城寺次郎という人間については後日記事にする)。90年代になっても電子版の前身であるNIKKEI NETの開設や広いIPアドレス帯の取得など、日経には実は古くから連綿と続くエンジニアリングの歴史がある。そして現在も、間違いなく業界のトップランナーとしてデジタルビジネスを作っているが、まだまだ紙のビジネスの代替たり得ない。

そういった大きな業界のうねりに身を置くことは、非常に意義を感じられる楽しくやりがいのある仕事だった。

なぜ辞めるのか

いかに新聞業界が斜陽産業と言われ年功序列で若い世代が年長者よりも給料が低いとはいえ、業界トップレベルの企業ということで待遇も申し分なかった。自分の年収はここでは明かせないが、詳しく知りたい人は直接聞いてもらうなりググったりしてもらえば分かるかと思う。社内には見晴らしのいい社食やトレーニングルーム、何なら診療所まである。長い目で見たときにこの先どうなるかは分からないが、それはどの会社でも同じだと思う。

辞めるのにはいくつかの理由がある。

1つは、プロジェクトの志半ばでチームメンバが大量に離脱したことだ。様々な事情があるにせよ、組織の論理が働いたことは事実だ。その人事異動に透明性と納得感があれば、あるいは会社を離れずに済んだかもしれない。組織に所属する上でどうしても避けられないことがあるのは百も承知しているしそういった場面でも受け入れて仕事をしてきたつもりだが、僕らは会社の犬じゃない。納得は全てに優先する。

もう1つは、働いているうちにエンジニアリングだけで解決するのが難しい事柄が増えてきたことだ。これはどんな組織でも同じだと思うが、最終的に問題は技術そのものでなく、組織デザインや意思決定に依る部分になってくる。ここにエンジニア個人としてのキャリアの限界を感じていた。もちろんエンジニアとして働いていけるのに越したことはないが、自分が思うユーザに本当に提供したいもの、ビジネスのあり方を考えていくうちに、フォーカスすべきは目先のエンジニアリングではないかもしれないと思うに至った。

強調しておくが、これは会社の問題ではなく、僕個人の人生観の問題だ。

しがないCSの学位もない凡庸な技術者だった自分を拾ってくれた会社には感謝しているし、道半ばで離れることを申し訳なく思う気持ちが強い。一緒に働いた同僚たちのことは大好きだし、これからも何かしらの形で関わっていきたい。

このエントリはある程度フェアな視点で書いたつもりなので、これを見て少しでも日経をはじめとする日本のメディアビジネスに興味を持ってくれると嬉しく思う。日経では中途・新卒問わずエンジニア・プロダクトマネージャなどデジタルビジネスの人材を募集しているので、興味の湧いた人、進路を考えている人は選択肢のひとつに入れてほしい。僕に直接DMを送ってもらっても構わない。

HACK The Nikkei

Shunya Shishido (@sisidovski) | Twitter

次について

Mobile Technical Solutions Consultantとして、12月からGoogle Japanで働いている。エンジニアという肩書きはなくなったが、引き続きwebに関わる仕事をやっていくし、コードもたくさん書いていく所存。PWAやAMPはもちろんのこと、他にも面白いweb技術をたくさん世に出していきたい。

というわけでみなさん、これからもよろしくお願いします。

f:id:sisidovski:20181209190453j:plain

Happy Hacking Keyboard 無刻印モデル 清掃時のキー配置

Happy Hacking Keyboardを掃除しようと思ったとき、キートップを外した後無刻印モデルはキーがどこに配置されていたのか分からなくなるという問題がある。掃除そのものに関してはたくさんの記事が他に存在しているのでそちらに任せるとして、キー配置についてだけ記述しておく。

実は無刻印モデルでもキートップの裏に番号が記載されていて、それを基にどこにキーが配置されていたかが分かるのだが、全てメモを取っていくのは骨が折れる。今回骨を折ってキー配置をメモした図を作成したので、無刻印のHHKBを利用している人は参考にするとよい。

f:id:sisidovski:20171125124315p:plain

上記のマップは、HHKB US配列 無刻印、製造年は2011-06の製品を基に作成した。HHKB、無刻印キーボードでも裏に謎の番号ではなくてどのキーに対応しているか印字しておいてほしいですね。

Mockeryでモジュールをモックに差し替える

Mockeryを遣ってNode.jsのテストを書いている。requireしたモジュールをそのままモックに差し替えるとき、差し替えたいモジュールに渡されている文字列と全く同じものを渡す必要がある。差し替えるモジュールが標準ライブラリやnpm経由で取得してきたものであった場合はそのまま書けばよいが、自前のモジュールを相対パスなんかで呼び出しているときに気をつける。例えばこんな場面。

/lib/module.js

module.exports = {
  ouputMessage: () => {
    console.log('Hello from mod');
  }
};

/lib/foo.js

const module = require('./module.js');
module.outputMessage(); // Hello from mod

/lib/bar.js

const foo = require('./foo.js'); // Hello from mod

/test/index.js

const mockery = require('mockery');

describe('test', () => {

  before(() => {
    const mock = {
      outputMessage: () => {
        console.log('Hello from mock');
      }
    };
    mockery.registerMock('./module.js', mock);  // ここ
    mockery.enable({
      useCleanCache: true,
      warnOnUnregistered: false
    });
  });

  after(() => {
    mockery.deregisterMock('mod1');
    mockery.disable();
  });

  it('should say hello from mock', () => {
    const bar = require('../lib/bar.js') // Hello from mock
  });
});

テストコードからの相対パスではなく、モック化したいモジュールのパスをそのまま記述しないと動作しない。割と忘れて気づかないパターンが多い。

github.com

The arguments to registerMock are as follows:

module, the name or path of the module for which a mock is being registered. This must exactly match the argument to require; there is no “clever” matching.

はい😇

fetch APIのredirect関連メモ

HTTPS環境下で特定のURLからデータを取得するときに、リソースがHTTPで配信されていた場合、当たり前だけどMixed Content警告が発生する。

fetch('http://www.yahoo.co.jp/');
// こんなのが出力される
// Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure resource 'http://www.yahoo.co.jp/'. This request has been blocked; the content must be served over HTTPS.
// Fetch API cannot load http://www.yahoo.co.jp/. Failed to start loading.(anonymous function) 
// Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: TypeError: Failed to fetch
// Uncaught (in promise) TypeError: Failed to fetch(…)

基本的にはHTTPS以外のものは呼ばない、CSPなどを利用してそもそもHTTPのリソースがブロックしてしまうといった解決策が存在するが、HTTPSのURLがHTTPのURLにリダイレクトする場合はどうすればよいのだろうか。

$ curl -I https://example.com
HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
Location: http://other-domain.com
Content-Length: 261

HTTPSからHTTPにリダイレクトなんてないだろ、と思ったりもするが、後方互換性を重視した開発の現場では稀にそういうことがある。IDの若い特定のユーザには古いページを表示する、古いページはHTTPSに対応していないといった状況などがそれにあたる。

fetch APIはredirectオプションによってリダイレクト時の振る舞いを制御できる。

developer.mozilla.org

redirect: The redirect mode to use: follow (automatically follow redirects), error (abort with an error if a redirect occurs), or manual (handle redirects manually). In Chrome the default was follow before Chrome 47 and manual starting with Chrome 47.

Fetch Standard

A request has an associated redirect mode, which is "follow", "error", or "manual". Unless stated otherwise, it is "follow".

w3cのspecにも未設定の場合はfollowが選択されるとあるし、実際の振る舞いもリダイレクトしてリソースを取得している。オプションの振る舞いをまとめると、2016年10月現在では以下のようになる。

  • follow
    • デフォルト. リダイレクト先まで追従してリソースの取得を行う
  • error
    • リダイレクトが発生した場合にネットワークエラーを投げる
  • manual
    • 手動でリダイレクトを制御する。このときResponseオブジェクトは opaqueredirect となる。opaqueredirectは、レスポンス内容が隠蔽された状態のオブジェクトで、実際のレスポンスはinternal responseにアクセスする必要がある(この辺りは検証不十分)

つまり、今回のようにHTTPS→HTTPへのリダイレクトが行われ得るリソースに関しては、redirectオプションにmanualを渡してあげれば一応mixed content警告は回避できそう。

fetch('https://redirect-to-http-resource.com', {redirect: 'manual'})
  .then(res => {
    // do something
  });

8月の現状

今日のこと。

七尾旅人の「兵士A」が各所で評判がよいので観たいと思うも、最終回17時半スタートという厳し過ぎる現実に突きつけられる。「兵士A」という名前からも、性質からもかの作品「911FANTISIA」を想起させるし、いずれお目にかかりたい。

代わりにちょうど公開初日だった「君の名は。」を観た。

f:id:sisidovski:20160827040419j:plain

まだ会ったことのない君を、探している

新海誠について、割と苦手意識があったのであまり期待していなかったのだけれど、大きく裏切られることになった。

新海誠作品に対して抱く名状し難い感情や身構えてしまうような感覚は、割と多くの人が共有できるところだと思う。肥大化した自意識と自己完結型の展開、それらに辟易としつつもある種の愛しさを湛えるようで、例えるなら村上春樹の影響をもろに受けた高校生の痛々しさ、そういう類の嫌悪、懐かしさであるように思う。今作はそういった「全力っぽさ」を上手に使えているようで、安心して観ることができた。今までの作品と比較すると、物語の骨子が強固なものになっていて、安定した土台があることが大きいようで、その土台があるからこそある種の白々しさを楽しめた、ということになるのだろうか。途中で挟まれるRADWIMPSの歌すら心地よく、新海誠的な思春期の男子が抱く陰湿な演出ではなく、ある種の爽やかさが駆け抜けるわけである。

新海作品の根底には常に「距離」が描かれていて、そのメタファ・舞台装置として空・線が登場する。今作も同様だが過去作品と比較してみると明らかに異質なのは、長編アニメーションとして成立するだけの奥行きを持ったストーリーが存在する点だ。そこから派生していくコメディ・SFに引き込まれ、夢中になっている内に、気がつけば「距離」の袋小路に入ってしまった。今までの作品は最初から「距離」を露骨に描きすぎいてどこか観ながら冷めてしまう自分がいたが、今作はよい意味で感情移入できて、最後は胸が締め付けられる思いだった。よい意味で新海誠らしからぬ普遍性あるストーリー・演出から来る面白さと、新海誠らしい言葉選び・距離感の描写・美しい映像が混じりあって、傑作だったように思う。セルフオマージュが散見しているところもよくて、秒速5センチメートルの、言の葉の庭の。ケモナーなってしまった細田作品と比較しても遜色がない程に面白かったのではと、思う。

youtu.be

夏の気持ち

この夏、蔵前や南千住で飲んだくれたり、隅田川をぼんやり眺めたり、シン・ゴジラを何度も観たり、サマソニでCreepを聴いたりと、割と楽しく過ごしている、たくさんサウナに行ったし、瞑想や筋トレもしてるし、夏の終わりにまた素晴らしい映画に出会えたし、やっぱ夏はエモくて最高っぽい。

観終わったあと、岐阜に住む祖父母に会いたいと思ったし、帰りに観た東京のビル群は、美しかった。何を書いてもネタバレになりそうだし、早く観た人と話したいし、早くもう一度観たい。

Progressive Web Appsについて

ドワンゴ Advent Calendar 2015 4日目の記事です。最近ちらほら耳にする、Progressive Web Appsというものについて。

Progressive Web Appsとは

Progressive Web Appsは、モバイルWebを開発する上での1つの標榜である、という認識でよさそう。Alex Russell氏がブログで提唱したのが始まりで、先日行われたChrome Dev Summitのkeynoteでも取り上げられている。

medium.com

www.youtube.com

Progressive Web Apps自体は特に何か新しい技術を指すわけではなく、既存の技術の組み合わせに対して名前をつけただけのいわばマーケティングワードみたいなもので、つまるところ「◯◯と☓☓を組み合わせて作られた、よりアプリっぽい快適な動作をするWebアプリ」のことをProgressive Web Appsと呼びましょう、これからのWebはそうやって作っていきましょう、という宣言のようなもの。上記エントリでは次のように表現されている。

they’re just websites that took all the right vitamins.

こういった考え方が出てきた背景には、モバイルでの開発を取り巻くいくつかの課題が存在している。

とりあえずアプリ作ればいいと思ってませんか?

ネイティブアプリは確かに使いやすいしユーザとのエンゲージメントも高いけど、インストールされなくては意味が無い。ユーザが1ヶ月で利用する平均的なアプリの数は25個程度で、他のアプリとその枠を巡ってしのぎを削らなくちゃいけない。あなたのアプリは果たして、ストアに存在する膨大なアプリの中から検索され、インストールされ、実際に起動される、その25個の中に入れるだろうか。また、iOSAndroid、その他プラットフォームも含めてそれぞれで開発しなくてはならないし、保守コストに関しても馬鹿にならない。

ネイティブアプリの方が便利じゃないですか?

問題点として、モバイルにおけるWebアプリが根本的にイケてないということが挙げられる。ネイティブアプリよりレスポンスが遅い・タッチした後のフィードバックが作りこまれていない、オフラインで利用できない、プッシュ通知がない、ブラウザはタブが使いにくい等、考えればきりがない。

Progressive Web Appsが解決するモバイルWeb

上記問題に対して、TitaniumやXamarin、PhoneGapなど複数プラットフォームで利用できるハイブリッドアプリの開発が考えられるが、それはプラットフォームごとに開発するコストを減らすのみで、デプロイが楽になるわけでもないし、ストアからインストールされる可能性を上げてくれるわけではない。もっとも、開発コストを減らしてスピーディーに開発ができているかは、実際にハイブリッドアプリからネイティブに書きなおした事例なんかを考えてみると、有用な場面はかなり限定されると思う。

そこで提唱されているのが、HybridではなくProgressiveなWeb Apps。各プラットフォーム上で動くハイブリッドアプリを作るよりも、Web上で動作するアプリケーションを作っていく方が、よりネイティブアプリに近い動きを実現し、またユーザが利用する機会も増やせるのではないかという仮説に基づいている。WebをWebたらしめる性質なので当たり前だが、Web上にホストしてインストール不要で使えること、ハイパーリンクが利用可能であることなどを考えると、ユーザが利用する上でも、アプリケーションを提供する側としても扱いやすいし、プラットフォームごとに開発するコストやストアという仕組み上の制約(上記インストールまで到達する困難さ、課金システム、表現上の問題など)を考慮する必要がないことは大きなメリットとなり得るように思える。App Indexで最近アプリも載せられるようになってきたけど、検索インデックスを利用することもできる。

Webアプリが"appy"になるために必要なテクノロジー

下記のような機能を用いて、よりネイティブらしいアプリケーションを提供する。先に紹介したAlex氏のエントリや動画を見ると他にも列挙されているが、ここではより重要と思われるもののみ抜粋している。

  • Responsive Web Design
  • Service Worker(オフラインでの利用を想定)
  • ネイティブアプリのようなUIとインタラクション
  • Push Notification on the Open Web(Webアプリでのプッシュ通知)
  • App Install Banners(いわゆるブラウザが提供するホームに追加)

Responsive Web Design

割愛。

Service Worker

一応Service Workerについて軽く説明。Service Workerは通常のWebページのとは独立したサイクルでjsの実行コンテクストを提供する、イベント方式の機能。リクエストをフックしてキャッシュから応答したり、サーバのイベントを受けてそれをクライアントに通知するといった、クライアント用proxyとして利用できる。Service WorkerはHTTPSもしくはlocalhostでしか利用できないので、遊びで使うならgithub pagesなどを使うのが遊びではよさそう。デバッグしづらいので、作業はシークレットモードで行った方がよい。まあとにかく、Servie WorkerはProgressive Web Appsのコアとなる技術で、以下のような役割を果たす。

  • 静的ファイルをキャッシュすることで表示速度を改善する
  • サーバから取得したデータをキャッシュすることで、オフラインでの利用が可能になる
  • PUSH通知を受け取る
  • Service Workerを利用する = TLSでの通信が行われることなので、安全性を高められる

http://www.w3.org/TR/service-workers/

Web App Manifest

アプリに関連するメタデータを記述したjsonファイルをサーバに置くことで、それらの情報をブラウザやクローラが解釈してくれる。Service Workerと併用することで、プッシュ通知やホームに追加が実現できる。

https://w3c.github.io/manifest/

Offline Use

Service Workerを使ってfetchイベントをキャプチャし、キャッシュが存在すればそれを返し、なければネットワークから取ってくる、といった動きが実現できる。これによって、1度閲覧したページはキャッシュされ、オフライン状態でもキャッシュから表示ができるようになる。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;
        }

        var fetchRequest = event.request.clone();
        return fetch(fetchRequest)
          .then(function(response) {
            if (!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          });
      })
  );
});

Push Notification

ブラウザからのPUSH通知。facebookなんかはもう実装されていた気がする。今回時間の問題で実装できなかったが、概ね以下のような手続きのはず。

  • GCMを使うためにGoogle Developer Consoleの利用登録してプロジェクトIDを取得する
  • manifest.jsonに書く
  • Service Worker登録
  • pushManagerを利用してsubscribe
  • Service Worker側でpushイベントをlistenする 

App Install Banners

バナーの追加UIを表示させる

  • manifest.jsonの配置
  • ServiceWorkerの登録

manifest.jsonに以下の値を設定する。

{
  "name": "a progressive web app for cats",
  "short_name": "PWA for cats",
  "icons": [{
        "src": "images/touch/ms-touch-icon-144x144-precomposed.png",
        "sizes": "144x144",
        "type": "image/png"
      }],
  "start_url": "/?homescreen=1",
  "display": "standalone",
}
  • iconは144*144サイズのものが必要
  • start_urlはホームから起動したときのURL
  • displayは起動時の表示方式。ネイティブアプリのようにブラウザとは別のタスクとして扱うことが可能
  • Service WorkerはregisterしていればOK

その他

他にも、ページ内のコンテンツとUIの部分を分けてUIのみ先に描画する工夫であったり、ブロッキングを行う読み込みをasyncなどを用いて改善するといった対応が必要。以下のページにこのあたりの改善方法についても書かれている。

Instant Loading Web Apps With An Application Shell Architecture | Web Updates - Google Developers

今回のサンプルは以下

sisidovski/progressive-web-app-sample · GitHub

事例

Flipkart.lite

以前ちょっと話題になっていた、インドのECサイトFlipkartのWebアプリケーション。 通信インフラが整っていない途上国はこういった軽いページに対する需要が高そう。

stories.flipkart.com

Pokedex.org

Progressive Web Appsとして作ったポケモン情報まとめ。パフォーマンス向上のためのテクニックがすごい。60fpsで動作させるための工夫が細かく説明されている。

www.pocketjavascript.com

まとめ

業界全体が「お陰さまで合計◯◯ダウンロード!」とか煽ったりしているしネイティブから脱却するとかは全く思わないけど、ネイティブアプリの市場が成熟しつつある中で、ネイティブ風というだけでなくWebらしさを活かしていくことがモバイルWebの流れとしては出てきたのかなと感じた。Progressive Web Appsに限らずiOS9のSearch API関連(Universal LinksやSpotlightによる検索)も含めると、アプリとWebの境界が曖昧になるというトレンドが浮かび上がってくる。この辺りの話は、先日のmosaic.fmがとても面白かったので未聴の方は是非聴くとよいと思う。

#20 Browser | mozaic.fm

SICP(計算機プログラムの構造と解釈)1.1

はじめに

『計算機プログラムの構造と解釈』を読む。動機は以下。

  • いわゆる情報系の勉強をしていないので、基礎を身につけたい
  • Lispインタープリタを実装してみたい
  • ストリーム、遅延評価、末尾再帰最適化、構文・字句解析器など、なんとなくしか知らないものを理解したい
  • すごいエンジニアがみんな読んでる

年単位でかかるかもしれないが、それでも終わらない可能性・挫折する可能性があるので、練習問題は無理に全部やらない。

資料

mobiをkindleに送ってkindleから読んでいる。

html版

計算機プログラムの構造と解釈 第二版

訳にかなり癖があるので、意味を掴みにくい場合は、原著を確認するとよいかもしれない。また、コード集はこちらにしかないので、適宜見るとよい。

Welcome to the SICP Web Site

HTML版は、スタイルが適用されていないので、読みにくい。epub化を考えたけど、自分がやる前に既にepubおよびmobiで公開してくれている方がいたので、ありがたく使わせていただく。

github.com

環境

環境はOSXLisp/Scheme派生の言語Racketをバイナリからインストールして使っている。DrRacketというIDEが同梱されているので、そちらを利用するか、/Applications/Racket\ v6.2/binにPATHを通せば$ racketで対話型コンソールを起動できる。

racket-lang.org

Emacsの使用経験がないため、エディタは検討中。vimでやるか、これを期にemacsを覚えるか。。。

本文

1.1.7

平方根について。数学的な関数とコンピュータの記述について。 数学では平叙文的(何であるか)記述をするのに対して、コンピュータは命令文的(どうするか)記述をする。どう計算するかというアプローチに対して、通常は次々と近似をとるニュートン法を用いる。

> (define (sqrt-iter guess x)
    (if (good-enough? guess x)
        guess
        (sqrt-iter (improve guess x)
                   x)))
> (define (improve guess x)
    (average guess (/ x guess)))
> (define (average x y) (/ (+ x y) 2))
> (define (good-enough? guess x)
    (< (abs (- (square guess) x)) 0.001))
> (define (sqrt x)
    (sqrt-iter 1.0 x))

> (sqrt 2)
1.4142156862745097
> (sqrt 3)
1.7321428571428572
1.1.8

手続きを抽象化してブロック構造をとる方法、パラメータのスコープについて。外の入れ子にある束縛されたパラメータを内部で利用する(レキシカルスコープ)。

(define (sqrt x)
  (define (good-enough? guess)
    (< (abs (- (square guess) x)) 0.001))
  (define (improve guess)
    (average guess (/ x guess)))
  (define ( sqrt-iter guess)
    (if (good-enough? guess)
      guess
      (sqrt-iter (improve guess))))
  (sqrt-iter 1.0))

問題

EXSERCISE 1.3

三つの数を引数としてとり , 大きい二つの数の二乗の和を返す手続き

> (define (square a) (* a a))
EXERCISE 1.4

schemeの評価モデルは、演算子が合成式である組み合わせでも使える

> (define (a-plus-b a b)
  ((if (> b 0) + -) a b))
> (define (sum a b) (+ a b))
> (define (larger-square-sum a b c)
    (cond ((and (< a b) (< a c)) (sum (square b) (square c)))
          ((and (< b a) (< b c)) (sum (square a) (square c)))
          (else (sum (square a) (square b)))))
> (larger-square-sum 3 4 5)
41
EXERCISE 1.5

作用的順序の評価と正規順序の評価について

EXSERCISE 1.6

特殊形式として定義されているifを通常の手続きとして再実装して、1.1.7における平方根の手続きを行った場合、どうなるか。

> (define (new-if predicate then-clause else-clause)
    (cond (predicate then-clause)
          (else else-clause)))

> (define (sqrt-iter guess x)
    (new-if (good-enough? guess x)
            guess
            (sqrt-iter (improve guess x)
                       x)))

結果、無限ループする。これは、Schemeにおける通常の手続きが作用的順序で行われることに起因する。作用的順序での評価は、以下の通り。

  1. 組み合わせの部分式を評価する
  2. 最左部分式の値である手続き(演算子)を残りの部分式の値である引数に作用させる

つまり、一般的なSchemeの評価規則で定義されたnew-ifの場合だと、先に部分式が評価されるため、

(good-enough? guess x)

が真であったとしても

(sqrt-iter (improve guess x)
           x

が評価されるため、無限ループする

EXERCISE 1.7

曖昧。 平方根の手続きにおいて、入力が非常に小さい値もしくは大きい値にテストすっとが失敗する。大きい値の場合は、浮動小数点の比較における誤差によるところ。桁数の増大によって仮数が計算機に無視されるため、無限ループする。値が小さい場合、予測値が基準値より下回ると真を返すため、値にかなりのずれがあっても再帰が終了してしまう。改良版未着手。

EXERCISE 1.8

未着手。立方根の問題。ニュートン法の実装を改良する。