miyarappoの試行錯誤

とあるエンジニアの試行錯誤

Oura Ring API を使って毎日の自分のコンディションをSlackに通知する

はじめに

会社のSlackの自分のtimesに自分のコンディションスコアを通知しています。

Slackへの通知
Slackへの実際の通知

アプリケーションでメトリクスを取得するのと同様に、人間の活動のメトリクスも取得したい!体調が悪くなる予兆をキャッチしたい!無理が効く体調か知りたい!そんな試みです。

やっていること

やっていることはシンプルですが、結構おもしろいです。

  1. Oura Ringを付けてコンディションや睡眠を記録
  2. Google App ScriptでOura APIからスコアを取得
  3. そのスコアに応じてSlackのプロフィールアイコンを変える & そのスコアを自分のtimesに通知する

Oura Ring とは?

自分はウェアラブルバイスが大好きでして、ここ数年はOura Ringというデバイスを愛用しています。

指に着用するだけで睡眠、心拍数、体温、行動量を取得して体調をスコアリングしてくれます。

Ouraアプリの画面
Ouraアプリの画面。取得したデータは全てアプリで確認する。

Oura Ringが出す数値は非常に納得感があり、スコアが悪いとちゃんと体調が悪くなります。笑

今までjawbone => Apple Watch => Fitbit => MiBandを試してきましたが、付け心地、充電の持ち、同期の速さ、数値の納得感が群を抜いており、とても気に入っています。

またOura RingはAPIも公開してくれていて、自分の生体データは自由にGETできます。

How

実際に自分が使っているコードはこちらになります。

以下では要点を絞って説明します。

Oura API実行

Oura Ringを購入するとアカウントの登録が必須なのですが、そのアカウトの管理画面からアクセストークンを取得します。

Oura API ドキュメント

const ouraBaseUrl = 'https://api.ouraring.com/v1/'
const ouraAccessToken = 'xxxx'

const ouraApiOptions = {
    "method": "get",
    "headers": {
    "Authorization": "Bearer "+ ouraAccessToken
    }
  }
const ouraReadinessUrl = `${ouraBaseUrl}readiness?start=${previousDate}&end=${currentDate}`

const response = UrlFetchApp.fetch(ouraReadinessUrl, ouraApiOptions)

const responseData = JSON.parse(response)
const readinessScore = responseData.readiness[0].score

Oura APIはちょっとイケてなくて、今日のスコアを取得するためには前日と今日の日付をクエリパラメータに入れる必要があります。

(アクセストークンは平文にしちゃっています。漏れても自分の体調が分かるくらいなのですがGASでクレデンシャルを安全に使う方法ってあるのでしょうか。。)

スコアに応じてアイコンとメッセージを決定

スコアに応じたアイコンとメッセージを定義しておき、愚直に分岐で決定させました。

function decideStatus(readinessScore) {
  const statusObj = {
    "great": {
      "emoji": ":star-struck:",
      "text": "絶好調!今日の俺は神"
    },
    "good": {
      "emoji": ":smile:",
      "text": "良い感じ"
    },
    "normal": {
      "emoji": ":simple_smile:",
      "text": "まぁまぁ"
    },
    "delicate": {
      "emoji": ":tired_face:",
      "text": "微妙。今日は早めに帰る"
    },
    "bad": {
      "emoji": ":scream:",
      "text": "無理ぽよ"
    },
    "sleeping": {
      "emoji": ":sleeping:",
      "text": "寝てます"
    }
  }

  let statusEmoji
  let statusText

  if (readinessScore >= 90) {
    statusEmoji = statusObj.great.emoji
    statusText = statusObj.great.text
  } else if (readinessScore >= 80 && readinessScore < 90 ) {
    statusEmoji = statusObj.good.emoji
    statusText = statusObj.good.text 
  } else if (readinessScore >= 70 && readinessScore < 80 ) {
    statusEmoji = statusObj.normal.emoji
    statusText = statusObj.normal.text
  } else if (readinessScore >= 60 && readinessScore < 69 ) {
    statusEmoji = statusObj.delicate.emoji
    statusText = statusObj.delicate.text
  } else if (readinessScore == 0) {
    statusEmoji = statusObj.sleeping.emoji
    statusText = statusObj.sleeping.text
  }else {
    statusEmoji = statusObj.bad.emoji
    statusText = statusObj.bad.text
  }

  return {statusEmoji: statusEmoji, statusText: statusText}
}

