@memo

ゆるくインプット、ゆるくアウトプット

GmailをSlackのチャンネルに通知する超シンプルなGAS

こんにちは。asatoです。

spaces.bzは友人と開発してまして、各種サービスは共有のGoogleアカウントを使っています。 どちらかしか通知を受けられない、操作できないのは面倒なので。

しかし、自分たちもGoogleアカウントを持っており、Gmailを見るためにアカウントを切り替えるのは面倒ですし、 コミュニケーションはSlackで行っているので、GmailをSlackのチャンネルに通知してSlackで会話できるといいなーと思っていました。

色々と調べると

  1. Slackの転送先メールアドレスをGmailの転送先に指定する
  2. Google Workspace Marketplaceの「Slack for Gmail」を使う
  3. Slackの「Email」アプリを使う
  4. GAS

など、いろいろな方法が紹介されていました。 しかしながら、どれも要件を満たすのが難しそう。

  1. slackbotへのDMになってしまいチャンネルポストができなそうです。
  2. ブラウザでGmailを開いて、メールごとチャンネルごとに手作業が必要です。
  3. 「Email」アプリが有料ワークスペースでないと使えなそうです。
  4. 検索で引っかかるコードを見る限り、割と複雑そうです...

んー、どうしたものか。

ん?4、本当に複雑なのか?

あれ?お前、実はもっとシンプルになるんじゃないのか?

ということで、GmailをSlackに通知するGASはすごくシンプルだよ!の例を示していきたいと思います。

サンプルコード

いきなりサンプルコードです。(メールをSlackに転送したいアカウントで作成します)

function main() {
  const threads = GmailApp.search('in:Inbox is:Unread', 0, 100)
  
  threads.forEach((thread) => {
    thread.getMessages().forEach((message) => {
      if (!message.isUnread()) { return }
      const text = create_message(message)
      send_to_slack(text)
      message.markRead()
    })
  })
}

function create_message(message) {
  return  `[Date] ${message.getDate()}`
          + `\n[From] ${message.getFrom()}` 
          + `\n[Subject] ${message.getSubject()}`
          + `\n[Body]\n${message.getPlainBody()}`
}

function send_to_slack(text) {
  const webhook_url = <SlackのIncoming webhook URL>
  const headers = { "Content-type": "application/json" }
  const data = { "text": text }
  const options = {
    "method": "post",
    "headers": headers,
    "payload": JSON.stringify(data),
    "muteHttpExceptions": true
  }
  UrlFetchApp.fetch(webhook_url, options)
}

send_to_slack関数はSlackにテキストメッセージを送るところなのでノーカウント(この記事では説明しません)とすると、めちゃくちゃシンプルじゃありません?

javascriptのコードはおいておいて、GmailApp系の部分をちょっとだけ解説します。

GmailApp.search( )

const threads = GmailApp.search('in:Inbox is:Unread', 0, 100)

GmailApp.search(query, start, max)queryの条件に合うスレッドの配列(GmailThread[])を取得する関数です。 今回は'in:Inbox is:Unread'を指定しているので、「受信ボックス」の中で「未読」のものを拾ってきます。 また、startに0、maxに100を指定しているので、最新のスレッドから100件までの中からqueryを検索してもらっています。

thread.getMessages( )

threads.forEach((thread) => {
  thread.getMessages().forEach((message) => {
    ...
  })
})

GmailThread.getMessages()でそのスレッド内のメッセージ(メール)の配列(GmailMessage[])を取得してます。 それをforEachで回すことで一つ一つのメッセージを扱えます。こいつがメール本体。主役の登場です。

message.isUnread( )

function main()
if (!message.isUnread()) { return }

GmailMessage.inUnread()はそのメッセージが未読ならtrue、既読ならfalseを返却します。 このコードではそれの否定(!)でif文を書いているので、既読の場合return、つまり後続の処理をせずにforEachを継続させています。

message.getXXX( )

function main()
const text = create_message(message)
 
...

function create_message(message) {
  return  `[Date] ${message.getDate()}`
          + `\n[From] ${message.getFrom()}` 
          + `\n[Subject] ${message.getSubject()}`
          + `\n[Body]\n${message.getPlainBody()}`
}

Slackに投稿するテキストメッセージを生成しているのがこの辺です。 GmailMessage.getXXX()を使ってメッセージを作ってます。getXXX()はいろいろな種類があるので、投稿したいテキストメッセージに合わせてカスタマイズしてください! 今回は、「受信日時(getDate())」「送信者(getFrom)」「タイトル(getSubject())」「プレーンテキストの本文(getPlainBody())」を利用してみました。

message.markRead( )

最後にmessage.markRead()でメッセージを既読にしています。 これで、次回以降は!message.isUnread()に弾かれて処理されなくなります。

まとめ

どうですか?意外にシンプルですよね。 あとはGASのトリガーを好きな時間単位で指定すれば出来上がりです。 僕たちはGASのエラー通知をすぐに受信したいので1分単位でトリガーをセットしています。

GASでこういうことできるよ!という記事はいっぱいあるのですが、Googleで検索してもなかなか公式ドキュメントにたどり着けないんですよね。 実はめちゃくちゃわかりやすいので公式ドキュメントを眺めてやりたいことを実現していきましょう!!👏

例えばGmailなら

誰かのためになれば嬉しいです。

こんな感じでGoogleからのエラー通知をバンバン受け取っているspaces.bzの応援もよろしくお願いします!

