bussorenre Laboratory

hoge piyo foo bar

Certified Scrum Master研修記録 2020.02.06-08

はじめに - CSMの概要 と 受けたきっかけ

2020.02.06 から2020.02.08 から3日間、 https://www.odd-e.jp/ja/service_csm/ の CSM研修 を受けてきました。研修を行っているのは、 odd-e ですが、資格を発行・認定するのは Scrum Aliance という会社です。

私が受けた会の講師は江端一将( ebacky )さんで、日本人唯一のCST (スクラムを教えられる認定コーチ)です。

ebacky さんいわく「参加者のレベルや状況によって内容をその場その場でアレンジしている」とのことなので、同じ研修でも会によって全然内容が異なります。この記事を読んでも「全然違う研修だった」ということもあると思います。

研修の流れですが、

  • odd-e のトレーニングプログラム(2日 or 3日)を受ける
  • webテストの案内が来るので、これを受けて合格する

以上の2プロセスにより、認定スクラムマスターになれます。認定スクラムマスターになって初めて受けられるアドバンスドな研修やイベント等もあるようなので、まずはここを目指すと良いのかなと思います。

認定の期間は2年間です。更新手続きもありますが、それは別途そのときに書こうかなと思います。

受講経緯

CSM の存在は、社内の過去の参加者のLT会等でも知ってましたし、界隈で有名だったので知ってました。 書籍だと kaizen journey 等でスクラムが取り上げられていたり、自社でも過去にodd-e の方をスクラムコーチとして呼んでいたこともあります。(私はその当時そのチームには居ませんでしたが…)

個人的なモチベーションとしては、個人の力の限界をひしひしと感じておりました。いくら一人が技術的・仕事的にスキルアップしても、一人の力はたかがしてれます。 当時は「組織として製品を作る」という経験が乏しいと感じていたので、そのための組織論であったり方法論であったりを深めたいというモチベーションがありました。

また、当時所属していたチームは完全な縦割りで、何かしらコミュニケーションを加速させる役割の人が必要だなとも思っていたので、そのための一歩として申し出て受けました。

研修を受けるにあたっての心構え・前準備

本研修は、スクラムに関する知識を体系的に学習するための講義ではない です。 (体系的な知識は、基本的に事前資料であったり、 Core Scrum を読む必要があります)

スクラムマスターならどう考え、どう行動(改善)するか」 という トレーニングが主目的です。

受け身の座学の部分も多少はありますが、基本的に自発的な参加を求められる「ディスカッション」や「ワーク」などの時間が最も多いです。曰く

スクラムマスターって言うのを雇うと良いって聞いたんやけど、実際何がええん??」と言う問いに答えられなければなりません。また、「スクラムマスターを雇ってよかったわ。ありがとな!」と言われるような行動を示さなければなりません。

また、この記事をリライトする前に、スクラム 仕事が4倍速くなる“世界標準”のチーム戦術 | ジェフ・サザーランド, 石垣賀子 |本 | 通販 | Amazon を読みました。スクラム創始者の一人、「ジェフ・サザーランド」さんの書籍です。事前に読んでおくと良かったと思う一冊ですが、研修を受けてから読んで理解した部分も多いので、教科書的に何度も読み返す事をして良い本だと思います。

スクラムに関する知識とか

言葉の定義など

グラウンドルールとしての、言葉の定義

本来の定義というよりは、「この場ではこういう意味で扱います」という意味で共有されたもの

  • 研修 : 一方的な情報伝達
  • レーニン : 自らの努力などの働きかけによって自らのスキルを伸ばす
