個人開発者とCovid-19 Radarプロジェクト

f:id:laiso:20200622042515j:plain
Endless road | During our roadtrip we turned off the highway… https://www.flickr.com/photos/98063470@N00/326044514

GitHubリポジトリ Covid19Radar に対して起ったことがかなり特殊な状況だったため、開発を追い掛けていた視線からレポートをします。

この記事の著者について

  • 代表作のない個人アプリ開発者(かなしい)
  • Covid-19 Radar Japan の人ではない
  • GAFAMやCode for Japan の人でもない

4/8 Covid-19 Radarを発見する

  • Covid-19 Radarとは、この時点ではシンガポールのTraceTogetherの日本版を目指した個人開発者 廣瀬一海さんのアプリのリポジトリ
  • 4月にContact Tracing技術について調べていたら偶然見つけた

  • 4/8時点で人が集っていて活発に開発されていた。Facebookで協力を呼び掛けていたらしい*1
  • sinsai.info みたいなムーブメントかな。ぐらいの認識だった

4/10 Apple/Google APIの開発が発表される

AppleとGoogle、 新型コロナウイルス感染症対策として、 濃厚接触の可能性を検出する技術で協力 - Apple (日本)

  • この時点では発表だけ。ベータリリースされたら使ってみるかぐらいの感想だった

4/15 コード・フォー・ジャパンもContact Tracingやってるらしい

5/5 APIを使えるアプリは各国1つのみ

  • ここで個人開発者オワタと思った

6/13 突然Covid-19 Radarが日本代表になっていたことを知る

  • コード・フォー・ジャパンのプロジェクトどうなったの? とかあれは個人プロジェクトでは? という疑問があった

6/15 開発が米マイクロソフトに移った??

  • 日経新聞の飛し記事が出る

  • 「「コード・フォー・ジャパン(CFJ)」にアプリ作成を任せる方針」が覆った理由が全然わからない
  • 5/11に投稿されたらしい 接触確認アプリ「まもりあいJapan」開発の経緯と今後について を読むと開発主体がCFJから厚生省に移ったことが要因らしい
  • Covid-19 Radarの作者はたしかに日本マイクロソフトの人だったから、会社を巻き込んだプロジェクトにしたのかなー、と思った
  • ちがうらしい

  • 今思うと厚生省で「大企業のほうが安心だ」と意思決定したおじいちゃんも「米マイクロソフトに発注したぞ!」と本当に認識していたのかもしれない

6/19 アプリがリリースされる

  • 開発者インタビューなどが世に出る
  • GitHubリポジトリだけを見てるとチームで開発してる感がなかったのでどんな開発体制なんだろうと疑問だったんだけど https://diamond.jp/articles/-/240905 でデザインやリレーションを担当している人が存在することを知る。あれ、開発は本当に1人でやっているの??

GitHubリポジトリを見始める

  • 日本初のApple/GoogleのContact Tracing APIを使ったアプリが出たぞということで技術的な関心駆動でコードを読み始める(Xamarinわからん過ぎるのでとりあえずチェックアウトだけして中身見てなかった)
  • この時点でいくつかのことが分かった

  • アプリサイドを廣瀬さん、サーバーサイドをdarkcrashさんが中心に開発している

  • runceelさんalbilagaさんなどがXamarin系の修正プルリクエストを送って手伝っている
  • norijiさんが前述の記事で出てくるデザイン作業で貢献している方
  • changeworldさんという人がもの凄い勢いでIssueやプルリクエストを捌いている

過去にAd-Hoc版ビルドのテスト版が配布されていたことを知る。デバイス登録上限に達するぐらい参加者がいたらしい(注: このアプリは5月末にリリースされたiOS 13.5にアップデートした端末か事前のiOSベータ版配布を適応しないと動作確認すらできない)

iPhone のテスターの方へ

この時の配布ツールApp Center用のライブラリが本番バイナリに入ってデータ送信していたらしく、mala(呼び捨て)が発見して取り除かれた。

Getting Started

  • アプリをビルドしてみて修正しようと思ったけど全然やり方がわからない
  • 開発環境構成ドキュメントはまだ存在しなく、なんとなくGitHubの外にチャットなどがあって、そこで開発が進んでいるのかな〜と思いはじめる
  • ふて寝する

6/20 なんとかなったぜ

  • いろんな方法を探っていたら結果うまくいってコードを編集して動作確認できるようになった
  • iOSのビルド設定が廣瀬さん固定になっていたので誰でもビルドできるような変更をプルリクエストしてみる

iOS Bundle SigningOption to 'Automatic' by laiso · Pull Request #471

  • ここでなんとApple/Google APIのデバッグは廣瀬さんの環境でのみしかできないことが判明(たぶん)。超SPOFじゃん・・と震える

(カルチャーフィットしなさそうなので厚生労働省には入らないことにしました)

結局Exposure Notification APIをモック化する仕組みが既に用意されていて(有能)それを使うことにした。

About messages when sharing the app on line

「LINEでシェアすると表示がおかしい」というIssueを見たので検証して直してみる

change appStoreUrl to valid URL by laiso · Pull Request #474

「俺の修正のおかげで直ったわ〜」と達成感を味わっていたらmala(呼び捨て)が出てきてLINEのサーバーで修正されてしまう(チートでは??)

Line breaks not works in a mail app

  • iOSのメールアプリでお問い合わせするとエスケープ済みの改行コードが入るらしい
  • メール改行テキストの修正のパッチを提案する(普通にググってうまくいくか試しただけ)

Line breaks not works in a mail app · Issue #479

この問題翻訳ファイルの元の問題を修正する必要あるんだけど、翻訳ツールがWindowsでしか動かないことを知る(Macの人居なかったのか・・!)

App crashes

  • App crashes · Issue #450 が再現しやすいので修正を試みる
  • ソースコードをいじって検証してみたけど前述のとうりSPOFで世界がヤバいので僕の環境ではデバッグすらできなく、直ったのかどうかわからんけどとりあえずプルリクエストにしてみる

fix a crash on StopExposureNotification() by laiso · Pull Request #484

  • このあたりで前述のApp crashes Issueのやりとりを見た人たちが「公共サービスをこのような認識で開発されているのはいかがなものか?」と騒いでるらしいことを知人に教えてもらう
  • 僕は彼等に対して距離を置くことで軽蔑やNOを示したいので、透明なおじさんは見えませんよみたいな感覚で見なかったことにした(不具合の事実の報告は重要なのでチェックだけする)
  • とはいえ当事者の人たちはダメージを受けていたらしく「SPOF〜〜」と見守っていた