SpreadsheetをDBとして使うぞ大作戦 on Nuxt

こんにちは。asatoです☀

いま面白いTwitterスペースと出会えるプロダクトspaces.bzを開発しています。

このプロダクトはDBが必要なのですが、できれば簡単でお金のかからないDBがほしい...そうだ!スプレッドシートをデータベースとして使いたい! ということで、Nuxt + Spreadsheet構成でプロダクトを開発してみたので、体験記をまとめたいと思います!

この記事でやること

スプレッドシートからデータを取得して表示するNuxtアプリを作ります。 サンプルのスプレッドシートは以下のようなイメージ。

idnameageemail
1Test A20test_a@sample.com
2Test B30test_b@sample.com
3Test C40test_c@sample.com

スプレッドシートJSONで返却するGASを作成

まずはスプレッドシートの情報を取得するAPIを作っていきます。 GASでプログラムを組んでWebアプリとして公開する、をします。GASの方から!

function doGet() {
  const users = getUsers()

  return ContentService
    .createTextOutput(JSON.stringify(users))
    .setMimeType(ContentService.MimeType.JSON)
}

function getUsers() {
  const ss = SpreadsheetApp.openById('スプレッドシートのID')
  const sheet = ss.getSheetByName('シート名')
  const rows = sheet.getDataRange().getValues()
  const keys = rows.splice(0,1)[0]

  const users = rows.map((row) => {
    const object = {}
    row.map((value, index) => {
      object[keys[index]] = value
    })
    return object
  })

  return users
}

ちょこっとコードをみてみます。

doGet関数

このコードの最初の肝はdoGet関数を用いていることです。 この後Webアプリで公開すると話しましたが、doGet関数はWebアプリで公開したURLにGETリクエストが来たときに動く関数です。

今回のコードではdoGet関数が動き出したらgetUsers関数を呼び出してスプレッドシートの中身をJSONに整形し、

return ContentService
  .createTextOutput(JSON.stringify(users))
  .setMimeType(ContentService.MimeType.JSON)

で、JSONを返却しています。

getUser関数

次に、getUser関数をみてみます。

const ss = SpreadsheetApp.openById('スプレッドシートのID')

最初に対象のスプレッドシートを読み込んでいます。スプレッドシートのIDはスプレッドシートのURLからわかり、https://docs.google.com/spreadsheets/d/*****/edit*****の部分です。

const sheet = ss.getSheetByName('シート名')

次に、そのスプレッドシートのシート名のシートを持ってきます。

const rows = sheet.getDataRange().getValues()

そして、そのシートのデータが入力されているセルのデータを行単位の配列で取得します。 サンプルの例だと、rowsの値は以下のようになっています。

rows = [
  ['id', 'name', 'age', 'email'],
  ['1', 'Test A', '20', 'test_a@sample.com'],
  ['2', 'Test B', '30', 'test_b@sample.com'],
  ['3', 'Test C', '40', 'test_c@sample.com']
]
const keys = rows.splice(0,1)[0]

お次はrowsの1つ目の配列を取り出してkeysに代入しています。これによって、keys、rowsは以下のようになります。

keys = ['id', 'name', 'age', 'email']

rows = [
  ['1', 'Test A', '20', 'test_a@sample.com'],
  ['2', 'Test B', '30', 'test_b@sample.com'],
  ['3', 'Test C', '40', 'test_c@sample.com']
]

これでkeysjsonkeyになり、rowsに残った項目がvalueになる未来が見えてきました。

const users = rows.map((row) => {
  const object = {}
  row.map((value, index) => {
    object[keys[index]] = value
  })
  return object
})

そんな未来を実現するコードがこれです。 rows.map((row) => { 処理 })rowsから配列を一つずつ持ってきて変数rowに格納し処理を実行しています。 処理の部分でもrow.map((value, index) => { 処理2 })としてrowの要素を一つずつ取り出し、 処理2でobject[keys[index]] = valueとすることで各配列をオブジェクトの形に整形しています。

最終的にusersの値はこんな感じです。

users = [
  {
    id: 1,
    name: 'Test A',
    age: 20,
    email: 'test_a@sample.com'
  },
  {
    id: 2,
    name: 'Test B',
    age: 30,
    email: 'test_b@sample.com'
  },
  {
    id: 3,
    name: 'Test C',
    age: 40,
    email: 'test_c@sample.com'
  }
]

これを最後にreturnする関数になってます。

これでWebアプリを公開したときにスプレッドシートのシートをjsonで返却するGASの準備ができたので、次はWebアプリを公開していきます。

Webアプリを公開する

WebサイトはGASのエディターの上部にある「デプロイ」ボタンから行います。

f:id:at946:20220201095829p:plain

「デプロイ」>「新しいデプロイ」を選択して...

f:id:at946:20220201095907p:plain

「種類の選択」>「ウェブアプリ」を選択して...

f:id:at946:20220201095929p:plain

「新しい説明文」にわかりやすい名前(productionとかtestとか)、「次のユーザーとして実行」に「自分」、「アクセスできるユーザー」に「全員」を設定し、「デプロイ」を選択して...

f:id:at946:20220201095959p:plain

(必要な場合は)「アクセスを承認」を選択して...

f:id:at946:20220201100017p:plain

Google認証」して...

f:id:at946:20220201100113p:plain

「Advanced」を選択して、「<プロジェクト名>(unsafe)」を選択して...