Slackに通知

上記で決定したアイコンとメッセージをSlackに通知します。

#genaralとかに通知されるとうざいので個人が好き勝手にできるチャンネルに通知しましょう。自分はtimesに通知しています。

事前準備としてSlack Botを作成する必要があります。

参考にした記事

↓ Slackのプロフィールのステータスを変更

function updateSlackProfile(statusEmoji, statusText) {
  const slackProsileOpstions = {
    "method": "post",
    "headers": {
    "Authorization": "Bearer "+ slackAccessToken
    },
    "contentType": "application/json",
    "payload": JSON.stringify({
      "profile": {
        "status_emoji": statusEmoji,
        "status_text": statusText
      }
    })
  }
  UrlFetchApp.fetch(slackProfileUrl, slackProsileOpstions)
}

↓ Slackのチャンネルに通知

function postSlackChannel(readinessScore, sleepScore, activityScore, statusEmoji) {
  const slackApiOptions = {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify({
      "username": "Oura Score Notification",
      "icon_emoji": ":green_heart:",
      "text":  text
    })
  }
  UrlFetchApp.fetch(slackHooksUrl, slackApiOptions)
}

実行結果

GASのスクリプトが書けたら定期実行します。

自分はtimesに以下のように通知しています。

timesへの通知
timesへの通知

timesに入っていない人にアピールするためにプロフィールアイコンがコンディションと連動するようにもしました。

プロフィールアイコン
プロフィールアイコン

調子が良い時は体調が良さそうなアイコンになりますし、調子が悪い時は体調が悪そうなアイコンになります。

体調が悪い時は労ってもらうことが目的です。

やってみた感想

かれこれ3ヶ月ほど運用してみましたが、自分の体調を周りに公開するのは新鮮な体験でした。

同僚の反応

特に最初の頃は面白がって多くの反応をもらえました。会社の同僚は皆優しい。

最初の頃はコンディションスコアのみを通知していましたが、現在は睡眠とアクティビティのスコアも通知しています。

Slackの通知
はじめたての頃。何人かは興味を持って反応してくれた

目論見通り、スコアが低い時は「大丈夫かな?」みたいな反応をもらえました。

↓の時は転職したばかりでテーブル構造の理解に苦しんでいた時でした。

Slack通知
スコアが低いと心配の声をもらえた

APIでデータを取得するためには、起床後にOura Ringに記録されたデータをアプリに同期することが必要です。

GASのスクリプトは9時から10時の間で定期実行をしているのですが、実行時にその日のデータを同期していないとスコアが0で通知されてしまい死亡説が流れました。

Slackの通知
バグでスコアが0になってしまった時は死亡説が流れた

心理的な障壁

「体調どう?」と幾度となく上司や同僚に問われ、また自分も質問してきました。

ですが、中々正直に言うことも言われることも少ないのではないでしょうか?

この課題を解決すべく本記事のような試みをしました。

自分は情報は基本的にオープンにする性質です。日々の体調を公開することくらいどうってことないと思っていましたし、実際に公開しています。

そんな自分でも「スコアが良い日が続くと頑張ってないと思われるのではないか?」「スコアが低いと不必要に心配されるのでは?」と思うときがありました。

生体データはある意味究極のプライバシーデータだと思います。全員が公開してお互いのデータを確認できたら便利だと思っていますが、個人情報保護の観点をクリアにするのが難しいだろうなと改めて感じました。

これってパルスサーベイの究極系なのでは?