Xamarinについて

  • 知人iOSエンジニアなどが「コントリビュートしたいけどな〜技術スタックがな〜Xamarinがな〜」と嘆いているのを見かける
  • たしかにXamarin+Mac+iOSは少数派ゆえ茨の道っぽい……

これまでの流れで思ったこと

  • 国の意向で日本代表の感染症対策アプリ開発が個人開発アプリに丸投げされたっぽい(そんなことあるの??)
  • 関係者の発言(公開された情報)によるとリリース日が事前に決まっていたらしい
  • Covid19 RadarはMPL v2.0に基くソフトウェアだけどプロジェクトは一般的に知られる「GitHubで開発が行なわれているOSSプロジェクト」ではなく個人開発者とその周辺のボランティアだった
  • GitHubリポジトリがとりあえずある(元が個人開発アプリなので)。というだけの状態
  • 今後このプロジェクトは厚生省及び開発委託先によって管理されるらしい
  • 「大企業のほうが安心だ」と意思決定する人がこのGitHubリポジトリを維持するものなのか
  • userUuid および secret の廃止 · Issue #514 のような提議をできる場はあるのか
  • 現在改善が進んでいるMPLのコードベースがどうなるのか
  • 人類がはじめて直面する事態に対してアプリケーションのプラットフォームが特例的に出した仕組みを使って、1つの国で1つのアプリを1人が作る*2異常な状況なので何が正解なのか誰も分からない……
  • 廣瀬さん関係者の方々お疲れさまでした

06/24/2020:この記事を投稿した後にわかったこととか

高木浩光さんの発言の反応について

  • App crashesのIssueは高木さんの投稿(RT?)から話題になっていたのだと後から知った
  • インストールしてセットアップ完了をさせる人数が重要で、それが生死に関わる問題のアプリケーションなので、感情や責任に対する議論に終始して「たいした問題じゃない」という言葉で弁護するのは賛同できないなと思った
  • 自分なら「クラッシュの回避はとても重要である」という意見になる。最新の発言しか読んでないけど高木さんも似たような指摘をしていた
  • そもそもフレームワークレベルで呼んだら捕捉できる例外を返して欲しい
  • あとあれは正常系だと思う(用語の話)
  • そして他に指摘されていた不具合はリリース当日の既にmasterブランチで修正されているものもあり(日付表示の問題など)、v1.0.0はその時点のスナップショットが世に出された印象だった(言いたいこととしてはフィードバックの修正が進んでいる)

HER-SYS(新型コロナウイルス感染者等情報把握・管理支援システム)

  • 接触確認アプリはHER-SYSとの連携を予定している(陽性者登録のシステムなど)
  • 接触確認アプリの開発はHER-SYS開発の契約の一部である
  • HER-SYSの発注先がパーソルプロセス&テクノロジーである

これが知りたかった情報にもっとも近かった(楠正憲さんは内閣官房情報化統括責任者補佐官でもある)

Covid-19 Radarへ

  • 接触確認アプリはパーソルプロセス&テクノロジー以下へ委託される
  • HER-SYSの基盤はMicrosoft Azureだった *3
  • Covid-19 Radarも4月時点でAzure+.NET+CosmosDBを利用してAPIが実装されていた
  • まもりあい JAPANのAPIサーバはAWS+Node.js+Firestore*4 で実装されていた
  • Serverless FrameworkのプロバイダをAWSからAzureに移行する。これは互換性の面では可能だがFirestoreは維持するとなるとマルチクラウド構成になってしまう
  • まもりあい JAPAN以外にもContact Tracingアプリ開発プロジェクトはあったし、スクラッチから開発するという選択肢もあった
  • パーソルプロセス&テクノロジーが「アプリ開発」の部分の再委託先を決めないといけない。それには時間がかかる
  • リスクを減らすには技術的な相性を加味する必要があるのではないか
  • (それ以外にも採用してるライセンスや開発担当者の身元など様々な要素があると思うけど……)
  • そして再委託先の決定+開発開始のリードタイムを埋めるべく作業を任されたのがCovid-19 Radar Japan?(ここまでは勝手な推測)
  • なので「無報酬で依頼を受けるべきでなかった」は一面的な見方だと思う

こういうこと?

  • 政府にとって第1イシューは「早期にリリースする」ことだった
  • 早期にリリースするために各自が出した答えが現在の状況と体制
  • 早期リリースは実現された

今後

  • 完全にメンバーが変わるのなら、バックエンドはまだしもアプリ部分はネイティブ実装(Swift/Kotlin)に置き換えられていく可能性は充分あるなと思った(そちらのが信頼のおけるサービスになるのなら)
  • GitHubリポジトリの運用も維持される可能性はありそう(他国事例や東京都サイトに続けて政府のIT施策アピールにもなる)。ただ委託先との契約上どうなっているのか次第なのかもしれない

お詫び

  • 文章を読み返してみたら厚労省やXamarinに対して貶めるような表現が多くバランスを欠いていたなと反省しました。謹んでお詫び申し上げます
  • 記事のコメントを読んでいたら著者について、実態よりCovid-19 Radar Japanのインサイダーっぽく受け取られているなと感じました。とくに開発に積極参加しているわけではなく、メンバーと交流があるわけではないのであくまでも部外者の視点であることを強調します

どんなプログラミング技術の学習に投資すべきか考える時にやってること

はじめに

とにかく次の10年を生き残りたい - 怠惰を求めて勤勉に行き着く を読んでいて、かー自分もここ10年ぐらい同じような内省をし続けていたではないかと深く共感したので、その過程で身に付けたやり方を書くことにしました。

目的

プログラミング技術を学習する目的を決めます。僕の場合は

  1. ソフトウェアエンジニアとしての市場価値を上げる(他のエンジニアから尊敬されたい)とか
  2. 素晴しいアプリケーションを作れるようになりたい(エンジニア以外からも尊敬されたい)

というものがあります。人によってはこれが「GAFAMNに入り渡米してメジャーデビュー」「OSSで一発当てる」「とにかくお金を稼いでアーリーリタイヤ」など様々かと思うので各自考えてください。

テーマ

目的が決ったら次は学習するテーマを決めます。僕の場合は「○○エンジニア」と呼称されている領域ごとに「この分野で先進的なネタは何だろう」というのをリサーチします。テックニュースやGitHubトレンド、口コミ、求人情報などから探します。

それを視覚化します(箇条書きでもなんでもいいです)、僕はMarkdownで下書きして、マインドマップにしました。

f:id:laiso:20200110000247p:plain

大テーマである「○○エンジニア」の部分は人によって違うはずです。

僕は素晴しいアプリケーションを作れるようになりたいので適切な技術を選定できるように分野を広めに保っているのだと想います。