スクラム関連の言葉の定義
  • チーム
    • 7±2 人で構成された組織。それ以上でもそれ以下でも定義に反するのでチームと呼ばない
  • プロジェクト
    • 現状と目的の差を埋めるための活動。(※ 現状と目的との差分の明確度や、個数などは問わない
  • プロダクト
    • 「価値」だと考えられるもの(難しいが、「価値そのもの」ではない。価値だと「考えられる」。お金でも無い)
    • 一つの「プロダクト」に複数の種類の価値が発生することもある
  • スクラム
    • 「プロジェクト」の現状を把握するための「方法論」の一つ
  • スクラムチーム
    • プロダクトオーナー(以下PO), スクラムマスター(以下SM), チーム で構成された組織

方法論とプラクティスの違い

方法論とは

  • 学問に基づいて理論立てて構成されたもの
  • いかなる条件や環境でも有効に働く
  • 目標に対して、直接的には寄与しない

ラクティスとは

  • それまでの習慣や、感覚的に「良さそう」と思われているもの
  • 特定の条件や環境の中でのみ有効に働く
  • 直接的に、目標に寄与する

先述の「スクラム」の定義によると、スクラムは「方法論」なので、具体的なプラクティスは厳密にはスクラムではない。 あくまで、「スクラム」という「方法論」があって、それを肉付けするのが「プラクティス」(具体的に日々やってたりやってなかったりすること)

スクラムは、基本的に「組織学」と「集団心理学」に基づいて構築された「方法論」なので、それらの分野に精通するとよりスクラムへの理解が深まる。

中央集権的組織 vs チーム中心主義的組織

中央集権的組織とは

  • いわゆる「トップダウン型」「三角形のピラミッド型」の構造を持った組織
  • 統制・統治する ことが最大目的の組織構造
  • ピラミッドの最も上に居る人から、末端にかけて「指示・司令」を出して行き組織のコントロールを生み出す。

チーム中心主義的組織とは

  • ボトムアップ型」「相互作用型」という形の構造を持った組織
  • より良い成果を出す ことが最大目的の組織構造
  • 中央集権型組織と異なり、「司令塔」のような存在は無く、各々が各々の「役割」を果たすことで最大の成果を生み出す。

スクラムチームは「チーム」の一種であるのが、スクラムチーム以外にも「チーム中心主義的組織」を生み出すことは可能。(スクラムじゃないから、研修では扱わないとのこと)

日本の企業はほぼみんな「中央集権的組織」になるが、開発で「スクラムチーム(チーム中心主義的組織)」を採用しようとすると、スクラムチームの中と外でどうしても構造上の歪が生まれてしまう。 特に、歪の渦中に飲まれやすいのは、人事評価する評価者(上司とか社長とか)であったり、スクラムチーム外とのやり取りが多い役割の人物であったり、スクラムマスターであったりする。

逆に言うと、これらの人はスクラムチームに余計なノイズが入ってこないように努力する必要がある。

スクラムチームの定義

PO(1人), SM(1人), チーム(5-9人) で構成された組織。

  • PO は スクラムチームの ROI を最大化する
  • チーム は スクラムチームの生産性を最大化する
  • SM は スクラムチームの成果を高めるための活動を見直し、「成果を高める確率」 を最大化する
プロダクトオーナーの責務

どんなプロジェクトにも「予算」は必ず存在する。ROI(投資対効果)を最大にするのがPOの責務。 ここで言う「予算」とは「お金」「人」「時間」など。

ROI を最大化するために、顧客に提供する価値を記述したプロダクトバックログを作成したり、それらの優先順位をつける。

チーム

一方、チームには生産性を最大化する責務がある。ここで言う生産性とは「プロダクト」に対する生産性である。「プロダクト」とは製品の製造だけでなく、関わるドキュメントの作成、メンバーのスキル、文化、なども「プロダクト」になる。何をプロダクトと捉えるかは、プロジェクトで決定される。

「プロダクト」を分解したものが「ストーリー」であり、「ストーリー」を数値化したものが「ストーリーポイント」である。 つまり、ストーリーポイント/スプリント = ベロシティ を最大化することがチームの責務になる。

「ストーリー」に紐付かない作業はすべて「タスク」として分類される。タスクは「ストーリー」ではないので、いくつクリアしてもストーリーポイントは0。つまり、ベロシティに貢献しない

スクラムマスター

上2つのロールと異なり、スクラムマスターは、実際に手を動かしたり、実際にプロジェクトに関わる意思決定をすることができない。 あくまで「成果を高めるための活動を見直す」ことに注力する。

例えば、チームの稼働時間のうち「タスク」に割いている時間の割合が多く、ベロシティに寄与していないと考えたのであれば「タスク」が多く積まれてしまっている原因はなにか、解決するためにはどうしたら良いのか、課題を発見し改善する方法を「提案する」

提案されたプランを採用するのは、PO あるいは チーム あるいは組織外の人の裁量によって行われる。つまり、良いスクラムマスターは自分の出したプランが、チームに採用される確率が高い。

提案したプランを実際に実行して効果が出るかどうか。より効果が期待できるプランを提案する必要があるし、一度提案したプランを見直し、更に良い状態にするための提案を繰り返し行う必要がある。

スクラムの構造

  • 学問
    • 集団心理学
    • 組織学
  • 状態
    • Transparency
    • Inspect
    • Adapt
  • ルール
    • Role
    • Event / Celemony
    • Artifact
    • Time

これらの要素が一つでも欠けていればスクラムではない。

学問

集団心理学、あるいは組織学に基づいて論理的に証明されたことをベースに組み立てられている。 なので、スクラムのルールに追加・変更がある場合は、基本的にそれらの学問において新たな論文が採択される時である。

状態

以下の3つの状態を満たしていなければ、スクラムではない。

  • Transparency
    • 正しい情報が、一箇所に集められており、次の行動が誘発され続けている状態
  • Inspect
    • 目的と現状の差分を把握し続けている状態
  • Adapt
    • 目的と現状の差分を埋めるための活動を続けている状態

Transparency について。「正しい情報」とは

  • 過去の情報ではなく、「現在どうあるか」という現在の情報であり、
  • 誰が見てもどう見ても、一意に同じ解釈ができるもの

つまり、チームの中で一人しか把握していなかったりすることは「正しい情報」ではない。 最初は一人しか把握していなかったことを、チーム全員に共有する(あるいはいつでも見れるようにドキュメント化するなど)をして初めて「正しい情報」になる。

次の行動が誘発され続けている状態とは、↑を見聞きして、「じゃぁ次これをやる必要があるな」と各々が判断できる状態のこと。

Incpect について。「目的と現状の差分を把握し続けている状態」を行うために「カンバン」などのツールを適宜利用する。ツールの運用等に関しては「方法論」ではなく「プラクティス」の領域なので講習の対象外

Adapt について。 これは単純に「ゴールに向けて頑張ろう」という意味だけではない。「このベロシティではこのゴールにたどり着けない」や「そもそも本当に価値のあるものではなくなった」など、変化に対して柔軟に差分を埋める活動を行う。つまり、「目的」さえも変化の対象になりうる。「Transparency」を満たすことが大事

ルール
  • Role
    • 先述の通り、PO(ROI), SM(成功確率), チーム(生産性)、の3つの役割がある。
    • 基本的に兼任は良くないとされているが、あくまで役割なので多少は崩しても良い
      • が、それは熟練したスクラムチームでも難しいので、あくまで欠員が出たとか緊急時のみに留めたほうがいいとの事
  • Event / Ceremony
    • やることが「Must」なこと。これらをやらないとスクラムは成立しない。上からスパンが長い順に並べてる
    • Product Backlog Refinement
      • 要するに優先順位付け
      • プロダクトバックログアイテムの洗い出しや、それらの価値の推定。優先順位決定を行う。
      • 中長期スパンで行う
    • Sprint Planning
      • そのスプリントで何をするかを決める。
      • ここで大事なのは 「何をどうすれば完了になるのかをチーム全員が把握している状態」 がゴールであること
      • スクラム初めてのチームは、スプリントプランニングだけで数日使うこともザラにある
      • (ebacky 先生曰く、最初は全部のチケットが同一ポイントになるよう分解すると、わかりやすいとのこと)
    • Sprint Review
      • スプリントの振り返りを行う
      • できた。できなかった、ベロシティの計測は基本。
      • スプリントプランニングで見積もったポイント数と、実際にやってみたポイント数の大きさの誤差を必ず記録する
        • ↑をやらないとどれだけスプリントを回しても見積もり精度が向上しない
    • Sprint Retrospective
      • チームの「振る舞い」に対する振り返りを行う。
      • 多分、全セレモニーの中で、最も難しく、最もスクラムマスターの腕が問われるイベント。
      • KPT は、チームの振る舞いを評価するフレームワークなので、できたできなかったの評価は違う(それはYWTなどのほうが向いている)。
    • Daily Scrum
      • 毎日やる
      • スプリントプランニングで決定した「やること」「ゴール」に対して、今どのあたりにいるかを全員で共有し、再計画する
      • DS は ただの日報共有になりがちだが、目的は「スプリントの再計画」なので、積極的に課題を共有したりしないと意味がない。
      • ここもガチでやるとかなり難しい。
  • Artifact
    • Artifact とは、製作物・遺作物 とでも翻訳される。下手に日本語にしたほうが難しいのでアーティファクトという概念で覚えたほうが良い
    • スクラムプロジェクトの過程で必ず生まれる物
    • Product Backlog
      • まず、「プロダクトバックログ」と「スプリントバックログ」は似て非なる別物である。ということから始まる。
      • プロダクトバックログには、価値の源泉となる「プロダクトバックログアイテム」が記載される。
      • プロダクトバックログアイテムの書き方の手法の一つして、「ストーリー」がある。プロダクトがユーザーや顧客に提供する価値を記述する。
      • 「ストーリー」という概念で表されることが多く、提供する価値の高い物 = ストーリーポイントの高いもの となる。
    • Sprint Backlog
      • 一方、スプリントバックログは、チームのタスクリスト。具体的な実装など、当面の作業計画を示すもの。
      • ここで列挙される「Aを実装する」などということは「タスク」という概念で表される。
      • タスクはストーリーではないので、ストーリーポイントは付かない。 あえて付けるなら0
      • タスクは、ストーリーと違い、完了の定義が明確に存在する。 = 誰が見ても「これはdone にしてOKだね」といえる状態でないといけない。
    • Increment List
      • リリース判断可能な単位のことを「インクリメント」と呼ぶ。
      • 複数のタスクの集合。「これらのタスクが全部done なら この機能に関してはリリースしてOKだね」と呼べる単位。
      • 実際には、複数のインクリメントの集合をどかんと一つのリリースとして出すことがあるが、「切り分けられるか」という意味で
    • Done / Undone
      • チームの視点を持ってして(つまりタスクとして) 完了しているかどうかの定義。
      • 「実装したけどセキュリティリスクがある」とかは「Done」ではない。
      • Done の定義をチーム全員で共有していることが大事
    • Acceptance
      • プロダクトオーナーの視点をもってして、「プロダクトバックログアイテム」が成立しているかの判断基準。
      • Done を満たすこと、Acceptance を満たすこと。両方を持ってはじめて価値として提供することが出来る。
    • Rapidly Shippable Product
      • Increment List が「タスクの集合」であるのに対し、こちらは、「プロダクトバックログアイテム」として成立しているかどうか。
      • より早く価値を提供するには、ストーリーを完成させる必要がある。インクリメントとストーリーが近いほど、高速な出荷(リリース)が可能になる。
  • Time
    • Sprint
      • 区切られた期間。別に一週間でも一ヶ月でも良い。チームの傾向に合わせて。
      • ebacky 先生的には、スクラムに慣れていないチームはスクラムの流儀に慣れるという目的で1週間をおすすめしているらしい
    • Sprint Stop
      • 開発終了のこと

ごちゃあっと書いたけど、これらをすべて満たしていないとスクラムではない。 例えば、スプリントリファインメントを行わないスクラムチームはスクラムではないし、PO, SM のいずれかが不在でもスクラムではないし、チームの人数が多すぎても少なすぎてもスクラムではない。

余談だが、 ebacky 先生は新たなルールとして「Working Agreement」を提唱している。

スクラムマスターの役割と、求められるスキル

スクラムマスターは、スクラムの専門知識をもってスクラムを回すのをサポートするが、スクラムを成立させることが最大目的ではない。

中央集権的組織の最大目的は、統治すること。つまり、関心が「人をどうするか」にある。 = Controll 一方、チーム中心主義の最大目的は、結果を手に入れること。つまり、関心が「事をどうするか」にある = Manage

Manage は、管理するとか訳されるけど、ニュアンスとしては、「(方法を問わず)いい感じにする」が近い。

スクラムマスターに求められる役割は、「価値を提供するための手段をスクラムチームに提案する」こと。 色々な選択肢の中から、より成功確率(よりユーザーに価値を届けられるかどうか)の確度が高い手段・方法をPO・チーム・その他の関係者に提案することがスクラムマスターの介在価値になる。

必要なスキルとして以下の4つを定義している

  • Facilitation(to be が明確なとき、to beまで導くことが目的)
  • Teaching(to beが明確なとき、as is から一歩踏み出させることが目的)
  • Mentoring(to be が明確ではないとき、 よりto be に近い方向に軌道を修正することが目的)
  • Coaching(to be が明確ではあるが、よりよい to be を提案することが目的)

これらは似たように聞こえるし実際似たようなことをやることもあるが、それぞれ目的が異なる。

更に、これらに加えて、Situationing(2つの事実の因果関係を見出す能力)が求められる。

体験編

講義中にワークショップとして、「1日目の講義を振り返ってスクラムマスターとして改善点を述べよ」や「←のアイデアを1位〜30位に順列をつけろ」などのワークショップがある。スクラムの知識が必要というよりは、論理的思考が求められる。

徹底した論理的思考、あるいは議論が求められる。「とりあえず締切が決まってるから締め切りまでになんんとか適当なアイデアを出そう」は決して許されない。それはスクラムの原理に思いっきり反しているから。しかし、それがわかっていたとしても、実際に実行するのは非常に難しい。

参加した感想とか

実際に参加した感想としては、思ったよりSIer 企業からの参加が多かった(ウェブ系ばっかりだと思ってた)。参加者のレベルによってクラスのレベルも変わるらしい。「これからスクラムをやるぞ」というよりは、「スクラムやってるけど理解が足りない」という人のほうが向いていると思う。ただ、これは講師の特性もあると思っていて、ebacky さんじゃないクラスのほうは、「はじめてスクラムやるぞ」という人向けなのかもしれない。

内容としては、自分が全然理解していなかったことが多かったので、その穴埋めが出来て非常に良かったと思う。一方、自分が所属するチームの開発手法はスクラムとはかなり乖離しており、自分が所属するチームをスクラムに持っていくのか持っていかないのかの議論から出発しないと行けない。「スクラムとしてやっていくぞ」という方向になれば、SMとしてスクラムの知識を回していきたい所存

人間同士の通信プロトコルを見直し、ユビキタス言語で会話しよう

【Mentor Ver.】TechTrain Advent Calendar 2019 18日目の記事として書いています。

はじめまして。 @bussorenre と申します。主に業務では Scalaを利用してサーバーサイドアプリケーションを書いたり、時々terraform スクリプトを書いたりしてAWSでインフラを整えたりしています。

TechTrain の説明を少しだけさせていただくと、なんと、「無料でエンジニアになんでも相談出来る」というすごいサービスです。 メンターの方々も、メルカリやLINE 等、第一線で活躍されているエンジニアさんばっかり。

相談出来る内容も

  • 具体的な実装の相談(ReactであれこれしたいがAという問題に詰まって悩んでる)
  • キャリアの相談(副業とかどうしてるのか、就職先をどうやって決めたか)
  • 起業相談(←代表の小澤さんが趣味でやってるだけ?笑)

などなど、幅広く、むしろ私が相談にのってほしい。(ダメ?笑)

はじめに - コミュニケーションロスと向き合う

業務としてプログラムを書き始めるようになると、それまで趣味でやってた時に比べて明らかに圧倒的に「他者とのコミュニケーション量」が増えます。「この実装いつまでに終わりそうですか?」といった簡単なほうれん草から、「この仕様を実現するために、どういう設計にしようか」といった複雑な議論まで、一人で仕事が簡潔しません。

チームに人数が増えれば増えるほど、物事を伝えるということに工数が増えていき、実作業に取られる時間が減っていくなんてことが起こりがちです。

人間同士の通信プロトコルを定義しよう

人間同士で「今質問していいですか?」「いいですよ」「ありがとうございます。Aということについてなんですが…」 と会話を始めるように、TCP コネクションを張るときは 「SYN」「SYN ACK」「ACK」パケットを送信しあって、通信を始めます。

我々が普段仕事で当たり前のように使っているインターネットがこんなに広く普及してるのは、早期に「通信規約(=プロトコル)」が標準化されたからです。

インターネットの通信方法がRFCで標準化されているように、人間と人間の間にも通信のプロトコルを明示的に示すと非常に便利です。 特に、「常識」と言われていることほど、「お互いにとって常識かどうか」を確認しあうと便利でしょう。

例えば、リモートワークなどをするときは、

  • 始業時に「リモートワークを開始します」とSlackで宣言する。
  • 昼食などで離席するときは「離席しています」とSlackで宣言する。
  • 就業するときは「リモートワークを終了します」とSlackで宣言する。

というプロトコルを共有しておくと、今誰が仕事をしているのか、今誰に話しかけても問題ないのかなどがわかって便利です。

大事なのは、プロトコルのルールの中身よりも

  • 「ルールをみんなで共有している状態」
  • 「ルールの是非をみんなが納得している状態」
  • 「柔軟にルールを変更できること」

が大事です。これらができていないと、せっかくプロトコルを規定しても、それに従わないメンバーが一定数出てきてしまい形骸化してしまうからです。 形骸化したルールを早く修正しないと、プロトコルは腐ってしまいます。

ユビキタス言語を定義して、言葉の齟齬を減らそう

例えば「アイテム」という言葉を使う時、そのアイテムって一体何なんでしょうか?

  • ゲームでプレイヤーが入手できる道具?
  • 課金して購入できる商品のこと…?
  • テーブルビューの一つの要素…?

英語的にはおそらくどれも正しいですが、チームメンバーが「アイテム」という時の意味は一体なんなんでしょうか?

ユビキタス言語はドメイン駆動設計において非常に重要な概念の一つで、チーム内で共有する固有の言語です。 今から作ろうとしているシステムの仕様や動作を伝えたりする時に使用します。

何が、何と関連しているのか。何が何を操作するのか。ソフトウェアが解決すべき問題(=ドメインモデル)を定義する時に使います。

日々の人間同士のコミュニケーションでも、今話している話が「ゲーム内のアイテム」なのか「商品」のことなのか等、共通の認識があると スムーズに議論が進むことができます。

私の本業のチームでは「○○チーム用語集」という共同編集可能なmarkdown で書かれたページが有り、そこにドメイン用語を記載しています。特に、「新しいチームメンバー」が入ったときほどユビキタス言語を見直す良い機会です。

「AdminUser と OrganizationAdminUser と CustomerAdminUser の違いってなんですか?」とか「NormalPermissionとGeneralPermission って意味かぶってませんか?」等、普段使ってて感覚が麻痺している古株のチームメンバーのいい刺激になります。

先程の、コミュニケーションプロトコルの話と同じですが、チームメンバーみんなが共通認識を持っており、理想状態に近づけるために柔軟に変更できる状態が最も望ましいです。

ドメインモデルでプログラムを表現できるようにしよう

ユビキタス言語と、アプリケーションの中での表現が一致していれば、これ以上便利なことは有りません。

これは、人間同士のコミュニケーションロスを減らすだけでなく、人間←→プログラムという変換ログを少なくすることもできます。

ドメインモデルだけでビジネスロジックを表現できるようなアーキテクチャを採用すると、技術的関心事に左右されることなく、 「このソフトウェアが実現しようとしている事はなにか」ということにのみ注力することができます。

具体的には「レイヤードアーキテクチャ」「クリーンアーキテクチャ」などと呼ばれる構造を取り入れたりすることで解決に向かうことができます。

この辺を語りだすと adventcalendar 毎日書いても終わらないので、参考になったリンクを張っておきます(下部に参考書籍も載せておきます)

buildersbox.corp-sansan.com

まとめ

業務エンジニアに必要なスキルでかつアマチュアエンジニアではそこまで必要ないスキルの特徴として、コミュニケーションという課題があるなと思い、 こんな感じで記事を書きましたが、中途半端なDDDの紹介に行ってしまった中途半端な記事になってしましました…。猛省。

まぁ、実際通信を実装するのも、アプリケーション内での通信の定義がほとんどだったりしますし、自分がコミュニケーションで使ってる「プロトコル」を見直すと 良いのかなと思いました。以上です。

以下に、参考書籍のリンクを張っておくので、ぜひよかったらどうぞ。

Google Chrome で「サードパーティーCookieが無効になっています」と表示され、はてなブログにログインできなくなる問題とその対応

対処方法

アドレスバーに chrome://flags/ と入力して、試験運用版の機能をオンにしたりオフにする必要があります。

f:id:bussorenre:20191106132813p:plain

その中の「SameSite by default cookies」を「Disable」にする必要があります。 (変更後は、再起動する必要があります)

f:id:bussorenre:20191106132815p:plain

どうして

SameSite Updates - The Chromium Projects を読むと、 Chrome 78 から、一部のユーザーで 「SameSite by default cookies」の Enable がデフォルトになったとのこと。

経緯は Chromium Blog: Developers: Get Ready for New SameSite=None; Secure Cookie Settings に書いてあります

要するにセキュリティアップデートですね。

技術的な解説は HTTP クッキーをより安全にする SameSite 属性について (Same-site Cookies) | ラボラジアン が参考になります

今後、SameSite=None を指定した場合(クロスオリジンであってもクッキーを送信させたい場合)は、Secure属性の付与も必須になります。

この辺は、サービスを提供している側が対応する必要があるので、現段階でエンドユーザーに出来ることはありません(カスタマーサポートに連絡するくらい?)

早くはてなに対応してほしい所ですが、結構根深い問題なのでM80 (全ユーザーにこの変更が適応される予定のバージョン)までに対応されるかは不明です。 (今の所、はてな公式からアナウンスはないです)

Scala で Functor っぽいものを実際に実装して挙動を追ってみる

こんにちは。 @bussorenre です。今日はScala です。 定型句となって恐縮ですが、「ここは表現がおかしい」とか「間違ってる」とか「もっとこうしたほうがいい」等ありましたら、Twitter で罵倒する前にコメントをいただけると凄く助かります。。。

はじめに

Functor を自分なりの理解で書いてみます。「実際実装してみて挙動を追ってみるか」と言うスタンスの補足解説的な記事です。 eed3si9n さんの独習Scalaz のページを読み解きながら書いたので、先にこちらをご覧になることをオススメします。

eed3si9n.com

また、そもそもFunctor とはなんぞやという解説記事は以下の翻訳ページが参考になりました。

qiita.com

このページでは、上述のページを読んでいて「ん?」ってなったところを、可能な限りScalaで書いて理解を深めていこうと思います。

map

Functor は日本語で関手(かんしゅ)と呼ばれます。Scala で書くとこんな感じです。

trait Functor[F[_]] {
  def map(fa: F[A])(f: A => B): F[B]
}

FunctorF[A]F[B] に変換するmap 関数を定義します。要するにmap が定義できれば Functorということかな…?。入力値 F[A]A => Bに変換する関数を定義します。A と Bは同型でも問題なさそう。Fだと分かりづらいので、具体的な型で実際に例を見てみましょう。以下の例は、F = Option の場合の実装です。

def map[A, B](fa: Option[A])(f:A => B): Option[B] = {
  fa match {
    case Some(a) => Some(f(a))
    case None => None
  }
}

Option の中身が Some なら中の値に対して関数fを適応し、中身が Noneならそのまま Noneを返しています。簡単ですね。

F = Either の場合はこんな感じになりますかね…?

def map[A, B, C](fa: Either[A, B])(f:B => C): Either[A, C] = {
  fa match {
    case Left(a) => Left(a)
    case Right(b) => Right(f(b))
  }
}

この例では右側だけをmap していますが、左側だけをmap する leftMap などもScalaz.\/ には定義されています。

関数へのFunctor

さて、独習Scalaz を読むと Scalaz は Function1 に対する Functor のインスタンスも定義する。 という記述があります。下記コードもそこからの抜粋。

scala> ((x: Int) => x + 1) map {_ * 7}
res30: Int => Int = <function1>

scala> res30(3)
res31: Int = 28

さてどういうことだろうか?と思って、実際にScalaz のFunction.scala を見に行きます。

override def map[A, B](fa: () => A)(f: A => B) = () => f(fa())

Scalaz のどこかに, () を 「なんでも引数指定可な関数」みたいな意味の拡張があるんだろうか…?それともどこかに別の定義があるんだろうか?とにかく、関数のFunctor は、関数の合成を表すことが出来る。 A → B → C という順番で処理を実行してくれる A => C という関数を定義出来るみたいだ…。

lift

「持ち上げ」などと言うが、コレは何をやっているんだろうか? 実際に定義を見に行くとこんな感じになっています。

/** Lift `f` into `F`. */
def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f)