コロナ禍で従業員の健康を質問するパルスサーベイを行う企業が増えたと聞きます。

自分も前職では1週間に1度、健康度を5段階で回答するサーベイが人事から届いていました。

でも自分は正直に回答することが出来ませんでした。しんどい理由があったとしても事情を第三者に説明するのってしんどくないですか?しんどい時なら尚更そうだと思います。

生体データなら手間もかからないし、偽ることができないです。

実現のハードルはありますが、企業が全ての従業員に実施したいパルスサーベイの究極系は生体データを取得することだと思います。

業務システムのデータとの関連性

勤務時間や仕事中の変化(異動とか)と照らし合わせて見ると面白いデータがとれると思います。

自分は転職して最初の1ヶ月は明らかにスコアが悪くなりました。笑

個人情報の観点から実施が難しいと予想しますが、タレントマネジメントの文脈で使えたら相当面白いんじゃないかと予想しています。

誰と誰が一緒に働くと睡眠のスコアが上がるとか可視化できたら楽しそうです。

自分は従業員のエンゲージメントを測定するサーベイを提供するSaaSの開発をしているのですが、生体データを活用できたら楽しいだろうなと想像しています。

おわりに

簡単なスクリプトでの実験でしたがやってみると色々な知見を得られました。

FitbitにもAPIはあるので同じようなことはできると思います。

テクノロジーウェルビーイングの掛け合わせは面白い分野だな〜。

「チームトポロジー」を読んで人間にできるソフトウェア開発を考えた

はじめに

社内の輪読会で読んだチームトポロジーという本が、非常に良かったので自分自身の整理ためにまとめてみました。

概要を把握するなら訳者自身による説明が一番まとまっていて分かりやすかったです。

現代におけるソフトウェア開発の難しさ

そもそもソフトウェアを構成するコードは大きくて複雑である。また、ソフトウェアは日々変更されるものである。大きくて複雑なものを変更するので難易度はさらに上がる。

現在のIT組織は、ソフトウェアシステムをすばやく安全に提供、運用すると同時に、成長を続け、ビジネスや規制環境の変化や圧力に適応しなければならない。企業が最適化の目的を安定性の向上か速度の向上かで選べた時代は終わったのだ。

多くの場合、ソフトウェアをデリバリーする組織の設計が間違っているというのが本書の主張である。

組織図を額面通りに受け取ってしまうと、人間をソフトウェアのように設計し、コミュニケーションをきっちりと決められた枠の中に収めようとする。だが、人間は組織図の線で繋がっている人たちだけとコミュニケーションするわけではない。仕事を終わらせるのに必要な相手であれば、誰とでも連絡を取る。ゴール達成のために必要なら、ルールを曲げる。

全くその通りだと思う。どんな仕事でも組織図に沿ったコミュニケーションだけで仕事を完結させることは難しい。現代では必要なら社外の人間ともコラボレーションする。

ならば全てのメンバーを同じチームに所属させるのはどうだろうか?

直感的に無理だと分かるが、本書では「認知負荷」というキーワードを使って説明している。

認知負荷について話すとき、ある瞬間に脳にとどめておける情報の量には誰でも限りがあることは容易に理解できる。チームの場合も同じで、チーム全員の認知容量の合計を超えることはできない。

認知負荷を考慮しないと、チームの責任範囲と担当領域は広がりすぎることになる。自分の仕事に熟達するだけの余裕がなくなり、担当業務のコンテキストスイッチに悩まされる。

当たり前だけど人間には限界があるということだ。

人月の神話で提唱された「遅れているソフトウェア・プロジェクトに人員を投入しても、そのプロジェクトをさらに遅らせるだけである。」というブルックスの法則を思い出す。

人を増やしてもコミュニケーションパスが増えるだけ認知負荷が増大してしまい、人が増えた分の成果よりもコストが上回ってしまう。ブルックスの法則も認知負荷で説明ができると思った。

人には限界があり、全ての人と効率的にコミュニケーションをとることはできない。認知負荷を減らすためにチームを分けてコミュニケーションのパスを整理する必要がある。