たとえば「Netflixで英語学習するツール」を実現したい時、ブラウザ上で動くNetflixのサイトにデータと機能を組み込んで連携させるのが一番良い方法だな、と思ついたとします(実際にそういうツールが存在します)。

これにはウェブアプリーケションが動作する仕組みとブラウザ拡張についてのフロントエンド開発の知識が必要になってきます。

ところがiOSアプリの開発スキルだけあってもこのアイデアを実現できたりはしませんし、そもそも方法を知らないと思い付かないかもしれません。逆にネイティブアプリでしか実現できないことが手持ちの知識の中にないとそれによって作るものが制限されてしまうこともあるかと思います。

しかしなるべくなら枝は少ない方が好ましいと考えています。枝を減らすコツは「別の技術で同じものを作れる」状況をなくすことです。例としてはRubyでもPythonでもWebサーバーは作れますが、SwiftでないとiOSアプリは作れないので、増やすべきはSwiftです。

ここに出てくる各固有名詞の製品自体を作りたい人はもっとCSの基礎技術を拾うべきでしょうし、数学、デザインや語学、データサイエンス関連も含める人もいるかもしれません。

先進的なネタの基準

ソフトウェアエンジニアとしての市場価値を上げるという目的があるので、「先進的なネタ」というのは今現在の業界で起っていることで次の時間軸にあるものを選びます。

たとえばRuby on Railsを使ってWebサービスを作っている企業があり「今後はマイクロサービス化しGo言語に移行したい」という動きがあるとします。それを見据えた時に学習のネタとなるのはマイクロサービスだったりGo言語だったります。無事これらの技術を習得する頃に、世の中に「マイクロサービス化してGo言語に移行したプロジェクト」の総数が増えていれば、需要も上っていてエンジニアとしての市場価値も増すので投資のリターンを得てやったねというわけです。

テーマに優先順位をつける方法

大テーマである「○○エンジニア」の部分は数が多ぎ過ぎなので、どれを優先させるべきかというのを考えます。

ここでの基準は

  1. 普段の業務でやらない分野
  2. 業務でやる分野だが自分の担当ではない
  3. 普段の業務でやっていること

の順で優先付けします。

これは「適切な技術を選定できるように分野を広めに保つ」という考えからまんべんなくステ振りできる方法として考えました。

「それぞれの分野の理解が浅くなってしまうのでは?」という器用貧乏症候群については、こう考えます

  • 普段の業務では深い理解を求め
  • 学習フェースでは広い理解を探索する

こうすることで効率よくハードスキルのT字の延せるのだと考えています。

いかがでしたか?

そもそも私はどの技術的なトピックが将来発展するのかという予測は不可能だと思っています。現実の世界では常に変化し続ける環境の要因が複雑に絡みあっているからです。

なのでビジョンを持って、常に変化し続ける環境に適応する(あとは運)。というやり方がしょうにあっているのだとソフトウェアエンジニア人生で学びました。

みなさんも独自の方法論を教えてください

Cloud FunctionsをGoで書く。またはFirebaseのためのマイクロサービスアーキテクチャ

f:id:laiso:20191216155312j:plain

Firebase Advent Calendar 2019 の17日目です。16日目はKesin11さんの「Firebase Emulator Suiteをフル活用してTDDで開発しよう」でした。

はじめに

FirebaseプロジェクトでCloud Firestoreを利用する時は通常Node.jsによるCloud Functionsでトリガーとなる処理を記述します。その他には関連するAPIサーバー、WebアプリのフロントエンドのSSR、バックエンドの非同期処理など、多くの場面でCloud Functionsが活用されています。

この開発→デプロイサイクルをお手軽に行ってくれるのがfirebase-toolsというnpmモジュールです。JavaScriptでFunctionを実装し、firebase deployコマンドを実行するだけでFirebaseプロジェクト用のCloud Functionsが自動で登録されます。

解決したい問題

firebase-toolsは便利に使っていたのですが、私たちのプロジェクトでは日々いくつものCloud Functionsのモジュールが増えてゆく過程で以下の問題が発生しました。

  1. デプロイ対象となるソースコードの量が増え、デプロイ完了までの時間がかかるようになった(--onlyオプションを付けても遅い)
  2. SSRを含むフロントエンド向けビルド、APIサーバーとして機能するFunction、Firebase固有の処理、などの依存が単一の package.json で管理されていてモジュール更新の影響が大きかった
  3. 複数のFirebaseプロジェクトでプライベートリポジトリにある自作モジュールを共有したかった
  4. Cloud Tasksや複数Functionへの分散した水平スケールでは実現できない、リアルタイムの大量データ処理を垂直方向にスケーリングしたかった

そこで私たちはまずはじめにfirebase-toolsの仕組みやCloud Functionsがどのように動作しているのかを理解して、最終的にfirebase-toolsで構成していたFunctionのビルドを徐々にマイクロなモジュールに分割していくことにしました。

Cloud Functions for Firebaseの仕組み

Cloud Functions for Firebase*1

Cloud Functionsは - リソース - イベント - 関数名

という組合せでFunctionを管理しています。特定のリソースAに発生したイベントαにアップロードしてビルド済みのプログラムから指定の関数を実行します。

/**
 * [Google Cloud Firestore トリガー](https://cloud.google.com/functions/docs/calling/cloud-firestore?hl=ja)
 */

const Firestore = require('@google-cloud/firestore');

const firestore = new Firestore({
  projectId: process.env.GCP_PROJECT,
});

exports.makeUpperCaseOne = (data, context) => {
  const {resource} = context;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = data.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();
  console.log(`Replacing value: ${curValue} --> ${newValue}`);

  return affectedDoc.set({
    original: newValue,
  });
};

という関数を持つ index.js のみのコードを用意して

# package.json を生成する
npm init && npm i @google-cloud/firestore

gcloud functions deploy makeUpperCaseOne --runtime nodejs8 \
  --trigger-event providers/cloud.firestore/eventTypes/document.create \
  --trigger-resource "projects/$GCP_PROJECT_ID/databases/(default)/documents/messages/{docId}"

というコマンドでデプロイすると(gcloudコマンドは別途セットアップします)

projects/$GCP_PROJECT_ID/databases/(default)/documents/messages/{docId} のリソースに対して providers/cloud.firestore/eventTypes/document.create というイベントが発生した時に 関数 makeUppercaseOne() をトリガーする。

というCloud Functionsのリソースが登録されます*2

試しにコンソールからFirestoreのドキュメントを作成してみます

実行されドキュメントが更新されました。

同等の処理を行うFunctionをCloud Functions for Firebaseで実装してみることにします。