A => B と引数にとって、 F[A] => F[B] という関数を返しています。先程のOption の具体例を引き続き使うと、以下のような感じ

val f = (x: Int) => x + 100

def map[A, B](fa: Option[A])(f:A => B): Option[B] = {
  fa match {
    case Some(a) => Some(f(a))
    case None => None
  }
}

def lift[A, B](f: A => B): Option[A] => Option[B] = map(_)(f)

lift(f)
res0: Option[Int] => Option[Int] = $$Lambda$910/314622131@5a97b17c

res0(Some(100))
res1: Option[Int] = Some(200)

Int => Int という関数を Option[Int] => Option[Int] という関数に変換されました。これにより、先程定義した Int => Int という変換が、Option という文脈で包まれて登場しても問題なく利用できるということになります。 これ、渡すのがOption ではなく関数gだった場合、関数g が一つの文脈 という事になるんだろうか…?

まとめ

Functor の実装を追いながら、実際にOption という具体例で動作を追ってみた。

  • def map(fa: F[A])(f: A => B): F[B]
  • override def map[A, B](fa: () => A)(f: A => B) = () => f(fa())
  • def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f)

個人的にOption がわかりやすいのでOption を使ったけど、 Either でも List でも何でも良いと思う。

TypeScript で Option っぽいものを作ってみる