では、ソフトウェア開発においてはどうすれば良いのだろうか?

本書では効果的なチームの分け方とチーム間のコミュニケーションを示している。

チームトポロジーのゴールは、コラボレーションが必要な場所やタイミング、実行に集中してコミュニケーションのオーバーヘッドを減らすべき場所やタイミングを、組織が適応しながら動的に見つけられるようなアプローチとメンタルツールを提供することだ。

コンウェイの法則

コンウェイの法則は有名な「システムを設計する組織は、その構造をそっくりまねた構造の設計を生み出してしまう」という法則だ。

本書ではコンウェイの法則を認めつつ、この法則を土台として議論が展開されていく。

コンウェイの法則の示す重要な点は、すべてのコミュニケーションとコラボレーションが良いとは限らないということだ。したがって「チームインターフェイス」を定義し、どんな仕事には強力なコラボレーションが必要で、どんな仕事には必要ないのかという期待値を設定することが重要になる。多くの組織はいつでもコミュニケーションが多い方がよいとかんがえるが、実際にはそうではない。

自分はコミュニケーションは多い方が良いと思っていたが、コミュニケーションはコストにもなり得る。人月の神話を読んだ時に理解したつもりだったが、認知負荷というキーワードで説明されるとさらに腑に落ちた。

本書では逆コンウェイの法則を使って、戦略的に組織を設計しようとしている。逆コンウェイの法則に則って組織をつくろうという議論は多くあるが、チームの分け方とインタラクションを定義しているのが本書の価値だと思う。

ソフトウェアアーキテクチャが組織に依存する以上、アーキテクトは技術的スキルと社会的スキルの両方が必要だとも書かれていた。ソフトウェアエンジニアを名乗る限り技術スキルを高めるだけではいかないらしい。エンジニアリング組織論への招待にも「エンジニアの仕事は不果実性を下げること」と書いてあった。不確実性の中には組織やコミュニケーションが入るのだろう。むしろ一丁目一番地かもしれない。

コンウェイの法則の背後にある同形性を示す証拠が増えていることを考えると、技術リーダーの意見を聞かずにチームの形成、責任、境界について決定を下すというのは、ソフトウェアシステムを構築する組織にとって非常に非効率的で、そしておそらく無責任なのだ。

4つの基本的なチーム

本書ではチームの重要性が説かれている。

チームが偉業を成し遂げるのは、単にそれぞれの資質が優れているからだけでなく、メンバーがまとまって1つの生命体と化すからだ。

モダンなソフトウェアを開発して進化させるために必要な情報の質と量を適切に扱い続けるのは、個人では継続不可能なのだ。

どんな天才であっても1人では世の中に大きな影響を与えるソフトウェアはつくれないだろう。個人の経験を振り返っても天才だと思っていたエンジニアが個人プレーに走ったプロジェクトは上手くいかなくて「こんなスキルのある人でも好き勝手やったら上手くいかないのか」と小さな絶望をしたのを思い出した。

チームに誰がいるかということより、チームの力学が重要なことが分かった。

自分はSaaSの開発をするエンジニアをしているが、現在のSaaSは一人の天才では到底無理な認知負荷の上に成りっていると思う。 なのでチームが大事であり、チームが機能するためには心理的安全性が大事なのだろう。 今の会社はこの前提を全員が共有しているので働きやすいのかもしれない。

本書ではチームを以下と定義している。

  • 5人から9人のメンバーからなる安定したグループ
  • 共有されたゴールのために働く単位
  • ソフトウェアの提供が可能な組織内での最小の単位

また、機能するチームの条件は以下だと説明している。

  • 長続きする。
    • 高信頼な組織では9ヶ月から12ヶ月にメンバーが在籍。
    • 通常の組織では、1年半から2年メンバーが在籍。
  • 自らが開発するソフトウェアにオーナーシップを持つ。
    • 各々のチームが各々のソフトウェアを設計からデプロイまで行える。
  • チームの認知負荷に合うように責任範囲を制限する。

