Strada探検隊

f:id:laiso:20211009143132p:plain
Strada

こんばんわ、Strada探検隊のお時間です。

Stradaとは?

Turbo NativeとモバイルOSのネイティブAPIを連携させるHotwireシリーズ最後のミッシングパーツだと目されています*1

Stradaは目下開発中の身であり全容が分っていないのですが、既にHEYアプリに投入されているため、HEY iOSアプリの構造を見て取ることでその仕組みが予測できるのではないかと考えました。

計画

Standardizes the way that web and native parts of a mobile hybrid application talk to each other via HTML bridge attributes. https://hotwired.dev/

とあるので特別なHTML定義をStradaが解釈してUIコンポーネントへ受け渡すことが予想できます。なので特殊なHTMLを経由していそうな箇所を探します。

turbo-ios

hotwired/turbo-ios: iOS framework for making Turbo native apps

turbo-ios をおさらいします。turbo-iosはturboで作成したウェブページをiOSのWKWebViewで動作させるために追加で入れるフレームワークです。

turboで作成したウェブページはそれ単体でもWebViewで動くただのWebアプリではあるのですが、画面制御や認証情報をOS側に保存するなどのWebViewの外で実行する必要のあるコードをturbo-iosを使って実装します。なのでStimulus*2のネイティブ版的な役割だと解釈しました。

ドキュメントには

This is fine for simple tasks, but we've found we need something more comprehensive for our apps, which is why we created a new framework called Strada. https://github.com/hotwired/turbo-ios/blob/956484e3524eaf203d12807313e7119427a5771d/Docs/Advanced.md

とあるので、WebViewの拡張ではなくてアプリ内ブラウザを作ってそこに自分たちのWebViewを乗せたいという意図を感じます。

ログイン直後

HEYアプリでログインをすると。JSONでアプリのロードに必要なファイルを取得しています。Web版にはない挙動なのでこれがアプリ実装独自のものであることがわかります。

スクリーンショットは市民の権利であるところの Charles Proxy を使用しました

navigation.json