以下のようにfirebase-functionsモジュールを使ってFunctionを定義すると

const functions = require('firebase-functions');

exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
    .onCreate((snap, context) => {
      const original = snap.data().original;
      console.log('Uppercasing', context.params.documentId, original);
      const uppercase = original.toUpperCase();
      return snap.ref.set({uppercase}, {merge: true});
    });

firebase deploy --only functions の内部で先程のgcloudコマンド同様のリソースができるわけです。

gcloud functions deploy makeUpperCase --runtime nodejs8 \
  --trigger-event providers/cloud.firestore/eventTypes/document.create \
  --trigger-resource "projects/$GCP_PROJECT_ID/databases/(default)/documents/messages/{docId}"

元のFunction用のコードからソースコードをそのままに1つの関数だけを抽出して、フォルダ構成としてはこのような独立したnpmパッケージとして管理できるようになります

functions/makeUppercase/
├── index.js
├── node_modules/
├── package-lock.json
└── package.json

Goランタイムへの置き換え

Cloud Functionsへ直接デプロイすることができたので、次にFunctionをGo言語で実装してCPUメモリあたりの実行速度の高速化を図ります。ボトルネックがI/Oではない典型的なデータ処理のFunctionはこれだけでスループットが向上が期待できます。

var projectID = os.Getenv("GCLOUD_PROJECT")
var client *firestore.Client

func init() {
    conf := &firebase.Config{ProjectID: projectID}

    ctx := context.Background()

    app, err := firebase.NewApp(ctx, conf)
    if err != nil {
        log.Fatalf("firebase.NewApp: %v", err)
    }

    client, err = app.Firestore(ctx)
    if err != nil {
        log.Fatalf("app.Firestore: %v", err)
    }
}

更新した値をFirestoreに書き込み更新する場合、Firestore Clientの準備をします。

init() はランタイムにより指定されているインスタンス毎の初期化処理で、Node.js版でいうグローバル変数にセットアップ済みの値を入れておく方法を似ています。

type FirestoreEvent struct {
    OldValue   FirestoreValue `json:"oldValue"`
    Value      FirestoreValue `json:"value"`
    UpdateMask struct {
        FieldPaths []string `json:"fieldPaths"`
    } `json:"updateMask"`
}

type FirestoreValue struct {
    CreateTime time.Time `json:"createTime"`
    Fields     Message    `json:"fields"`
    Name       string    `json:"name"`
    UpdateTime time.Time `json:"updateTime"`
}

type Message struct {
    Original struct {
        StringValue string `json:"stringValue"`
    } `json:"original"`
}

func MakeUpperCaseGo(ctx context.Context, e FirestoreEvent) error {
    fullPath := strings.Split(e.Value.Name, "/documents/")[1]
    pathParts := strings.Split(fullPath, "/")
    collection := pathParts[0]
    doc := strings.Join(pathParts[1:], "/")

    curValue := e.Value.Fields.Original.StringValue
    log.Printf("Uppercasing: %q", curValue)

    newValue := strings.ToUpper(curValue)
    data := map[string]string{"original": newValue}
    _, err := client.Collection(collection).Doc(doc).Set(ctx, data)
    if err != nil {
        return fmt.Errorf("Set: %v", err)
    }

    return nil
}

Function本体の処理です。Go言語の型システムの仕様上Firestore内の値のパース処理を、Node.js版でいう firebase-functions にあたるユーティリティがないためドキュメントパスの解決などを自分で実装する必要があります。

デプロイします。

gcloud functions deploy MakeUpperCaseGo --runtime go111 \
  --trigger-event providers/cloud.firestore/eventTypes/document.write \
  --trigger-resource "projects/$GCP_PROJECT_ID/databases/(default)/documents/messages/{documentId}"
func TestFunc(t *testing.T) {
    var projectID = os.Getenv("GCLOUD_PROJECT")
    jsonStr := `{
      "original": {"stringValue": "hello"}
  }`
    var message Message
    var err error
    err = json.Unmarshal([]byte(jsonStr), &message)
    if err != nil {
        log.Fatal(err)
    }
 
    value := FirestoreValue{
        Name:   "projects/" + projectID + "/databases/(default)/documents/messages/1",
        Fields: message,
    }

    result := MakeUpperCaseGo(context.Background(), FirestoreEvent{Value: value})
    if result != "HELLO" {
        t.Error()
    }
}

動作確認をGoのユニットテストとして記述できます。

写真のリサイズFunctionをGoで実装してみる

Functionのイメージ*3

ユーザーがアプリケーションから写真を登録したらシステムで必要なサイズの画像を自動で生成するようなFunctionをGoに置き換えてみます。

  1. Document更新トリガで関数を実行
  2. パースしてきたURLから画像をダウンロードしてくる
  3. リサイズを実行
  4. 画像をCloud Storageに保存

という一連の流れです

conf := &firebase.Config{ProjectID: projectID}
opt := option.WithCredentialsJSON([]byte(os.Getenv("SERVICE_ACCOUNT_JSON")))
app, err := firebase.NewApp(ctx, conf, opt)

Firebase Admin SDKの初期化時にクレデンシャルを含むJSONファイルのパスを指定するのではなく、環境変数から読み込むようにします(confも同じ形式にできるのですが、秘密情報を含まないためコードで指定しています)。

gcloud functions deploy Resizing --set-env-vars SERVICE_ACCOUNT_JSON=$(cat secretkey.json)
// var fbStorage *storage.Client
fbStorage, err = app.Storage(ctx)
if err != nil {
    log.Fatalf("app.Firestore: %v", err)
}

Cloud Storageのクライアントも init() で初期化しておきます。

type User struct {
    ProfileImageUrl struct {
        StringValue string `json:"stringValue"`
    } `json:"profile_image_url"`
}

ドキュメントの構造体をこのように定義しました。profile_image_url というキーに画像がアップロードされたURLが保存されます。

func Resizing(ctx context.Context, e FirestoreEvent) error {
    url := e.Value.Fields.ProfileImageUrl.StringValue
    cli := http.Client{}
    resp, err := cli.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    src, _, err:= image.Decode(resp.Body)
    if err != nil {
        log.Fatal(err)
    }

    size := 320
    img := imaging.Resize(src, size, 0, imaging.Lanczos)
    encoded := &bytes.Buffer{}
    jpeg.Encode(encoded, &*img, nil)

    bucket, err := fbStorage.Bucket(bucketName)
    if err != nil {
        log.Fatal(err)
    }
    path := fmt.Sprintf("resized_images/%dx.jpg", size)
    obj:= bucket.Object(path)
    writer := obj.NewWriter(ctx)
    io.Copy(writer, encoded)
    defer writer.Close()

    return nil
}