こんばんは。 @bussorenre です。趣味でTypeScript をはじめました。

Option っぽいもの

Scala には、値が無いことを許容する仕組みとして Option という仕組みがあります。Some(A) は値がある状態。 None は値がない状態を示します。 中の値を取り出すには、get というメソッドを用いますが、None に対してget を実行してしまうとエラーになってしまします。

値があるか無いかわからないけど中の値を操作したい時、 map を使います。

以下に例を示します。

val a: Option[Int] = Some(100) // or None

// a が Some でも None でも実行できる
a.map(x => x + 100)

a match {
    case Some(a) => println(a)    // 200
    case None => println("None")  // None
}

これと似たような物をTypeScript で再現します。あくまで「Option っぽいもの」なので、厳密にはOption ではありません。

案1: 共用体を使う

最初に思いついた方法です。共用体を使います。共用体は代入可能型を絞ることができます。

let a: number | null = 100
a = null // 問題なし
a = "hogehogehogehoge" // エラーになる

これを用いて、nullable な変数は全部 A | null にしてしまえばいいなじゃないかと思いつきます。しかし、毎回値が有効か、null かをif 文でチェックする必要があり、面倒です。

let a: number | null = 100 // or null
if ( a === null) {
    // error
}
console.log(a)

案2: もうちょっといい感じに共用体を使う。