f:id:at946:20220201100130p:plain

「Allow」して...

f:id:at946:20220201100153p:plain

完了!!🎉 「ウェブアプリ」の方に書いてあるURLがAPI(サイトとしても)公開しているURLになります。ちょっとcurlしてみましょう。

$ curl -L <URL>

[
  {"id":1,"name":"Test A","age":20,"email":"test_a@sample.com"},
  {"id":2,"name":"Test B","age":30,"email":"test_b@sample.com"},
  {"id":3,"name":"Test C","age":40,"email":"test_c@sample.com"}
]

返却されてるー!!

画面じゃないとわかりにくいなと思い、画像多めでお届けしました。 ってことなので、これをNuxtでの表示を作っていきます!

NuxtでAPI叩いて表示

最後に、今公開したAPIをNuxtアプリから叩いてデータを表示していきます。

やることは

  • 外部のAPIを叩くためにaxiosを導入する
  • CORS対応のためにserverMiddleware経由でGAS APIを叩く
  • これらを使ってデータを取得して表示する

です!

まず、@nuxtjs/axiosを導入してきます。

$ yarn add @nuxtjs/axios

モジュールを登録します。

# nuxt.config.js
  export default {
    ...
    modules: [
+     '@nuxtjs/axios'
    ],
    ...
  }

次に、serverMiddlewareを作成して経由して叩くことを考えます。

$ yarn add express
$ mkdir api
$ touch api/index.js
# api/index.js
const express = require('express')
const axios = require('axios')

const app = express()

app.get('/', async (req, res) => {
  const users = await axios.get(<ウェブアプリ(GAS API)のURL>)
  res.send(users.data)
})

module.exports = {
  path: '/api/',
  handler: app,
}

nuxt.config.jsにも登録しておきましょう。

# nuxt.config.js
  export default {
    ...
+   serverMiddleware: [
+     '@/api/'
+   ],
    ...
  }

これで内部の/api/を経由してGAS APIからデータを取得することができるようになりました。

ウェブアプリのAPIを叩く準備が整ったので、ページを作成していきます。

# pages/index.vue
<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>id</th>
          <th>name</th>
          <th>age</th>
          <th>email</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for='user in users' :key='user.id'>
          <td>{{ user.id }}</td>
          <td>{{ user.name }}</td>
          <td>{{ user.age }}</td>
          <td>{{ user.email }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      users: []
    }
  },
  async fetch() {
    this.users = await this.$axios.$get('/api')
  },
}
</script>

ちょっと長いですが、templatetablev-forで表形式に表示しているだけで、scriptaxiosで取得したユーザー情報をthis.usersに格納しているだけです!シンプル!

これでページにアクセスしてみると...

f:id:at946:20220201151108p:plain

データ取得成功!

スプレッドシートと同じ情報を表示できていますね!!🎉🎉🎉

まとめ

今回はスプレッドシートをDB代わりに使えないかなー、ということで、

をやってみました! それぞれいろいろな参考記事を書いてくださっている方々がいらっしゃったんですが、あわせ技のところで困ったりしたので一連通してやるとこうなるぞという記事を書いてみました。 誰かの参考になれば嬉しいです。

spaces.bzも応援よろしくおねがいします!

NASAゲームでチームで活動する意義を実感した

はじめに

こんにちは、asatoです。

チームでNASAゲームをやってみました。嬉しいことにメンバーからチームで活動する意義を実感したとの声を多数もらえました😄 この記事でゲームの内容や進め方を共有するので、興味持ってもらえたら嬉しいです。

NASAゲームとは

NASAゲームは、グループで課題に取り組み合意形成しながら結論を導き出す、コンセンサスゲームのひとつです。「宇宙で遭難した状況で母船にたどり着くために、利用可能な15個のアイテムに優先順位をつけるゲーム」です。

NASAゲームで期待できる効果

NASAゲームをチームでやってみることでこんな効果が期待できます。

  • チームで議論するハードルが下がる
  • チームで活動する意義を体感できる

仕事だとなかなか議論をしにくいなと感じているメンバーもいると思います。でも率直に議論していきたいですよね。NASAゲームはグループで合意形成するゲームなので議論が必須です。仕事に関係のないテーマなので、いつもより議論しやすいはずです。そこで率直に議論できた体験を得られれば、仕事でも率直に議論するきっかけにできるはずです。

また、NASAゲームは最終的に「個人で考えた答えよりチームで出した答えの方が良い結果を生む」ことを実感できます。実際にチームでやったときも、この感想がすごく多かったので間違いないです!

ゲームの内容

イントロダクション

ゲームのイントロダクションはこんな感じです。

あなたは宇宙飛行士です。 今、あなたの乗った宇宙船は機械トラブルで、月に不時着してしまいました。 本来、陽のあたっている月面上に母船が迎えにくることになっていますが、現時点から300km離れています。 生き残るためにはなんとかして母船にたどり着かなくてはなりません。 不時着した衝撃で宇宙船は完全に壊れ、中の設備もほとんど使い物になりません。 15個だけ使用可能なアイテムがありました。 母船にたどり着くために、この15個のアイテムに優先順位をつけてください。

15のアイテム

アイテムは以下の15個です。

  • マッチ
  • 宇宙食
  • ロープ(15m)
  • 月から見た星座表
  • 救命いかだ
  • 方位磁石
  • パラシュート
  • ヒーター
  • ピストル
  • 水(20L)
  • 粉ミルク
  • 注射器の入った救急箱
  • 酸素ボンベ(2本)
  • ソーラー式FM送受信機
  • 発火信号(照明弾)