Function本体です。imaging(https://github.com/disintegration/imaging )を使い 320x にリサイズしてアップロードします(別途Storageへの追加をトリガーにしてUserドキュメントにパスをセットします)

検証してみたところNode.js版での公式ドキュメントでの解説にあるようなImageMagickのconvertコマンドを外部で実行するような方法*4と比べて、ファイルに書き出しがない部分がうまく効いて大量のリサイズが一度の実行でできそうでした(リサイズ処理の品質に差がでるかもしれないので別途評価が必要です)。

デプロイ速度やFunction起動速度について

Cloud Functionsのデプロイはローカルにあるソースコードを対象として、依存モジュールが記述されている package.jsongo.mod から自動的にクラウド環境でビルドが走るようです。

firebase-tools を使ったデプロイを行っていた時は、functions/ にあるすべてのファイルが対象になり1つのFunctionのリソースへアップロードされていました(GCPコンソールからアップロード済みファイルが取得できるので確認できます)。

またFirebaseユーザーの間でFunctionの高速化テクニックとして環境変数から探索して、Node.jsの依存モジュールの動的読み込み制御する方法が知られています。*5

これらの方法と比べてアーキテクチャ的に改善する可能性はあるなと思いつつも、まだ安定性やアーキテクチャの評価中なので詳しくは比較できていない状態です。

ビルド+デプロイツールの改善

firebase-toolsを使わくなることで、複数のFunctionの依存を管理するための方法や開発やデプロイを楽にする方法を別途用意しなければいけません。

Functionを分割して複数の依存を管理するパッケージができたことで、ソースコードはmonorepoの状態になります。そのためlerna(https://lerna.js.org/ )やBazel(https://www.bazel.build/ )のようなツールが機能する環境になるかもしれません。

ただfirebase-toolsを使った環境は並行して維持できるので、段階的に移行して検討するつもりです。

GCPサービスを使ったさらなるFirebaseアプリケーションの拡張

※Firebase & Google Cloud Platform*6

Cloud FunctionsはGoランタイムの他にはPythonランタイムもあり、そちらでも同様にトリガーFunctionが書けるので何か活用法があるかもしれません。

また各FirebaseやGCPサービスには対応したREST APIが公開されていることも多く、SDK対応言語以外でもクライアントを自作して拡張することができます。

もちろんCloud Functionsですべてを行うことにこだわらずとも、HTTPリクエストをトリガーとしたAPIサーバーのFunctionや、SSRを実行して動的HTMLを返す常に待ち受けしているFunctionは、同時処理数に優れたCloud Runに移すことができそうです。

またCloud Run同士で連携して(RESTやunary gRPC)サーバー間通信でシステムを拡張の目的でも利用できそうです。

他にはCloud SchedulerとCloud Tasks、Cloud Dataflowを使ったバッチ処理。Firebase AnalyticsとCloud FirestoreをBigQueryにエクスポートして分析し、その結果をシステムに反映させるなどを私たちも既に行っています。

まとめ

このようにFirebaseはGCPの既存の仕組みを使い易くラップしたものなので、必要に応じてGCPのリソースを活用して最適化することができます。

Firebase Advent Calendar 2019 - Qiita

明日の担当はVexus2さんです。お楽しみに。

参考文献

いい感じにiOSアプリ開発プロジェクトを立ち上げる方法を考える

f:id:laiso:20160718030202j:plain
pxhere.com*1

ここ数年、新規に開発するモバイルアプリのリードになる機会が何回かあり。プロジェクトの開始時期に毎度、README.md に開発方針を記述していたのだけど、いつも似たような内容になり公共性がありそうなのでそのままブログにした。

普段から「今回はアーキテクチャは何を採用しましょうか?」みたいな段階から議論がはじまるのを避けた方が建設的だと思っているので、その思想が反映されている。

想定する状況

我が社はこれからゼロからモバイルアプリを使ったサービスを提供するところであり、人手は少ないが開発初期段階から技術に投資する意気込みはあり、いずれはサービスの大ヒット、組織の大規模化を見据えて段階的に成長するアーキテクチャをバーンとやっていきたい。

事業領域は例えば決済サービスやビジネス系のモバイルアプリを想定している。

これは何?

iOSアプリのソフトウェア設計面についての現時点での考えを未来のメンバーに共有するための文書

基本方針

「ふつうのことを普通にやる」

ふつうとは—— ふつうの〇〇プログラミングというシリーズの良書があったり、Railsコミュニティなどでコンテキストとして利用されていたりする便利なイディオム。*2

僕の解釈では問題に対して多くの開発者が共通認識で選択する解決手法を採用すること。それによって環境要因や個人の嗜好によって偏りができるのを防ぐことができる。

なぜそうしたいのかというと、先の見えない開発なので技術的に凝ったことや挑戦に労力を使うよりプロダクトのUXに大きく寄与する部分に注力するため。技術的な挑戦がUXに直接的に関わる時は別。個人やチームの成長のための技術的投資は基本的にサイドプロジェクトでやることにしてる。

プロダクトの特性上、モバイルのアプリケーションの表面上でできる体験より。バックエンドのビジネスシステムの信頼性や運用にかかる効率化が価値となると考える。 (一方TikTokやインスタみたいな没入型の体験を提供するアプリケーションの開発に従事する人たちにはまた別の価値観があるのかもしれない)

決めたこと

  • React Nativeを使わない
    • 開発対象が素朴なUIを持つツールである。ドラゴンも出てこないしアイドルのライブ配信もない
    • ユーザーがKyashやメルカリみたいなものを期待するとギャップが生じガッカリさせてしまう
    • アップデート・保守面のコストが許容できない
    • PMF検証フェーズであり、クロスプラットフォーム提供の旨みがない。アーリーアダプタもほぼiOSユーザーと予想
  • RxSwift/MVVMでがんばらない
    • 「大いなる力には大いなる責任が伴う」 (意訳: 強い人は勝手にやってくれ)
    • 経験上FRPや宣言的View+データバイディングをがんばりはじめると、受け入れる複雑さがコードの保守性や品質向上にペイしないため
    • 非同期処理の問題解決のためだけに採用されるケースもある。将来のSwiftでasync/await世代の方法論が出てきた時のために、サードパーティ依存な部分を減らしたい
    • 小さく使う方法はあるのかもしれない。RxCocoaデータバインディングだけ抜き。とか。
  • Storyboardを利用。Feature単位でファイルを切る
    • 多人数開発ではないのでコンフリクトのリスクが低い
    • 標準コンポーネントを使って、デザイナ作業が不要である程度楽に開発が進む
  • 単体テストは書く
    • 状態管理フレームワークを検討するぐらい複雑なら必須!
    • TDDはがんばらずに、脳内でシュミレートして開発を促進させそうな部分だけ書く(自己判断)
    • CIサーバーの運用とかがんばらずに最初は手元で実行できればいい
    • 一方デプロイ自動化は今後何百回もやることがわかり切っているので最初からやる
    • モック化とかも考えずにE2Eになる部分がでてしまってもよい(むしろ設計に役立つ)
    • ドメイン/プレゼンテーション分離の設計を意識するため(あとからまとめてリファクタリングするのは難しい)

迷っていること

  • サーバーサイドとのデータやりとり
    • 受信のみ。送信のみ。双方向。リアルタイム性。などの性質を加味して検討してる
    • 巨大なHTTPライブラリ: Alamofireのようなものを採用するのか。範囲を絞って小さく使うのはありかもしれない
    • またはAPIKitの仕組み: ユーザーは少ないけど筋がいい
    • REST API: protobufやswagger-codegenみたいなものである程度自動生成するのか。実践したことないのでわからない
    • gRPC: サーバーがPaaSなど制限があると検討外になりそう
    • Firestore: 最近うまくいった(レイテンシ以外は) *3 APIクライアント層を書くかわりにサーバーエンジニアがCloud Functionsを書くことになりがち
    • AppSyncやApollo: GraphQLツール群次第

Firebaseを使った成長するモバイルアプリのための高速なプロダクト開発 / Rapid Mobile Application Development using #Firebase

「Firebaseを使った成長するモバイルアプリのための高速なプロダクト開発」というプレゼンテーションのスライドを公開します。

2018年にバンコクにきて以来取り組んでいたプロジェクトについて技術的な内容をトークにしました*1。以下が概要です

モバイルアプリケーションの開発プロジェクトはあらゆる意味で速度との闘いです。

複雑化するシステムアーキテキチャやプラットフォーム、開発ツールのエコシステム。数多くある選択肢の中から私達は2018年にFirebaseを使い、1つのモバイルアプリケーションを開発しました。

本トークではその時の経験を元に、プロダクト開発を高速化するために技術者視点でどのような貢献ができるのか? という知見をシステムアーキテクトの立場としてお伝えします。

開発中はFirebaseコミュニティの情報などが非常に助けになりました。この場をお借りして感謝の御礼を申し上げます。

*1:実はDroidKaigi 2019 応募用に書いていたやつ

WEB+DB PRESS Vol.108 に「フルスタックエンジニアの憂鬱」というエッセイを書いた

いつものごとくインターネットでイキっていたところ、突然発言数0でデフォルトアイコンのTwitterアカウントから執筆依頼のDMが送られてきたので快諾しました。

でも初見では完全に信用していなくて、この話に乗っかると僕の暗号通貨ウォレットの秘密鍵がコインをチェックされてしまうやつ*1だと疑っていたので、とりあえず締切前まで放っておきました。

しかしその後の担当の人のメールの文体がいかにも編集者っぽい口調だったので本物っぽいなと思いながらやり取りしていました。万が一、入稿用のバイナリ実行ファイルが送られてきたら、たぶん話は終わっていましたけど……

どういう理由で執筆依頼が来たのかはよく理解していません。インターネットで見かけました、と聞きました。界隈の知り合いやその知り合い・他が既に執筆していたことがあるので、リファラルが効いてるのか上位互換の面子の誰かが受けられなかったのか、技術書書きたいと言ってたのを拾ってもらったのか、最近数ヶ月に1回は真面目にブログを書いていたからなのか、カンファレンスなどに出席してまともに対話できる相手だということがバレてしまったのか。

どうやったらWEB+DB PRESSに執筆できるのか?(コネか?) とよく周りで話題になっていたので、僕の結論としてはとりあえずインターネットでイキるのがよいのでは。という答えになりました。

閑話休題。エッセイは「壁の先に見えたもの」というテーマがあり、4人の著者がそれぞれフリーで2ページの文量を書いているそうです。独立した執筆なので他の人のことはよく知らないので発売当日に読むのが楽しみです。あとZOZO開発の特集も面白そう。

企画の「壁の先に見えたもの」というテーマを聞いた時の印象としては、なんかエモい感じの原稿が収集できそうな題材だなと思いました。ので素直にそれに乗っかりました。今考えると絶妙なお題だったのかもしれない。

書いた内容は要約すると、少数派な技術的志向を持つソフトウェアエンジニアでも強く生きろというもので、いつも説教臭くボヤいてるようなことです。「フルスタックエンジニアの憂鬱」というタイトルはとくに助言されたわけではなく自分でウケそうなやつを嗅覚で選びました。別に I'm フルスタックエンジニア. というわけではなくて、専門性を1つに絞れない立場である人(CTOとか)、または難儀な性格な人(自分)、を対象にしていて、詳しくは読めばわかります。短かいし。

WEB+DB PRESS Vol.108 にどうぞご期待ください。

WEB+DB PRESS Vol.108

WEB+DB PRESS Vol.108

ボツネタ

  • 詐欺師症候群について延々と説いていたらそれだけで埋まってしまったので全部消しました。Web検索してください

*1:NEM強奪犯は半年前から社員と交流を重ね、信用させたうえでウイルスを仕込んだメールを送りつけていた http://b.hatena.ne.jp/entry/s/www3.nhk.or.jp/news/html/20180512/k10011436321000.html

iOSエンジニアが知るべきProgressive Web Apps開発のエッセンス #iOSDC 2018

f:id:laiso:20180918235716p:plain

iOSDC Japan 2018 に採択されたトーク「iOSエンジニアが知るべきProgressive Web Apps開発のエッセンス」のブログ版記事です。

CfP提出後に内容は二転三転しまして、当初は最新Web開発事情について技術的に突っ込んだ内容にしようと目論んでいたのですが、「iOSエンジニア」へ「伝えたいこと」という軸で作っているうちにこの内容に落ち着きました。

当日聞きに来てくださった方、声をかけていただいた方。このような場を下さったiOSDCスタッフの方々にはたいへん感謝しています。

Introduction📛

f:id:laiso:20180918224204p:plain

昨年末 SafariにService Workersが実装され「iOSがPWAをサポートした」というニュースが業界内で話題になりました。 (※Progressive Web Apps 以降 PWA という略称で話します)

f:id:laiso:20180918224304p:plain

この時話題になっていたSafariのPWAサポートとはすなわちService WorkersのAPIの実装を指していたようです(Service Workersについては後ほど解説します)

このニュースは憶測や曲解を呼び、時には「ネイティブアプリの時代が終わった」という強い主張になって話される場面もありましたので、みなさんもどこかで目にしたことがあるかもしれません

f:id:laiso:20180918224347p:plain

状況を整理しましょう。SafariでService Workersが使えるようになったは事実です。これは既にその時点での開発計画にありましたので規定路線でした。

そして「ホーム画面に追加」でお馴染のSafariのWeb Clipの機能がWeb Manifestファイルに対応してアイコンなどを指定できるようになりました

しかし実際のアップデートはこれだけで、間違ってもAppleの誰かが「PWAに対応しました」などの声明を出したわけではありません

f:id:laiso:20180918224649p:plain

このようにPWAを取り巻く情報の解釈は複雑です。

私が思うに業界内で刺激的な話題が先行してしまったのは、Webとネイティブアプリ開発それぞれの側面で技術をただしく評価できていなかったからではないかと感じました。

ですから先ずWebアプリとネイティブアプリの開発者の目線で、Service WorkersとPWA、iOS Safariのことを理解するのが必要だと考えています。

f:id:laiso:20180918224804p:plain

このトークの前にiOSアプリ開発をしている知り合いのエンジニアたちへ「PWAという言葉を聞いてどう思うか」と質問してみました。

そこではUIやUXのクオリティやアプリストアでのファイダビリティの懸念などの意見をいただきました。

そこで気づいたのですが、PWAと名前が付けられたことによってネイティブアプリと比較して優劣を語る人の多さでした。ここの思い込みを先ず解かないといけなさそうです

f:id:laiso:20180918224911p:plain

また現状はAndroidのChrome上での体験とSafariでの体験は全く別物であると言えます。

その為、同じPWAという言葉を使って話していても、iOSユーザーとAndroidユーザーで捉えている像がそもそも噛み合わないということが起りえました

f:id:laiso:20180918225012p:plain

「PWAがネイティブアプリを置き換える」というストーリーは本質的ではありません

まず私が主張したいのがこれです。

その根拠となる理由を今からお話していこうかと思います

f:id:laiso:20180918225055p:plain

まず私がどういう立場でお話するのかというのを明確にするために、ここで自己紹介をします

私は普段スタートアップで Webエンジニアとして 仕事をしています。

そしてスマートフォンのアプリ開発も長く手がけているので、 iOS エンジニアのみなさんと近い価値観でお話できると思います

新しい技術全般が好きで、特に GUI のアプリケーション開発環境のことをよく調べています

PWAについて本質的な部分を知る

f:id:laiso:20180918225201p:plain

PWAという言葉は、広義ではWebアプリのUXを向上するムーブメント(運動)の通称です

最初はGoogle Chrome開発者らによって提唱されました

PWAに限らずGoogleは一枚岩の組織ではありませんから、Googleのどのチームの人達が言っているのかというのに注目するのがよいと思います

f:id:laiso:20180918225300p:plain

次に各プラットフォーマーにとってPWAがどのような意味を持っているのかというのに触れたいと思います。

Googleは古くはGearsやIE拡張、DartVM、Chrome Appsなどあらゆる方向からWebをよりよくする為の改善を続けています、その延長線上にPWAがあるのは不思議ではありません。

MicrosoftもWindows向けのHTMLアプリの強化を近年進めていて、先日正式にMicrosoft StoreへのPWAの登録を可能にしました。タブレットやデスクトップ向けのアプリ開発市場を活性化させたい意図が見えます。

MozillaにとってWebであることはNativeなのでWeb技術でネイティブアプリを作るのは当然のことです(ex: Firefox OS)

f:id:laiso:20180918230051p:plain

しかしAppleさん、完全にノーポジです。

WebKitにService Workersは実装されましたが、Appleは基本的にPWA関連の戦略を発表していません。

f:id:laiso:20180918230452p:plain

余談ですがiOS SDK誕生以前、アップルはサードパーティの開発者がWebアプリとして自分のアプリを公開するような構想を持っていました

これはセキュリティ上の理由による決定だったらしいのですが、開発者らの猛反発にあい現在のネイティブSDKの提供となったわけです

f:id:laiso:20180918230559p:plain

PWAとは元々、ネイティブアプリでは実現できるがWebアプリではできない体験の溝を埋めるために、ブラウザ側に実装された新機能を使うべしという側面があります。

Service Workers は「Webページ」という層より更に低レイヤー・ネイティブに近い層として処理を記述できる仕組みが用意されています。

そのため、PWA関連機能はService Workersと組み合わせて使うという例がよく出てきます。

f:id:laiso:20180918231106p:plain

では Service Workers とは何ものでしょうか? それは「ブラウザのオフライン基盤」と捉えるのがより本質的だと思います。

「プログラミング可能なネットワークプロキシ」という表現もされています。

f:id:laiso:20180918231204p:plain

この「ローカル」というのを「アプリがインストールされる」と考えるとiOSエンジニアにも理解しやすいと思います。

実際Service Workersの登録・アップデートの管理をブラウザが透過的に行ってくれる仕組みを含んでいます。

これによってWebページによるアプリの表の面とシステム上で動く「裏の面」が表現されているのです。

f:id:laiso:20180918231504p:plain

その他のブラウザの新機能(API)の例も見ていってみましょう

f:id:laiso:20180918231608p:plain

Android端末ではこのようにManifestとService Workersが定義されたWebアプリを開くとインストールを訴求するバナーが出現し、ホーム画面に追加できるようになります

一方Safariについてですが——

「ホーム画面に追加」 と Safari

f:id:laiso:20180918231654p:plain

PWA関連の記事で、まるで最新のアップデートでWebアプリのアイコンをホーム画面に追加できるようになったかの記述を見かけます

しかし、これは「Web Clip」という機能で、iPhone 3G時代から存在しました。

f:id:laiso:20180918231813p:plain

旧来、独自メタタグをHTMLヘッダに埋め込む形式の仕様だったものがService Workers実装後にWeb Manifest(JSON形式)をサポートしました。

f:id:laiso:20180918231946p:plain

このようにホーム画面に登録したページはSafariから切り離された単独のウィンドウとして管理されます(しかしcredentialの管理など既知の問題もあります)

f:id:laiso:20180918232040p:plain

次にPush Notificationです、Safariは今のところPush APIに非対応です。

デスクトップ向けにSafari Push Notificationsという独自の仕様があり、これとiOS Safari+APNsを統合しないことにはPush APIはiOSにくる未来はおそらくないのでしょう。

f:id:laiso:20180918232323p:plain

Payment - 支払い関連のAPIも最新のSafariで利用できます。

iOS SafariでもApple Payは利用できますし、この機能はうまくWeb標準とAppleの仕様が噛み合った例としてうまく機能しています。

f:id:laiso:20180918232602p:plain

つまりPWAとは、ブラウザに実装された、新機能を使うと、従来のWebアプリに不足していたUXが実現できる。

というだけの話なんです。

もしくは、WebアプリのUX向上のために新たなルートが引かれたと解釈すると良いかもしれません

f:id:laiso:20180918232749p:plain

なのでネイティブアプリと比較して優劣を語るのではなく、すべてのブラウザ共通で動作する従来のWebアプリと比較するべきなのです

Webアプリとネイティブアプリの比較

そうしたことをふまえて、次に「Webアプリとネイティブアプリの比較」です

f:id:laiso:20180918232815p:plain

Webアプリの利点として「動作環境が多用 」ということと「柔軟な配布方法」できる。ということがあげられるかと思います

Webアプリは外部アプリのWebViewの中からでも起動できますし、ブラウザさえあればどこでも動作します(そしてホーム画面からでも)

f:id:laiso:20180918232956p:plain

Webアプリの柔軟な配布方法についてです。Webアプリは常にサーバー上の最新版を参照し、Service Workersによってイントールする単位をインクリメンタルに管理することも可能になりました。

f:id:laiso:20180918233208p:plain

ここでいうオフラインファーストとは何でしょう。 それは、データを取得しなくてもUIを構成できるということです。

具体例としてはキャッシュされたアセットやレスポンスから読み込むことができます。

f:id:laiso:20180918233340p:plain

App ShellモデルというのもChromeチームが提唱したWebアプリのアーキテクチャの名前で、ネイティブアプリのメンタルメデルに近いのでiOSエンジニアには理解しやすいかと思います。

f:id:laiso:20180918233441p:plain

しかしiOS開発でオフラインファーストを意識する場面はどの程度あるでしょうか?

ネットワークのリトライ処理やキャッシュのハンドリングなどを独自に実装されている方も多いと思います。

f:id:laiso:20180918233628p:plain

WebアプリがWebであるがゆえに根本的に抱える問題というのもあります。

OSが持つ機能との接続はAPIが存在するか次第でありますし、ネイティブに見た目を近付けるほどその違和感が出てくるジレンマなどもあります。

UI遷移などはおもたる例で、ブラウザ上のレンダリングエンジンでOS権限でグラフィックAPI叩けてハードウェアに近い最適化のできるネイティブな実装に近付けるのは指南の技でしょう。

f:id:laiso:20180918234558p:plain

一方Safariの課題としては、Windows版Safari廃止以降に進んだOS独自機能の取り込みというのがあげられます

機能によってはWeb標準への準拠の動きはコンフリクトするため、うまく統廃合する必要があるでしょう

f:id:laiso:20180918234654p:plain

またiOSではChromeやFirefoxなどのサードパーティのブラウザはWebKitの利用を強制され、システムのデフォルトにできません。

ホーム画面にショートカットを置けるのもSafariだけです。

このためAPIの実装の進んだブラウザを使うという選択をとることができません

f:id:laiso:20180918234712p:plain

これは個人的な予想と願望なのですが、Appleがなぜ今頃になってWeb標準の実装を進め出したのかというと

既に世界的にシェアがあるモバイルブラウザ向けに作られたWebアプリをiOSで動かし利用者を取り込みたいのだと思います

つまり、AppleはAppleで自社デバイスのUX向上のためにSafariを改善していくのではないか? ということです

iOSエンジニアがWebアプリ技術に注目しておいた方がいい理由

f:id:laiso:20180918234740p:plain

2014年に話題になった「ロードマップ指向とエコシステム指向」というブログ記事があります。

ここでは、大きな企業が主体となって推進する技術的なトレンドが、オープンソースのコミュニティを中心とするエコシステム=生態系が技術を進歩されるということが示されています

f:id:laiso:20180918234833p:plain

エコシステム指向とはなんでしょうか? それは技術の革新を牽引する存在がプラットフォーマーなどの大きな企業から、それぞれの意思を持つ小さなコミュニティに移り変わったことだと思います

例えば、Googleのように。モバイルOSの開発をしつつブラウザ体験をよくする、しかしそれを置き換えるような新プラットフォームも開発している。という企業はそれぞれの方向性は違うが時には統合して進化している。という点が、非常にエコシステム的であるといえます

f:id:laiso:20180918234921p:plain

ここで注目したいのは現代でエコシステムを中心に急成長してきた技術としてのWeb開発環境です

ブラウザベンダーは標準化された仕様の実装でそれぞれ競争し、サードパーティのライブラリは各分野で御互いのいいところを取り込んだり、もっと良いやり方を提案したり巨大なOSSプロジェクトを戦略的に企業が運用・参加しています

f:id:laiso:20180918234953p:plain

Web技術のコミュニティではエコシステムで進化を促進する仕組みが機能しているのです。

iOSエンジニアの皆さんに近年のWebアプリ開発技術の進化の速度がすごい! というのを本日は知っていただきたいのです。

f:id:laiso:20180918235109p:plain

なぜWeb技術の進化が早くなるのか。それは、Webに関わる人口の多さが関係しているのだと思います。

ウェブブラウザやデバイスの利用環境は多様化していますし、Webアプリのフロントエンド開発技術はJavaScriptに集約されています。

f:id:laiso:20180918235125p:plain

思い起してみると今から10年前の2008年に「iOSエンジニア」という職業は存在しませんでした。

この10年、CocoaからiOS開発の環境は目紛しく変化しました。毎年OSやIDEが一新され、アプリは壊れ、プログラミング言語すら切り替わってしまいました。

AppStoreとわたしたちデベロッパーが作り出したアプリ開発のエコシステムが「iOSエンジニア」を作りだしたわけです

それと同じようなことはWeb開発の世界では今起っています。

f:id:laiso:20180918235332p:plain

AppleのロードマップによるiOS開発環境の進化を待つだけでは、もはや劇的な進化は訪れません。

私たちデベロッパーがエコシステムを乗りこなし、iOSの開発環境を進化させるべきだと思います。

例えばServer-side Swiftなどはまだまだ未成熟なので、非常にコミットする余地のある領域だと思います

f:id:laiso:20180918235359p:plain

10年後の「iOSエンジニア」を思い浮かべてみてください。Swiftで変わらずにアプリを開発しているでしょうか? それともFlutterでDartを書いているでしょうか?

10年後もiOSプラットフォームがユーザーにとって魅力的であるように…iOSエンジニアという職業を残すには、自分たちで未来を作ることが最良の選択です

そのために最新のWebアプリ開発技術を正しく理解して評価し、iOSアプリ開発に活かすのは重要なことだと思います

f:id:laiso:20180918235454p:plain