https://yakst.com/?locale=ja
Yakst
2023-08-15T09:13:41+09:00
Yakst
https://yakst.com/ja/posts/5797
2023-08-15T09:02:46+09:00
2023-08-15T09:13:41+09:00
スタートアップはいかにしてその活力を失うのか
<p>ちゃんとしたシードステージのスタートアップでは、エンジニアは業務経験を「夢中だ」と表現する。大きな会社では、得られるのは最良のケースでも「楽しい」程度だ。どうしてこうなってしまうのだろう。これは避けられないのだろうか。</p>
<p>スタートアップを夢中になれるものにするのは何なのかを調べてみよう。エンジニアはその時間のほとんどをこのループの中で過ごすはずだ。</p>
<ol>
<li>必要ならユーザと話し、ユーザの問題を見つけ出す。</li>
<li>作るべきもののアイディアを思い付く。</li>
<li>必要なら同僚とそのアイディアについて議論する。</li>
<li>アイディアを実装する。</li>
<li>成功を祈りつつリリースする。お祝いするか振り返りする。それからステップ1に戻る。</li>
</ol>
<p>10人以下の段階なら、各ステップは「楽しい」ものになり得る。</p>
<ol>
<li>興味のあるユーザに直接連絡し、彼らとビールを飲んでしまえる。</li>
<li>思いついたアイディアが会社にとってもあなたにとっても価値のあるものなら、それに取り掛かるためにそれ以外のものは全て放っておける。</li>
<li>そのアイディアについてほとんどすべての同僚があなたと議論するのに興味を持つ。なぜなら
<ol>
<li>彼らは深くコミットしている。あなたのアイディアがいいなら、あなたがそれを引き受けるよう勧めることが彼らの利益になる。あなたのアイディアが良くないなら、何が良くないのか説明することが彼らの利益になる。彼らはユーザとしても関心を持ってくれる。</li>
<li>彼らはあなたと一緒にそのアイディアに取り組みたいと思うだろう。</li>
<li>全員がコードベースの大部分をよく知っているので、彼らは役立つ考えを提供してくれる可能性が高い。</li>
</ol></li>
<li>実装が早い。
<ol>
<li>使いたいツールなら何でも使える。セキュリティレビューはない。</li>
<li>コードベースが小さい。すべてのことを頭の中に入れておけるので、広範囲にわたるリファクタリングを気持ちよくできる。ホットリロードが高速なので、トライアンドエラーのデバッグをすぐできる。</li>
<li>変更のマージが早い。CIには遅いテストは存在しない。安全な変更のためにPRレビューにブロックされる必要がない。masterにプッシュしてしまうことすらできるかもしれない。安全でない変更に対しては、PRレビューをお願いするのに同僚の肩を叩けばよい。</li>
<li>それほど多くのユーザがいないなら、バックエンドやデータベースの破壊的変更を素早くやるのにダウンタイムも許されるだろう。</li>
</ol></li>
<li>何でもできる!そのアイディアで株がいくらかの価値になるかもしれない。今後何年にもわたってその機能上に何かを作っていくかもしれない。歴史の本でビジョナリーとして取り上げられるかもしれない。ユーザとのパーティに参加してユーザから「このアイディアを思いついたのはあなたですか?何てこった、これこそ私を救ってくれたんです」と言われるかもしれない。チーム内の全員が深くコミットしているので、彼らはみんなあなたを応援してくれている。みんなの鼓動が同期している状態だ。</li>
</ol>
<p>会社が大きくなるにつれて、この楽しみはステップごとに減らされていく。100人を超える段階では、ループはこんな感じになる。</p>
<ol>
<li>ユーザと話すのはプロダクトマネージャのためだ、バカらしい!あなたは自分の得意なことにこだわる。良くてユーザインサイトの概要と、それに基づいた合理的タスクの優先度順リストが得られるくらいだ。最悪の場合、ユーザの誤解とマネージャの自分勝手なビジョンに基づいた混乱を招くタスクリストが存在し、各タスクがなぜ重要なのか誰も説明できないということもあり得る。</li>
<li>やりたいことにただ取り組むということはできない。調整はO(N^2)のコミュニケーションの混乱を招くだろう。マネージャは、他に進行中のことを考慮に入れて設計したあなた向けの計画を持っている。それら全てを終わらせた後なら自分のアイディアに取り掛かれるかもしれない。あるいは空き時間に秘密のうちにならそれらに取り組めるかもしれない。</li>
<li>自分のアイディアについて同僚と話すことはできるが、彼らはあまり興味を持ってはくれないだろう。あなたの成功から彼らは得るものが少ないのだ。実際には、昇進をあなたと争うこともあり得る。彼らには長いタスクリストがある。彼らには、やるべきことをせずにあなたと働く自由はない。そして彼らはあなたが触ろうとしているコードについて多くは覚えていない可能性が高い。</li>
<li>実装が遅い。
<ol>
<li>すべてのツールはすでに決定されている。もっと良い新しいツールが使えるかもしれないが、一貫性を崩して導入するまでの価値はない。「あえて」一貫性を崩してはいけない。何か新しいものを使いたいなら、マネージャに聞いてセキュリティレビューチームにメールを送ろう。</li>
<li>主にレガシーな理由によりコードベースが大きい。触るのが怖いコードがたくさんあり、誰ももはや理解していないコードがたくさんある。完全なテストが、何も壊していないという自信を持つための唯一の源泉だ。たくさんのトライアンドエラーのデバッグに依存する必要があるが、コードのコンパイルにしばらくかかるが故に、それぞれのトライアンドエラーのループは遅い。</li>
<li>小さなPRが取り込まれるのに2週間かかる。CIが成功するのに20分待ち、不安定で、テストを再実行する。レビュアからのフィードバックを待つ間にマージコンフリクトが積み重なっていく。レビュアーは十分なコンテキスト知識がなく、重要な提案をするインセンティブがないので、細かいことを指摘してくるだけだ。要求された変更を加え、変更をプッシュし、20分待つ。PRごとに3回繰り返す。</li>
<li>あらゆるインフラへの変更は、1000万ユーザに影響するダウンタイムやデータロスを避けるため、14ステップのプロセスから構成される。</li>
</ol></li>
<li>3ヶ月の静かな苦労の末、何かをリリースする。金曜日のミーティングでそれをデモするチャンス、すなわちあなたが取り組んだことをCEOに見せるチャンスを得られる。本当に注意を払うのはごく少数の同僚だけだ。あなたのやったことは、社員のほとんどには何の関係もない。何人かは成功をほめてくれるだろう。マネージャは、これは次の5ヶ月の評価期間において大きな成果だと言う。最も良いケースで20%の給与増と肩書きの変更が得られる。最悪の場合、4ヶ月の間に組織変更があり、新しいマネージャがきて、やっていたプロジェクトの優先順位が下がるかなかったことになる。この会社であなたがやったことに関して、新しいマネージャを納得させる必要がある。</li>
</ol>
<p>これは予防できるだろうか?私はそうは思わない。</p>
<p>注意深く見たなら、これらの問題はすべて基本的には次のことから発生していることが分かる。</p>
<ol>
<li>コミットの低下、それが引き起こすチームの団結の弱体化。</li>
<li>N^2倍のコミュニケーション、それによるマネージャと専門化の必要性、さらにそれによる個人の力と学びの幅の狭小化。</li>
<li>リスク許容性の低下、それによるあらゆるスピードの低下。</li>
</ol>
<p>1と2は、より多くの従業員を抱えることによる避けられない結果だ。3はより多くのユーザを抱えること、そして一部は政府の規制による避けられない結果だ。</p>
<p>しかし、この楽しさの死を遅らせるためにできることはある。1つは、それを加速しないことだ。多くのスタートアップは、通常はやり方をそのまま持ってこられるマネージャを大企業から引き抜くことで、巨大企業のプロセスをコピーすることによる企業化を不必要に加速している。例えば、多くのスタートアップは大企業がJiraを使っていると言う理由でJiraを使う。Jiraは使ってはいけない。Y Combinatorは創造性が逆の方向に向かうよう世界に気づいてもらおうとしている。つまり、大企業がスタートアップのように運営されるべきであると言うことだ。</p>
<p>大企業のやり方が大企業向けであると言うだけではなく、それは時代遅れでもある。あなたのスタートアップが大きくなるまでには、組織拡大の問題に対するより良い解決法が存在しているはずだ。従って、組織拡大の痛みを経験する時には、第一原理からそれを解決しようとするか、同じサイズにも関わらずより早く進んでいる他のスタートアップからインスピレーションを得ようとするべきだ。</p>
<p>あなたの会社を、独立したスタートアップの集まりのような組織にしよう。実際のところあなたのできる限りそうすべきだ。Ripplingのような会社はそれをある程度実現している。しかし、あなたのプロダクトのコンポーネントは密結合されすぎていて、かつほとんどのコンポーネントはそれだけでは収益を産まないので、これは通常はそううまくは行かない。</p>
<p>インセンティブを賢く設計することで1は意味がある程度に遅らせることができる。これについては別のブログ記事で深掘りするつもりだ。しかしインセンティブがないことは、大量の株とバリューへの影響の力の組み合わせと同じくらい良いことだ。</p>
<p>さらに言えば、一番確実なのは採用を抑えることだ。間違ったマネージャへのインセンティブと、急成長する他のスタートアップのようになるよう迫る無知な投資家と、従業員1人あたりの本当のコストを理解していないことから、ほとんどの会社は早く採用しすぎていると私は考えている。ツール(特にAI関連)が良くなるにつれて、小さなチーム1つがサポートできるユーザの数は増えていく。未来の起業家たちは拡大の痛みをそれほど心配する必要はないかもしれないし、仕事は誰にとっても楽しいものになるかもしれない。</p>
<hr /><b>関連タグの記事 </b><a href="https://yakst.com/ja/tags/startup#?from=feed">startup タグのついた記事</a>
doublemarket
https://yakst.com/ja/posts/5655
2020-03-24T19:20:57+09:00
2020-03-24T19:20:57+09:00
リモートワーカーのためのお役立ち情報
<p>Tips from Matt Yonkovit, Chief Experience Officer, Percona</p>
<p>Percona社は長年フルリモートチームとして働いてきた経験があります:</p>
<ul>
<li>私は15年以上リモートで働いてきました</li>
<li>当社の従業員の多くは、少なくともパートタイムでリモートで働いた経験があります</li>
<li>採用面接で尋ねた結果、我々と一緒に働きたいと考えた人にとって、大きな理由の一つが「リモートワーク」であることが分かっています</li>
</ul>
<p>現在、コロナウイルスの流行により、多くの人々が長期にわたってリモート作業を必要とする状況にあります。
しかし、リモートカルチャーとリモートワーキングのメリットを世界的な危機に結びつけるべきではないと考えています。
リモートワークによってあなたは以下のようなことが可能になります。</p>
<ul>
<li>どこにいようとも世界最高峰の人材を登用できます。</li>
<li>多様性、および革新を促進できます。異なる文化は、異なる経験および意思決定能力をチームにもたらします。</li>
<li>遠隔地の従業員でより多くの仕事をこなせるようになります!</li>
<li>健康的なワークライフバランスを促進できます。</li>
<li>従業員の定着率と仕事の満足度を高めることができます。</li>
</ul>
<h3>注意すべき5つのこと:</h3>
<p>1. ワークライフバランスは自宅作業の大きなメリットですが、仕事と家庭生活の境界を失いやすいです。特に、同じ環境で仕事と社交をする場合は起こりがちです。</p>
<p>2. あなたが家にいることに家族が慣れていない場合、彼らは仕事と生活の境界を混乱させてしまう可能性があります。例えば、配偶者が毎日オフィスに出社しなければならない時、「家にいるなら」という理由で新しい雑用を頼まれるかもしれません。</p>
<ul>
<li>リモートで作業を始めた時、これは私にとっても大きな挑戦でした。妻が毎日のように用事を私にお願いしてくるのです…「配管工に電話してもらえる?」「食料品店にお使いに行ってもらえる?」といった具合に。</li>
<li>自宅で仕事をすることで、あなたとその家族に柔軟性が与えられます。利点として、あなたがその柔軟性を使用することは何も悪いことではありませんが、やりすぎに注意してください。そうでなければ、あなたはフルタイムのただ家にいるだけの人になってしまい、本来果たすべきビジネスができなくなってしまいます。</li>
<li>子どもたちは、就業時間と非就業時間の違いをよく理解していません。彼らはあなたが家にいることを知っていて、注意を引こうとしてきます。</li>
</ul>
<p>3. 孤独が問題になる可能性があります。自宅で仕事をするのは素晴らしいことですが、長時間仕事をしているとしばしば孤立感や孤独感につながってしまいます。実在の人々とつながる必要があるでしょう。例えば、ビデオ、チャットなど無しで、誰とも対面することなく終日コーディングをすると、暫く経って非常に孤立してしまうことがあります。</p>
<ul>
<li>たとえそれが仮想的なものであっても、個人としても仕事としても他者と互いにつながり続けることが重要です。</li>
<li>長期に渡り誰とも連絡を取らないでいると、同僚やプロジェクトの状況を把握するのが難しくなります。可能な限り、オンラインのチャットや電話会議などで声を出して、常に関わっているようにしましょう</li>
</ul>
<p>4. 自宅で作業をしていると、通信のラグ(≒ ネットが重い状態)は致命的です。通常の日中帯は、あなたが自宅のインターネット通信を優先利用できるでしょう。しかし家族が帰宅するやいなや、彼らの情報デバイスやPCが通信をし始め、あなたのビデオ通話に影響を与えてしまうかもしれません。自宅のネットワークが過負荷になった場合でも作業を完了できるよう、こまめなバックアップを心がけましょう。</p>
<p>5. 憶測の透明マント。同僚があなたがオンラインで仕事をしていること、そして物事の状態を知っていると思い込まないでください。完全に仮想化された世界でもないので、対面をどれだけ情報を拾い上げているかは分かりません。</p>
<ul>
<li>プロジェクトまたは作業のステータスは、直接連絡または更新しなければ失われていきます</li>
<li>ソーシャルキューやボディーランゲージがなければ、多くのことを見失います。人と会う時、彼らが「動揺している」「興奮している」「ぽかんとしている」かどうかをボディランゲージから読み取ることができます。Slackではできません。</li>
</ul>
<p><img src="https://www.percona.com/blog/wp-content/uploads/2020/03/work-from-home-1536x562.png" alt="引用1"></p>
<h3>健康的な職場環境を促進する5つのこと</h3>
<p>1. 現実(物理)と仮想の境界が必要です:</p>
<ul>
<li>自宅の作業場所を出来る限りドアのある部屋の中にして、あなたが家族と普段過ごしている場所から離します。リビングルームで一日中働いていると、仕事と仕事以外の境界線がぼやけます。また、一日の終わりに出入りすることができるオフィスを持つことは、あなた自身の幸福につながるバランスと境界を作ることに役立ちます。</li>
</ul>
<p>2. 成功のために机をセットアップしましょう:</p>
<ul>
<li>当たり前のことかもしれませんが、背中をしっかり支えることができる素敵な椅子に投資してください。</li>
<li>複数のモニタと、適切なクレードルを準備しましょう。</li>
<li>重要なデータをバックアップするための、暗号化されたドライブがあることを確認してください。</li>
<li>「仕事」専用のマシン/ラップトップで作業を行い、私用PCとは区別してください。これは境界を設定するのに役立つだけでなく、セキュリティとコンプライアンスの理由からもベストプラクティスです。</li>
</ul>
<p>3. カレンダーを活用しましょう:</p>
<ul>
<li>あなたのカレンダーに勤務時間をはっきりとマークします。実際、私は毎日午前7時前と午後6時以降をブロックする設定にして、他の人にミーティング予定を入れられないようにしています。 Googleカレンダーには、勤務時間を設定する優れた機能があります。</li>
<li>休憩と昼食の時間を(アラート付きで)カレンダーに設定します。自宅はとても快適です。デスクで食事をしたり、長いストレッチをしたりも簡単にできますが、起きてストレッチしたり、少し散歩したりすることも重要です。新鮮な気分を保つためには休憩を取る必要があります。</li>
<li>家族やルームメイトとスケジュールを共有して、可能な限り休憩中に現実の人とコミュニケーションできるようにします。</li>
<li>定期的なスケジュールを登録しておくことで、個人的なアポイントメントや「休業中」の時間に家の周りで面倒を見る必要があるものをスケジューリングできます。</li>
</ul>
<p>4. 強固なインターネット回線とバックアップを用意しましょう。誰もが時折インターネットの問題を抱えてしまうものなので、しっかりしたバックアップ計画を立てておくのは良いことです。お住まいの地域のサービスに応じて、それは携帯電話かもしれませんし、第2回線である場合もあります。</p>
<ul>
<li>過去には、プライマリおよびセカンダリインターネットオプション(ケーブルベースのインターネットおよびDSLベースのインターネット)を準備してたこともありました。これは必ずしも必須要件ではありませんが、もしあなたがコンサルタントとして働いており、時給換算または期限が厳しい場合には、作業が何もできなくなるダウンタイムは避けなければいけません。</li>
<li>モバイル通信でのインターネットは会議や軽作業には十分ですが、契約しているプランの上限を把握し、シグナルが家の中で上手く機能することを確認してください。問題がある場合は、ほとんどのプロバイダーからセル信号ブースターを入手できます。</li>
<li>電源とインターネットは、リモートワーカーにとって最も重要なユーティリティです。ノートパソコンや携帯電話を充電するために外部バッテリーを用意することが不可欠です。</li>
</ul>
<p>5. 仕事上付き合う人々との関係を保つ</p>
<ul>
<li>リモートワークをしていても、同僚と会話をしたり、生活の一部を共有したりするのをやめることにはなりません。遠隔地にいる場合は難しいかもしれませんが、こうした人間関係を維持することはさらに重要です。</li>
<li>週末の予定について周りに尋ねてみたり、隣人について愚痴を言ってみたり、バケーションの計画を話したりしましょう。普段の仕事中のように!</li>
</ul>
<h3>マネージャーがリモートワーカーのためにすべき5つのこと</h3>
<p>1. 定期的なチームミーティングを開催し、カメラの電源を入れます!リモート環境では対面の時間が必要です。これは大きな違いを生みます。</p>
<p>2. やりすぎか!というくらいコミュニケーションをとりましょう。人々が知らないことを知っていると思い込むのはとても簡単です。 Slackで毎日、日次ミーティングまたは朝礼を実施することは、人々の関心を引き付け、ループを維持するのに役立つ素晴らしい方法です。</p>
<ul>
<li>Slackで、毎日のリマインダーを設定して、「今日の良いニュースは?」または「今日、支援が必要なタスクは何ですか?」とメンバー全員に簡単な質問を送ります。</li>
<li>チャットを開始する時に挨拶し、サインオフする時は別れを告げます。実際のオフィス環境で行うことは、チャットでも行いましょう。</li>
<li>邪魔にならないようにしつつ、チームの成果を把握し、他の人と広く共有してください。</li>
<li>チーム用のソーシャルチャンネルを設定して、チームメンバーがお互いに繋がり、関与し続けられるよう、些細な会話・冗談などを奨励します。</li>
</ul>
<p>3. 全ての直属の部下、および重要な同僚と定期的に1対1の打ち合わせを設定し、継続します。ビデオチャットで実施し、良いメモを保存し、会議後にそれらのメモを共有するようにしてください。可能であれば、チームメンバーが編集・追加できるトピックのリストを事前に用意してください。</p>
<p>4. 定期的にいくつかの実際に対面する時間を計画します。リモートワークは素晴らしいですが、年に数回、直接会うことは、健全なチームビルディングに不可欠です。</p>
<p>5. タスクマスターにならないでください!</p>
<ul>
<li>あまりにも多くの会議を細かく管理したりスケジュールしたりしないでください。初めてリモートワークを導入する場合、多くのマネージャーが人々の足並みを揃えるために大量の「会議」時間を過度にセットアップ・スケジュールしますが、そこでは誰もが何もできません。</li>
<li>従業員に柔軟に対応しましょう。家族のスケジュールと衝突しないように、会議の時間を調整します。午後に子供たちがバスで帰宅してくる場合は、その時間帯に会議を入れるのを避け、少しの間サインオフして家族と交流するように促します。あまりに多くの用事に引っ張られてるのでもない限り、彼らはより生産的になります。</li>
</ul>
<p><img src="https://www.percona.com/blog/wp-content/uploads/2020/03/work-from-home1-367x220.png" alt="引用2"></p>
<h3>リモート環境に不可欠な5つのツール</h3>
<p>1. Slack (or 同等のビジネスチャットサービス)</p>
<ul>
<li>これはメールよりも大切です。同僚とすばやくチャットできることは、リモート作業環境にとって不可欠です。</li>
</ul>
<p>2. Zoom (or あなたの好きなビデオ会議ソフトウェア)</p>
<ul>
<li>対面は非常に重要であり、信頼できる会議ツールが不可欠です。</li>
</ul>
<p>3. Google Calendar (or 同様の共有オフィスカレンダー)</p>
<ul>
<li>リモート環境では、カレンダーを適切に設定することが重要です。あなたがオフィスにいる時には、ドアが開いていれば人々は立ち寄って確認することができますが、リモートの場合、カレンダーはあなたのオフィスのドアとなります。</li>
</ul>
<p>4. Google Docs (or Office 365)</p>
<ul>
<li>ホワイトボードが無いため、これらはアイデアをすばやく共有し、コメントを収集し、フィードバックを提供・共有する方法として機能します。</li>
</ul>
<p>5. Task Management Software (Asana, Basecamp, JIRA, etc.)</p>
<ul>
<li>業務がコーディングであれグラフィックスであれ、ほとんど全てのチームメンバーは、引き渡しまたは期限付きの成果物を必要とする大規模プロジェクトの一員です。プロフェッショナルなタスク管理システムにより、すべてのチームメンバー間で明確な期限とタスクの処理が容易になります。</li>
</ul>
<p>Percona社が世界中からどのようにデータベースにイノベーションをもたらしているのか、さらに知りたい方は<a href="https://www.percona.com/about-percona">こちら</a>を参照してください。
また、Percona team にジョインしてくれる方も<a href="https://www.percona.com/about-percona/careers">こちら</a>から募集しています!</p>
<hr />
kakuka4430
https://yakst.com/ja/posts/5604
2019-12-17T12:15:40+09:00
2019-12-17T12:15:40+09:00
Etsyにおけるエンジニアのキャリア開発
<p>2018年の5月の終わり頃、Etsyは社内でエンジニアリングのキャリアラダー (Career Ladder) を発表しました。
現在では、広く一般にこのラダーを公開しており、私たちがなぜこのようなものを作ったのか、なぜこの内容になったのか、そして発表以降どのように活用されてきたのかを詳細化しています。</p>
<p><a href="https://etsy.github.io/Etsy-Engineering-Career-Ladder/">こちら</a>をご覧ください。</p>
<h3>キャリアラダーの定義</h3>
<p>キャリアラダーは、企業内におけるエンジニアの成長パスを描くのを助けるツールです。
エンジニアが最良の状態で新たな職務を担うにはどうすればよいかの指針を提供し、彼ら彼女らのマネージャーがそのパフォーマンスや振る舞いを評価、また監督できるようにすべきものです。
よくできたキャリアラダーは、キャリアの前進が、企業文化やビジネスの目標およびガイドの原則に寄り添うようように作られており、採用やトレーニング、そしてパフォーマンス評価を導くリソースとしての役割も果たします。</p>
<p>Etsyは、今回以前にもいくつかのキャリアラダーの形を設けていました。
以前のキャリアラダーはEtsyの全ての従業員に適用されるもので、あらゆる従業員に対し、全ての原則について同じレベルの期待内容のセットを定めていました。
総じて、以前のラダーは小さな企業だったEtsyに対してはうまく機能しましたが、エンジニアリングチームが成長を続けるにつれ、その内容を実践的な期待に応えるものへアップデートする必要があると私たちは気づきました。従来のラダーの内容は幅広すぎて、具体的なアクションに落とし込めないように感じられたからです。</p>
<p>その結果、私たちはエンジニアリングに特化した、このキャリアラダーを開発しました。期待内容をより明確化し、Etsyにおいて一定のレベルのエンジニアになるとはどのようなことなのか、統一された見解を作り上げるためです。このラダーが発表されてから1年あまりになりますが、その間にもパフォーマンスレビューや昇進のサイクル、たくさんの採用、そして1-on-1のキャリア開発に関する会話が行われてきました。私たちはEtsyのエンジニアリングのキャリア開発に有意義な改善ができたと自信を持って言えますし、このキャリアラダーを一般に公開することで、他の企業においてもエンジニアリングのキャリア成長をサポートする上で役立てていただければ幸いです。</p>
<h3>Etsyにおけるエンジニアリングのキャリアラダーをデザインする</h3>
<p>私たちは、様々なレベルのエンジニアやエンジニアリングマネージャーを集めて、新しいキャリアラダーのイテレーションを作り上げるためのワーキンググループを結成しました。ワーキンググループにはMiriam Lauter、Dan Auerbach、Jason Wain、そして私 (Ryan Bateman) がメンバーに含まれていました。まず私たちは、当時の全社的なキャリアラダーを研究することから始め、その利点と限界、またそれがエンジニアリングのキャリア開発にどのような影響をもたらしたかを議論しました。新しいバージョンのラダーがEtsy独自のものである必要があることを理解しつつも、私たちは一般に公開されている他社のラダーを調査することに時間を費やしました。戦略的なアプローチと採用可能なフォーマットを理解する上で、似たようなプロセスを踏んできた企業を参考とするためです。
とりわけ<a href="https://labs.spotify.com/2016/02/08/technical-career-path/">Spotify</a>、<a href="https://gist.github.com/jamtur01/aef437a79fee5a9cefdc">Kickstarter</a>、<a href="https://technology.riotgames.com/news/debugging-titles-part-i">Riot Games</a>、そして<a href="http://dresscode.renttherunway.com/blog/ladder">Rent the Runway</a>には、彼ら彼女らのプロセスと結果に対する洞察を提供してくださったことを感謝します。これらの企業の資料をレビューすることの価値は計り知れないものでした。</p>
<p>私たちの最初のステップは目標を共有するうえで互いの認識を合わせることだと定め、いくつかの演習を実施した結果、草稿作成のプロセスを促進し、内容の有効性を評価するうえで有意義な方法をもたらすような一連の理念が生まれました。
これらの理念は、ラダーを開発する上での我々のアプローチの基礎を定めました。</p>
<h3>キャリアラダーの理念</h3>
<h4>エンジニアにとって有意義なキャリアの成長をサポートする</h4>
<p>私たちのキャリアラダーは、社内のあらゆるエンジニアに対し方向性を示すうえで、十分に明確かつ柔軟なものでなければなりません。
私たちは、このドキュメントが確かな影響力のある形でキャリアを発展させられるよう、具体的なアクションに落とし込めるステップを提供することを念頭に置いています。
理想的には、エンジニアたちがこのラダーを利用してEtsyで過ごした時を振り返り「ここで伸ばしたスキルは、このさき自分のキャリアを通じてずっと使っていける」と言えるようなものであってほしいです。</p>
<h4>エンジニアリングにおける期待を統一する</h4>
<p>特定のレベルにおける期待に応えるうえで必要とされる事柄について、全てのエンジニアリングの部署と足並みを揃える必要がありました。
もし私たちのラダーに自由な解釈の余地がありすぎると混乱を招きますし、特にそれが昇進プロセスに関係する場合はなおさらです。
私たちは、皆が自分のレベルを表現する上で簡潔かつ覚えやすい方法を持ってほしいと考え、どのようにして昇進が発生するのか、また彼ら彼女らが同僚たちとともに何を期待されているのかを正確に理解できるようにしたいと思いました。</p>
<h4>有効なキャリアパスの多様性を認識する</h4>
<p>たとえあなたが<a href="https://codeascraft.com/2019/08/02/code-as-craft-understand-the-role-of-style-at-e-commerce-shopping/">機械学習のモデルを構築</a>していようと、<a href="https://codeascraft.com/2018/09/26/how-etsy-localizes-addresses/">プロダクトのローカライズ</a>をしていようと、エンジニアリングには幅広いコンピテンシーに渡るスキルが必要ですし、各チームやプロジェクトはそれぞれに強みを持った人材を採るものです。
私たちは規律について信じること、すなわち有効で意味のあるキャリアパスは、異なる視点や能力を携えたエンジニア達のあらゆるレベルにおいて存在するのだということ、そして誰もがエンジニアとして同じように成長するわけではないのだということを、明確化したいと思いました。
私たちが幅広いコンピテンシーにおける成長に価値を置くこと、また全ての人がキャリアにおける特定の時点において同じ一連の強みを持つとは想定しないことを、明文化するよう意図したのです。</p>
<h4>成功を認識するにあたり、バイアスの発生する余地を制限する</h4>
<p>キャリアラダーは、組織における潜在的なバイアスを和らげるのを助けるツールのうちの一つです。
私たちは自分たちの使う言葉に思慮深くある必要があり、インクルーシブで客観的、そしてアクションにつながるようなものにしなければなりません。
キャリアラダーは、採用や昇進のように主要なキャリア進展時の礎として利用されるものですので、こういったプロセスにおける潜在的バイアスを和らげるうえで、明確で一貫性のあるラダーを開発することが欠かせません。</p>
<h3>Etsyにおけるエンジニアリングのキャリアラダー開発</h3>
<p>これらの理念を念頭に、成功に必要なものはなにかを知るべく、私たちは最初の一歩を踏み出しました。
ラダーの草案フォーマットを作成するのに加えて、どのようにしたら自分たちの成し遂げた改善を定量化できるかの見極めに取り掛かったのです。
私たちはエンジニアリングのリーダー層、人事、従業員のリソースグループ、そしてもちろんエンジニアといったステークホルダーを直接巻き込む必要のある、主要な領域の要点を押さえました。
ラダーの使い手となるような、昇進したいと考えているエンジニアや、昇進に向けてエンジニアをガイドしたいと考えているマネージャー、あるいは建設的なパフォーマンスフィードバックをする必要のあるマネージャーなど、複数の視点を盛り込むようにしています。</p>
<p>暗黙のバイアスは、これらのプロセスに潜んでいるのを認識すること、取り除くことが難しいことでよく知られており、そのためには可能なかぎり多くの個人から受けたフィードバックを直接組み込む必要があると、私たちは考えました。内部の人からも外部の人からも、専門領域や規律を超えた、幅広い視点をラダーに組み入れられるようにです。</p>
<p>私たちの前進を計るための戦略には、フィールド調査とオープンなフィードバック提供のお願いが含まれ、加えて1対1での深掘りフィードバックセッション、そして私たちの言葉が成長志向であり、かつ慣用的表現に陥っていないかの監査が第三者によって行われました。
内容の構造や成り立ち、ラダーの詳細な解釈、キャリアの議論の指針とするうえでのラダーの使い道、そして私たちの理念との連携度合いについてフィードバックを受けたのです。</p>
<p>受けたフィードバックは、ラダーを形作る上で欠かせないものでした。
重複や、不要なもの、また混乱を招く内容を取り除くことを助け、さらには私たちの明言している理念に沿っており、かつ意図通りに伝わると思えるような形のフォーマットを作ることを助けてくれました。</p>
<h3>そしてようやく、Etsyのエンジニアリング・キャリアラダーです。</h3>
<p>Etsyのエンジニアリング・キャリアラダーの最終版は<a href="https://etsy.github.io/Etsy-Engineering-Career-Ladder/">こちら</a>です。</p>
<p>Etsyのエンジニアリング・キャリアラダーは二つのパートに分かれます。
レベルの進行度とコンピテンシーのマトリックスです。
この構造は、エンジニアリングにおける各レベルの定義を保ちつつ、Etsyがどのようにして多様なキャリアパスを運用するのかを明確にします。
レベルの進行度はキャリアラダーの根幹を成すものです。
各レベルに対し、ラダーは期待や実績、コンピテンシーのガイドラインといった全ての要件を列挙します。
コンピテンシーのマトリックスは、本人のロールや職務、組織における目標を満たすうえで欠かせない行動やスキルを列挙します。</p>
<h4>レベルの進み具合</h4>
<p>レベルの進行度における各セクションは、その肩書を持つエンジニアに求められる要件について簡潔な定義を提供します。
そこではエンジニアが解決する問題のタイプや、組織の目標や優先度に対して当人の仕事がもたらす影響、一緒に働く他のメンバーにどんな影響を与えているかといった、数々の要素が詳細化されています。
エンジニアレベル1以降は、期間を通じて上げた成果を規模と複雑さの面から詳細化するような、期待される業績の概要を書きました。
そしてコンピテンシーの成長に対する期待内容を設定するうえで、私たちはエンジニアがどのレベルの習熟度に達する必要があるのかを広く概略化しました。</p>
<h4>コンピテンシー</h4>
<p>レベルの進行度が、特定のレベルのエンジニアに<em>なに</em>が求められているかを詳細化するものであるならば、コンピテンシーはいわば、<em>どのようにして</em>彼ら彼女らが期待に合致していると予期できるかを詳細化するものです。私たちは5つのコアコンピテンシー領域の概要を作成しました:</p>
<ul>
<li>デリバリー</li>
<li>ドメインにおける専門性</li>
<li>問題解決</li>
<li>コミュニケーション</li>
<li>リーダーシップ</li>
</ul>
<p>この5つのコンピテンシー領域それぞれにおいて、様々なレベルの習熟度に達するのがどのようなことを意味するのか分かるように、コンピテンシーマトリックスは例となるようなリストを提供します。
コンピテンシーの習熟度は累積的です。
すなわち、問題解決において「上級」の人は、「中級」や「初級」の問題解決者が持つスキルと性質を備えていることが期待されます。</p>
<h3>私たちの成功を評価する</h3>
<p>私たちは、このラダーを2018年の5月に内部リリースしました。
ただ、サイクルの途中で成功の評価のしかたを変更しないことが重要だったので、パフォーマンスレビューのプロセスに対してすぐに変更を加えたりはしませんでした。
私たちは単に、キャリア開発の議論を進める際にエンジニアたちとそのマネージャーたちが活用できるよう、参考資料としてラダーをリリースしたにすぎません。
次のパフォーマンスサイクルが開始した際に、ラダーの詳細をドキュメンテーションやコミュニケーションと融合させはじめ、評価の基準として利用されるようにしました。</p>
<p>今日では、このキャリアラダーはEtsyのエンジニアのキャリア成長の指針となる、主要なツールの一つです。
全社的な調査から取得したデータを活用することで、エンジニアがキャリアの機会をどのようにみているか、またマネージャーがその成長を導く手腕において、有意な改善を見てきました。</p>
<p>プロセスの初めに概略化した理念を思い返すことで、過去の1年半の年月を振り返り、Etsyのエンジニアに起こった変化を認識し、私たちが成功につながると考えた目標と照らし合せてラダーを評価することができます。
ここで、一つ一つの理念と、私たちが各々をどのように達成してきたかを振り返ってみましょう。</p>
<h4>エンジニアにとって有意義なキャリアの成長をサポートする</h4>
<p>キャリアラダーの内容は、私たちの文化と<a href="https://www.etsy.com/about">ガイドの原則</a>によって導かれているものの、概してどのコンピテンシーもEtsy特有のものではありません。
コンピテンシーのカテゴリにおける期待、業績、それに「初心者」から「周囲をリードするエキスパート」に至るパスは、エンジニアの影響力の成長を示し、彼ら彼女らがロールやチーム、そして会社にもよらず、自身のキャリアを通じてどのようなことを成し遂げられるかを認識できるように設計されたものです。</p>
<p>またコンピテンシーのマトリックスによって、あるレベル<em>内での</em>エンジニアのキャリア開発を指導することもできます。
新しいレベルへ昇進することが、時間をかけて期待に合致していることを示す必要のある主要なマイルストーンである一方、いくつかの主要なコンピテンシーに注目して習熟度のレベルを進めれば、たとえ同じレベル内であっても継続的な成長を実証することができます。
これによってエンジニアとマネージャーは、次の昇進に向けた幅広い要件を満たすための計画という達成困難になりがちなタスクに追われる代わりに、そこへ到達するための目標を段階的に設けることが推奨されます。</p>
<p>以前の私たちのラダーに比べ、スタッフエンジニアになるにあたって専門性の幅を広げる必要性に縛られるようなことはなくなりました。
私たちはどの専門領域もたいへんに複雑で、解決しなければならない未知の問題があり、各分野で大いに成功している本人の領域を超えて他の分野へも専門性を広げるように要求することが、エンジニアの成長を制限していたことに気づいたのです。
いまのラダーに概要が書かれた期待内容のもとであれば、エンジニアは現在の領域で思う存分専門性を深められるでしょう。</p>
<h4>エンジニアリングにおける期待を統一する</h4>
<p>各レベルの定義は、いくつかの期待内容、業績、そしてコンピテンシーの習熟度のレベルに関するガイドラインのみから成り立っています。
読み解くのは易しく、必要になった時にすばやく要求を理解できるよう見返すことができます。
少しの振り返りを行えば、エンジニアは自身のレベルにおける3つから5つの期待内容に対し、自分がどのくらい合致しているかを容易に描くことができるでしょう。</p>
<p>ラダーのリリースに先立ち、これらの定義が組織におけるエンジニアの期待の現実に沿ったものになっているかどうか、全てのエンジニアリング組織のリーダーから同意を得ました。
そしてリリース以来、昇進プロセスをラダーの内容に合わせてきました。
マネージャーに対しては、新しいレベルに必要とされる業績として明記された期間を通じ、候補者がどのようにして期待に合致してきたのかを説明し、提示されたコンピテンシーの習熟レベルを実証できるような実例をもって適格とするよう、要求しています。</p>
<h4>有効なキャリアパスの多様性を認識する</h4>
<p>マネージャーに対しては、キャリアの進展について語るとき、特定のロールを念頭に置いてコンピテンシーのドキュメントを利用するようお願いしました。
コンピテンシー・マトリックスにおける個々の例は、プロダクトエンジニアやセキュリティエンジニアなど、個別のロールによってどの程度適用可能かが異なってくるものですし、この順応性は、習熟度のレベル定義で合意した振る舞いや結果と足並みを揃えつつも、個々の規律ごとの成長を可能にするものです。
一連のスキルの例は、様々な専門領域におけるコンピテンシーへの適用を、より良くそれぞれの文脈に沿ったものにできるよう、各コンピテンシーのカテゴリ毎に提供されます。
さらに、私たちはあえて、チームや組織の成功に欠かせないコンピテンシーを詳細化していません。</p>
<p>コンピテンシー・マトリックスに内在する柔軟性と習熟度のレベル分けのしかたをマネージャーに受け入れてもらうことで、互いの違いを受け入れてあらゆる形の成功に価値を見出すチームを築きつつ、私たちは様々な形で表れるエンジニアの成長を普遍的に認識できるようになりました。
たとえば技術的なイニシアティブを前に進めるような、特定の分野のスキルが高いエンジニアリング・リーダーや、<a href="https://noidea.dog/glue">皆を結びつける</a>役割を担って正しい問題解決に向けチームを調整するような、コミュニケーション能力の高い他のエンジニアリング・リーダーを見出したりすることにより、マネージャーはより多様性のあるチームを成長させることができます。
私たちは、リーダーシップは様々な形態を取ることに気づいていますし、そのことはコンピテンシーマトリックスにも反映されているのです。</p>
<h4>成功を認識するにあたり、バイアスの発生する余地を制限する</h4>
<p>キャリアラダーは、組織として潜在的なバイアスを和らげる方法のほんの一部でしかありません。
Etsyの人事プロセスやキャリア開発プログラムの他の部分にも、組み込まれたチェックやバランスがありますが、キャリアラダーは他のプロセスを形作るうえで重要な役割を果たすため、私たちはかなり意識的にこの理念へアプローチしています。</p>
<p>コンピテンシーは本人の人格に基づくものではなく、例えば「フレンドリーである」など、人柄や行動に対する主観的な捉え方に基づく内容を取り除くよう配慮しました。
人によって吸収や理解のしかたに差が出ることを減らそうと注力した結果、全ての内容は慣用的な表現を排除したものとなっています。
また私たちは、各々の期待内容に対しカテゴリを定義することによって、レベル間の言葉に一貫性が保たれるようにしました。
たとえば、各レベルにおいてエンジニアが解決することが求められる問題の複雑さを定義することにより、私たちはレベル間で責務の乖離が発生する余地をなくし、以前のレベルからの成長と結び付けられなくなることがないようにしました。</p>
<p>さらには定量化できるような言葉も明確に避けました (例: 2つ以上のカンファレンスに登壇した、等)。
なぜなら特定の数値を達成するための機会は、あなたの役割、チーム、そして個人的な状況により著しく制限されますし、コンピテンシーの背景にある真の意図を汲まないキャリアのアドバイスに繋がってしまうからです。
加えて、たとえば昇進の時などにラダーと照らし合わせて個人を評価することは、数字に要約できるものではないのです。
点数を計算したり、個人をチャートの上にグラフ化するなどということはありませんし、明確に定められたロールやプロジェクトにおける経験年数もありません。
主観性を減らすことが潜在的なバイアスを減らすのに欠かせない一方で、このように凝り固まった数値のガイドラインは、個人のロールに十分な柔軟性を与えず、実際のところ私たちの理念に反します。</p>
<p>もっとも重要なことは、このラダーはEtsyのエンジニア、すなわち個々の状況がいかにしてキャリアの進展に寄与するか、あるいは妨げになるかについて、個人的に実経験を持ったエンジニアたちからのフィードバックを受けて直接形作られたものだということです。</p>
<p>私たちは、Etsyにおけるエンジニアの現在進行形のキャリア成長を支えることに対し非常に情熱を持っていますし、自分たちのミッションを真に支える形でそれを実践しています。
プリンシパルエンジニアへのパスは全てのインターンに対して開かれていると信じていますし、このラダーはそのパスを明確で行動可能なものに落とし込むものだと思います。
このラダーが一つの例としての役割を果たし、私たちが参考にした一連の資料のように、あらゆる場所のエンジニアのキャリアを導く一助となれば幸いです。</p>
<p>もしあなたが、私たちと共にご自分のキャリアを成長させることに興味をお持ちでしたら、ぜひお話がしたいです。<a href="https://www.etsy.com/careers?ref=ftr#engineering">こちら</a>をクリックしてください。</p>
<hr />
meiq
https://yakst.com/ja/posts/5585
2019-09-25T12:08:13+09:00
2019-09-25T12:08:13+09:00
バグについて謝るのはやめよう
<blockquote>
<p>ハニー、見てくれよ。俺はテクノロジーを使ってるんだから</p>
<p>謝ってる時間なんてなかったよ</p>
<p><strong>THE STOOGES, <a href="https://www.youtube.com/watch?v=eDHdleEX6-s">SEARCH AND DESTROY</a></strong></p>
</blockquote>
<p>この数年というもの、私が作ったもののバグについて謝るのを意識的にやめようとしてきました。</p>
<p>バグについて謝ろうという誘惑は強いものがあります。昔は自分も謝っていました。私の書いたコードのせいで、同僚の1日が台無しになってしまったり、ユーザに対する影響が出てしまった時には、「おっと、ごめんよ!そんなこと考えもしなかった」と言っていたものです。</p>
<p>この謝罪の動機はもっともなものです。あなたはチームのためにベストなことをしたいわけです。つまり、ほんのちょっと違ったやり方をしていれば、問題は防げたはずなのです。そう考えると、バグについて謝るのは害がないように見えます。しかし、次のような文化的なアンチパターンの原因になります。</p>
<ul>
<li>ある人またはコードの一部を、ある障害の原因として非難してもいいという考え方を助長します。</li>
<li>コードを書いた時、もっとよく書く<em>べきだった</em>という印象を与えてしまいます。これは、ほとんど確認しようのない非現実的なことです。</li>
<li>自分の書いたコードにバグがあることを恥ずかしいと思うのが正しい感情であると位置付けてしまいます。自分がもっといいエンジニアだったら、もっといいチームメイトなら、バグは存在しなかったのに、というように。</li>
<li>あなたがチーム内では上の方の立場なら、これらのアンチパターンの影響は非常に大きくなります。あなたがバグについて謝っているのを見れば、バグのないコードを書こうと頑張らなくてはならないとチームメンバーは考えるはずです。コードにバグがあったら恥ずかしいと感じることでしょう。</li>
</ul>
<p>あなたが頭ではこれらの間違いを<em>信じて</em>いないとしても、バグについて謝っていてはこれらを信じていると裏付けてしまうことになります。チームメイト達はあなたが何を本当に信じているかには気づきません。あなたが何を言って何をしているかしかわからないのです。</p>
<p>どんなコードにもバグがあることは、みんな知っています。コードは何らかの制約の下に書かれるものです。締め切りであったり、品質よりゴールであったり、未来に関する不完全な知識であったり。<em>エンジニアとしてのあなたのスキルも制約</em>です。バグのない完全なコードを書こうとしても、それを成し遂げることはできません。だとすると、バグについて謝るのは理にかなっていると言えるでしょうか?</p>
<p>私が自分に課したこのルールは、制約が原因で発生した問題と、私自身の過ちによって発生した問題を強制的に区別させてくれます。もし、ある問題が私の不注意(あるいはアクションを起こさなかったこと)が原因で問題を起こしたと考えるなら、それは私が謝るべきことでしょう。しかし、なすべきことのためにコードを書いて、その結果、状況に合わせてそれが動かないということになったのであれば、謝るべきことは何もありません。いつも<em>何か</em>は起きる運命だったのです。</p>
<p>バグについて謝るのをやめるという誓いを立てましょう。リーダーシップをとる立場ならなおさらです。それが、間違いや失敗を前向きに捉えるよう態度を変えるための、シンプルな方法です。</p>
<hr />
doublemarket
https://yakst.com/ja/posts/5588
2019-09-06T08:20:46+09:00
2019-09-06T08:20:46+09:00
インシデント指揮官トレーニングの手引き
<p><em>私が <a href="https://www.hashicorp.com/">Hashicorp</a> で担った最初の仕事のひとつは、社内向けのインシデント指揮官のトレーニング資料を作ることでした。</em>
<em>これは私自身がインシデントへの対処にあたりながら何年ものあいだ肌身に感じてきた、あらゆる類の考えをまとめ上げる良い機会となり、最高に面白いタスクでした。</em></p>
<p><em>以下は私の書いたトレーニング資料、ほぼそのままです。</em>
<em>あなたがインシデントレスポンスのポリシーを定義するにせよ、即興でインシデントレスポンスを行うにせよ、お役に立てたら幸いです。では、どうぞ!</em></p>
<h3>さて、インシデント指揮官になることをお望みですね。</h3>
<p>私たちのインシデントレスポンスのやりかたにおいては、<strong>インシデント指揮官</strong> の存在が欠かせません。
インシデント発生時に指揮をとるのは、やりがいのある仕事です。
しかし、おそらくあなたの仕事で発揮したことのないようなスキルが必要になります。
それはもしかすると、あなたがこれまでスキルだと思ったことすらないかもしれないようなスキルです!</p>
<p>このドキュメントでは、インシデント指揮官になるにはどのようなことが必要かを説明します。
その後で、インシデントの最中にしばしば間違った方向へ行ってしまいがちな事柄の概要や、そのような事態に取り組むための戦略について述べていきます。</p>
<p>インシデント指揮官になるには、まず以下のことをやりましょう:</p>
<ol>
<li>このドキュメントを読む。</li>
<li>インシデント指揮官リファレンスに親しむ。<em>(筆者注: ここで言及しているリファレンスシートも将来ブログ記事で公開できるように、許可を得ようと試みるつもりです。基本的には、公式な手続きを解説したステップごとの指示書です)</em></li>
<li>実際のインシデントに取り組んでいるインシデント指揮官のやりかたを見て学ぶ</li>
<li><strong>@inccom</strong> のSlackグループに入れてもらう</li>
</ol>
<p>以上のステップを完了できたら、おめでとうございます!
これでインシデント指揮官の仲間入りです。
次に誰かが <strong>@inccom</strong> グループにpingしたら、自ら申し出て仕事を引き受けましょう。
インシデント指揮官のリファレンスを参照するのを忘れずに。
Slackのどのチャネルでも <code>start incident</code> と叩けば出てきますから。</p>
<h4>インシデント指揮官は何をする?</h4>
<p>インシデント指揮官の仕事は、インシデントを解決に向けて動かしていくことです。
しかし、インシデント指揮官の仕事は問題箇所を直すことでは<em>ありません</em>。</p>
<p>インシデント指揮官は、他に手を動かす人が誰もいない状況でもないかぎり、自分でターミナルを触ったりグラフを見たりデプロイしたりしてはいけません。
特にエンジニアリングのバックグラウンドを持つ人にとっては、これはやりづらいと感じられるかもしれません。
おそらくは、なんだか自分が役に立つ仕事をしていないかのような気分になるでしょう。
しかし覚えておくべきことは、普段のあなたの仕事が何であれ、インシデント指揮官になったときはインシデント指揮官に徹するのがあなたの仕事だということです。</p>
<p>自分では何も直すことなく、インシデント対応を解決に向けて動かすにはどうしたら良いのでしょうか?
まず、複数人のチームで取り組めばインシデント対応は前進します。
インシデント指揮官の仕事は、チームが同じ認識を持つ状態を作り上げてキープすることです。
それは集中を要する高度なタスクで、3つの要素からなります:</p>
<ol>
<li>インシデント対応のための階層型組織を浸透させる</li>
<li>究極の意思決定者になる</li>
<li>情報展開をファシリテートする</li>
</ol>
<p>上記について順番に解説していきましょう。</p>
<h4>インシデント対応のための階層型組織を浸透させる</h4>
<p>インシデント発生時は、問題に取り組む人たち全員が特定の役割を持っています。
決まり事として、ロールが明示的にアサイン<em>されていない</em>人は、問題に取り組んではいけません。
関与する全ての人が、自分が何の責務を負い、誰に対し説明責任があるのかを知っていることが肝要です。</p>
<p>インシデントレスポンスには、早急に任命された後インシデントがクローズされるまで欠かすことのできない、4つの <strong>主要ロール</strong> があります。
インシデント指揮官リファレンスシートに詳しく書かれている通り、インシデント指揮官には、Slackチャンネルのトピックで主要ロールに誰が就いているのかを、最新の状態に保つ責務があります。</p>
<p>主要ロールは以下の通りです。</p>
<ul>
<li><strong>インシデント指揮官 (IC = Incident Commander)</strong> - あなたです!</li>
<li><strong>主任SME (SME = Subject Matter Expert)</strong> - 問題に対し技術的な調査を行う</li>
<li><strong>外部通信役 (External Liaison)</strong> - インシデントに関する顧客との連絡を司る</li>
<li><strong>書記官 (Scribe)</strong> - Slackにインシデントに関するノートを書き、フォローアップが必要な項目を追い続ける</li>
</ul>
<p>インシデントの最初期には、インシデント指揮官としてのあなたは、上記のうち複数のロールを兼任する必要があるかもしれません。
ですが契機が来たらすみやかに、他のロールは他の人にアサインしましょう。
もしくは、あなた自身が問題解決に最適な人物ならば、他の人をインシデント指揮官にアサインして、自分自身は主任SMEを務めましょう。</p>
<p>誰かをロールにアサインするときは、宣言的にやるのが良いです。
「誰か、書記をやってくれる人はいないかな?」と尋ねる代わりに、誰か特定の人を選んで「<code>(相手の名前)</code>、このインシデントの書記官をやってくれないか?」と言ってみましょう。</p>
<p>インシデントレスポンスに4人だけでは足りないことは非常によくあります。
しかし前述したように、インシデントに関与する人たち全員が、自分が何の責務を負い、誰に対し説明責任があるのかを知っていることが肝要です。
このため、誰か新しい人がインシデントに取り組み始めたいというときは、その人を主要ロールにアサインするか、すでにアサインされている人の直属のロールにアサインしなければなりません。
たとえば、外部連絡役が顧客とのコミュニケーションで手いっぱいになっていて助けを求めていたならば、次に相当するようなことを言ってみると良いでしょう:
「<code>(相手の名前)</code>、これからは君は副外部通信役だ。報告先は <code>外部通信役</code> である。確認してほしい。」</p>
<p>誰が何に責任を持つのかについては、インシデントに関わる全員が、インシデント管理者へ最終的な判断を仰ぐ必要があります。
これは究極の意思決定者たることの一部です。</p>
<h4>柔軟さこそがあなたの特権</h4>
<p>事前に定められたインシデントの階層型組織は、大半のシナリオに合致するようデザインされたものです。
ときには、その組織構造ではあまり<em>うまくいかない</em>インシデントもあるでしょう。
インシデント指揮官は、その時々のケースに合わせて、目の前のインシデントに一番合う形へ階層型組織を変更する権限を持ちます。
慣例に固執するあまり、あなたのもたらす効果が阻害されることがないようにすることが重要です。
あなたは、その場に合うようにシステムを一時的に変更する力を持つのです。</p>
<p>たとえば、主任SMEを複数設けるのもアリかもしれません。
もしインシデントが複数システムに跨って影響するなら、各システムに対しSMEを選んでも良いでしょう。
複数のSMEをアサインする手続きはSMEが一人の場合と同じですし、彼ら彼女らの責任についても同様です。
こんな風に言うと良いでしょう:「<code>(相手の名前)</code>、これから君は <code>(特定の責任範囲)</code> における主任SMEである。確認してほしい。」</p>
<h4>究極の意思決定者であれ</h4>
<p>インシデント指揮官が「究極の意思決定者」だと言われるとき、それはなんらかの<em>より優れた</em>決定ができることを期待されているという意味ではありません。
私たちの意味するところは、インシデント関係者の全員が、インシデント指揮官の決定を最終的で拘束力のあるものとして受け止めるということです。
いつも正しい決定をすることにそこまで重きを置くのではなく、あなたが<em>一つの</em>決定を下すことが重要なのです。</p>
<p>インシデント指揮官が決断を下せる状態にあることで、他の人たちはインシデントに要求されるがままに振る舞えるようになります。
果たしてあの対応でよかったんだろうかと後悔したり、メリットデメリットを検討するのに多くの時間を割いたりする代わりに、皆はあなたに重要な決断を託すことができるのです。
情報さえ入手できれば、あなたはどの道に進むのが一番良いかを選ぶことができるでしょう。
そしてあなたの決断は最終的で拘束力を持ちますから、決断そのものによって皆が同じ認識を持つ状態を保てるようになります。</p>
<p>もう一つ、前述の点ほど明確ではないけれども、究極の意思決定者としてのインシデント指揮官の恩恵があります。
皆が本番環境で起きている問題について調べていたり、顧客対応に追われていたりする最中は、各人は絶え間なく他の人たちが把握していないコンテキストを構築しています。
そのような人たちは自分の心理状況を十分に説明し、インシデント指揮官が素直な内容で重要な決定を下せるよう協力しましょう。
これはインシデントが起きている間、情報が十分に行き渡るようにする手段の一つです。</p>
<h4>情報展開をファシリテートせよ</h4>
<p>情報の流れを管理することは、インシデント指揮官にとってただ一つ、最も重要な責務です。</p>
<p>インシデントの最中の情報というと、たいてい私たちは遠隔システムから出てくるデータや、実行するコマンドの出力結果について考えます。
私たちが一番手軽に広めがちなのは、この手の情報です。
しかしインシデント指揮官は、このように具体的で、見つかっている情報だけを気にしていれば良いわけではありません。</p>
<p>インシデントレスポンスでは、関与している人たち全員の頭の中にも情報があります。
誰もが、インシデントに対して異なる視点を持っています。
そして概してこの人たちは、他の人たちが探しているイメージの断片を自分が持っていることに気づいていません。
したがってインシデント指揮官は常に、有用なコンテキストを皆の頭の中から引き出し、共有ナレッジの中に持ってくる機会を窺うべきです。</p>
<p>情報展開をファシリテートする上で鍵となるのは、 <strong>シグナル対ノイズ比</strong> です。
「シグナル」はインシデント解決に向けて利用できる情報を意味し、「ノイズ」はそうではない情報を意味します。
このため、誰かに届ける必要のある情報が存在するときは、インシデント指揮官はシグナルを強化して、正しい所に届くようにしましょう。
逆に、チャンネル内の誰かが関係ないシステムの挙動をアップデートしてきたり、ビデオ会議の中の誰かが重複した状況アップデートを求めてたりしていたら、ノイズを抑制するのがあなたの仕事です。</p>
<p>簡潔にいえば、インシデント指揮官は、全てのインシデント関連のコミュニケーションチャネルを高シグナル・低ノイズな環境に保つ責任を持つのです。</p>
<h3>インシデントレスポンスが横道に逸れるとき</h3>
<p>インシデントは一つ一つ異なるものですが、インシデントレスポンスの方向性が失われてしまう典型例をいくつか知っておくと便利です。
これらのアンチパターンに陥っていると気づいたら、チームが元の道に戻れるよう、以下に示す戦略を適用しましょう。</p>
<h4>主題がブレる</h4>
<p>おそらく最もありがちなインシデントレスポンスのアンチパターンは <strong>主題のブレ (thematic vagabonding)</strong> です。
これは、対応者が一般的な調査領域に対して次から次へと手をつけては他へ移っているような状態です。
以下のようなことに気づいたら、主題のブレが生じているサインです:</p>
<ul>
<li>対応者が、具体的に何が悪そうなのかについて考えを述べることなく、手がかりを求めていろいろなところを探し回る。</li>
<li>問題の性質に対する考えが「たぶんAPIレイヤーが何かおかしい」程度の曖昧さに留まっている。曖昧なアイデアを行動につながる理論へ落とし込めるような推進力に欠けている。</li>
<li>主任SMEの一連の考えについていくのが難しくなっている。</li>
</ul>
<p>主題のブレはノイズの根源です。たくさんの情報を生み出しますが、意味のある情報としてまとめられるものではありません。</p>
<p>もし主題のブレに気づいたら、主任SMEに対して、どのような考えから各々のアクションを取っているのかを聞くのを始めてみると良いでしょう。
たとえば「データーベースのエラーログを調べている」と言われたら、「データベースにエラーがありそうだという考えに至った理由は何かな?」といった返しをしてみます。
調査の中、どのようにしてデータベースの課題から問題が引き起こされうるのかや、なぜデータベースのエラーログによって根本原因を突き止められそうなのかをを説明するよう、主任SMEたちをかき立ててみましょう。</p>
<p>もし主題のブレが主任SMEからではなく他のロールの人たちから生じたのならば、その人たちの注目を主任SMEが調べていることへ向けるのが良いでしょう。
たとえば、主任SMEがロードバランサーの異常を調べているのに、他の誰かが「最近のデプロイで大きな変更がなかったか調べてみようか」と提案してきたら、あなたは「その前に、ロードバランサーの異常へ十分に目を通しておきたい。<code><主任SMEの名前></code>、怪しそうなログの内容を解釈するのに助けがいるんじゃないかな?」と言ってみてもよいでしょう。</p>
<h4>視野狭窄に陥る</h4>
<p>視野狭窄 (tunnel vision) はある意味、主題のブレとは逆の現象です。
対応者が、もはや生産的なアイデアではないにも関わらず、間違っている可能性のある特定の考えにハマってしまった状態です。
視野狭窄は、調査担当者が、調査を次のフェーズへ推し進めるためのシグナルを入手し損ねた時に起こります。</p>
<p>主題のブレとは逆の症状ではありますが、視野狭窄に対しては似たようなアプローチで取り組むことができます。
調査担当者に、どのような動機から調査しているのかを詳しく説明してもらうのです。
ときどき、説明を繰り返しているうちに本人たちがドツボにはまっているのに自分で気づいたりします。</p>
<p>もう一つ視野狭窄を止める上で便利な戦略は、対応者に<em>反証となる</em>エビデンスを探してもらうことです。
たとえば、主任SMEが調査中に特定のコード変更がまずかったのだという考えにハマっているのだけれども、実を結ぶ様子がみられなかったら、こう尋ねてみましょう。
「もし、この変更が問題の原因では<em>ない</em>ことを証明するとしたら、どうやって証明する?」
このように概念的な見方の転換を行うことで、必然的に調査者は狭いトンネルの外のアイデアに目を向けることとなり、しばしば前進を再開することにつながります。</p>
<h4>一貫性のないメンタルモデル</h4>
<p>効果的に協業するために、インシデント対応者たちは、調査中の問題がどのようにして引き起こされうるかについて、一式のアイデアを共有している必要があります。
このようなアイデアのことを <strong>仮説 (hypotheses)</strong> と呼びます。</p>
<p>仮説が不足していたり不十分なコミュニケーション下にあったりすると、インシデント対応は停滞する傾向にあります。
インシデント指揮官にとっては、どの仮説がいま盛り上がっていて、どの仮説がすでに却下されているのか、対応者全員が同じ認識のもとにあるようにすることも仕事の一つです。
さらに、どの仮説を契機に皆が調査のアクションへ向かっているのかの経過を追うのは良い考えでしょう。
もしあなたが、なぜ主任SMEがキューイングのメトリクスについて調べているのかはっきり理解できないのであれば、先に進むより前に、おそらく彼ら彼女らの思考プロセスを説明するようお願いしてみるべきです。</p>
<p>ときに、調査対象となるような明確な仮説がもう残っていない状態になると、インシデント対応の進捗は遅滞するか停止します。
この状態が明言されないと、対応状況は主題のブレや視野狭窄に陥ってしまうことがあります。
あなたが仮説の枯渇に気づいたときは、進行中の調査を一旦すべて止めてもらい、新しい仮説を得るべくブレインストーミングするよう呼びかけることが有用です。
すると、一部のメンバーにはこれが時間の無駄のように感じられ、反発を受けることもあるでしょう。
しかし時折、具体的な復旧作業に進むためには、抽象的なアイデア思考をする必要があるのです。</p>
<h4>インシデント指揮官と主任SME間の分断</h4>
<p>インシデント指揮官と主任SMEの認識がズレると、もっともまずい大惨事につながります。
インシデントレスポンスでの取り組みにおける全メンバー間の関係の中で、この二者間の関係はもっとも重要なものです。
実際、あまりにも大事なので、インシデント指揮官と主任SMEの関係を良好に保つことだけを目的としたプロセスがあります。
これを、 <strong>ハンズオフ状況アップデート (hands-off status update)</strong> と呼びます。</p>
<p>インシデントの最初期、インシデント指揮官と主任SMEがビデオ通話にアサインされて参加したら、インシデント指揮官は速やかにハンズオフ状況アップデートを要請しましょう。
「ハンズオフ」というからには、状況アップデートが終わるまでの間、どちらの側もキーボードを叩いたりクリックしたり、また何かを読んだりしてはいけません。
両者が、完全にお互いとのコミュニケーションだけに集中する必要があるのです。</p>
<p>ハンズオフ状況アップデートは、5つの質問から成り立ちます:</p>
<ul>
<li><p><strong>ハンズオフ状況アップデートをしたいのだが、準備は良いか?</strong>
この質問は、ハンズオフ状況アップデートが始まることを改めて知らせて、インシデント指揮官と主任SMEがそれに集中するよう促します。
もし、主任SMEがまだ準備<em>できていない</em>と言うのであれば、今から60秒後だったらできるか聞いてみましょう。</p></li>
<li><p><strong>想定できる範囲で、どのくらいの影響が出ていると思うか?</strong>
現在進行形で調査中の問題によって、何人の顧客が影響を受けているか、どれくらいカスタマーエクスペリエンスに混乱が生じているかは、常に明確であるとは限りません。
それでも、主任SMEに一考してもらうのは役に立つものです。</p></li>
<li><p><strong>ありうる根本原因は何だと考えているか?</strong>
この質問は、主題のブレや視野狭窄に陥るのを、未然に防ぐのに役立ちます。
主任SMEが思考プロセスを声を出して明言することにより、ビデオ会議の中の全員 (主任SMEたち自身を含む) が、このさき進む道をはっきり意識できるようになります。</p></li>
<li><p><strong>次に取れる一手は?</strong>
根本原因に対する認識が薄れないうちに、問題解決の次のステップを確立する機会を設けましょう。
インシデント指揮官にとっては、主任SMEの言っている次の一手が、前の二つの質問で聞いた想定影響と根本原因からみて妥当であるものかを確認することが責務です。</p></li>
<li><p><strong>協力を要請したい人はいるか?</strong>
最後に、誰かインシデント対応を進める上で有用なスキルを持った個人がいるか、主任SMEに考えてもらう機会を作ります。
誰かの名前が挙がったら、全力でインシデントレスポンスのチャンネルやビデオ会議に参加してもらうよう働きかけ、さらに可能であれば、彼ら彼女らを主任SME直属のSMEに任命しましょう。</p></li>
</ul>
<h3>インシデント指揮官リファレンスシート</h3>
<p>一貫性のある役割分担や情報のハンドリングを確かなものにするために、インシデント指揮官のリファレンスシート内に標準的な手続きを定義しました。
インシデント指揮官に志願する前に、リファレンスシートを見直しましょう。
読みながら、インシデント指揮官の3つの責務を思い出してください。</p>
<ol>
<li>インシデント対応のための階層型組織を浸透させる</li>
<li>究極の意思決定者になる</li>
<li>情報展開をファシリテートする</li>
</ol>
<p><em>※訳注: 現時点では、リファレンスシートそのものは公開がHashiCorp社内に限られているため、当記事上での公開はありません。</em></p>
<h3>おすすめのリソース</h3>
<ul>
<li><p>📄 <a href="http://jeffreymbradshaw.net/publications/Common_Ground_Single.pdf">Common Ground and Coordination in Joint Activity</a>
この論文は認識の集結、すなわち私たちがインシデントの解決に当たるためにやることを、「共通見地 (common ground)」の観点から分析しています。
論文では、最も頻繁に「共通見地」が崩壊してしまうありかたの一つが記述されています。
筆者はこれを <strong>根本的な共通見地の破綻 (Fundamental Common Ground Breakdown)</strong> と呼んでおり、これを理解し認識することで、より効果的なインシデント指揮官になれるでしょう。</p></li>
<li><p>🎬 <a href="https://youtu.be/qKrLPY_8Cyk">How to Create a Differential Diagnosis</a>
インシデントレスポンスで私たちが行うことは、医師が診断を下そうとするときに行うことと多くの共通点があります。
両ケースとも調査者は、非常に複雑なシステムと、容赦ない時間の経過、そしてシステムの挙動を説明するにあたり限られた戦略上の兵器を手に直面することになります。
このビデオはソフトウェアエンジニアではなく医学生を対象にしたものではありますが、インシデント指揮官は差分診断の原則を学び、インシデントレスポンスを体系化する上で大変な恩恵を得ることができるでしょう。</p></li>
</ul>
<hr /><b>関連タグの記事 </b><a href="https://yakst.com/ja/tags/sre#?from=feed">sre タグのついた記事</a>
meiq
https://yakst.com/ja/posts/5564
2019-09-05T08:08:39+09:00
2019-09-05T08:08:39+09:00
「なんにもしない」スクリプトを書く: 段階的な自動化を進めるために
<p>どんな運用チームにも、まだ自動化するところまで手が回っていない手作業があるものです。
<a href="https://landing.google.com/sre/sre-book/chapters/eliminating-toil/">トイル (toil)</a> が完全に無くなることは決してありません。</p>
<p>成長企業のチームに非常にありがちなのが、インフラの変更手続きやユーザーアカウントのプロビジョニングが、最大のトイル源となっているケースです。
後者の例について手順の一部を書き出してみると、たとえば以下のようになるでしょう:</p>
<ol>
<li>ユーザーのSSHキーペアを作成する</li>
<li>公開鍵をGitにコミットしてmasterにプッシュする</li>
<li>ビルドジョブの完了を待つ</li>
<li>従業員のディレクトリからユーザーのメールアドレスを見つける</li>
<li>1Password経由でユーザーに秘密鍵を送信する</li>
</ol>
<p>これは比較的短い例ですが、一連のプロセスが20ステップに達することもあります。
またあるときは作業が分岐するケース、作業を進めるとともに経過を追っていかなければならないような特殊ケースが発生することもあるでしょう。
時が経つにつれ、このような手順は手のつけようのないほど大きく複雑になってしまう可能性があります。</p>
<p>こういった手続きは、考える必要のあることはほとんどないにも関わらずじっと注視していなければならないので、フラストレーションが溜まるものです。
全力で注意を向けていなければらならないのに、私たちは面白い問題や充実した解決策によって報われることはありません。単に、終わったらチェックボックスに作業完了のチェックが入るだけです。
このような手続きのことを、私は <strong>スログ (slog, 長くつらい仕事)</strong> と呼んでいます。</p>
<p>この手の手続きこそ、まさに自動化の対象といえるでしょう。
個々のどのステップも、どうやって自動化したらいいかは容易にわかります。
そしてコンピュータは私たちよりもずっと速く、正確に、かつ<a href="https://risk-engineering.org/concept/Rasmussen-practical-drift">実務上のブレ (practical drift)</a>に至りにくく、忠実に指示を実行してくれることも知っています。</p>
<p>しかしながら、ときにスログの自動化は1か0かの命題のように感じられます。
ええ、ステップ2やステップ5を実行するスクリプトは書けるでしょう。
しかし、それだけでは手続きの煩わしさを<em>本当に</em>減らすことはできません。
単一の目的を持ったスクリプトを別の規約や期待に応えるよう拡張することにつながり、今度はスクリプトを使う上で複数ステップからなる手続きに従う羽目になるかもしれません。</p>
<p>このような無駄の認知こそ、手作業のスログから脱出するために本当に解決しなければならない問題です。
私は、そのようなときにかなりの確度で役立つアプローチを見つけました: 「なんにもしない」スクリプトを書くことです。</p>
<h4>「なんにもしない」スクリプトを書く</h4>
<p>ほぼどのようなスログであっても、<strong>なんにもしないスクリプト</strong> へ落とし込むことができます。
なんにもしないスクリプトとは、スログの手続きをコード化したスクリプトで、各ステップを関数の中にカプセル化したものです。たとえば上記の手順なら以下のように、なんにもしないスクリプトを書けるでしょう:</p>
<pre><code>import sys
def wait_for_enter():
raw_input("Press Enter to continue: ")
class CreateSSHKeypairStep(object):
def run(self, context):
print("Run:")
print(" ssh-keygen -t rsa -f ~/{0}".format(context["username"]))
wait_for_enter()
class GitCommitStep(object):
def run(self, context):
print("Copy ~/new_key.pub into the `user_keys` Git repository, then run:")
print(" git commit {0}".format(context["username"]))
print(" git push")
wait_for_enter()
class WaitForBuildStep(object):
build_url = "http://example.com/builds/user_keys"
def run(self, context):
print("Wait for the build job at {0} to finish".format(self.build_url))
wait_for_enter()
class RetrieveUserEmailStep(object):
dir_url = "http://example.com/directory"
def run(self, context):
print("Go to {0}".format(self.dir_url))
print("Find the email address for user `{0}`".format(context["username"]))
context["email"] = raw_input("Paste the email address and press enter: ")
class SendPrivateKeyStep(object):
def run(self, context):
print("Go to 1Password")
print("Paste the contents of ~/new_key into a new document")
print("Share the document with {0}".format(context["email"]))
wait_for_enter()
if __name__ == "__main__":
context = {"username": sys.argv[1]}
procedure = [
CreateSSHKeypairStep(),
GitCommitStep(),
WaitForBuildStep(),
RetrieveUserEmailStep(),
SendPrivateKeyStep(),
]
for step in procedure:
step.run(context)
print("Done.")
</code></pre>
<p>実際のところ、このスクリプトは手順にあるステップに関して何も<em>しません</em>。
それゆえに、なんにもしないスクリプトと呼んでいます。
1つのタイミングにつき1つのステップをユーザーに見せて、手作業が完了するのを待つだけです。</p>
<p>初めは一見、このようなスクリプトに意味があるとは考えにくいかもしれません。
おそらくは、単に手順を読みづらくしただけなのではないかと。
ですが、なんにもしないスクリプトの効果はてきめんです:</p>
<ul>
<li>今やっていることを見失い、ステップを飛ばしてしまうような失敗を減らせます。その結果、スログに集中して注力することが楽になります。</li>
<li>手続きの各プロセスが関数の中にカプセル化されているので、ステップ内のテキストを、自動化されたアクションをとるコードに置き換えていくことが可能です。</li>
<li>しだいに、あなたは便利なステップのライブラリを開発するようになり、将来的に自動化のタスクの効率が向上するでしょう。</li>
</ul>
<p>なんにもしないスクリプトは、あなたのチームの手作業を減らすことはありません。
しかしタスクを自動化するに至るまでのハードルを下げ、チームが徐々にトイルを削減するのに役立つことでしょう。</p>
<hr /><b>関連タグの記事 </b><a href="https://yakst.com/ja/tags/sre#?from=feed">sre タグのついた記事</a>
meiq
https://yakst.com/ja/posts/5567
2019-08-13T12:27:30+09:00
2019-08-13T12:27:30+09:00
Python3.8の新機能
<h3>はじめに</h3>
<p>もうすぐPythonの最新バージョンのベータ版が公開される予定です。最終の安定版が利用可能になるまでは、もう少し時間がありますが(リリーススケジュールは<a href="https://www.python.org/dev/peps/pep-0569/#id6">こちら</a>)、どのような新機能が追加されているのかを調査することには十分価値があります。Python 3.8 では、言語としての構文の追加、既存の挙動に関しての軽微な変更、そして全体的な速度の改善が追加されています。これらの変更は、以前の3.7のリリースまでの習慣を維持したものとなっています。</p>
<p>この記事では Python 3.8 に関して、最も重要な追加点・変更点の概要を説明します。ぜひ見てください。</p>
<h3>1. セイウチ演算子</h3>
<p>代入式が Python にもセイウチ演算子<code>:=</code>としてやってきました。これにより、式の一部として変数を代入することができます。後続の条件で式の値を利用したいとき、コードの行数を減らすことができるというメリットがあります。
例えば、このような場合、</p>
<pre><code>line = f.readline()
while line:
... # 何らかの処理
line = f.readline()
</code></pre>
<p>以下のように短く書くことが出来ます。</p>
<pre><code>while line := f.readline():
... # 何らかの処理
</code></pre>
<p>これはコードを簡潔にしますが、コードの読みやすさに影響を与えると言う人もいるでしょう。最初のコードの方が明確で明示的であると言うこともできます。この議論は、Python コミュニティーにおける論争の的となっていました。(詳細は<a href="https://lwn.net/Articles/757713/">こちら</a>)</p>
<h3>2. 位置引数のみに制限</h3>
<p>特別な記号<code>/</code>を使うことで、この記号の左側の引数を位置引数のみに制限する関数を作ることができるようになりました。Python において、キーワード引数は関数内で<code>*</code>の記号で利用することができます。また、位置引数のみに制限する追加された<code>/</code>を使うと、言語の一貫性が増し、堅牢なAPI設計が可能になります。</p>
<p>関数の例を記載します。</p>
<pre><code>def pow(x, y, z=None, /):
r = x**y
if z is not None:
r %= z
return r
</code></pre>
<p><code>/</code>記号を使うと、<code>x</code>、<code>y</code>と<code>z</code>をキーワード引数としてではなく、位置引数のみを許可するように制限することが出来ます。振る舞いを以下に示します。</p>
<pre><code>>>> pow(2, 10) # 正しい
>>> pow(2, 10, 17) # 正しい
>>> pow(x=2, y=10) # 正しくない、タイプエラーが発生
>>> pow(2, 10, z=17) # 正しくない、タイプエラーが発生
</code></pre>
<p>より詳細な説明や、動機、使い方などは<a href="https://www.python.org/dev/peps/pep-0570/">こちら</a>を参照してください。</p>
<h3>3. f文字列が"="をサポート</h3>
<p>Python プログラマーは、デバッグ時に printfスタイルをよく使用します。この書き方は昔はかなり冗長でした。</p>
<pre><code>print "foo=", foo, "bar=", bar
</code></pre>
<p>f文字列を使うことで、少し良くなりました。</p>
<pre><code>print(f"foo={foo} bar={bar}")
</code></pre>
<p>しかし、まだ文字を繰り返す必要があります。つまり、文字列の"foo"と変数の"foo"の両方を書かなければいけません。
<code>f'{expr=}'</code>として使われている<code>=</code>指定子は、式の文字列、等号記号、それから評価された式の表現に展開されます。なので、下記のように簡単に書くことが出来ます。</p>
<pre><code>print(f"{foo=} {bar=}")
</code></pre>
<p>言語としては小さな一歩ですが、デバッグ時に<code>print()</code>をあちこちにちりばめる人にとっては、大きな一歩ではないかと思います。</p>
<h3>4. <code>reversed()</code>が<code>dict</code>として動作</h3>
<p>Python 3.7 以来、辞書はキーの挿入順序を維持します。組み込みの <code>reversed()</code> 関数を使うと、辞書に逆の順番でアクセスすることができるようになりました。ちょうど、<code>OrderedDict</code>のような挙動になります。</p>
<pre><code>>>> my_dict = dict(a=1, b=2)
>>> list(reversed(my_dict))
['b', 'a']
>>> list(reversed(my_dict.items()))
[('b', 2), ('a', 1)]
</code></pre>
<h3>5. <code>return</code>や<code>yield</code>に対して反復可能オブジェクトの展開の単純化</h3>
<p>Python 3.2 以来、括弧なしで <code>return</code> や <code>yield</code> 文内で展開をすることができない、という意図しない挙動が存在していました。なので、以下は許可されます。</p>
<pre><code>def foo():
rest = (4, 5, 6)
t = 1, 2, 3, *rest
return t
</code></pre>
<p>しかし、以下2つは <code>SyntaxError</code> となります。</p>
<pre><code>def baz():
rest = (4, 5, 6)
return 1, 2, 3, *rest
</code></pre>
<pre><code>def baz():
rest = (4, 5, 6)
yield 1, 2, 3, *rest
</code></pre>
<p>最新のリリースではこの挙動が修正されたので、上記の2つの書き方は許容されるようになります。</p>
<h3>6. 新しい文法の警告</h3>
<p>タプルやリストの前にコンマがないような場合、今回の変更でPythonインタプリタが<code>SyntaxWarning</code> を発生させるようになりました。しかし以前は、もし誤って以下のようにした場合、</p>
<pre><code>data = [
(1, 2, 3) # おっと、コンマを忘れているよ!
(4, 5, 6)
]
</code></pre>
<p><code>TypeError: 'tuple' object is not callable</code>というエラーが表示されます。この警告は何が間違っているのかが非常にわかりにくいです。役に立つ警告はコンマが抜けている、ということを指摘することです。これによりデバッグの役に立つでしょう。また、最新のコンパイラは、特定のリテラル型( string や integers など)に対して同一性検査(<code>is</code> や <code>is not</code> などを使って)した場合も <code>SyntaxWarning</code> を発生させます。<code>None</code> 以外のリテラルと比較したいことはほとんどないでしょう。コンパイラの警告によって、わかりにくいバグを回避することができます。</p>
<h3>7. パフォーマンス改善</h3>
<p>今回のリリースでは、 Python 3.7 のリリースに続いて、いくつかのパフォーマンス改善が追加されています。</p>
<ul>
<li><code>operator.itemgetter()</code>が33%速くなっています。引数処理を最適化し、単一の負でない整数インデックスの一般的なケースの高速パスをタプルに追加することによって可能になりました(これは標準ライブラリの一般的な使用例です)</li>
<li><code>collections.namedtuple()</code> のフィールド検索が2倍以上速くなりました。これにより、Python におけるもっとも高速なフィールド検索となりました。</li>
<li>入力された反復可能オブジェクトが既知の長さを持つ場合(つまり len が実装されている)、<code>list</code> コンストラクタは内部のアイテムバッファを過剰に割り当てません。これにより、作成されるリストは平均して12%サイズが削減されます。</li>
<li>クラス変数への書き込みが2倍速くなりました。非dunder属性(double underscore)が更新されるとき、アップデートスロットの不必要な呼び出しが存在していましたが、最適化されました。</li>
<li>いくつかの組み込みと関数が20-50%速くなりました。引数をこれらの関数に渡す時のオーバーヘッドが削減されます。</li>
<li><code>uuid.UUID</code> がメモリフットプリントを削減するために、スロットを使うようになりました。</li>
</ul>
<p>※dunder属性(double underscore) ... __init__などの特殊属性のこと</p>
<h3>まとめ</h3>
<p>Python の次のリリースは、言語としての大きな変更や、基礎的な部分に対する重要なパフォーマンス改善が含まれています。Python 3.8 へのアップグレードの中で、既存のコードを変更する必要が出てくる<a href="https://docs.python.org/3.8/whatsnew/3.8.html#porting-to-python-3-8">挙動の変更</a>も何点かあります。しかし、パフォーマンス改善や新規構文による全体的なメリットの方が大きいでしょう。全ての新しくなった点の詳細な変更履歴は、<a href="https://docs.python.org/3.8/whatsnew/3.8.html">ここ</a>で見つけることができます。</p>
<hr /><b>関連タグの記事 </b><a href="https://yakst.com/ja/tags/python#?from=feed">python タグのついた記事</a>
hiroya
https://yakst.com/ja/posts/5547
2019-07-17T11:59:14+09:00
2019-07-17T11:59:14+09:00
マイクロフロントエンド
<p>あなたの現場には、リビルドに時間がかかって仕方がない、大規模なUIはありませんか?
複数のチームで作業していてしょっちゅうコードがコンフリクトするなど、インテグレーションに問題が発生することはありませんか?
ひとつのアプリが、あまりにも多くの機能を抱えすぎていませんか?
そんなとき、きっとマイクロフロントエンドが役に立つでしょう。
マイクロフロントエンドは、バックエンドのエンジニアリングにおけるマイクロサービスアーキテクチャの概念を、フロントエンド開発に適用したものです。
しかしUIを複数のフロントエンドへ分割することが、どのようにしてプロジェクトがスケールするのに役立つのでしょうか?</p>
<p>以前、2012年から2016年まで勤めていた職場で、私は複数の国にまたがる会社のUIフレームワークのリード開発者を勤めており、チームでマイクロフロントエンドアーキテクチャを設計・実装しました。
この記事では、マイクロフロントエンドがもたらす恩恵、および私がそこから学んだことを共有していきます。</p>
<h4>マイクロフロントエンドとは?</h4>
<p>マイクロフロントエンドは、特に決まったフレームワークやAPIがあるわけではなく、あくまでアーキテクチャ上の概念です。
マイクロフロントエンドの前提にあるのは、ひとつのアプリにつきひとつの機能だけに集中できるよう、元のアプリケーションをいくつかの小さなアプリへ分割し、それぞれ用の独立したレポジトリを用意することです。
このアーキテクチャは、様々な方法で適用することができます。
アーキテクチャはいくらでもリベラルにでき、各々のアプリケーションを異なるフレームワークで実装することができる一方、むしろ制限をかける方向性でツールを提供し、設計上の決定事項を強制することもできます。
どちらのアプローチにもメリットとデメリットがあり、組織のニーズに大きく左右されることでしょう。</p>
<p>マイクロフロントエンドで特筆に値することは、アプリを複数のプロジェクトに分けたとしても、最終的にはひとつのシングルページアプリケーション (SPA) に統合できるということです。
エンドユーザーから見れば、複数ではなく、正真正銘のひとつのアプリとして見えます。
たいていは、各アプリのライフサイクルを扱う親玉のランタイムがあるので、シングルページアプリケーションのエクスペリエンスを提供することができます。
ゆえに、このアーキテクチャを実装することによって、ユーザーエクエスペリエンス面で何かを失うことはありません。</p>
<h4>アーキテクチャの使いどころは?</h4>
<p>個人的な意見を述べると、アプリ分割における最良のアプローチは、固有の画面や機能のセットで分割を行うことです。
ご自分のスマートフォンを思い浮かべてください。異なる機能のためには異なるアプリがありますね。
ダイヤルするためには電話アプリがあり、テキストを送るためにはメッセージングアプリがあり、番号を保存するためにはコンタクトアプリがあります。
これらのアプリはしばしば互いに協調し合いますが、しっかり区別された目的があるので別々のアプリとして実装されているのです。</p>
<p>他の例としては、大学の管理システムの開発を想像してください。
そのようなシステムでは、職員のプロフィール、学生のプロフィール、授業の詳細やタイムテーブル、教材の配布、試験の結果などといったものを管理するページを設けることになるでしょう。
これらの機能は緩やかに互いに依存し合っていますが、大部分はスタンドアロンな機能であるといえます。
マイクロフロントエンドアーキテクチャで実装するには格好の適用対象プロジェクトになるでしょう。</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*GCNCYz8-hoRsrF5JPNWgdw.png" alt="大学の管理システムの例"></p>
<h4>このアプローチをとる理由は?</h4>
<p>マイクロフロントエンドが何かは分かりましたが、わざわざこのような複雑なアーキテクチャを選択する理由はなぜでしょうか?
私がマイクロフロントエンドが大規模な開発で真価を発揮すると思う理由は、以下のようなことです。</p>
<h5>高速なビルド</h5>
<p>プロジェクトが大きくなればなるほど、プロジェクトのビルドには時間がかかるようになりがちです。
私が思うには、たとえWebpackやParcelのようなバンドラが複数のスレッドやキャッシュを利用し、頑張ってパフォーマンスを大幅に改善したとしても、もっと根本的な問題に比べれば些細な差をもたらすにすぎません。
その手のパフォーマンス改善を行なっても、アプリが成長するについれ、ビルドにかかる時間のは徐々に長くなってしまいます。
覚えていてください。<strong>開発者にとってのエクスペリエンスが良くないのに、ユーザにとって良いエクスペリエンスを実現するのは困難だということを。</strong></p>
<p>アプリをいくつかの異なるプロジェクトに分割し、それぞれ毎のビルドパイプラインを整備すれば、どんなにシステムが成長したとしても各プロジェクトのビルドはとても高速に行うことができます。
このようにするとプロジェクトを並列化でき、個別にビルドした上で最後の最後で結合すればよいので、継続的インテグレーション (CI) のシステムにも有利に働きます。</p>
<h5>動的なデプロイ</h5>
<p>私が思うに、マイクロフロントエンドのいちばん素敵なところは、ランタイム含め、他の部分を一切リコンパイルすることなしに新しい機能を追加できることです。既存のシステムがあるときでも、システム全体を移行して再インストールすることなく、新しい機能だけを出してインストールすればよいわけです。これは驚くほど強力で、配布のしかたに新たな可能性をもたらします。たとえば、システムのうち特定の機能だけをライセンス化したい場合、その機能だけを別々のインストーラーに切り出すことができるのです。</p>
<h5>並列化した開発</h5>
<p>UIを別々のプロジェクトに分割することで、UIを複数のチームで開発する道が開かれます。
各チームは、システムのひとつの機能だけに責任を持てばよいのです。
たとえば、あるチームが電話アプリに注力するあいだ、他のチームはコンタクトアプリに注力する、といった具合です。
各チームは自分たちそれぞれ用のGitレポジトリを持ち、独自のバージョニングや変更ログと共に、好きなタイミングでデプロイを走らせることができます。</p>
<p>ほとんどの部分で、これらのチームはお互いについて知る必要はありません。
ただ、どこかでこれら二つのアプリを統合するタイミングは必要です。
各チームは、パブリックなAPIを用意して後方互換性を定義し保証する必要があります。
このAPIはたいてい、URLを使用して実装されます。</p>
<h4>どうやって実装する?</h4>
<p>マイクロフロントエンドの実装方法にはいくつか異なるアプローチがあり、本記事ではアプローチの詳細については触れませんが、このアーキテクチャを実装する上で、考える必要のある大切なことがいくつかあります。</p>
<h5>ルーティングとアプリの読み込み</h5>
<p>通常のアプリでは、しばしばコード分割 (code splitting) をサポートしたルーターが用いられます。
特定のroute群が定義され、各々がimport文を持つ形です。
ただマイクロフロントエンドアーキテクチャでは、これだとスケーラブルになりません。
システムの中のひとつひとつのアプリや機能の全てにおいて、いちいちrouteを定義しなければならないような状況は避けたいことでしょう。
それでは新しい機能をデプロイするのが難しくなってしまいます。
代わりに私だったら利用するアプローチとしては、URLをlistenすることにより、アプリのインスタンス化を扱うランタイムを持つ方法があります。</p>
<pre><code>onRouteChange (route) {
// Assuming routes are "/<app_name>/<internal-app-url>".
let parts = route.split('/');
let app = parts[1];
let app_url = parts.slice(2).join('/');
if (this.isRunningApp()) {
this.suspendCurrentApp();
}
import(`/${app}/main.js`).then(app => {
this.startApp(app, app_url);
});
}
</code></pre>
<p>上記のコードにおけるランタイムの関心事は、アプリのコードが配置されているフォルダの名前に対応させる想定で設けられた、URLの最初の部分だけです。
すでにアプリを実行しているかもしれないので、まずアプリをサスペンドします。
その後で <strong>予想できるフォルダ構成</strong> からコードをインポートし、ロードされたら残りのURLを渡して再開しましょう。</p>
<p>なおバンドラはこのコードに混乱し、ファイルが見つからない旨のエラーや警告を投げるかもしれません。
import文を無視するようにバンドラの設定をする必要が出てくるでしょう。</p>
<h5>アプリのライフサイクル</h5>
<p>先のセクションで述べたように、アプリは開始、サスペンド、再開、終了してリソースを開放します。
ですので、モバイル系OSで見られるのと似たようなライフサイクルを実装してもよいでしょう。</p>
<pre><code>class MyApp extends Application {
constructor (args) {}
onAppSuspended () {}
onAppResumed () {}
onAppQuit () {}
}
</code></pre>
<h5>アプリ間の通信</h5>
<p>開発者からよくされる質問に、どうやって複数のアプリ間でデータを渡したらよいか? というものがあります。
通常のスタンドアロンなアプリだと、データを一連のpropsとしてコンポーネントに渡すことが多いです。
けれども、もはやアプリや機能が互いの実装へ直接アクセスできない状況下では、この方法は取れません。</p>
<p>シンプルなデータの場合は、全くもってURLで十分でしょう。
アプリや機能に責任を持つチームが、後方互換性を保証したpublicなAPIを実装すればよいです。
これは、アプリがAndroid OSと通信する際に、異なるintentでカスタムなURLハンドラを登録するやりかたと似ています。
OSはこれらのURLを傍受して、対応するアプリを読み込んでデータを渡します。
基本的にこのやりかたが、マイクロフロントエンドでも使えます。
ランタイムがURLを傍受し、アプリをロードし、残りのURLのデータをアプリに渡します。</p>
<p>しかし、ものすごく複雑で、到底URLでは扱えないようなデータを渡す必要があるときも出てくるでしょう。
アプリ間通信を行うメカニズムには、他にもよい方法がたくさんあります。
ひとつのアプローチとして、一時的にデータのblobをサーバに保存してから、データのblobを取得するクエリ用の一時的なIDをURL内で利用する方法があります。
これはより複雑で、サーバー上でデータを注意深く管理する必要があるものの、単にデータを渡す以上の恩恵をもたらします。
たとえば意図しない再読み込みをしてしまったりブラウザがクラッシュしてしまったときでも、データに永続性を持たせることができます。</p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*8TA3USqGy7-59ArN0fcsAQ.png" alt="アプリ間の通信"></p>
<h4>ライブラリの共有</h4>
<p>マイクロフロントエンドでよく開発者が心配する課題のひとつに、それぞれのフロントエンドで各自のバージョンのフレームワークをインポートすることによる、リソースの無駄遣いがあります。
たしかに、バンドラをデフォルトの状態のまま使った場合には問題になりますが、<strong>そのようにする必要はありません。</strong></p>
<p>私の個人的なおすすめは、共有ライブラリを利用することです。
このようなライブラリはシステムに既にインストールされていることが多く、あらゆるアプリケーションからimportすることができます。
以下は、ライブラリをシステムへデプロイする際に利用することができるフォルダ構成の例です:</p>
<pre><code>libraries/
preact/
8/
10/
components/
1/
2/
</code></pre>
<p>ここに出てきている番号はなんでしょうか? ライブラリのメジャーバージョンです。
ライブラリがセマンティックバージョニング (semver) に従っていれば、その定義からして、同じメジャー番号のライブラリには後方互換性があります。
アプリがライブラリを使いたいとき、特定のマイナーバージョンやパッチのバージョンまで指定するよりは、メジャーバージョンを指定するでしょう。</p>
<p>このやりかたの利点は以下の通りです。</p>
<p><strong>キャッシュ面での利点</strong>: ブラウザキャッシュがより効果的に活用される可能性が高まります。
というのも、アプリが参照するのは別々にキャッシュされている複数の特定のバージョンのライブラリではなく、同じライブラリのファイルになるからです。</p>
<p><strong>更新にあたっての利点</strong>: この方法をとれば、ライブラリにバグ修正や軽いUXの変更があったとしても、ただ新しいバージョンのライブラリをデプロイするだけで済みますし、ライブラリのメジャーバージョンしか指定していないので、全てのアプリがリコンパイルなしで自動的に最新バージョンを利用するようになります。</p>
<h4>iframeはどう?</h4>
<p><strong>使うのはやめましょう。</strong> もっとも、お試しのサンドボックス的な用途で使うにはよいのですが、iframe利用下でナビゲーションや親フレームを使ったメッセージングをやろうとすると悲惨な目に合いがちです。</p>
<p>代わりに、アプリの全てのロジックを親側のランタイムで実行しましょう。
そのためにはグローバル変数やCSSに注意を払う必要が出てきますが、強力なlintingルールや便利なhelper関数があれば、それほど心配する必要はないでしょう。</p>
<p>多くのアプリでは、グローバル変数はそこまでの問題にはなりません。
綺麗なやり方ではないかもしれないにせよ、そこまでたくさんの副作用はないはずです。</p>
<p>しかしマイクロフロントエンドアーキテクチャでは、グローバルなスコープにあるものは入念にコントロールする必要があります。
ここでグローバルと言っているのは変数や状態の話だけではなく、windowやdocumentのイベントハンドラ、requestAnimationFrameのループ、永続的なネットワーク接続と言った、アプリがDOMからいなくなった後も動き続ける可能性を持つあらゆるものを含んでいます。
こういったものがリークしてしまう恐れがあることはいとも簡単に忘れられがちですし、不要になったらきちんと解体してあげる必要があるのです。</p>
<h4>おわりに</h4>
<p>マイクロフロントエンドは、全てのプロジェクトにフィットするものではありません。
世間の大半のプロジェクトにおいては、コード分割さえ行えば十分すぎるくらいだと思います。
マイクロフロントエンドアーキテクチャは、膨大な機能を持つ、大きなエンタープライズレベルのアプリケーションに適しています。
プロジェクトを始めるときは、プロジェクトが将来的にどれくらいの大きさに達するかを考えてみてください。
もし、プロジェクトがスケーラビリティの問題にぶつかるのが避けられないような気がするなら、このアーキテクチャを利用して問題解決するのも一案でしょう。</p>
<p>読んでくださってありがとうございます!</p>
<hr />
meiq
https://yakst.com/ja/posts/5499
2019-06-24T11:15:46+09:00
2019-06-24T11:15:46+09:00
モノリポによって得られる多くの恩恵
<p><img src="https://pspdfkit.com/images/blog/2019/benefits-of-a-monorepo/article-header-2ff86fd7.png" alt=""></p>
<p>Facebook や Google で行われているように、私たちはほぼ全てのプロダクトをただ一つのリポジトリーで管理しています。ここで管理しているものは iOS および Android、Windows、MacOS 版 の SDK と、iOS 版の PDF ビューワー、そしていくつかの内製ツールで、これは PDF 上の情報を簡単に探ることができる PSPDFInspector のようなものを指します。実際には Web 版の PSPDFKit や Android 版の PDF ビューワーといった例外があるのですが、それらを除いて私たちの全プロダクトはモノリポの上に生きています。</p>
<p>必ずしもモノリポを利用していた訳ではありませんでした。しかし時間を経て、モノリポからは多くの恩恵が得られるという結論に至ったのです。欠点も存在しますが、総じてモノリポはそれだけの価値があると断言できます。</p>
<h4>不遇な生い立ち</h4>
<p>2011 から 2013 年まで、PSPDFKit は iOS でだけ利用可能でした。しかし最終的に私たちは Android を無視することができなくなり、Android 版の PSPDFKit の開発を別リポジトリーで着手したのです。Android 版では完全に新しい UI が必要であるのに加え、Core と呼ばれる PDF ドキュメントの解析とレンダリングを担当するライブラリーについても新しいものが必要でした。iOS 版において私たちは、Apple のコアグラフィック PDF レンダリングエンジンに対し多くのカスタマイズを加えたものを使っていて、それによって PDF を解析して注釈を書き込む機能を実現していました。しかし全て Objective-C のコードだったため Android に流用することはできず、それはつまり新しい2つのリポジトリー(<code>PSPDFKit-Android</code> と <code>Core</code>)を一から始めることを意味していたのです。</p>
<p>私たちは双方のコンポーネントを交互に開発していたので、Core への変更の多くで Android 版の JNI ラッパーへの変更が必要になりました。そのため、1つの機能のために2つのプルリクエストを作成することは珍しいことではなかったですし、その2つのマージというのは同時に行う必要がありました。もちろん、Core リポジトリーへの PR のマージだけを行い Android 版の PR を残しておくこともできるのですが、私たちの目標は両 master として常に互換性を保つことであり、これによって多数のブランチを抱えることにより発生する潜在的なコンフリクトが減ったという追加の恩恵を得ることができていました。そうして、<code>Core</code> リポジトリーを <code>PSPDFKit-Android</code> へマージするまでに時間はかかりませんでした。これによってワークフローに対する大きな恩恵が得られるためです。他のプラットフォームのために <code>Core</code> リポジトリーを使うことになったとしても、後から分離することは可能であることがわかっていました。</p>
<h4>Android 版のリリースと Core の成長</h4>
<p>Android 版がリリースされた後、私たちはすぐに全体をコントロールできる恩恵を目の当たりにすることになりました。Apple の CGPDF はすばらしい PDF レンダラーですが、一部の PDF が正常に描画されなかったり、非常に処理が遅かったり、さらに悪いことには Apple のコードの根深い部分でクラッシュすることが、私たちの顧客の間で常日頃に見つかっていました。Android 上ではこれらの問題をすぐに修正できましたが、iOS 上では radar(訳注:バグ報告機能)を送信するくらいしかできることはありませんでした。(ここは私たちがかなり知見を積んだ部分になります。)</p>
<p>もっと知りたい:<a href="https://pspdfkit.com/blog/2016/writing-good-bug-reports/">Writing Good Bug Reports</a></p>
<h4>iOS 版への新エンジン導入</h4>
<p>2014 年、私たちは「iOS 版の Core を利用する」という大規模なプロジェクトを開始しました。私たちには3つの選択肢がありました。</p>
<ul>
<li><code>PSPDFKit-Android</code> を iOS 版のサブモジュールとして含め <code>Core</code> を再利用する。</li>
<li><code>PSPDFKit-Android</code> から <code>Core</code> を抽出して、以前のようなバラバラのプルリクエストを扱う。</li>
<li>iOS 版に加えて Android 版も(!)、新しい master リポジトリーにマージする。</li>
</ul>
<p>多くの話し合いが持たれましたが、最終的に多数派の意見により一つの大きなリポジトリーへ移動するという案に決まりました。私たちは(まさしく文字通り)ヒストリーを書きかえるという任務に着手し <code>PSPDFKit-Android</code> を <code>PSPDFKit-iOS</code> にマージしました。プラットフォームごとのサブフォルダーが用意され、新しいリポジトリーには次の4つのフォルダが含まれました。</p>
<ul>
<li>iOS</li>
<li>android</li>
<li>core</li>
<li>documentation</li>
</ul>
<h4>iOS/Android 版 PDF ビューワー</h4>
<p>私たちが iOS 版および Android 版の PDF ビューワーの開発を始めた時、モノリポがフィットするとは考えていませんでした。結局のところ、それらは SDK の特定のバージョン(通常は安定版)を見ているので、複数のリポジトリーにまたがった PR というのは必要ありません。一方で、ビジネスパートナーへ新しい挙動や API を紹介する前に、新しい機能や拡張のテスト版としてアプリを使う機会が長年に渡って増えています。自分たちの API をドッグフーディングすることは、インターフェイスを良くする助けにもなりました。一方で否定的側面として、PDF ビューワーが開発中の API を使っていて、master の更新によって PDF ビューワーが突如として壊れるということが定期的にありました。</p>
<p>PSPDFKit SDK の構築と改良のために、さらに多くのチームのメンバーがメインアプリとして PDF ビューワーを使い始めましたが、ソースコードを介しての結合は各バイナリーをビルドするよりずっと簡単でした。私たちはまたもや多数のプルリクエストの組み合わせやサブモジュールの衝突に陥ってしまっていましたが、それも iOS 版の PDF ビューワーをモノリポに移すと決定するまでのことでした。</p>
<p>作業は非常に複雑なものになりました。私たちはヒストリーに関して非常に気にかけていましたし、大きなバイナリ成果物を Git LFS(Large File Storage)へ移行してしまうことで、すでにかなり大きなモノリポがさらに大きくなってしまうことを避けようとしました。自動移行スクリプトを書くのに2週間程度かかりましたが、私たちはついにそれを実施し、全ての GitHub Issue についても移行を行いました。</p>
<p>今月に Android 版 PDF ビューワーで同じ作業を計画しているのですが、ここでは Gradle のビルドスクリプトが非常にシンプルになったという追加の恩恵もありました。</p>
<h4>Web とサーバー:特別なケース</h4>
<p>Web 版 PSPDFKit (<code>PSPDFKit-Web</code>) はピュア JavaScript ライブラリーとして 2016 年に開発が始まりました。Core API をラップした Unix デーモンと協調する Elixir アプリで、PSPDFKit サーバーを介してドキュメントを描画します。私たちはモノリポに <code>cli</code> プロジェクトは追加したものの、その当時モノリポに Web や Server まで追加する理由は思いつきませんでした。つまる所、それらはかなり異なったエンティティです。</p>
<p>1年後、私たちは WebAssembly の調査を始めて、そこから Web スタンドアロン版 PSPDFKit が生まれました。それは Core を直接 WebAssembly にコンパイルして JavaScript モジュールとして呼び出します。この変更の結果として、Core と Web の関係性が急激に近くなり、時々 Core の更新によって Web の機能が壊されてしまうことさえありました。私たちは別々に分かれたリポジトリーを扱うことに対してすでに辛さを感じていましたし、Web 版 PSPDFKit で Core を使うことが多くなってきている状況をかんがみて、私たちは Web (と最終的には Server も)のモノリポへの移行に現在取り組んでおり、これによって私たちの作業はよりシンプルなものになるでしょう。</p>
<h4>否定的側面</h4>
<p>とは言うものの、モノリポにも否定的な側面があります。リポジトリーは頻繁に変更されるので、pull した時に再コンパイルが必要になることが多くなります。また、Slack に進捗の経過を push するフックのようなツールは、基本的に使うことが出来ません。これは、プラットフォームごとにトラフィックを別にしてチャンネルを分けるような有効な方法がまだ存在しないからです。(しかし、<a href="https://github.com/integrations/slack/issues/384">a rather active issue for GitHub’s Slack integration</a> には、まさしくそういった機能を求める人々が参加しています。)</p>
<p>リポジトリーは何年もかけて数ギガバイトにもなっており、Git LFS システムを使ってその成長を緩和させているものの、古い履歴を書き変えることにそれほどの価値があるとは思えません。CI テストは実行時間がかすかに長くなるでしょう。これは最初に新しくチェックアウトし直すためです。(もちろんキャッシュすることは出来ますが、それでも小さなリポジトリーと同じくらい速くとはいきません。)</p>
<p>しかし、<code>git submodule</code> を扱わなくて良いことは喜ばしいことであり、チームの誰もが Grand Merge (TM) 以前の状況には戻りたくありません。</p>
<p>また、各プルリクエストが現在全てのサポートしているプラットフォームをトリガーするので、CI サーバーのロード時間が急激に増加しました。私たちはカスタム Jenkins スクリプトを記述して、変更されたファイルからその影響のあるプラットフォームのみを賢くトリガーするようにこれを修正するようにしました。</p>
<p>もっと知りたい:<a href="https://pspdfkit.com/blog/2016/selective-pull-request-testing/">Selective Pull Request Testing with Jenkins</a></p>
<h4>結論</h4>
<p>総じて、私たちはモノリポを採用して最高に満足していますし、さらに多くのプロジェクトをそこに含めようと計画しています。ワークフローがシンプルになると共に、各プラットフォームチーム間の距離が近づいたことで「深く掘り下げる」ことがしやすくなり、他のチームに事を任せてしまうのではなくプラットフォームをまたいだ問題解決がなされるようになりました。</p>
<hr />
msh5
https://yakst.com/ja/posts/5512
2019-06-14T11:53:47+09:00
2019-06-14T11:53:47+09:00
責任ある開発者のためのHTTPヘッダー
<p>This article was <a href="https://www.twilio.com/blog/a-http-headers-for-the-responsible-developer">originally published on twilio.com</a>, and translated with the permission of Twilio and <a href="https://twitter.com/stefanjudis">the author</a>.</p>
<p>当記事の<a href="https://www.twilio.com/blog/a-http-headers-for-the-responsible-developer">原文はtwilio.comにて公開されたもの</a>であり、Twilio社および<a href="https://twitter.com/stefanjudis">原著者</a>の許可を得て翻訳しています。</p>
<hr>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/hero-header.width-808.jpg" alt="HTTP headers for the responsible developer"></p>
<p><em>この記事は、プレゼンテーション "HTTP headers for the responsible developer" を文章に書き下ろしたものです。<a href="https://speakerdeck.com/stefanjudis/http-headers-for-the-responsible-developer">スライド</a>、および<a href="https://www.youtube.com/watch?v=1jD7dBsg_Nw&list=PLPcgQFk9n9y_kZkCkvGhVhUnwhfypNoeI&index=5">動画</a>も視聴することができます。</em></p>
<hr>
<p>近年、オンラインであることは、多くの人たちにとって当たり前の状態になりました。
誰もがみな、ショッピングやチャット、記事を読んだり目的地への行きかたを検索したりして時間を過ごしています。
Webは私たちと全世界をつなぎますが、確実に言えるのはWebは人と人とをつなぐということです。
私自身はWebを20年使ってきました。そして8年前に開発者になったとき、私とWebの関係に変化が訪れたのです。</p>
<h4>開発者は人々をつなぐ。開発者は人々を助ける。開発者は人々に可能性をもたらす。</h4>
<p>開発者はあらゆる人々のためのWebを作り上げる力を持ちますが、その力を使うことには責任が伴います。
つまるところ大切なのは、人々を助け、それまでできなかったことを可能にするものを作るということです。</p>
<p>この記事では、人々にとってより良いWebを目指したプロダクトを構築する上で、HTTPヘッダーがどのような助けとなるのかを解説していきたいと思います。</p>
<h3>HTTP - Hyper Text Transfer Protocol</h3>
<p>まずはHTTPの話をしましょう。HTTPは、コンピューターがWeb上でリクエストやデータを送るのに使われるプロトコルです。</p>
<p>ブラウザがサーバーにあるリソースを要求するとき、HTTPが使われます。
この要求にはkey-valueペアのセットが含まれていて、ブラウザのバージョンや、ブラウザがどんなファイルフォーマットを解釈できるかといった情報を提供します。
このkey-valueペアは<strong>リクエストヘッダー</strong>と呼ばれます。</p>
<p>サーバーは要求されたリソースとともに応答を返しますが、それだけでなく<strong>レスポンスヘッダー</strong>を送信し、リソースやサーバーに関する情報を提供します。</p>
<pre><code>Request:
GET https://the-responsible.dev/
Accept: text/html,application/xhtml+xml,application/xml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,de;q=0.7
...
Response:
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Date: Mon, 11 Mar 2019 12:59:38 GMT
...
Response Body
</code></pre>
<p>HTTPは現在のWebの基礎であり、ユーザーエクスペリエンスを向上させる様々な方法を提供します。
さあ、どうやってHTTPヘッダーを使えば <strong>安全で、誰にも手頃でアクセスしやすく、ユーザーを尊重した</strong> Webが作れるのか、一緒に見ていきましょう。</p>
<h3>Webは安全でなければならない</h3>
<p>かつて私は、インターネットをブラウズしながら危険を感じるようなことはありませんでした。
けれどもWebについて学べば学ぶほど、心配事が増えてきたのです。
<a href="https://shoptalkshow.com/episodes/special-one-one-hacker/">CDNにホストされたライブラリをハッカーが書き換える可能性</a>、<a href="https://thenextweb.com/contributors/2018/03/10/protect-website-cryptojacking-attacks/">様々なWebサイトが悪用されて訪問者のブラウザ上で行われる暗号通貨マイニング</a>、<a href="https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident">日常的にソーシャルエンジニアリングしてメジャーなオープンソースプロジェクトに侵入する人々</a>、などという実例を読むことができます。
むろん好ましくはないですが、しかしなぜこういったことを気にかけなければならないのでしょう?</p>
<p>いまの時代Webを構築するのであれば、コードを書くのはあなただけではありません。
最近のWeb開発では、一つのサイトをたくさんの人々の労力が支えています。
あなたはおそらく、多くのオープンソースのコードも利用していることでしょう。
さらには、マーケティング目的のためにいくつかのサードパーティーのスクリプトもインクルードしているかもしれません。
数百もの人々が、あなたのサイトで動くコードを提供しています。
開発者はまず、この現実に対処しなければなりません。</p>
<p>この人たち全員と、書かれたオープンソースのコードを全部信用しなければならないのかって?</p>
<p>いえ、そこらのサードパーティーのコードを信用するのは無理だと思います。
幸いなことに、あなたのサイトを保護し安全にする方法はいくつかあるのです。
加えて、<a href="https://www.twilio.com/blog/securing-your-express-app-html">helmetのようなツールはexpressのアプリケーションを保護するのに役立ちます</a>。</p>
<p><em>もし、あなたのサイト上でどれくらいのサードパーティーのコードが実行されるか知りたければ、お好みの開発者ツールのnetworkパネルを見るか、<a href="https://requestmap.webperf.tools/">Request Map Generator</a>を試すとよいでしょう。</em></p>
<h4>HTTPSとHSTS - コネクションを安全に</h4>
<p>安全なコネクションは、安全なインターネットの基本です。
<a href="https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https">HTTPSで暗号化されたリクエスト</a>がなければ、あなたのサイトと訪問者のあいだに誰もいないことの確証はとれません。
何者かがサクッとパブリックWifiを立ち上げれば、<a href="https://www.troyhunt.com/the-beginners-guide-to-breaking-website/">繋いでくる人に対し中間者攻撃</a>を仕掛けることができます。
あなたは、どのくらいの頻度でパブリックWifiを使いますか?
そして、どのくらいの頻度でそのWifiが信頼に足るものか確認していますか?</p>
<p>幸い、<a href="https://letsencrypt.org/">いまやTLS証明書は無償</a>で手に入ります。
HTTPSは標準のものとなり、ブラウザベンダーは安全なコネクション下でないと先進的な機能を使えないようにしていますし、HTTPS対応していないWebサイトを安全でないと表示して、HTTPSの導入を促しています。
残念ながら、私たちはいつも安全にWebをブラウズしているわけではありません。
誰かがWebサイトを開こうとして、アドレスバーにプロトコルを入力していなかったら (そもそもなぜ入力しないといけないのかという話がありますが) このアクションは、暗号化されていないHTTPリクエストになります。
安全な運用をしているサイトなら、HTTPSにリダイレクトするでしょう。
けれども、安全でない最初の一発目のリクエストを何者かが傍受していたら?</p>
<p>そこで<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security">HSTS</a>レスポンスヘッダー (HTTP Strict Transport Security) を利用すると、当該サイトはHTTPS経由でのみ動作することをブラウザに伝えることができます。</p>
<p><code>
Strict-Transport-Security: max-age=1000; includeSubDomains; preload
</code></p>
<p>このヘッダーは、ブラウザに対しHTTPリクエストを利用したくない旨を伝え、同一オリジンへの後続リクエストでは自動的に安全なコネクションを使うようにします。もう一度同じURLをHTTP経由でアクセスしようとしても、ブラウザはHTTPSを利用し内部的にリダイレクトを行います。</p>
<p>もう一度HTTPを使いたいようなケースのために、この設定をどれくらいの期間有効にするかは <code>max-age</code> で秒単位の設定ができます。
設定をサブドメインにも適用したい場合は <code>includeSubDomains</code> を使えば可能です。</p>
<p>さらにひと手間かけてでも、ブラウザがあなたのサイトを決してHTTPでリクエストしないようにしたければ、<code>preload</code> ディレクティブを設定し<a href="https://hstspreload.org/">グローバルリスト</a>にサイトを登録しましょう。あなたのサイトのHSTS設定が最低一年の <code>max-age</code> 要件を満たし、サブドメインでも有効化されていれば、HTTPSでのみ機能するサイトとしてブラウザの内部レコードに保持されるようになります。</p>
<p>ところで <code>my-site.dev</code> のようなローカル環境が、HTTP経由で利用できなくなったのはなぜだか疑問に思ったことはありますか?
実はこの内部レコードこそがその理由で、<code>.dev</code> ドメインが2019年2月に実際のトップレベルドメインになって以来、自動的にこのリストに含まれるようになったためです。</p>
<p>HSTSヘッダーはサイトを少しばかり安全にするだけではなく、高速化も行います。
仮に、誰かが低速なモバイル接続であなたのサイトを訪問しているとしましょう。
もし最初のリクエストがリダイレクトを受け取るHTTPのみであった場合、ユーザーは数秒間何も見えない状態で待たされるかもしれません。
HSTSを使えばこのような無駄な数秒間をなくし、ブラウザが自動的にHTTPSを使うようにできるのです。</p>
<h4>CSP - サイトで何が許されているのかを明確に</h4>
<p>これであなたのサイトは安全なコネクション下で稼働するようになりましたが、今度はmixed-contentポリシーのために、ブラウザが安全でないアドレスへのリクエストをブロックしはじめる問題にぶつかるかもしれません。
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Content Security Policy (CSP)</a>ヘッダーは、このような問題を解決するうえで素晴らしい方法を提供します。
CSPのルールセットは、HTMLのメタ要素かHTTPヘッダーで定義することができます。</p>
<pre><code>Content-Security-Policy: upgrade-insecure-requests
</code></pre>
<p><code>upgrade-insecure-requests</code> ディレクティブは、まるで魔法のように全てのHTTPリクエストをHTTPSにアップグレードするようブラウザへ伝えます。</p>
<p>CSPは、ただのプロトコルではありません。
あなたのサイト上でどのようなリソースやアクションが許されているかを定義する上で、個々の粒度でできる設定方法を提供します。
たとえば、どのスクリプトが実行されるべきかとか、画像はどこからロードされるべきか、といったことを定義することができます。
もし許可されていないことが行われようとすればブラウザはブロックし、あなたのサイトを潜在的な攻撃から守ります。</p>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/OgnYZBz1NTYoxy42rxnEatqsFJoozi-T6o5G9apTEAIZcM.width-800.jpg" alt="CSP log"></p>
<p>いまこれを書いている時点では、CSPには24種類の設定オプションがあります。
これらのオプションは、スタイルシート上でのスクリプト使用からService Workerのオリジンに至るまで多岐にわたります。</p>
<hr>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/Q0VKdwaOOJg19jVUtF-H2nyr_cU1L5B8zxjkM9vCEo2CsH.width-800.png" alt="CSP options"></p>
<p><em>完全な概要はMDN上で見ることができます</em></p>
<hr>
<p>CSPを使うと、サイトが何をインクルードすべき/すべきでないのかを明確にすることができます。</p>
<pre><code>Content-Security-Policy: default-src 'self'; script-src 'self' just-comments.com www.google-analytics.com production-assets.codepen.io storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: images.contentful.com images.ctfassets.net www.gravatar.com www.google-analytics.com just-comments.com; font-src 'self' data:; connect-src 'self' cdn.contentful.com images.contentful.com videos.contentful.com images.ctfassets.net videos.ctfassets.net service.just-comments.com www.google-analytics.com; media-src 'self' videos.contentful.com videos.ctfassets.net; object-src 'self'; frame-src codepen.io; frame-ancestors 'self'; worker-src 'self'; block-all-mixed-content; manifest-src 'self' 'self'; disown-opener; prefetch-src 'self'
</code></pre>
<p>上記のルールセットは私の個人サイトで使っているものですが、もしこのCSP定義の例を考えつくのはかなり難しいように思えたとしたら、全くもってその通りです。
このCSPルールセットを自分のサイトで実装するにあたり、正しく機能させるまでに3回は試行錯誤する必要がありました。
ルールセットのせいでサイトが何度か壊れてしまったので、デプロイしてはロールバックを繰り返したものです。
しかし実は、もっと良い方法が存在します。</p>
<p>サイトが壊れるのを防ぐために、CSPにはレポートのみを行うオプションがあります。</p>
<pre><code>Content-Security-Policy-Report-Only: default-src 'self'; ... report-uri https://stefanjudis.report-uri.com/r/d/csp/reportOnly
</code></pre>
<p><code>Content-Security-Policy-Report-Only</code> モードを利用すると、ブラウザはブロックされる見込みのリソースのコンソールログ出力だけを行います。
このレポートの仕組みはルールセットの動作確認と調整に役立ちます。</p>
<p><code>Content-Security-Policy</code> と <code>Content-Security-Policy-Report-Only</code> の二つのヘッダーは、ルール違反とログ情報を <code>report-uri</code> へ送信するエンドポイントの定義手段を提供します。
ログ出力用のサーバーをセットアップすれば、送信されたログ情報を使って、リリースする準備ができるまでCSPルールの微調整を行うことができます。</p>
<p>おすすめの手順は、まずCSPをレポートモードだけでデプロイし、本物のトラフィックからどんな違反が入ってくるかを分析し、あなたの制御下にあるリソースに対する違反が出てこない場合にだけ、設定を有効にすることです。</p>
<p><em>もしこのようなログを扱うためのサービスをお探しなら、私は<a href="https://report-uri.com/">Report URI</a>を使っていますが、とても役に立っています。</em></p>
<h5>CSPの普及状況</h5>
<p>近年の<a href="https://caniuse.com/#search=csp">CSPのブラウザサポート状況は良好</a>ですが、残念なことに利用しているサイトはそれほど多くありません。
どれくらいの数のサイトがCSPとともにコンテンツを配信しているかを調べるため、<a href="https://httparchive.org/">HTTP Archiveに</a>クエリを投げてみたのですが、クロールされているサイトのうちCSPを利用しているものはわずか6%しかありませんでした。
私たちはWebをより安全な場所にするためにもっとうまくやれることがあると思いますし、ユーザーが気づかないうちに暗号通貨のマイニングをさせられるようなことも避けられるはずです。</p>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/jAQW_VflYXdzcl9ER0yTVPGpTBRQAOVZrIvgjSW6ucayZH.width-800.jpg" alt="CSP ratio"></p>
<h3>Webは手頃でアクセスしやすいものでなければならない</h3>
<p>この記事を書いているあいだ、私は高速なWifi接続を利用した比較的新しいMacBookの前に座っています。
開発者は、このような環境が大多数のユーザーにとって当たり前ではないのだということを、しばしば忘れてしまいがちです。
私たちのサイトを訪問する人たちは、古い電話端末に貧弱な接続環境を使っています。
数百ものリクエストで重たく膨れ上がったサイトは、彼らに対し好ましくないエクスペリエンスを提供してしまいます。</p>
<p>それにエクスペリエンスの話だけではありません。<a href="https://whatdoesmysitecost.com/">人々はどこに住んでいるかによって、データに対し支払う金額が異なります</a>。
仮に、自分が病院のWebサイトを構築していると想像してみてください。
まさにこのサイト上の情報が、ときに決定的なものとして、人々の命を救うかどうかを左右するかもしれないのです。</p>
<p>病院サイトでの1ページが5MBもあると、サイトは低速になるばかりでなく、本当に必要としている人たちにとって高価になりすぎてしまう可能性があります。
ヨーロッパやアメリカ合衆国における5MBの値段は、アフリカでの5MBのデータに対する値段とは比べ物になりません。
開発者には、Webを誰にも手頃でアクセスしやすいものに保つ責任があります。
この責任には、適切なリソースを配信することや、妥当なツールを使うための評価 (ランディングページにJavaScriptフレームワーク、本当に必要ですか?) そして不要なリクエストを避けることが含まれます。</p>
<h4>Cache-Control - 変更されていないリソースへのリクエストを避ける</h4>
<p>最近のWebサイトは、スタイルシートからスクリプトのファイル、そして画像に至るまで、容易に数百にもなるリソースを内包します。
<code>Cache-Control</code> ヘッダーを使うと、開発者はリソースをどのくらいの期間「十分新しい」とみなし、ブラウザキャッシュから提供しても差し支えないと考えられるかを定義できます。</p>
<pre><code>Cache-Control: max-age=30, public
</code></pre>
<p>適切な <code>Cache-Control</code> の設定を行うとデータ転送は抑えられ、ファイルは <code>max-age</code> で指定された一定秒数の間、ブラウザキャッシュから利用することができます。
定められた時間が経過したら、ブラウザはキャッシュされたリソースを再検証することになります。</p>
<p>しかし訪問者がページを再読み込みした場合は、キャッシュされたデータがいまだに有効かどうかを確かめるべく、<a href="https://code.fb.com/web/this-browser-tweak-saved-60-of-requests-to-facebook/">ブラウザは参照されているリソースを含めて再検証</a>を行います。
サーバーはキャッシュされたデータがまだ良好であることを示す304応答を返すか、更新されたデータを配信しつつ200応答を返します。
これは転送データ量の削減にはつながりますが、必要に応じて行われたリクエストとは言えません。</p>
<p>そこで役に立つのが <code>immutable</code> の機能です。</p>
<h5>immutable - 同じリソースは二度とリクエストしない</h5>
<p>モダンなフロントエンドのアプリケーションでは、通常スタイルシートとスクリプトファイルは <code>styles.123abc.css</code> のような、ユニークなファイル名を持っています。
このファイル名は特定のファイルの中身によって決まります。
もしファイルの中身が変われば、異なるファイル名がつけられます。</p>
<p>このようなユニークなファイルは、ユーザーがページを再読み込みしたときを含め、いつまでもキャッシュされてしまう可能性があります。
<a href="https://hacks.mozilla.org/2017/01/using-immutable-caching-to-speed-up-the-web/">immutableの機能</a>は、ブラウザに対し一定期間のあいだリソースを再検証しないように伝えることができます。
これは変更されることのない固有の資産を扱うとき、再検証のリクエストを避ける上で有意義なものです。</p>
<pre><code>Cache-Control: max-age=31536000, public, immutable
</code></pre>
<p><strong>最適化されたキャッシュのやりかたは非常に難しく、特にブラウザでのキャッシュには複数の設定方法が提供されており、あまり直感的ではありません。以下のリソースを参照することをおすすめします。</strong></p>
<ul>
<li>Harry Robertsによる<a href="https://csswizardry.com/2019/03/cache-control-for-civilians/">cache-controlとその設定に関する優れたガイド</a></li>
<li>Jack Archibaldによる<a href="https://jakearchibald.com/2016/caching-best-practices/">キャッシュのビジュアル詳解ベストプラクティス</a></li>
<li>Ilya Grigorikによる<a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching">キャッシュに関するヘッダーのhow-toフロー図</a></li>
</ul>
<h4>Accept-Encoding - 最大限まで圧縮する (えっと... 最小限か)</h4>
<p><code>Cache-control</code> を利用すると、リクエストの回数、および繰り返しネットワーク上を行き交うデータ量を削減できるのを見てきましたが、私たちはリクエスト数を減らせるだけではなく、転送されるもの自体を小さくすることもできます。</p>
<p>リソースを配信するとき、開発者はできる限り小さなデータを送るよう心がけなければなりません。
HTMLやCSS、JavaScriptのようなテキストベースのリソースでは、転送されるデータ量を減らす上で、圧縮が重要な役割を果たします。</p>
<p>近年、もっとも良く使われている圧縮はgzipです。
サーバーはテキストファイルをその場で瞬時に圧縮できるくらい高速ですし、リクエストに応じて圧縮されたデータを提供することもできます。
けれども、いまやgzipはベストな選択肢ではありません。</p>
<p>ブラウザが行なっているHTMLやCSS、JavaScriptといったテキストベースのファイルに対するリクエストを調べ、リクエストヘッダーを見てみると <code>accept-encoding</code> ヘッダーが見つかるでしょう。 </p>
<pre><code>Accept-Encoding: gzip, deflate, br
</code></pre>
<p>このヘッダーは、サーバーがどんな圧縮アルゴリズムを解釈するかを伝えます。
さほど良く知られていない <code>br</code> は<a href="https://github.com/google/brotli">Brotli圧縮</a>のことで、GoogleやFacebookのような高トラフィックのサイトで利用されています。
Brotli圧縮を使うには、サイトはHTTPSで稼働している必要があります。</p>
<p>この圧縮アルゴリズムは、小さなファイルサイズを目標に作られたものです。
ローカルマシン上で手動でのファイル圧縮を試してみると、実際Brotliはgzipよりも優れた圧縮ができることが分かるでしょう。</p>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/WdigoLiMIP2UPVhDrABHt_bJYNztLyO6-FFLQ8Jo8wyrXh.width-800.jpg" alt="br vs gzip"></p>
<p>もしかすると、Brotli圧縮は低速だと聞いたことがあるかもしれません。
その理由は、Brotliには11種類の圧縮モードがあり、長いエンコーディング時間をかけてでも小さなファイルサイズを保証することを優先した結果、デフォルトモードが選択されるためです。
一方のgzipには9種類のモードがあり、デフォルトモードはスピードもファイルサイズも考慮に入れて選択されます。</p>
<p>このような判断のため、Brotliのデフォルトモードはデータ伝送時の圧縮に用いるには不向きですが、圧縮モードを変更すると、gzipと遜色ないスピードで小さなファイルサイズを実現することができます。そうすればBrotliをデータ伝送時の圧縮に使うこともできますし、サポートされているブラウザに対してはgzipの代替に相当するものとして扱える可能性もあるでしょう。</p>
<p>加えて最大限にファイルサイズを削減したいのであれば、動的にファイルを圧縮する考えはやめて、<a href="https://www.telerik.com/blogs/maximize-compression-with-zopfli">zopfliを利用して最適化されたgzipファイル</a>やBrotliファイルを事前生成して、静的に配信する方法もあります。</p>
<p><strong>さらにBrotli圧縮について読み、gzipと比べてどのようなものかを知りたければ、Akamaiの人たちが<a href="https://blogs.akamai.com/2016/02/understanding-brotlis-potential.html">このテーマで発展的な研究</a>をしています。</strong></p>
<h4>AcceptとAccept-CH - ユーザーに特化したリソースを提供</h4>
<p>テキスト資産を最適化すると何KBものファイルサイズを抑えられるのは素晴らしいことですが、もっと重たい画像のようなリソースでさらに大きなデータを削減するにはどうしたら良いのでしょうか?</p>
<h5>Accept - 適切なフォーマットで画像を配信</h5>
<p>ブラウザは、どんな圧縮アルゴリズムを解釈するかを教えてくるだけではありません。
ブラウザが画像をリクエストするときは、どんなファイル形式を解釈できるかの情報も提供しています。</p>
<pre><code>Accept: image/webp, image/apng, image/*,*/*;q=0.8
</code></pre>
<p>ここ数年、新しい画像フォーマットを巡る競争が行われてきましたが、どうやら <code>webp</code> が勝者のようです。
<a href="https://developers.google.com/speed/webp/">webpはGoogleによって開発された画像フォーマット</a>であり、最近では<a href="https://caniuse.com/#feat=webp">webp画像はかなり広くサポート</a>されています。</p>
<p>このリクエストヘッダーを使うと、たとえブラウザからは <code>image.jpg</code> がリクエストされたときであっても、開発者はよりサイズの小さな <code>webp</code> 画像を配信するようにすることができます。
Dean Humeは、Service Workerでこれを実現する方法について<a href="https://deanhume.com/service-workers-dynamic-responsive-images-using-webp-images/">優れたガイド</a>を書いています。すごく良いですね!</p>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/9JczExPcmc402uz1B2mY_KmhuaQNl6UExmSYcs2ZH7yNDk.width-800.jpg" alt="webp"></p>
<h5>Accept-CH - 適切なサイズの画像を配信</h5>
<p>サポートしているブラウザに対しては、<a href="https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints">クライアントヒント</a>を有効にすることもできます。
クライアントヒントは、viewportの幅や画像の幅、さらにはRTT(round trip time)や <code>2g</code> の接続方式のようなネットワークの条件の追加ヘッダーを送信するよう、ブラウザに伝える方法です。</p>
<p>このような設定はメタ要素の追加によって有効化することができます:</p>
<pre><code><meta http-equiv="Accept-CH" content="Viewport-Width, Downlink">
<meta http-equiv="Accept-CH-Lifetime" content="86400">
</code></pre>
<p>あるいは、最初のHTMLリクエスト時にヘッダーを設定してもよいです:</p>
<pre><code>Accept-CH: Width, Viewport-Width
Accept-CH-Lifetime: 100
</code></pre>
<p>サポートしているブラウザは、後続のリクエストでは定義された時間の長さ (<code>Accept-CH-Lifetime</code> の秒数) に関する追加情報を送信するようになり、開発者がHTMLを変更することなしにユーザーの状況に合わせた画像を用意してくれます。</p>
<p>たとえば画像の幅の追加情報をサーバーサイドで受け取れるようにするには、画像に <code>sizes</code> 属性をつけ、この画像がどのようにレイアウトされるのかをブラウザに伝えるようにしましょう。</p>
<pre><code><!-- この画像はフル幅、すなわち100 viewport width (vw)で表示されます -->
<img class="w-100" src="/img/header.jpg" alt="" sizes="100vw">
</code></pre>
<p>最初の段階で <code>Accept-CH</code> レスポンスヘッダーと <code>sizes</code> 属性のついた画像を受信すれば、サポートしているブラウザは <code>viewport-width</code> と <code>width</code> ヘッダーを画像へのリクエストに含め、どんな大きさの画像が最適かをサーバー側に教えてくれます。</p>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/8u07oEaYg3fBaV6Z35RcBDXW7hehK8wPrPSthhRpQlHN3i.width-800.jpg" alt="viewport-width"></p>
<p>サポートされた画像フォーマットと寸法があれば、エラーが起きないようファイルフォーマットやサイズに気を遣った以下のような画像要素を繰り返し書かなくとも、ユーザーに合わせて作られたメディアを送信することができます。</p>
<pre><code><picture>
<!-- Chrome, Edge, Firefox, Operaにはwebp画像を配信する -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
/image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
/image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
type="image/webp">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
/image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
/image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
type="image/webp">
<!-- 他のブラウザにはjpeg画像を配信する -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
/image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
/image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
/image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
/image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
<!-- 画像をサポートしないブラウザのためのフォールバック -->
<img src="/image/thing.jpg" width="50%">
</picture>
</code></pre>
<p>viewportの幅と画像サイズにさえアクセスできれば、資産をリサイズするロジックをサーバー側処理の真ん中に据えることも可能です。</p>
<p>しかし考えなければならないのは、正確な画像の幅が分かるからといって、毎回全ての幅に合わせた画像を作りたいわけではないことです。
特定の寸法の幅に合わせた画像 (<code>image-200</code>, <code>image-300</code>...) を送ることは、CDNのキャッシュを活用し、計算時間を削減することにつながります。</p>
<p>さらには、Service Workerのような最近の技術があれば、クライアント側に介入してリクエスト自体を変更し、最も適した画像ファイルを提供することもできます。クライアントヒントが有効化されたService Workerがあれば、レイアウト情報へのアクセスとともに<a href="https://cloudinary.com/">Cloudinary</a>のような画像APIを利用することができ、適切なサイズの画像を受け取れるようブラウザ内で画像URLを調整することができるでしょう。</p>
<p><strong>さらにクライアントヒントに関する情報をお探しなら、<a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints/">Jeremy Wagnerの記事</a>か、<a href="https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints">Ilya Grigorik</a>の記事を読むと良いでしょう。</strong></p>
<h3>Webは人々を尊重するものでなければならない</h3>
<p>1日のうち何時間もの時間をオンラインで過ごす私たち全員にとって、私がすごく重要だと思う最後の側面、それはWebが人々を尊重するものでなければならないということです。</p>
<h4>preload - 待ち時間を短縮</h4>
<p>開発者として、ユーザーの時間は大切にしたいものです。
時間を無駄にしたい人は誰もいません。
これまでのセクションで議論してきたように、適切なデータを配信することは、かかる時間やデータ量を減らす上で大きな役割を果たします。
それには何に対するリクエストが行われるかだけでなく、タイミングや順番も関わってきます。</p>
<p>例を考えてみましょう。
サイトでスタイルシートをインクルードすると、読み込みが済むまでブラウザでは何も見えません。
ブラウザは、何も表示していないあいだもHTMLをパースし続け、取ってくる必要のある他のリソースを探します。
スタイルシートが読み込まれてパースされると、今度はフォントのように、他にもリクエストが必要になるクリティカルなリソースが含まれているかもしれません。
このように連続したプロセスは、訪問者からみた読み込み時間を増やしてしまいます。</p>
<p><a href="https://www.w3.org/TR/preload/"><code>rel=preload</code></a> を使うと、これからどのようなリソースがリクエストされるのかの情報をブラウザに伝えることができます。</p>
<p>リソースはHTML要素でプリロードすることができます。</p>
<pre><code><link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin="anonymous">
</code></pre>
<p>もしくは、ヘッダーでも。</p>
<pre><code>Link: </font.woff2>; rel=preload; as=font; no-push
</code></pre>
<p>このようにするとブラウザは、ヘッダーを受信するか <code>link</code> 要素を発見すると、必要になった時にブラウザキャッシュに載せておけるよう、すぐにリソースへリクエストを行います。
このプロセスは、訪問者の時間に価値を置くものです。</p>
<p>リソースを最善の方法でプリロードし、設定内容についてくまなく理解するには、以下の情報を参照することをおすすめします:</p>
<ul>
<li><p>Yoav Weissはプリロードの専門家で、<a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/">素晴らしい情報</a>を投稿しています。</p></li>
<li><p>Addy Osmaniは、<a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf">プリロードや <code>prefetch</code> <code>preconnect</code> のような他の仕組みについて、見事な詳細</a>を書いています。</p></li>
</ul>
<h4>Feature-Policy - イラっとさせる人にならないように</h4>
<p>仮に、もう私が見たくないものを一つだけ挙げるとすれば、それは問答無用でいちいち許可を求めてくるWebサイトです。
私の同僚、Phil Nashの言葉から引用すれば十分でしょう。</p>
<p><strong>ページの読み込み時に通知許可を要求するな。</strong></p>
<p>開発者は人々に敬意を払い、訪問者をイライラさせるようなサイトを作ってはいけません。
人々は許可を求めるダイアログを片っ端からクリックするわけです。
Webサイトや開発者は相当な信用を失いますし、輝かしいあらゆるWebの新機能も、開発者が配慮して利用しなければその力を失ってしまいます。</p>
<p>しかし、大量のサードパーティーのスクリプトをサイトにインクルードすることが必須で、これらのスクリプト群が次から次へと許可を求めるダイアログを出してくるような場合、どうしたら良いのでしょう?
インクルードされた全てのスクリプトの振る舞いを、いかにして知ることができるでしょうか?</p>
<p>こんなときが <code>Feature-Policy</code> ヘッダーの出番です。
このヘッダーを使うと何の機能が許可されるのかを定義でき、サイトで実行される他者のコードが引き金となって現れる、許可を求めるダイアログのポップアップを制限することができます。</p>
<pre><code>Feature-Policy: vibrate 'none'; geolocation 'none'
</code></pre>
<p>これはヘッダーを使って定義することもできますし、サードパーティーの統合でありがちな、iframeのように埋め込みをするコンテンツでも定義することができます。</p>
<pre><code>< iframe allow="camera 'none'; microphone 'none'" >
</code></pre>
<p>いまこれを書いている段階で <code>Feature-Policy</code> はまだかなり実験的ではあるものの、このさき追加されるオプションを見てみると、とてもワクワクできる内容です。
そう遠くない未来に、開発者はイラっとくるダイアログを防げるだけでなく、最適化されていないメディアをブロックできるようになる可能性があります。
このような機能は、ユーザーエクスペリエンスに大きな差をつけるでしょう。</p>
<hr>
<p><img src="https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/9kC_q_Icf-bbsY5eGHjS0u3KTLtTpM__7uqqmh-WfVgPc6.width-800.jpg" alt="Feature-Policy"></p>
<p><em>Feature-Policyに関する一連の概要はMDN上にあります。</em></p>
<hr>
<p>上のfeature policyのリストを見ると、一番やっかいなプッシュ通知はどうなるのか気になるかもしれません。
いざやってみると、思いのほか <code>Feature-Policy</code> の実装は難しかったようです。
さらに知りたければ、<a href="https://github.com/w3c/webappsec-feature-policy/issues/243">GitHubのissue</a>を追ってみるとよいでしょう。</p>
<p>feature policyを使うと、図らずもあなたやサードパーティーにより、サイトが<a href="https://2018.bloomca.me/en">許可要求ブロック祭り</a>と化してしまうのを防ぐことできます。
残念ながら最近の多くのサイトでは、それが普通の状態になってしまっているのですが。</p>
<h3>Webはあらゆる人のためのものでなければならない</h3>
<p>この記事では、ユーザーエクスペリエンスの改善に役立ついくつかのヘッダーを紹介したにすぎません。
ほぼ網羅されたヘッダー群の概要とその可能性について知りたければ、<a href="https://mobile.twitter.com/derSchepp">Christian Schaefer</a>のスライド <a href="https://schepp.github.io/HTTP-headers">“HTTP headers – the hidden champions”</a> がおすすめです。</p>
<p>いまの時代、素晴らしいWebサイトを作るのは困難なことだと思います。
開発者はデザイン、デバイス、フレームワーク、それから... そうです。ヘッダーがどんな役割を果たすのかを考えなければいけません。
願わくばこの記事が、あなたが次のWeb開発のプロジェクトで、安全性や手の届きやすさ、ユーザーを尊重することについて考えるきっかけになることを望みます。
なぜならそういった要因こそが、Webを本当の意味で誰にとっても素晴らしくするものだからです。</p>
<hr>
<p>筆者にコンタクトしたい場合は、以下からどうぞ:</p>
<ul>
<li>Email: <a href="mailto:sjudis@twilio.com">sjudis@twilio.com</a></li>
<li>Github: <a href="https://github.com/stefanjudis">stefanjudis</a></li>
<li>Twitter: <a href="https://twitter.com/stefanjudis">@stefanjudis</a></li>
</ul>
<hr /><b>関連タグの記事 </b><a href="https://yakst.com/ja/tags/uiux#?from=feed">uiux タグのついた記事</a>
meiq