模範解答の優先順位とその理由は以下です。

  1. 酸素ボンベ(2本):生命の維持に必須。
  2. 水(20L):生命の維持に必須。(酸素よりは我慢できる)
  3. 月から見た星座表:方向を知るために利用できる。
  4. 宇宙食:生命の維持に必須。(水分よりは我慢できる)
  5. ソーラー式FM送受信機:母船に近づいたときに、母船と通信ができる。
  6. ロープ:段差の上り下りや命綱に使える。
  7. 注射器の入った救急箱:救急箱の中のビタミン剤や薬が使える。
  8. パラシュート:太陽光を遮断するのに使える。
  9. 救命いかだ:炭酸ガスボンベが推進力として使えるかもしれない。
  10. 発火信号(照明弾):母船が見えたときに遭難信号を送れる。(FM送受信機の通信の方が確実)
  11. ピストル:発射の反動を推進力に使えるかもしれない。(救命いかだよりは力が小さい)
  12. 粉ミルク:食料なので生命の維持に使えるが、水が必要になるので宇宙食の方がよい。
  13. ヒーター:日があたっているのでいらない。
  14. 方位磁石:月面は磁気がないので使えない。
  15. マッチ:月面は酸素がないので使えない。

スコア

スコアは、それぞれのアイテムの個人またはチームの回答の優先順位と、模範解答の優先順位の差の合計です。

個人またはチームのあるアイテム item_i​の優先順位をP(item_i)、模範解答の優先順位を P_a(item_i)とすると、

score = \sum_{i=1}^{15}|P(item_i) - P_a(item_i)|

です。数式使ってみたかっただけです。

ルール

  • 個人で考える時間と、チームで議論する時間があります。個人でもチームでも、時間内に全てのアイテムの優先順位をつけてください。
  • チームで議論するとき、多数決は禁止です。議論を通して全員が納得いく回答を導き出してください。
  • ググったら答え出てきちゃうと思うので、やめてください。
  • (チーム対抗戦ができる場合)チームのスコアが最も小さかったチームの勝ちです。

くらいかなと思います!

さて、ここまでNASAゲームの内容については以上です!Let's enjoy NASA game!!

NASAゲームの進め方

ここからは、こんな感じでファシリテートしたよ、を共有したいと思います。 今回は9人いたので、4人 vs. 5人のチーム戦にしてみました。

準備

開催前に以下の準備はしました。オンライン開催です。

  1. Slides:
    イントロダクションやルール説明、結果発表後のポイント(三人寄れば文殊の知恵)など
  2. Spreadsheet:
    個人、チームが優先順位を入力するシート。それぞれのシートからスコアを集計するシートも用意しておくと進行がスムーズです。
  3. Zoom:
    リモートでやったので、コミュニケーションはZoomで。チームごとに議論するためにブレイクアウトルームを作れるように設定しておきました。

タイムスケジュール

ワークショップはこんな感じで進めました。

  1. イントロダクション、ルール説明(5min)
  2. 個人ワーク(5min)
  3. グループワーク(20min)
  4. 結果発表(5min)
  5. ふりかえり(5min)

今回のワークショップでは「チームでのディスカッションのハードルを下げる」ことが第一目標だったので、個人ワークと比べるとグループワークの時間を多く取りました。個人ワークとグループワークの時間が違いすぎると、個人よりグループの結果がよくなったとしても「そりゃ時間かけたからでしょ」と言われてしまう可能性もあるので、目的に応じて時間を変更するといいと思います。

イントロダクション、ルール説明

このゲームの導入とルールの説明をします。「わからないこと」を考えたり議論することが重要なので、必要最小限の情報だけ伝えます(各アイテムの詳細な説明や、月の環境については話しませんでした)。

個人ワーク

個人個人で15個のアイテムに優先順位をつける時間。時間内にすべてのアイテムに優先順位をつけることを念押しします。予め参加者がわかっていたので、スプレッドシートにそれぞれのシートを用意してそこに入力してもらいました。

グループワーク

グループワークはZoomのブレイクアウトルームで行いました。こちらもスプレッドシートにチームのシートを予め用意し、そこに優先順位を入力してもらいました。

Zoomのブレイクアウトルームを使ってやりましたが、以下は反省点です。😥

  • それぞれブレイクアウトルームを選択して入ってもらおうとしたが、慣れていない人もいてグループワークを始めるまでに時間がかかった。やり方を視覚的に説明したり、ファシリテーターが部屋に入れる操作をする方がよかったかも。
  • ブレイクアウトルームを終了」してから1分の猶予があるのを失念していた。ちょっと進行が遅れてしまった。(設定でなくせるんですね)

結果発表

まず、チームのスコアを集計します。今回はスプレッドシートに集計シートを用意していたので、非表示にしていた集計シートを表示にするだけでした。予めの用意が難しい場合は、模範解答を共有して、それぞれに集計してもらうのがよいと思います。 結果も大事ですが、チームが納得いく議論をできたが重要なので、「納得いく議論はできましたか?」などと呼びかけると内省を促せて効果的だと思います!