interface を用いて、Some型とNone 型を定義し、それらを type で 共用型にします

interface Some<A> {
  type: 'some';
  value: A;
}
interface None {
  type: 'none';
}
type Option<A> = Some<A> | None;

これは比較的良さそうです。Option<A>Some<A>None を持ちません。しかし、Option.map みたいな芸当ができません。無理やりmap 関数を作るとすればこんな感じになりそうです。

function map<A, B>(obj: Option<A>, f: (obj: A)=> B): Option<B> {
  if (obj.type === 'some') {
    return {
      type: 'some',
      value: f(obj.value),
    };
  } else {
    return {
      type: 'none',
    };
  }
}

案3: 素直にクラスを使ってみる。

Option<A> というインターフェースを用意し、そこから派生させたインターフェース Some<A>None を定義します。また、それぞれ Some<A> を継承したクラスSome<A>を定義し、実際のmapget の挙動も定義します。None も同様です。

interface Option<A> {
    get(): A | null
    map<B>(f: (a: A) => B): Option<B>
}

interface Some<A> extends Option<A> {
    value: A
    get(): A
}

interface None extends Option<null> {
    get(): null
}

class Some<A> implements Some<A> {
    constructor(value: A) {
        this.value = value
    }
    get(): A {
        return this.value
    }
    map<B>(f: (a: A) => B): Some<B> {
        return new Some(f(this.value))
    }
}