チームを効率的に機能させるために本書ではチームを4つのタイプに分類している。

  • ストリームアラインドチーム
    • 顧客に価値ある機能を提供する。
    • フィーチャーチームやプロダクトチームとほぼ同義(だと思う)だが、サービス・フィードバック・失敗・学習を大事な概念として扱い、流れに注目して変化を捉えるストリーム重視なものの見方を重要視している。
  • イネイブリングチーム
    • ストリームアラインドチームが持っていない特定の能力を獲得するを助ける。
    • 短期的な支援を通してストリームアラインドチームにナレッジを蓄積させる。
    • Railsだけで開発していた組織に、ペアプロしながら横串でSPAの導入するチームとか。
  • コンプリケイテッド・サブシステムチーム
    • 習得や育成の難しい知識が必要な領域を支援する。本当に必要な時だけ編成される。
    • イネイブリングチームは支援先に知識を蓄積させるが、コンプリケイテッド・サブシステムチームは蓄積を目的としない。
    • 特定ドメインスペシャリスト集団。機械学習でリコメンド機能したい時とか。
  • プラットフォームチーム
    • ストリームアラインドチームの認知負荷を下げるための内部サービスを提供する。
    • 基盤チームやSREとか。

今の会社は上記のような分け方ではなく、ミッション型でチームを構成している。 この本を読んでいると紹介された4つのタイプが正しい気になるが、フェーズによっては不適切かもしれない。 この辺はチームメンバーと議論して気づきが多かった。

高速なフローの実現

上記の4つのチームタイプは、ストリームアラインドチームによる高速なフローを実現することに注力している。

なぜか?運用から学べることが多いというのは理由の一つだろう。

本番システムからの情報のフィードバックに価値を置く組織は、ソフトウェアをすばやく改善できるだけでなく、顧客やユーザーへの対応力も高まるのだ。

素早く対応するために、同一のチームがシステムの設計・開発・テスト・デプロイ・運用を行うのに必要なスキルを全て兼ね揃えてなければならない。

そのため、ストリームアライドチームが備える能力は以下のように多岐に渡る。

ストリームアライドチームは同一チーム内で全てのサイクルを回し学習し続ける必要があるため、認知負荷が大きくなる傾向がある。認知負荷が大きくなりすぎるとチームは疲弊しフローが遅くなってしまう。そのような事態を防ぐため他の3つのチームがストリームアラインドチームに協力するのだ。

本当に役に立つソフトウェアを開発するためには、開発者あるいは開発者と密接なコミュニケーションをとれるメンバーが、ユーザーの課題を感じ、短期間で学習のサイクルを回す必要があると思っていた。ストリームアライドチームはまさにそのようなチームであり、それを実現するために組織全体を設計するというのは納得感のあるものだった。

一方でまだPMFができていない場合は、どんな風にチームを分けて良いか分からない場合も多い。チームトポロジーが効果を発揮するのはある程度プロダクトが大きくなって認知負荷が大きくなり分けるべきストリームがはっきりしてきた時になると思う。

ストリームは見方によってはDDDにおけるドメイン駆動設計(本書ではストリームの切り方は他にも提唱されている)と言えるかもしれないと思った。だとすると、自分たちは組織全体でDDDをやっているのかもしれない。

適切にチームを分割して、機能するストリームアラインドチームと独立してデプロイできるサブシステムを用意したい。

目指すべきは「チーム間のコミュニケーションをさほど要さずに、設計からデプロイまでの作業を完遂できる能力」を促進アーキテクチャを生み出すこと。

このような話はマイクロサービスを始める理由と同じだと思うが、昨今マイクロサービスを入れたものの運用が難しいという話は、ストリームの切り方を間違えたり、手助けをするチームが機能していないことが原因かもしれないと思った。

3つのチームインタラクション

チームを分けたあとは、それぞれのチームが効果的にコミュニケーションをすることが重要だ。