次に、個人のスコアを集計します。こちらもスプレッドシートの集計シートで自動集計しましたが、準備が難しい場合は、個人個人でスコアを集計してもらいましょう。 ここで、自分の結果と自分が所属していたチームのスコアを見比べてもらいました。今回はなんと、全員がチームのスコアの方が良いという結果でした!できすぎた結果でしたが、あたかも当然かのように以下を伝えました。

  • 三人寄れば文殊の知恵
  • 普段のチームでの活動でもそれぞれの意見を出し合うことで良い結果に向かうことができる

ふりかえり

最後にチームごとにふりかえりをしてもらいました。 ワークを通じての気づき、議論中のよかった点、次やるとしたらカイゼンしたい点を共有しあってもらいます。 チーム内のふりかえりができたら発表してもらい、他のチームにも共有してもらいました。

これでワーク終了です!

まとめ

今回はコンセンサスゲームのひとつであるNASAゲームの内容と進め方を紹介しました。 チーム内の議論のハードルを下げたい想いから企画したものでしたが、それ以上にチームで活動する意義を体感できる素晴らしい時間になりました。 もしよかったら、ぜひみなさんのチームでも取り組んでみてくださいね!

参考

心理的安全性の第一歩としての自己紹介

こんにちは。asatoです☀️。

心理的安全性の第一歩として、自己紹介って大事なイベントだなって話をします。

心理的安全性と自己開示

自己紹介は自己開示のひとつです。

心理的安全性と自己開示は、互いに他方を強化します。心理的安全性が高いとチームメンバーの自己開示がより進み、自己開示が多くなるとチームの心理的安全性はより高まります。
自分を受け入れてもらえていると感じられたり、ひととなりを知ることでより率直に話し合えるきっかけにつながります。

先行するのは自己開示

チームの心理的安全性はほっておいても高まりません。メンバーひとりひとりができる行動は自己開示を増やすことです。

しかし、自己開示も簡単なことではありません。受け入れてもらえるか不安があるでしょうし、ひとりだけが積極的に自己開示に取り組んでいても効果的とは言えません。

そこで最初は、チームをファシリテートする存在(マネージャー、リーダー、スクラムマスターなど)が、メンバー全員の自己開示を支援するのが良いでしょう。それが、自己紹介会です。

自己紹介会を催す

自己紹介会はその名の通り、自己紹介をし合う会です。チームビルディングの方法は色々ありますが、最もオーソドックスで馴染みも深い自己紹介はどんなチームにとっても扱いやすいはずです。

とはいえ、「じゃ、この時間は各自自己紹介してください」では不十分です。
特に最初の頃は、自己開示の程度もさぐりさぐりのはず。コミュニケーションを円滑にするために必要不可欠な情報と、その人のひととなりが少し垣間見える情報をうまく引き出すファシリテーションが求められます。

その場でのファシリテーションは置いておいて、「こんな項目を自己紹介し合いましょう」を決めておくことは自己紹介会を成功させる単純で効果的なデザインです。

自己紹介の項目の勘所

自己紹介の項目は、「コミュニケーションに不可欠なこと」と「ひととなりが今より少し垣間見えること」が重要です。

コミュニケーションに不可欠なことは、例えば

・名前
・ニックネーム(チームでなんと呼ばれたいか)
・顔写真、プロフィール写真
・年齢(生年月日)
・社歴
・どんなコミュニケーションが好きか(直接会って、チャット、集中してるときは反応が遅れるかも、いつでも爆速でリプ、など)

などがあります。なんと呼ぶかを確定させたり、日本の場合は年功序列・敬語の文化があるので年齢や社歴を公開し合うとコミュニケーションの取り方が確定しやすくなります。

最近はリモートも多く、カメラOFFがデフォの人は顔もわからないなんてことがあります。視覚情報はコミュニケーションにおいて重要なので、自己紹介でも顔がわかる写真を公開してもらえると良いと思います。(僕は名前と顔を一致させるのが超苦手なので、これがあるととても助かる)

ひととなりが今より少し垣間見えることは、例えば、

・出身地、ご当地自慢
・すきなこと、きらいなこと、最近はまってること
・得意なこと、苦手なこと
・家族構成

などがあります。出身地・ご当地自慢は答えやすく盛り上がりやすいので、特にチーム形成の時期はおすすめです。

好き・嫌い vs. 得意・苦手はマトリクス図で表現(好き・得意、好き・苦手、嫌い・得意、嫌い・苦手)するのも視覚的に面白いです。

自己紹介は残る形で

割と大切なのは、これを残る形でやることです。例えばmiroやgoogle slidesなど、気になったときにすぐにアクセスできる状態にしておきます。

一回揮発的に自己紹介しても覚えきれません。一方で、自己紹介した側は「自己紹介で言ったじゃん」になりかねないので、特にチームの形成期はコミュニケーションの前に確認できる状態にしておくと便利です。

また、新しいメンバーがジョインしたときにも効果的です。残しておいて得しかないです。

まとめ

心理的安全性の第一歩として、自己紹介が効果的という話をしました。自己紹介は自己開示のひとつであり、自己開示はチームの心理的安全性を高めてくれることが期待できます。

「では自己紹介をどうぞ」と無茶振りをしても効果的でないことも多く、自己紹介でもファシリテーションが必要です。自己紹介の項目をあらかじめ決めておくことは効果的です。

自己紹介の項目は、「コミュニケーションを円滑にする項目」と「今より少しひととなりを知れる項目」の切り口でデザインすると良きです。これらは自己紹介会の後でも残る形でチームメンバーがいつでもアクセスできるようにすると、自己紹介会後も活用できます。