class None implements None {
    constructor() {
    }

    get(): null {
        return null
    }

    map<A, B>(f: (a: A) => B): None {
        return new None
    }
}


let a: Option<number> = new Some(100)
console.log(a.map( x => x + 100))    // Some(200)

let b: Option<number> = new None
console.log(b.map( x => x + 100))    // None


console.log(new Some(100).map(x => x + 100))    // 200
console.log(new None().map( (x: number) => x + 100))    // new None.map はできない。x の型を明示しないとコンパイルエラー

get ができて、map が出来る、Option っぽいものができました。めでたしめでたし。

最後に

決り文句的で恐縮ですが、「もっとこうしたらいい」とか「ここ間違っているよ」等があれば、ご指摘いただけると幸いです。

技術書典7で定価よりお安く頂いた「実践TypeScript - BFF と Next.js & Nuxt.js の型定義 吉井健文著」が非常にわかりやすくて勉強になっています。良書をありがとうございます。

追記

プロトタイプを使うことにより、更にScala のOption っぽいものに近づけることが出来るみたいです。勉強になりました…。 https://github.com/AlexGalays/spacelift/blob/master/src/option/index.ts

ISUCON9 に参加し惨敗しました

お久しぶりです。 @bussorenre です。

前々から存在は知っていたものの、なかなかプライベートの予定が合わず参加出来なかったISUCON にようやく初参加出来ました!! 結果は、最終スコア2910 (初期スコア 2100くらい)で、まぁものの見事に惨敗です 😇