多くの組織では、チームのインタラクションと責任の不明瞭な定義が軋轢や非効率の原因となっている。自律的だ自己組織化だと言われているチームでも、メンバーは自分たちの仕事を完成させるために他のチームとやりとりせざると得ない。

本書ではチームがインタラクションする3つの基本的なモードを定義している。

  • コラボレーション
    • 他のチームと密接に協力して作業すること。
    • 1つのチームが同時にコラボレーションモードを使用するのは最大1チームまで。1つのチームが同時に2つ以上のチームとコラボレーションモードを使用してはいけない。
    • プラットフォームチームと共同作業するストリームアラインドチーム等。
  • X-as-a-Service
    • 最小限のコラボレーションで何かを利用または提供すること。
    • 1つのチームが同時並行で多数のチームとX-as-a-Serviceインタラクションを使う想定が必要になる。
    • コプラットフォームチームが提供するサービスをその他の3つのチームが使う等。
  • ファシリテーション
    • 障害を取り除くために他のチームを支援したり、支援を受けたりすること。
    • 1つ以上のチームが別のチームから積極的に作業の一部をファシリテーションしてもらう。
    • 他の3つのチームを支援するイネイブリングチーム等。

3つのモードは直感的に理解できるのではないだろうか。

本書で紹介されている研究によると「人が他人と最もうまくやれるのは、その人の行動が予測できるとき」らしい。協力し合うチームが現在のコミュニケーションの形態を相互に認識していると、効果的に動けるのだと思う。

チームトポロジーの進化

本書では組織設計は動的なものと位置づけられている。ソフトウェアが大きくなったりデリバリーのリズムが遅くなったりしたら組織内のチームトポロジーを再設計しようと言っている。

運用していく中でインサイトを得て変更するという点では、ソフトウェアもチームも変わらないらしい。

本書の重要な概念である、認知負荷・4つのチームタイプ・3のインタラクションモードが共通認識にあると会話がしやすいと思った。

ストリームアラインドチームの認知負荷が増えているので分割しようとか、難しい要件なので詳しいメンバーでコンプリケイテッド・サブシステムチームを編成してストリームアラインドチームをファシリテーションしてもらおうとか議論できると良さそう。

(カタカナが多い & 長いは慣れるしかない。)

この本の内容を組織全体で前提として持っているとめちゃめちゃ動きやすそうだとは思った。

感想

ソフトウェア開発においてもコミュニケーションの最適化が重要だという話だが、その理由と問題の構造がとても腑に落ちるものだった。4つのチームタイプと3つのチームタイプという一つの解を示しているのも参考になる。

個人的な経験と照らし合わせても納得感のあるもので、人間的なアプローチでソフトウェアの課題を議論するという点で「人月の神話」以来の衝撃があった。

本書はチームメンバーとの輪読会で読んだのがきっかけだったが、現在の自分のチームの分け方がミッション別の編成だったのでその議論も盛り上がった。

ところで、チームトポロジーを一言でいうのが難しい。検索しても明確な定義はなく難し説明が多かった。上司が「チームの責任範囲」だと表現していて今のところこれが一番しっくりきている。

ソフトウェアはますます「ユーザーのためのプロダクト」というより「ユーザーとの継続的な会話になっている」なっている。この継続的な会話を効果的なものとし、成功させるために、組織にはソフトウェアの「ケアの継続性」が必要だ。ソフトウェアを設計し構築するチームは、最初から効果的に構築できるように、その運用面に関与しなければならない。この「設計と運用」ケアの継続性を提供するチームは、ソフトウェアサービスの商業的な成長について何かしら責任を持つ必要がある。さもないと、財務的に現実から切り離された状態で意思決定がされるだろう。

自分は上記の言葉が一番印象に残った。

ソフトウェアに責任をもつために技術と組織両方やっていきたい。

(プログラムスキルがあればOKだと思ってエンジニアになったがソフトスキルは避けては通れかった。笑)