良いプロダクトは良いチームから。
この記事が誰かの役に立ったなら嬉しいです😃🤞

喜怒哀楽ふりかえり

こんにちは。asatoです☀️。

今回は、最近ハマっている喜怒哀楽ふりかえりの話をします。
なんとなく喜怒哀楽をテーマにふりかえりやってみたらどうだろうと思い始めてみたところ、割といい感じなのでご紹介。
個人のふりかえりとして始めましたが、チームのふりかえりにも応用が効くかもです。

心をふりかえりたかった

ふりかえり手法としては、KPTやTimeline、YWTなどさまざまあります。これらは、実際に起きた「事実」にフォーカスすることが多い気がしています。こういうことがあって成果が出た、こういうことがあって進捗が遅れた、みたいなことです。

もっと「心」にフォーカスしてふりかえりをしたらどうなるんだろう、と思い、始めてみたのが喜怒哀楽ふりかえりです。
特に個人のふりかえりでは、自分の心に向き合ってあげるのも良いんじゃないかなってことで。自分と向き合いたいとき、チームの心に焦点を当てたいとき、ちょっとふりかえりのテイストを変えたいときなどにやってみると意外な発見があるかもですね。

喜怒哀楽ふりかえりの進め方

その名の通り、ある期間やプロジェクトに対して自分の喜怒哀楽を吐き出して、喜びや楽しさを増やすにはどうしたらよいか、怒りや哀しみを減らすにはどうしたらよいか、を考えるふりかえりです。

進め方も至ってシンプルです。以下で紹介していきます。

1. テーマを決める💭
最初に何についてふりかえりをするか、テーマを決めます。僕の場合は毎週金曜日に1週間のタスクや出来事に対して喜怒哀楽をふりかえるようにしています。

2. 思考を休ませる🛌
次に、思考を休ませます。心にフォーカスを合わせる状態になるための時間です。人によってアクティビティが異なると思います。
僕の場合は、10分間ほど瞑想をします🧘‍♂️。Meditopiaとアイウォーマーを使って頭と目を休ませてますね。

3. 喜楽を書き出す✍️
思考が休まったら、喜(嬉しかったこと)楽(楽しかったこと)から思ったことを書き出していきます。5分くらいが目安です。この時間は心を書き出すことに集中しましょう。
喜と楽から始めるのは、ポジティブな気持ちでふりかえりをしたいからです。頭をすっきりさせてまずネガティブなことから考えるのはやだなーってことですね。実際、人はネガティブな思考からポジティブな思考な切り替えるのが苦手らしいなで、ポジティブから始めるのはおすすめです。

4. 怒哀を書き出す✍️
同じように怒(イライラしたこと)哀(哀しかった、嫌だなと思ったこと)を書き出します。これも5分くらいが目安です。

5. 書き出したものを眺める👀
書き出したものを眺めてみましょう。
自分はこんなときポジティブな気持ちになりやすいんだな、こんなときはネガティブな気持ちになりやすいんだな、ということが見えてくるはずです。

6. 喜楽の機会の増加・怒哀の機会の削減のアイデアを考える💡
書き出したものを眺めてみると、「このポジティブな気持ちを増やしたいな」「このネガティブな気持ちを減らしたいな」と感じるでしょう。
最後に、そのアイデアを考えます。仕組みを変えることかもしれませんし、誰かの力を借りることかもしれません。自分の捉え方を見直すことかもしれません。そのアイデアを探し出して、行動してみます。

以上が喜怒哀楽ふりかえりの進め方です。
思考を休ませるフェーズがけっこう鍵を握っています。あとは心にフォーカスしているだけで、進め方自体は普段のふりかえりと大きく変えることなく取り組めるはずです。

まとめ

今回は、喜怒哀楽ふりかえりを紹介しました。心にフォーカスするふりかえりで、自分を見つめ直したり、もっと積極的に仕事に取り組めるようになるきっかけになるアクティビティだと思いますので、ぜひお試しください!🤞

定例会をなくしたら進捗した話

こんにちは。asatoです。

定例会で進捗共有して課題議論して宿題持ち帰って。会社によっては、プロジェクトが始まるときにまずは定例会の日付を決めることもあると思います。

前携わったプロジェクトでも、定例会を開催していました。
毎週課題を持ち寄り持ち帰り、また次の週に回答し回答をもらい、そんな定例会。

ふと「定例会開催まで課題がどっちかのボールで留まってしまうのはどうなんだろう?」と感じ、定例会を一旦やめる提案をしました。

課題管理にはBacklogを使っていたので、進捗をすぐに共有することはできるはず。定例会もBacklogをみんなで眺めて更新していくやり方だったので、それを個々人が行えばいいだけ。

結果はどうなったのか。

ある人との課題はめちゃくちゃスムーズに進捗するようになりました。
定例会があることで、進捗は定例会で共有するもの(進捗は定例会以外で共有するものではない)と思っていたようです。
1日に1回キャッチボールがあれば、5営業日で定例会有りのときと比べて5倍の進捗になります。

ある人との課題は進まなくなりました。Backlogを更新するやり方がわからないとか、更新する暇がないとか。
これには困りましたが、その人と同じグループの人が使い方をレクチャーしたり、最悪代わりに更新したりすることで、最終的には定例会以上には進捗するようになりました。