やったことの振り返りと、次回参加とかに向けての反省をば

チーム構成

会社のメンバーと組みました。と言っても普段業務で一緒に仕事をしているメンバーではなく、全く別のプロダクト開発に従事ししているメンバー、つまりほぼほぼ「はじめまして」のメンバーでチームを組みました。 メンバー全員にruby 知見があるので、使用言語はruby に即決。それぞれの担当としては以下の通りです。

  • @Yoshimaru46
  • @Miyamizu
    • アプリケーションの改善
  • @bussorenre
    • 計測環境の整備やデプロイの自動化。ミドルウェアのチューニング等

@Miyamizu 氏が9月入社のメンバーで、@Yoshimaru46 氏がそのメンターということもあり、二人のペアプロ体制が最初に出来上がり、そこに私が乗っかった形のチーム構成です。

事前準備

チームが決まったのが8月最終日くらいだったので、事前準備にあまり時間を割けなかったのですが、就業後の時間等を利用し、物理的に集まって作戦会議等をしていました。

作戦会議で決めたことは以下のとおりです。

  • 目標
    • isucon に挑戦する(0次予選を突破する = 起床して課題と向き合う)
    • 0点を回避する(必ずスコアが出る状態で終了する)
    • 出来ることを確実にやる

初挑戦から優勝を狙いに行く強者もいますが、我々の実力的に(準備期間的にも)まずそれは難しいだろうということで、「わからないことに挑戦すること」を最も重要な目的としました。 雑に「絶対優勝すっぞ!!」とか言うと、圧があってかえって挑戦するマインドを萎縮させかねないので、「まぁ楽しもうぜ」ということを、共有しました。