{"items":
[{"title":"Imbox","app_url":"https://app.hey.com/imbox","hotkey":"command+1","highlighted":false,"icon":{"name":"imbox","android_url":"https://production.haystack-assets.com/assets/icons/imbox-79d9f42c3186cc31d1032da369799121be75c8a2ec6dd32210c45bbae96662f4.svg","ios_url":"https://production.haystack-assets.com/assets/icons/ios/imbox-2bbd76a4e5cba992f779de32a6c5820426e35f31bb9560284f6373cc87a46678.png"}},
{"title":"The Feed","app_url":"https://app.hey.com/feedbox","hotkey":"command+2","highlighted":false,"icon":{"name":"feedbox","android_url":"https://production.haystack-assets.com/assets/icons/feedbox-b595638e37da5175c6ef848245712f0037964fccf0106b90e2b0de95545788ac.svg","ios_url":"https://production.haystack-assets.com/assets/icons/ios/feedbox-cc4dd8e4c3a28175e54b4e8e7fd0a87f5414658beaeff5af0c33d3ebe473ac8c.png"}},
// ...

データから推測するとフッタのナビゲーションウィンドウの中身だと分かります。Server-driven UI っぽい*3

ios-v3.json

正規表現 => properties の定義が配信されてくる。"presentation": "modal" とあるのでURLのパターンにマッチした画面の制御を行っている仕組みだと思いました。

これはturobo-iosのPath Configuration機能らしい *4

  "rules": [
    {
      "patterns": [
        "/recede_historical_location"
      ],
      "properties": {
        "presentation": "back",
        "visitable": false
      }
    },
    {
      "patterns": [
        "/new$",
        "/edit$",
        "^/topics/[0-9]+/publication",
        "forwardings/outbounds",
        "contacts/[0-9]+/box_settings",
        "contacts/[0-9]+/notification_settings",
        "/projects/[0-9]+/status",
        "/collections/[0-9]+/status",
        "/my/preapproval",
        "/folders/[0-9]+/confirm_destroy",
        "^/mailto/",
        "postings/projects/[0-9]+/add_topics",
        "postings/collections/[0-9]+/add_topics",
        "/my/accounts/[0-9]+/external_accounts/[0-9]+/trash",
        "domains/[0-9]+/auto_screening",
        "domains/[0-9]+/notification_settings",
        "/autofileables/.*/autofilings",
        "/attachments/senders"
      ],
      "properties": {
        "presentation": "modal",
        "always-dismiss": true
      }
    },

topics

メールの内容や返信がtopicsの単位になっている。ここはWeb版とほぼ同じなので普通にWebViewでHTMLを受け取ってturb-frameで更新しているのだと思われる。

toolbar

topic下部に出てくるメニューのこと。turbo-frameで取得されている。

class="u-hide@mobile" はデスクトップとモバイル版で出し分けをするために定義されている。

<turbo-frame id="topic_toolbar">
  <turbo-frame id="topic_composer" data-controller="disable-on-mobile reset-frame-source">
      <div class="page-toolbar ">
        <div class="page-toolbar__content" data-controller="action-bar bridge--action-bar" data-action="action-bar:updateSelection->bridge--action-bar#updateSelection" data-action-bar-highlight-class="page-toolbar__action--selected" data-bridge--action-bar-highlight-class="page-toolbar__action--selected">
          <div class="page-toolbar__item">
      <a role="button" data-topic-typing-target="replyPrompt" data-controller="hotkey bridge--hotkey" data-hotkey="r,R" data-bridge-hotkey="command+r" data-bridge--action-bar-target="item" data-bridge-title="Reply" data-bridge-icon-name="reply" data-bridge-icon-android-url="https://production.haystack-assets.com/assets/icons/android/reply-fae46c7a6889c98d1ac6439182bc23fb1eafcb01a4a7db45f042101a98569668.svg" aria-label="Reply now" class="page-toolbar__action page-toolbar__action--reply btn--focusable" href="/entries/460727690/replies/new">
        <span class="u-hide@mobile">Reply Now</span>
        <span class="u-hide@desktop">Reply</span>
        <kbd class="page-toolbar__action-hotkey action-group__action-hotkey">r</kbd>
</a></div>

momd

更に奥地へ進んで行きます。念力を使ってパッケージバンドルを透視したところmomdが含まれているのでCore Dataを使っているのが分かりました*5

Haystack.momd/
├── Haystack.mom
└── VersionInfo.plist

アプリ向けDBモデルが構築されていることが分かります

turbo-iosはWebViewとその制御が責任範囲で、ローカルストレージのDBをどうするのかという部分は独自に実装されていると思います。なのでここはStradaっぽさがある

composer

composer.html からなる小さなローカルWebアプリがある。trix-core.jsが組込まれていることからメールエディタの部分ではないかと思われる。

これだけローカルに含まれているのはオフライン時にメール書いたり、とかの機能を担保するのが目的かもしれない。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
  <title></title>
  <link rel="stylesheet" type="text/css" href="hey-composer://localhost/_bundle/trix.css">
  <link rel="stylesheet" type="text/css" href="hey-composer://localhost/_bundle/composer.css">
  <script type="text/javascript" src="hey-composer://localhost/_bundle/trix-core.js"></script>
</head>
<body>
  <form>
    <input id="content" value="" type="hidden" name="content">
    <trix-editor class="entry-composer__textarea trix-content" id="editor" input="content" data-blob-url-template="/rails/active_storage/blobs/redirect/:signed_id/:filename"></trix-editor>
  </form>
</body>
</html>

HEYのカスタムスキーマhey:// のはずなので hey-composer:// は内部リソースを読み込むために解釈される? なぞの部分。

strada.js

Strada本体のJavaScript部分。

数十行のファイルで、やっていることは document.addEventListener() して postMessage() するだけ。これ自体は検索すれば出てくるぐらいに、WebViewとネイティブでメッセージングした時によくある実装方法だと思う*6

Strada_Strada.bundle/
├── Info.plist
└── strada.js
document.addEventListener("web-bridge:ready", () => window.webBridge.setAdapter(this))
// ...
window.nativeBridge = new NativeBridge()
window.nativeBridge.postMessage("ready")

他にコンポーネントregister/unredister 関数がある。おそらく最低限のサンドボックスの仕組みだと思う。

その後

他にも念力を駆使しようと試行錯誤したけど、新たな発見はしばらく得られなかったので我々はStradaを後にした。

僕より強いサイキッカーの人が挑めば何か収穫があるのかもしれない……

わかったこと

  • HTML Over The Wire と言えどHEYアプリには多数のJSONレスポンスを返すAPI呼び出しがある。
  • モバイルアプリだけ独自 <strada-feature></strada-feature> タグとか謎の通信してるのを期待したが思ったより普通のネイティブアプリ+JSON APIの部分しか観測できなかった
  • Server-driven UI っぽいものをがんばろうとしているのは明かだった。でもこれはAirbnb事例を見てもStradaに限らず業界的なトレンドであるとは思う

豆知識

  • HEYのプロジェクトのコードネームは Haystack *7
  • Basecampの人々はJSの行末セミコロン書かない派

Airbnb’s Server-Driven UI System(Ghost Platform) の感想

Airbnb’s Server-Driven UI System(Ghost Platform) とは何か

Airbnbが社内で取り組んでいる最新UI開発システム Ghost Platformとそのコンセプトの名前

Airbnb AndroidエンジニアRyan Brooksによるポストを参照

A Deep Dive into Airbnb’s Server-Driven UI System | by Ryan Brooks | The Airbnb Tech Blog | Medium

f:id:laiso:20210920210659p:plain
A Deep Dive into Airbnb’s Server-Driven UI System

どういうものか

  • サーバー(API)からデータと画面構造(UI)を配信して各プラットフォームで再生する仕組み
  • UI言語部分を抽象化したもの
  • Web、iOSAndroidに対応する

なぜ画面構造を配信するのか

  • アプリケーション上で動的な画面構成を実現するため
  • データから画面構造を計算するロジックをサーバーサイドに寄せる
  • インターフェイスにGraphQLを使う

GraphQLが必要な理由

  • プラットフォーム間で共有するためにGraphQLスキーマの表現力を使いたい
  • AirbnbではGraphQL Fragment からクライアントサイドのコンポーネントで利用するTypeScriptやKotlinの型定義を自動生成してくれるらしい*1

今までの方法

  • APIからデータのみを受け取る
  • データからクライアントサイドで画面構造を計算して描画する
  • もしくは画面構造が静的なので計算しない

Ghost Platformの方法

変わらない部分

  • ビューの描画や画面遷移をするためのコードをクライアントサイドで書く
  • イベント処理のコードをクライアントサイドで書く
  • ローディングのビューやスケルトンをクライアントサイドで書く
  • 各画面上での操作で通信が発生したり画面を更新したり

コンテキスト

疑問点

  • クソデカJSONを毎回取ってくるの? CDNに乗せられないデータはあるのか
  • プラットフォームごとの分岐の必要は? iOSネイティブではメニューが上だけどAndroidは下とか
  • Ghost Platformクライアントフレームワーク部分が公開されないと解明できなさそう

まとめ

Ghost Platformでは動的なレイアウトシステムを実現するために

FAQ

これを実現するための技術的なブレークスルーがあったの?

  • ないと思うけどAirbnbでのGraphQL活用とUIシステム研究が悪魔合体して発表できるぐらいの粒度になったのかもしれない
  • 画面構造をサーバーから取ってきて動的に組替える。程度のことは各プロジェクトやっていた

宣言的UIにしなきゃだめなの?

  • だめじゃないけど可変レイアウトやコンポーネント単位に切り出してコード書くので相性はよさそう
  • AirbnbではJetpack Composeはまだ使ってない(Epoxyがある) *2

審査なしでApp Storeのアプリをアップデートできますか?

  • 画面構造の更新だけで提供できるアップデートなら可能
  • Airbnbの例ではアプリの全ユーザーの機能を最新にできていいよね。みたいなことを言っている
    • versioning 機能を使わないと古いバイナリに新しいコンポーネントが同梱されてなくてエラーとかは起きそう
  • 新機能の画面を非表示でリリースしてあとで入れ替えるということも原理的には可能だけどAppleはそれは認めていない

私達のプロジェクトで使えますか?

  • 動的なレイアウトを実現するネイティブアプリのUIを作っていてGraphQLを活用しているのだったらコンセプトは真似してみる余地はありそう
  • ただ別にAirbnbフレームワークを公開しているわけでもないからエンジン部分から自作しないといけない
  • Airbnbアーキテクチャをコピーしなくても最小限の画面構造と入れ替え可能なコンポーネントでServer-Driven UI Systemをはじめることはできると思う
  • UIシステムに関心がなくただ開発工数を減らしたいのならWebView使えばいいと思う
  • Davidっていうレーサーが「テクノロジーの多くは、それが生まれた環境を反映しています」って言ってました *3

References

Re—Engineering Travel with GX 3番目のRyan Brooksのセッション

Ryan Brooks's Post | LinkedIn

Clubhouseのユーザーインターフェイスを支えるObjective-Cの確かな信頼と実績

f:id:laiso:20210202231917j:plain

ClubhouseのiPhoneアプリは各所でお馴染みのObjective-Cライブラリが使用されており、アプリ自体は最先端のムーブメントながらもUIからはシニアの職人技を感じます。根拠はないですがアプリの実装もObjective-Cでゴリゴリ書いてそうです。

ここではそんなObjective-Cライブラリの一部を紹介します。

IGListKit

https://github.com/Instagram/IGListKit

Instagram開発チームのコレクションビューの差分描画最適化のノウハウが詰ったライブラリです。

アプリの肝となるフィード系の画面で使われています。

UIScrollView+InfiniteScroll

https://github.com/pronebird/UIScrollView-InfiniteScroll

無限スクロールを実現するライブラリです

FlagPhoneNumber

https://github.com/chronotruck/FlagPhoneNumber

TEL入力フィールドに登場します

DZNEmptyDataSet

https://github.com/dzenbot/DZNEmptyDataSet

empty state として使われています

SZTextView

https://github.com/glaszig/SZTextView

標準テキストフィールドの代替ビューです

autorelease

ありがとう Brad Cox

Hotwireの感想

Hotwire

f:id:laiso:20210124034255p:plain

https://hotwire.dev/

  • Turboを中心としたウェブアプリケーションアーキテクチャの要素技術やコンセプトをPRするための名称
  • Hotwireというライブラリがあるわけではない
  • 役割としてはMicro FrontendsとかReactのlearn once, write anywhereなどに似ている
  • アプリケーション実装言語非依存だけど現状Railsアプリケーションしか実用できる基盤がない

Hotwireの思想

  • アプリケーション開発者の生産性を上げることを目的にしていること
    • サーバーサイド言語でフロントエンドを実装したいアレではなかった
  • プログレッシブ(段階的に利用可能)であること
    • 必要な技術だけを使い無駄なことをしないことで効率化する
    • Hotwireが列挙する技術は1つづつ有効にできる
  • クライアントサイドでViewを差分更新する現在の主流のシングルページアプリケーション開発のオルタナティブなこと
    • 既存の方法は効率が悪いという作者たちの考えが反映されている
    • とくにビルドツール依存の開発が目の敵にされている
    • 逆にSPAが必要ない人やSPAのクライアントサイド開発者には刺さらなそう

Hotwireの仕組み

  • TurboというJavaScriptライブラリがある
  • これがウェブブラウザ上で動き続けることでアプリケーションに動的な機能を提供する
  • 通常すべてのドキュメントリクエストを乗っ取り、XHRで取得したコンテンツ(HTML)部分だけをブラウザではなくTurboが置き換える
  • turbolinksと呼ばれていたRailsの機能のことで、JavaScriptイベントの挙動に副作用が生じてよく無効化されていた。これがTurbo Driveという概念になった

Turbo Frame

  • Turbo Driveが書き換えるコンテンツを <turbo-frame> タグ以下で制御する仕組み
  • <turbo-frame> 内にある <a> タグや <form> タグでイベントが発生した時にXHRのレスポンスの内容を現在のViewに差分更新する
  • コンテンツ自体はサーバーサイドで出力された結果になる
  • <turbo-frame> 外のコンテンツを書き換えるオプションが用意されており画面の共通部分の更新などはここを経由する

Newボタンをクリックする。このボタンのマークアップがaだったりformだったりする。

Turboによって https://app.hey.com/folders/new へリクエストが送信されると <turbo-frame> でダイアログの中身が返される

iframeのようにこの中身を直接開ける

Turbo Stream

  • ユーザー側のイベントではなくサーバー側から更新をPushしていて更新する仕組み
  • それぞれのサイバーサイドフレームワークが各自の機能を使ってブラウザからWebSocketやSSEでメッセージを待ち受け、決った仕様のHTMLをDOMのAPIに渡すとあとはTurboが画面更新をする
  • それを turbo-stream のタグに定義してアプリケーション開発者がテンプレートに埋め込めるようにする
  • 制約上、サイバーサイドフレームワークへの依存が強い。turbo-rails ではAction Cableを使って実現されている

Stimulus

  • Turboがサーバーサイドで生成したHTMLをいかに画面に適応するかという役割を「のみ」を担っている
  • 更新されたHTMLに対してボタンが押された時にCSSクラス名を変えたいのだとか、クライアントサイドだけで完結すればいい処理を書く場所が必要でそれがStimulus
  • 必須ではないがTurboはアプリケーションで実行されるJavaScriptのロジック面倒を見てくれないので何かしろクライアントサイドのコードを管理する仕組みが必要
  • 特定のHTMLのブロックに対して割り当てるふるまいをJavaScriptのclassとして記述していく
  • その仕組みがHTMLをブロック単位で配信するTurboと相性が良い

Turbo Native

  • 動作環境がウェブブラウザではなくiOS/AndroidアプリのWebView上になる
  • https://hey.com/apps/ で利用されてる
  • WebView上のアプリケーションが効率よく動作するためにクライアントサイドSDKが配布されていて、アプリケーション開発者はこの基盤の上で利用する

Strada

  • Turbo NativeからiOS/AndroidアプリのネイティブAPIをブリッジして呼び出して開発できるようにする仕組み。未リリース
  • ウェブアプリケーションをブラウザとiOS/Androidアプリに共通でホストするという点でアプローチがIonicに近い
  • ただしStradaはリモートのウェブサイト経由でネイティブAPIを実行しようとしているところがIonic(Cordva/Capacitor)と設計が異なる
  • 通常この方法はAppStoreのガイドライン接触し問題になるソフトウェアが過去にいくつかあった

2.5.2 Appはバンドル内で完結している必要があります。他のAppを含め、指定されたコンテナエリア外に対するデータの読み書き、またはAppの特徴や機能を導入したり変更したりするコードをエリア外からダウンロード、インストール、実行することは許可されません。 https://developer.apple.com/jp/app-store/review/guidelines/#performance

  • ただしCodePushのように「既にバンドルされているネイティブ実行コードではないアセットファイルを外部から更新する」というスキームで回避できている例もある(しかし結局レビュワーガチャによってリジェクトされている)

https://github.com/microsoft/react-native-code-push#store-guideline-compliance

勘違いしていたこと

  • Phoenix.LiveView インスパイヤなのでWebSocketやロングポーリング必須だろう
    • Turbo Streamを動かす場合に必要なだけだった。WebSocketサーバーがなくても他の機能は利用可能

ReactやVueを使った開発と比較して期待できそうな点

  • サーバーサイドレンダリングがデフォルト
  • 実装するレイヤーが減ってシンプルになる
    • APIエンドポイントとクライアントがいなくなり、DBからデータを取得してViewまで落とし込むまでの距離が短かくなった
    • React Server Components の解説でもこの利点が書いてあった

不安な点

  • ReactやVueを前提とした便利なUIライブラリの存在
  • JavaScriptビルドパイプラインは回避することができるけど謎のJS/CSSをインストールするだけのgemを管理し続ける問題は残ったまま……(Railsの場合)
  • Turbo自体の学習コスト。テンプレートを細かく分けて記述するようになって、どこで何が起きているのか把握しずらかった
    • ReactやRxやMVVMにも複雑さを感じていたが慣れたので、いずれ気にならなくなるかも……
  • React以降にUIをデータと関数でプログラミングできるようになって前進した手応えがあったが、Stimulusで生DOM API+手続きコードのBackbone.js時代に戻されてしまったかのような不安
  • 実際 hey.com コンソールを開くと大量の contoller.js クラスが……
  • TypeScriptは諦めてるような雰囲気(ブラウザで動かないものだから)
    • 一旦型に保護されたプログラミングに慣らされると、取り上げられた時に不安になる

実際のプロジェクトでの利用

  • 今の主流のシングルページアプリケーションのスタックについてはモバイルアプリのAPI開発との共通化が影響していると思っているのでHotwireを全面的に使うか? という判断はWebView製のモバイルアプリを許容できるか。というところにかかっていると思う
  • WebView製のモバイルアプリがどんなものかというのは実際のHeyのiOS/Androidアプリで試すことができる
  • WebView製のモバイルアプリが不要、もしくは許容できるとしてもプロジェクトに充分なフロントエンドエンジニアやモバイルアプリエンジニアが居たとしたら技術スタックごとに分担した方が効率がいいという意見の人は多いと思う
  • なので「フロントエンドエンジニア+React Native+サーバーレスアーキテクチャで最小構成ではじめる」例のように、「RailsエンジニアがDBとAPIとSPAを同時に作らなくていい方法」として局所的な活用方法に最初はなるのではないか
  • 最小のRailsアプリケーションを開発して、少しづつシングルページアプリケーション化を取り入れていくという時の用途にも使える
  • 今までの考え方からすると、ある時えいやでSPA化するということをみんなしていた

この技術が分からん2020

2020年に作ったソフトウェアや開発技術をふりかえる で分かったことばかり書いたけど相変わらずなんべん勉強しても分からんな〜と思うことも多いのでそれもリストアップしてみることにした。

SQL

10年以上触っているはずだけど集合のイメージが頭に入ってこなくて全然文を組み立てられずにいる。ゆるふわORMを適当に使ってる。

CSS

10年以上触っているはずだけど制約のイメージが頭に入ってこなくて全然レイアウトを組み立てられずにいる。ゆるふわTailwindCSSを適当に使ってる。

Unity

何回もダウンロードして教材を買ってるんだけど。アセットを組み立てて何か意味のあるものを作るっている状態まで行かない。Flashは使いこなしていたはずなのになぜ

UIデザイン

作る時に一定の理屈っぽいこだわりがあるんだけど、何か自分で作るというところまでいかない上に、深く理由を考えたことすらなかったので、こだわりがあるけど関心が低い。という変な状態だと思う。

VSCode

IntelliJ IDEA派だからなんだけど。VSCodeで使うことを前提とされたツールとかを見て大丈夫かなこれと思ったりする。

GoやRust

最近のシステム言語を身につけた方がキャリアの広がりができてよいとずっと思っているんだけど、何か別の言語で簡単に同じことできるじゃんという邪念が邪魔して真剣に取り組めてない。書けば分かるよ? 書けばな〜。必要性がな〜。と言い訳している

ネットワーク

サブネットとかルートテーブルとか概念は分かるがいつもはい…はい…と念じながら整えている感覚があり気持ち悪い。

数学

ありがち。必要→納得→興味→関心のパイプラインにすら到達していない気がする。

競プロ

アルゴリズムコンピュータサイエンス、数学ぐらいの具象化されたものより抽象的なものを求めているのか楽しさが分かってなくてのめりこめずにいる*1

あと単に基礎知識が不足している。

英語

英語がしゃべれないと死ぬ(比喩)ような環境に飛び込んでたらいける論があるけど普通に生活に支障が出ているし生存バイアスから漏れてしまっている。

人間の気持ち

オライリーブックス『詳解 人間の気持ち』が欲しい

そもそも——

プログラミング自体が数年がかりでできなかった。同時期にネットをはじめた知人がPerlCGIテキパキ組んで仕事をしているのをうらやましく見ていた。

その後、数年わからん続けて本屋で 改訂版 FreeBSD徹底入門 を買って読んでみたけど全然わからなくて読むのをあきらめた。

そのまた数年後 みるみるプログラミングがわかる本 を読んで壁を超えた気がする。この本自体が初学者にすごくおすすめというわけではなかったんだけど、特徴ある点としてはプログラミング言語とは言語の違いではなくて開発ツール(IDE)の違いですってことが書かれていたと思う。現在も開発環境おたく気質があるからそこが性に合っていたんだろ思う。

あと、自分は高等教育を受けることに失敗しているので、独学してみて分からなく挫折する状態に慣れているんだと思う。良い面もあるし悪い面もありそう。

分からんのスコープ

ソフトウェアで何かやりたいことがあるとして

  • やり方を知らべる
  • ドキュメントを読む
  • 実行する

という行動が取れる自信がある時は自分はそれを「できる(できそう)」にカテゴリしてるんだけど、他の人と話していたらそれは実績がないから「できない(やったことない)」に区分されるという意見を聞いてそうなんだと驚いたことがある。

それ以来は、分からないけどやったらできそうではある。となるべく正確に答えることにしている。

*1:https://b.hatena.ne.jp/entry/4695815452087964034/comment/redreborn を読んで思ったけど抽象的という喩えよりいい言い方があるのかもしれない

2020年に作ったソフトウェアや開発技術をふりかえる

概要

よくある年末っぽい日記の記事です。

だいだいこれどうりのバランスでソースコードも書いてる

  • 言語はなんでもいい時はNode.jsで書く。移植性が高いので。複数人でメンテしそうな時はTypeScriptを採用し、プライベートの時は型を完全に無視する
  • PHPはほぼLaravel。ビジネスのみの関係
  • Swiftはそんなに書いた記憶がないけどアプリのメンテをしてたと思う
  • Vueも仕事で使っていたけど最近はReactに傾いてる
  • Objective-Cは書いてない
  • グラフに含まれてない部分だとAndroidアプリでKotlinを使って、データ分析でPythonを書いた

このグラフは GitHub Profile Summary Cards っていう便利ツールを使わせてもらって自動生成している。

記録方法

  • コードを書く時はおもむろに ~/tmp 以下にディレクトリ掘ってIDEを開きはじめるので実質そこがプロジェクトサンドボックスみたいになってる。
  • なのでその場所を時系列に並べてチェックしていくことで何をやっているのか分かった。
  • あとはGitHubとかTwitterとかに記録が残っていた。

フロントエンド

ReactやVueに本格入門した年だった。

Nuxt

SPA+外部APIアクセスで開発するのに最適なウェブアプリケーションのプロジェクトがあったので採用した。

SSRが不要なのでvue-cliを使うか迷ったけれど、コミュニティの充実ぶりを見てNuxtにした。

複数人でメンテする可能性もあるので最初からTypeScript化した。苦心したのはStore層であんまり型が生かせてない書き方しちゃったり、vue/test-utils を使ったユニットテストがうまく構成できずにVueコンポーネントでラップしてテスト書いてたという部分。こういうのって他所の開発チームに実際入ってみないと作法がよく分からないなと感じた。

Amplify

外部APIAWSで動いてて、それにあわせてデプロイ先をAWS Amplify コンソールにしたがこれがすごい楽でよかった。また使いたい。

いっさいドキュメント系読まないでも、よくある既存のPasSのあの機能みたいなものね〜という感覚ですんなり運用できた。

Amplifyのバックエンド系の機能は全然使ってないので、そこは別の学習コストがあるのかもしれない。

Next

DBに数千件ある既存のコンテンツのページを生成する、各ページもSPAとして動的な機能を持つっていうのを作っていてv10.0が出て丁度良さそうだったNextを使った。

最初はFirebase Hostingへgenerateしたhtmlをアップロードしていったんだけど、ビルド時間の膨大化問題に悩まされてこれはSSRが必要になってくるなと思いVercelに移行した。

Vercelに移行してSSRを併用しはじめたらNext印象がまた変化しました。魅力をひとことで説明するのは難しいのだけど「いかに静的に確定する部分を増やして、それを CDN に置くか」 っていう文章を読んでそれや! としっくりきてしまった。

アプリケーションを書く視点で見ると、サバクラ2階層MVCの手続き型のコードだった部分が丸ごと宣言型のコードになるのでだいぶシンプルになったように感じました。

ちょうど2014年ぐらいにReactをはじめて使った時のDOM操作の複雑な手続きが一気になくなった感覚がウェブアプリケーション全体に拡張されているかのようです。

Gatsby

Nextのプロジェクトと似た構成で、数千コンテンツのページを生成するツールを作ってた。こっちはJAMstackという言葉に踊らさせていた時期だったのでNetlifyにデプロイしていた。

HerokuのPostgreSQLをパブリックで空けてデータを突っ込み、Netlifyのビルドサーバーから gatsby-source-pg 経由で吸う。という構成にしていた。

GraphQLの参照系のパワフルさはこのGatsbyの利用で体験できた。最終的にはもしろGraphQLフレンドリーになるようにポスグレのスキーマ側を調整したりしてた。

Gatsbyもnext buildと同じく最終的にはビルド時間の最適化との闘いになりそうだなと思った。Gatsby社のプラットフォームにインクリメンタルビルドの仕組みが提供されているけど。自力でそれを構成するには骨が折れそう(できる人は別のアプローチも取れそう)、という部分もVercelと似ている。

Laravel

はじめてまともに使ったけどRails並によくできていた。

強いて言うならPHP以外だとよかった。が、Symfony Components にもとづく物であろうのでPHPでなければ駄目だった。ありがとうPHP

PHP自体も久しぶりに書いたけどあんまり変わってなかった。ウェブに出回っている利用例の情報量は多いのだけど、Kubernetes環境+nginxの組合せで使っている人たちがあんまりいなかったのでインフラ構築に苦労した。

Viewは時代に逆行してSPA完全無視でbladeとlaravel-admin活用して作ってる。その方が目的が早く達成できそうだったのでそうした。

Helm

2019年はKubernetesとKustomizeに入門したので、今年はHelmfileでHelmパッケージとしてシステムをデプロイできるようにしてみた。2019年に導入しかけて、難しいなーと思い見送った時よりウェブ情報が充実していたのですんなり理解できて良かった。

Rails

3.x系のまま誰にもメンテされなかったアプリケーションを引き取り、サーバーの体調が悪化した時に救急手当をしばらくしていたんだけど「これ? Dr.コトーじゃん・・」と思ったのでSlackのアイコンをDr.コトーにした(とくに意味はない)

2020年まで精一杯生きて静かに息を引き取った。

Python asyncio

クローラー開発でPythonを使うことを決め(参考実装がPythonだった)、素朴なPythonブロッキング処理を書いていたんだけどそうすると長時間化していって動作環境のGitHub Actionsの従量課金に苦しめられることになったので並列実行を検討しはじめた。

今どきのPythonはasync/awaitでノンブロッキング処理書くぞと噂で聞いていて知っていたので挑戦してみた。Node.jsで慣れてるのですんなり使いこなすことはできたけど、どうしても外部ライブラリが非同期処理に対応できていなくて最終的にマルチプロセス+スレッドプール+asyncioで非同期と多重にしたら現実的なラインまで改善したので良しとした。

この過程でGitHub Actionsを月4000分以上使うなら1人チームのGitHub Enterpriseを申し込んだ方が安い。というニッチなtipsを得た。

自然言語処理機械学習

Pythonプログラミングのリハビリをして気をよくしたので、かねてからの懸案だった「数理的な知識をつける努力をせずに機械学習ツールを使いこなせるようになりたい」という実現に乗り出すことにした。

データ収集、前処理、トレーニング、テストなどの必要な手順の概念と、代表的なアルゴリズムチュートリアル。sklearn,gensim,kerasなどの組込み方を覚えたので。他人からアドバイスを受けて何か実践する。ぐらいのことができるようになったと思う(自分でウェブ検索しても出てこないような問題のアプローチを1から考える、というのは難しい)

Android

Jetpack Compose

ちいさなアプリを書いてだいたいどういう感じか把握した。

[Jetpack Compose] Glide経由でコンテンツ一覧を表示してタップされたらブラウザで開く

SwiftUI版も作ったと思うだけど、コードが残っていなかった。何も思うところがなかったのだろう

トルコ語アルファベット

トルコでのみアプリが落ちるという不具合があって、調べてみたらKotlinのenumJavaのStringに変換する処理で端末のロケールが使われトルコ語アルファベットになり正常に動作しないという現象が起きていた。

トルコ語アルファベット自体をこの時はじめて知ったので勉強になった。

Flutter Engine

Flutterは明かに今までのモバイルクロスプラットフォームとはアーキテクチャが違うので、その内部構造から理解したいなぁと思い。C++で実装されたエンジン部分のソースコードをチェックアウトして動かしたりしてみた。

Dartの部分を別の処理系に置き換えるには何が必要? という観点で読んでいた。1-2月に取り組んでてだいぶ間が空いてしまったのですっかり今は忘れてしまったけど。DartVMからSkia命令を叩く一連の流れを把握したと思う。

Mac DE Xamarin

Covid-19 Radar 関連でひととうりVisual StudioやらXamarin SDKを揃えたんだけどもう残ってない。

mhlwリポジトリは今見てみたらコミットが積まれていた。やるじゃんパーソル

https://github.com/cocoa-mhlw/cocoa/commits/master

Hiraganize

ウェブページを全部ひらがなに変換してしまうiPadのウェブブラウザ。漢字を読めない人とかに便利だと思う。

expo(React Native) + kuromoji.js でJSベースでテキスト変換処理をがんばっていたがモバイル端末で動かすには重過ぎたので途中からlibmecabとSwiftで書き直した。

ネイティブアプリの強みを生かしてSafari Actionにして、アプリ起動しなくても今見てるページをSafari上でそのまま変換するってところまで作った (https://twitter.com/laiso/status/1225438961738235905)

その後、漢字重要だよねということに気付いたので漢字は残しふりがなをふるブラウザに拡張してみた。

今は放置してる

プリント検索アプリ

私立小学校というアナログ推進団体と利害関係を持っているんだけど、彼等が毎日毎日 大量のミッションクリティカルなA4書類を配布してきて毎回紛失して困っていたので電子化して検索できるようにした。

これには ionic というフレームワークを使ってまずAngularベースのウェブアプリケーションを構築して

  1. 写真を取ってCloud Storageに上げる
  2. Cloud FunctionsでCloud Vision APIを叩いて文字情報を読み取る
  3. 文字情報をAlgoliaでインデックスして検索可能にする

という設計にした。ついでにオンブラウザでJPEG -> PDF変換機能も実装してみた。

ionicのいいところとしてはウェブアプリケーションとして作ってみて、その後モバイルネイティブAPIが必要になった段階でフォールバック的にWebViewでラップしたアプリに拡張できる点だと思う。

Angularが第一級市民だったので使ったけど今はクロスコンポーネント化されてReactやVue3向けのインターフェイスも提供されている。

note関連ツール

unnote

https://github.com/laiso/unnote

なんとなくこんな検索結果になったおもしろいなと思って作っていたやつ

ウェブサービスとして提供している人がいるのでそれを使うのがいいと思う。

noteのバックアップ/エクスポートファイルを無料で作成 - noteエクスポートβ

md2note

Electron下からnoteの非公開APIをNodeで叩いてパースしたMarkdownファイルで無理矢理記事を作成しちゃうやつ。

汎用的なツールにできないので自分だけ使っている状態……

findlist

https://gist.github.com/laiso/85b6e8666569a0ed6c86391f70e783cc

Twitterの非公開リストのパーミッションが壊れる障害があった時に、連番で探索するスクリプトを組んでた(いい話)

ここをこう持つ

歴代Xcodeのダウンロードサイズ

Xcodeがデカ過ぎることにむかついて作った

Qiitaからはてなブログにデータ移行するコード

Qiitaからはてなブログに記事を一括インポートする - Qiita

LINE BOT経由でYoutube動画をダウンロード

ペアブラウジング

なんとなくこういうツールが作れそうだと思ってやってみたけど用途はよく分からない

まとめ

  • 環境に恵まれているのもあってとにかくずーっとコード書いてた
  • 一方プロジェクトにはすぐ飽きてしまう。やりきる力が来年の鍵
  • 生活や健康には悪影響があると思うのでほどほどにできたらいいなと思う
  • あとこういうプログラミングが趣味な人は学習コストを低く見誤る傾向があると思った
  • この記事を自分で読んでて思ったけどこのまま行くと「町の変人発明家」ポジになってしまうのでは????

個人開発者と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のインサイダーっぽく受け取られているなと感じました。とくに開発に積極参加しているわけではなく、メンバーと交流があるわけではないのであくまでも部外者の視点であることを強調します