もちろんこれで顔を合わせなくなったわけではないです。
複雑な話やホワイトボードを使って議論したい内容のときはアドホックに時間を取ってMTGを開きました。

すべての定例会がダメだとは思わないし、定例会があった方がリズムが生まれたりすることもあります。
でも、週一とか隔週とかの定例会を当たり前に思ってしまっていないでしょうか。その当たり前の定例会がプロジェクトの進捗を滞らせてしまっている可能性も確かにあります。

当たり前を疑って、定例会を見つめ直すのも悪くないなって思った話でした。

チームに知っておいてほしいチームのこと

こんにちは。asatoです。

チームで仕事をする機会はどんどん多くなっているらしいです。
一方で、「チーム」について誰かと話したり、誰かから教わったりする機会はあまりないように感じます。

仕事で「チームってさ」みたいなことを話す機会があったので、それを元にチームに知っておいてほしいチームのことを書きます。
これがチームの全てではないですし、これを知っておけば、やっておけば、チームとして完璧になれるって公式はないと思っています。チームが少しでもチームのことを話し合うきっかけになればってnoteです。

チームとは

チームの話をするので、チームの定義が必要です。私が「チーム」に強い興味を持ったのは、Googleのre:workが要因の一つでした。ですので、そこから引用させていただきます。あ、re:Workの「効果的なチームとは何か」を知るは一読お願いします🙏

rework.withgoogle.com

ワークグループ
相互依存性が最小限という特徴があり、組織または管理上の階層関係に基づいています。ワークグループのメンバーは、情報交換のために定期的に集まる場合があります。

チーム
メンバーは相互に強く依存しながら、特定のプロジェクトを遂行するために、作業内容を計画し、問題を解決し、意思決定を下し、進捗状況を確認します。チームのメンバーは、作業を行うために互いを必要とします。

個人ではない人の集団は、メンバーが相互に依存し合う「チーム」と依存しあわない「ワークグループ(または単にグループ)」に分けられます。

グループは、例えば定例で進捗を共有している同じ部の同僚とか、定期的に意見交換するコミュニティのメンバーとか、そういう集まりですね。

チームは、同じ目的に向かって協働している人たちの集まりになります。短期のプロジェクトであっても長期のプロダクト開発のようなものであっても、同じ目的を達成するために協働しているのであればチームとして捉えるといいです。

効果的なチームとは

せっかくチームを組んで目的に向かって走っていくのですから、効果的なチームでありたいですよね。では、効果的なチームとはどんなチームでしょうか?

唯一の答えはありません。ぜひチームで議論したいテーマですよね。
re:Workの続きですが、Googleのプロジェクト・アリストテレスの研究結果では、マネージャーは「チームが成果(売上高、サービスの立ち上げ、など)を挙げられているか」を、チームメンバーは「チームの文化や風土(心地よいか、成長できるか、など)」を指標とすることが多いようです。

そう考えるとこの両面を達成できているチームが効果的なチームと言えそうです。

効果的なチームの特徴

プロジェクト・アリストテレスでは、効果的なチーム(成果が出ていて、文化や風土も良い)に共通する特徴も挙げています。それが以下の5つです。

心理的安全性
「チームの中でミスをしても、それを理由に非難されることはない」と思えるか

相互信頼
「チームメンバーは、一度引き受けた仕事は最後までやりきってくれる」と思えるか

構造と明確さ
「チームには、有効な意思決定プロセスがある」と思えるか

仕事の意味
「チームのためにしている仕事は、自分自身にとっても意義がある」と思えるか

インパク
「チームの成果が組織の目標達成にどう貢献するかを理解している」か

この中でも特に「心理的安全性」が重要であり、他の4つの特徴を生み出すための土台と言われています。なのでこれに注目するのは良いのですが、他の4つを忘れてはいけません。チームの心理的安全性についてはよく取り上げられるので「私たちのチームは高い心理的安全性を保っているから大丈夫」と思っている場合は、「私たちは「相互信頼」「構造と明確さ」「仕事の意味」「インパクト」を持っているだろうか?」というもう一歩先の議論を進めてみるとよいと思います。いい雰囲気のチームでも、この質問に対しては色々な意見が出て面白いと思いますよ😁

チームの心理的安全性

効果的なチームになる土台はチームの心理的安全性です。

チームの心理的安全性とは、チームの中で対人関係におけるリスクをとっても大丈夫だ、というチームメンバーに共有される信念のこと、です。
対人関係におけるリスクは、「無知」「無能」「邪魔」「否定的」と思われることです。例えば、「わからないので教えてください」とか「こういう捉え方もあるんじゃないですかね」とか、チームが機能するためには当然必要であろう言動だけど周りの目が気になってなかなかできないものってありますよね。あれが自然とできる状態がチームの心理的安全性が高い状態です。

率直に話せること」とも表現されています(僕はこの表現がシンプルで好きです)。

チームの心理的安全性について、面白い本もあるので輪読とかしてみてもいいかもしれないですね。📚

www.amazon.co.jp

www.amazon.co.jp

www.amazon.co.jp

チームの心理的安全性の四象

チームの心理的安全性はよく「仲が良い」と誤用されるそうです。それを説明するために、四象限の図が用いられています。

無題のプレゼンテーション (2)を拡大表示

この図は「 恐れのない組織」の図から少し改変しております。