一方で、出来ることは必ずやるようにしようという目標も設定しました。具体的には以下の事を必ずやるようにしようと事前に決めました。

  • アプリケーション系
    • index をしっかり貼る
    • N+1問題の解消
    • SQLの並列化
    • 画像をバイナリでDBに持っているのをやめる
    • 可読性低いコードをやめる
  • 計測系
    • スロウクエリの自動検出
    • デプロイの自動化
    • nginx のログをalp で統計する
  • ミドルウェア
    • nginx じゃないhttpサーバーが来たらnginx にする(わからないから)
    • DBロックの調査
    • Cache Control
    • タイムアウト/キャッシュのの設定を適切に
    • Worker数を適切な数に

また、alibaba Cloud という普段使い慣れないクラウドが舞台なので、確実にインスタンスを立ててssh 出来るようにする練習を行いました。

予選開始前から、解くべき課題を見つけるくらいまで

予選当日は9時過ぎくらいに会社に集合し、設営を行い、9:50分ごろから事前ブリーフィングを行いました。

まずは午前中にやることを共有し、私は以下のことを担当することになりました。

  • 起動しているサービスを確認する service --status-all
  • 全ての情報をgithub に乗せる
    • application (全実装)
    • conf(nginx や mysql など)
  • ↑で載せた情報をシンボリックリンクにする
  • デプロイ自動化
  • alp の構築
  • 全体的に基本的なnginx.conf の見直し

私がこれらの基本的な設定に取り組んでいる間に、他二人のチームメンバーには課題を読み込んでもらい、N+1等、解決できそうな問題を発見してもらうことにしました。

諸々の準備が整ったのが11:52分。その頃には「どうやら /users/transactions.json あたりがN+1でヤバそう」という情報がアプリケーションコードからも、alp の統計からもう読み解くことが出来ました。

昼食。とりあえず寿司を発注する

メインイベント このあたりで、絶対集中力が尽きるだろうと思っていたのと、前日のチームが寿司を発注しているのをTLで観測したのを受け、「これはもう寿司頼むしかねーな!」という感じで頼みました。 発注から1時間足らずで届いたのにはびっくり。銀のさら様様です。

f:id:bussorenre:20190908130418j:plain

昼食を取りながら、お互い次に取り組むことを決めますが、どうもアプリケーション側にボトルネックが多いと判断し、私も午後からアプリケーション側を見ることになりました。 予定ではnginx の最適化とか、せっかくインスタンス三台使えるんだからDBとapp 分けるかとか考えていたのですが、アプリケーションを改善しないとインスタンス分離してもそもそもスコアが出ないだろうという判断の元決定しました。

午後:地獄のN+1潰し

このあたりで普段の業務を如何にライブラリや既存の仕組みにに乗っかかってるだけか反省することが多かったです…… 😇 join を使ってN+1を解消するのに結構時間がかかりました。普段如何にORMに頼っているかわかりますね。

あーでもないこーでもないと3人で言いながら /users/tranzaction.json/new_items のアクセスを早くした頃にはもう16 時でした……。

「N+1潰したからベンチ上がるやろ!」と思い意気揚々とベンチを回すも、上述の「2910」までしかスコアが出ず 😇 改めてalpを確認すると、たしかにそこはかなり改善しているのですが、今度は /buy のレスポンスが遅く、更に殆どのリクエストが4xx系のエラーを返していることに気が付きます。

このとき今更「そういえば、今回のスコアは商品の売上合計だったよな」ということに気が付き、よりたくさん商品を売りさばくチューニングをしなければ行けなかったことに気が付きます。問題の趣旨の理解が出来たのが16:30頃

必死にbuy の改善に取り組むも、「外部APIの仕様わからん」「なんでこんな403ばっかり返しているのかわからん」等と言ってるうちに18時となりゲームセット。 (※ 最後はポータルの不具合で18:10 まで時間が延長になりましたが…)

最終スコアは2910でした。

反省

大きく2つで、

  • 焦らずもっと課題とドキュメントを読み込む事
  • 環境構築系のスクリプトをもっと事前に用意しておけばよかった事

が課題でした。特に後者は、「本当に解くべき課題がなにか」を理解するのに遅れたという痛恨のミスです。

作問担当がメルカリということで、メルカリかメルペイっぽいアプリが来るだろうなーという予想を立てていたんだから、メルカリっぽいサービスを適当に作ってみるとかをしても良かったかなと思います。 この辺は実装経験が差をつけるかなと。

後者に関しては↑の課題理解をより早く行うためにある課題だと思っていて、構築系の自動化・高速化はもうちょっと備えていきたいかなという所です。

今回、チームメンバー全員がisucon 初挑戦という非常にフレッシュなチームで、個人的にはかなり楽しくさせていただきました。 チームメンバーの Yoshimaru46氏 と Miyamizu氏には改めて感謝を。そしてまたぜひ来年一緒に挑戦して予選突破しましょう!!笑

最後になりましたが、isucon 運営の皆様、非常に楽しい大会を提供していただきありがとうございました!!