つまり、「心理的安全性が高い」だけでは「快適」止まりで高い成果にはつながらず、「学習する高いパフォーマンスのチーム」は高い心理的安全性に加えて「高い成果基準」が必要だ、ということです。

ちょうど、プロジェクト・アリストテレスでマネージャーとメンバーの考える「効果的なチームの基準」の両軸にあたります。効果的なチームになるためには、高い心理的安全性と高い成果基準(目標)が必要だということがわかりますね。

ヘルシーコンフリクト(健全な衝突)

コンフリクトとは「衝突」のことです。基本的に衝突はチームのパフォーマンスに悪影響を及ぼします。

しかし、心理的安全性の高いチームでは、むしろチームのパフォーマンスに好影響を与えることがわかっているそうです。心理的安全性が高いということは、「対人関係のリスクがない」というマインドがチームの中に存在することになります。他人の発言は自分を陥れたりコントロールしようとするものではなく、純粋にチームを、成果をよりよいものにするためのものだと心から捉えることができることで、建設的な議論に発展していくんでしょうね。

HRT

HRTは、『 Team Geek』で紹介されている、チームで働く上で人間関係を円滑にし健全な対話とコラボレーションの基盤となるソーシャルスキルの三本柱、です。

Humility(謙虚)
自分は世界の中心でも、全知全能でも、絶対に正しくもない。常に自分を改善する。

Respect(尊敬)
一緒に働く人のことを心から思いやる。一人の人間として認め、能力や功績を正しく高く評価する。

Trust(信頼)
自分以外の人は有能で正しいことをすると信じる。

効果的なチームになるためには、メンバーがこれらに共感し、行動で示すのがよいと思います。

amzn.to

『Team Geek』自体はエンジニアチームを題材にしているのですが、三本柱を見て分かる通り、あらゆるチームに適応できることがある本です。

タックマンモデル

また少し違う理論からチームを見てみます。タックマンモデルという、チームのフェーズと成果を時間軸で捉えたモデルがあります。

無題のプレゼンテーション (3)を拡大表示

タックマンモデルによると、チームは高パフォーマンスを発揮するために4つのフェーズを経験します。

形成期(Forming)
お互いのことをよく知らず、チームの目標も不明瞭な段階

混乱期(Storming)
お互いの役割や考え方によって対立が起きている段階

統一期(Norming)
チームの行動規範や役割が確立し、他の人の考え方を受容し合っている段階

機能期(Performing)
チームに一体感が生まれ、目標達成に向かっている段階

図や説明は『チーム・ジャーニー』を参考にしています。

www.amazon.co.jp

今までのチーム活動をふりかえってもらううなずける曲線になっていると思います。

ただし、この曲線はただ待っていても動きません。形成期から混乱期に突入するためには「衝突」が不可避であり、混乱期を抜けるためには更なる「衝突」が必要です。統一期、機能期でも傾きを正に保つためには衝突(この段階ではヘルシーコンフリクトになっているだろう)がより一層大切になります。
また、この曲線は人の出入りやきっかけによって、形成期に逆戻りしたりもします。

各フェーズを抜けるために必要な期間はチームメンバーや活動によって異なります。「はじめまして」の状態からいかに早く心理的安全性のきっかけを掴み、衝突を機能期に向かうために必要なものだとチームメンバー全員が認め、形成期、混乱期を短期間で抜け出せるかが、チームで成果を上げるための核になります。

ダニエル・キムの組織の成功循環モデル

こちらも『チーム・ジャーニー 』でも取り上げられている組織のモデルです。(またまた参考に図を掲載)

無題のプレゼンテーション (4)を拡大表示

「関係の質」「思考の質」「行動の質」「結果の質」はひとつの循環システムになっているという話です。これにはGood CircleBad Circleがあります。

Good Circleでは「関係の質」を高めることにフォーカスします。互いに尊重し認め合い、一緒に考えるスタンスをチームにもたらす(関係の質の向上)ことで、気づきを共有し合い、当事者意識が芽生え(思考の質の向上)、自発的、積極的、挑戦的な行動が生まれ(行動の質の向上)、成果が出る(結果の質の向上)。成果が出たことでまた関係の質が向上し...という強化システムが生まれます。

Bad Circleは「結果の質」にフォーカスすることです。成果が上がらないことに集中してしまい、対立・押し付け・命令が増えます(関係の質の低下)。その結果、受け身のスタイルがチームに定着(思考の質の低下)し、自発的、積極的、挑戦的な行動が生まれなくなり(行動の質の低下)、ますます成果が出ない(結果の質の低下)...というスパイラルに陥ってしまいます。

note.com

目の前で「結果の質」が上がらない、下がっていることに過剰に反応せずに「関係の質の向上」に注力し、「結果の質の向上」まで待つことはかなり忍耐力やチームの力とチームの成長を信じる力が必要不可欠な難しいことです。しかし、チーム全員でこういったモデルの存在を知り共感しておくことで、効果的なチームに近づけるのだと思います。

まとめ

この記事では、チームに知っておいてほしいチームのことを書いてみました。一部だけですが、まず特に知っておくといいんじゃないかなという者たちです。

あと、なにか一つの理論やモデルではなくて、いろいろなことを知るとそれぞれが補完し合ったり共通項が見つかったり、楽しいですよね。

もっと大事なのは、チームでチームの話をすることかなと思っています。
この記事もあくまできっかけでしかなくって、「じゃあうちらって」ってチームの対話が始まるといいなって。