ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

リモートシャッターで子供のうんちをSlack通知する その1

みなさんこんにちは。サーバーサイドエンジニアの柿本です。

1歳3ヶ月になる私の息子は毎日快便です。
多いと1日5回くらい。おむつを替えているそばからうんちをします。

毎朝妻が保育園の連絡帳を書いてくれるのですが、

妻「昨日、Kくんはいつうんちしたっけ?」

私「お風呂入る前と寝る前だと思う。。。いや、寝てる間もしてたな。」

となります。

困ったなーと思って近所のドラッグストアにおむつを買いに行った時にこんな物を見つけました!

リモートシャッター

こ、、これは!!ダイソーで300円で買える IoT ボタン、として一時期話題になっていたリモートシャッター!!

少し値上がりしていますが、おむつに比べれば安い安い!!ということで迷わず購入しました。

それでは早速リモートシャッターでうんちの記録を残せるようにしていきます。
「うんちシャッター!」と呼ぶことにします。

ソースコード全体はこちらにあります。

github.com

目次

全体の構成

子供がうんちをしたら、おむつを替える時にボタンをポチッと押すことにします。

Slack に通知するだけだとうんち履歴が文字通り流れてしまうので、 Googleスプレッドシートにも残すようにします。

当初はリモートシャッターと Raspberry Pi を Bluetooth でつなげようと思っていたのですが、Raspberry Pi をガラクタ入れから引っ張り出してみたら、壊れていて起動しませんでした。。

気をとりなおして、自分の Mac を使うことにします。

全体の構成 最終版

リモートシャッターと Mac を Bluetooth で繋げて、ボタン押下の信号を受け取った Mac が IFTTT の Webhook を叩くという流れです。 IFTTT と Slack や Googleスプレッドシートとの連携はすでにしてあるものとします。

準備

構成が決まったところで開発の下準備を始めます。

MacでBluetoothを扱えるライブラリは意外と少なく、 Python だと『pybluez』『lightblue』『BluefruitLE』、 node だと『noble』『noble-mac』といったところです。

ちなみに iOS での実装については過去のしだのエントリーをご覧ください。

tech.unifa-e.com

色々調べたり実際に使ってみたりしたのですが、 Catalina でまともに動きそうなのは noble-mac だったのでこれを使うことにします。

% npm init -y

noble-mac は公開されていないので dependencies に追記して npm install します。

"dependencies": {
    "noble-mac": "https://github.com/Timeular/noble-mac.git"
}

ついでに IFTTT の Webhook を叩くので request もインストールしておきます。

% npm install
% npm install request --save

実装

Bluetooth(BLE) では通信するデバイスをそれぞれ Central とPeripheral と呼びます。

Central と Peripheral

イメージとしては Peripheral には Service が入っており(複数の場合もあるらしい)その中にサービスの特性としての Characreristic があります。

デバイスの検出

ひとまずリモートシャッターの Peripheral UUID がわからないと話にならないので、それを取得します。

// find_device.js

'use strict';

const noble = require('noble-mac');

const discovered = (peripheral) => {
  console.log(`Device Discovered: ${ peripheral.advertisement.localName }(${ peripheral.uuid })`);
  console.log(`    address: ${ peripheral.address }`);
  console.log(`    serviceUuids: ${ peripheral.advertisement.serviceUuids }`);
  console.log(`    rssi: ${ peripheral.rssi }`);
  console.log('-----------------');
}

noble.on('stateChange', (state) => {
  if (state === 'poweredOn') {
    noble.startScanning();
  } else {
    noble.stopScanning();
  }
});

noble.on('scanStart', () => {
  console.log('[scanStart]');
});

noble.on('discover', discovered);

実行してみると、 AB shutter3 というデバイスが見つかり、 Peripheral の UUID や提供する Service の UUID を確認できます。

find deviceのログ

ちなみにですが、 Service の UUID は GATT により定義されており、 1812Human Interface Device になります。

www.bluetooth.com

Characteristicのsubscribe

Peripheral UUID がわかったところで、内部の Characteristic を取得します。

ググったところ、どうやら 2a4d という Characteristic UUID でボタンの押下情報を取得できるらしいことがわかりました。ちなみに Characteristic UUID も GATT により定義されており、 2a4dReport になります。

www.bluetooth.com

// subscribe_report.js

// ---- 省略 ---- //

const REPORT_CHAR = '2a4d';

const discovered = (peripheral) => {

  if( peripheral.uuid == PERIPHERAL_UUID){

    peripheral.on('connect', () => {
      console.log('[connect]');
      peripheral.discoverServices();
    });

    peripheral.on('disconnect', () => {
      console.log('[disconnect]');
    });

    peripheral.on('servicesDiscover', (services) => {
      services.forEach(service => {

        service.on('characteristicsDiscover', (characteristics) => {
          characteristics.forEach(characteristic => {
            console.log('Characteristic Discovered');
            console.log(`    serviceUuid: ${ characteristic._serviceUuid }`);
            console.log(`    uuid: ${ characteristic.uuid }`);
            console.log(`    name: ${ characteristic.name }`);
            console.log(`    type: ${ characteristic.type }`);
            console.log(`    properties: ${ characteristic.properties }`);

            if (characteristic.uuid === REPORT_CHAR) {
              characteristic.on('data', (data, isNotif) => {
                const jsonStr = data.toString('utf-8');
                const jsonData = JSON.parse(jsonStr);
                console.log(jsonData);
                // TODO: ここでIFTTTにPOST
              });

              characteristic.subscribe((error) => {
                console.log('=> Subscribe Started');
              });
            }

            console.log('-----------------');
          });
        });

        service.discoverCharacteristics();
      });
    });

    peripheral.connect();
  }
};

// ---- 省略 ---- //

しかし、ここで暗礁に乗り上げます。

リモートシャッターの Peripheral に含まれる全ての Service の Characteristic を書き出していますが、 2a4d という Characteristic は見つかりません。

subscribe reportのログ

思考錯誤をしてわかったことは、

  • デバイスもしくはライブラリの問題で Report の characteristic が見つからない
  • そもそもリモートシャッターと Mac の Bluetooth の接続が不安定である
  • というか、すぐに切れる。切れていたかと思うと突然つながる
  • リモートシャッターのボタンを押すと、つどつど接続する

苦肉の策

ボタンを押すと一瞬だけ接続されることは検知できるので、これをトリガーにすることにします。

// ifttt_on_discovered.js

// ---- 省略 ---- //

let discovered_at;

let req_options = {
  uri: `https://maker.ifttt.com/trigger/${ IFTTT_WEBHOOK_EVENT }/with/key/${ IFTTT_WEBHOOK_KEY }`,
  headers: {
    'Content-type': 'application/json',
  },
  json: {
    'value1': 'うんち'
  }
};

const discovered = (peripheral) => {
  if( peripheral.uuid === PERIPHERAL_UUID){
    const elapsed = new Date() - discovered_at;
    if (elapsed > 5000) {
      console.log(`Device Discovered: ${ peripheral.advertisement.localName }(${ peripheral.uuid })`);
      request.post(req_options, (err, res, body) => {
        if (!err) {
          console.log('Report Sent');
        }
      });
      discovered_at = new Date();
    }
  }
}

// ---- 省略 ---- //

接続が不安定な結果、 connect と disconnect を3秒間くらいの間に大量に繰り返すので、5秒のインターバルをおくことにしました。

結果的になんとも情けない感じにはなりましたが、なんとか「うんちシャッター!」として機能するようになりました。

動作結果

Google スプレッドシートにもこの通り履歴が残ります。

最後に

上の画像ではキレイに6件通知されましたが、実際にはボタンを押していないのにリモートシャッターと Mac の Bluetooth が突然つながったりもするので、息子がうんちをしていないのに通知が来ることもあります。

なんとかして Report の characteristic を subscribe できれば防げるので、これを今後の改善点と見据えて、タイトルに「その1」とつけました。

せっかくボタンが2つあるので、うんちが硬かったら大きいボタン、ゆるかったら小さいボタン、みたいにできたらいいなと思います。

ちなみにですが、弊社の連絡帳アプリのキッズリーではうんちの状態を4段階(かたい/ふつう/ゆるい/水様便)で登録することができます。
あいにく「うんちシャッター!」の販売予定はございませんので、弊社キッズリーをご利用ください。

ユニファでは 『うんち 保育をハックする』 エンジニアを募集中です!

www.wantedly.com

Slackを活用する上で気をつけたい8のこと

みなさんこんにちは! スクラムマスターの渡部です。

今回は開発とは直接関係は無いのですが、私達の日々の生産性に大きな影響を与えるツールであるSlackを活用する上で、注意した方が良いと私が考えていることについてお話していきます。

「相手の返信ペースに合わせること」や「目上の人の入力中表示の場合は待つこと」などの儀式のようなものではありません。

実際に日々のコミュニケーションがスムーズになるもので、すぐに使える・意識できるものになっています。

複数書いていますが、全部を一度に変えてみるのは大変ですので、先ずはなにか一つ、みなさんの中で「お、これは!」と思ったものから順番にチャレンジしてみていただくのが取り組みやすいかと思います。

目次

  • 目次
  • 気をつけたいこと
    • チャンネル入りすぎ問題
    • 同じトピックの会話が新規投稿されることで遡れない & 投稿が流れ過ぎる問題
    • Slackのスターで個人タスク・ピン留めでチームのタスクを管理する問題
    • 重要な共有・永続的なルールがサラッと共有される問題
    • スレッドの議論が超長いな問題
    • DM(ダイレクトメッセージ)・プライベートチャット多い問題
    • 常時(メンションの度)Slackをチェックしてしまう問題
    • 緊急な連絡がSlackのみ問題
  • 最後に
続きを読む

DynamoDBで集計とJavaScriptのPromise

こんにちは、プロダクト開発部のチョウです。最近一部古いJavaScriptをcallbakからPromise/async/awaitのスタイルに書き換えるようにしてみました。その中で印象に残ったコードを紹介します。

やりたいことが簡単です。あるセンサーのデータ個数を日ごとに集計します。テーブルの構造はこういう感じです。

SensorId: Number(Partition Key)
Date: String(Sort Key)
DataCount: Number

Partition KeyとSort KeyはDyanmoDBでテーブルを作るとき必要な情報ですが、とくに本文に関係ありません。

普通にRelation Databaseで考えると、最初にレコードを作るのは要注意です。複数クライアントでレコードを作ろうとすると、一個だけが実際のDBに残り、集計が正しくない状態になります。 普通のDBならunique indexなどを使います。SensorIdとDateを一つのunique indexにして、同じindexで作ろうと一つだけが成功し、ほかはすべて失敗です。 DynamoDBにunique indexがないですが、ConditionExpressがあります。レコードを追加したり更新したりするとき条件に満たさないと失敗になります。一個のセンサーに一日に一個のデータを作るには

    this.ddb.putItem({
      TableName: 'SensorDigest,
      Item: {
        DeviceId: {N: String(sensorId)},
        Date: {S: date},
        DataCount: {N: String(1)}
      },
      ConditionExpression: 'attribute_not_exists(Date)'
    }, (error, data) => {
      if(error) {
        if(error.code === 'ConditionalCheckFailedException') {
          // 重複
        } else {
          // 他のエラー
        }
      } else {
        // 先生
      }
    });

attribute_not_existsを使えば、レコードがない前提で追加できます。パラメータのDateは実際なんでもいいですが、必ずあるKeyにしたほうをおすすめです。

次にDataCountを増やすのも簡単ではありません。複数クライアントで一斉に更新すると、一部クライアントのデータがロスするかもしれません。 この問題に対し、普通のDBはversionカラムなどの方法があります。 DynamoDBになると、UpdateExpressionに SET DataCount = DataCount + increment や、1を増やす場合、ADD関数を使えます。 更新のコードはこうなります。

    this.ddb.updateItem({
      TableName: 'SensorDigest',
      Key: {
        SensorId: {N: String(sensorId)},
        Date: {S: date}
      },
      UpdateExpression: 'SET DataCount = DataCount + :increment',
      ExpressionAttributeValues: {
        ':increment': {N: String(increment)}
      }
    }, (error, data) => {
      if(error) {
        // 失敗
      } else {
        // 成功
      }
    });

レコードの追加と更新をあわせて考えると、ごく普通で、日ごとにセンサーのデータを集計することが複雑になりました。 あるセンサーのデータが来ると、まず追加ですか。それとも更新ですか。

ここで、私の考えは、

  1. 更新
  2. 更新失敗でしたら、追加
  3. 追加失敗ししたら、更新

最初更新にするのは、99%以上が更新だからです。追加は1回のみで、毎回やる必要がありません。 一日最初の更新は失敗するので、そこでレコードを追加します。 もし運悪く、複数クライアントで1の更新失敗し、2の追加をすることになったら、一つのクライアントだけが成功し、失敗したクライアントは更新モードに戻ります。今度こそ失敗しないです。

流れが完璧ですが、コードは少々複雑です。Promise/async/awaitなしのバージョン

  // pattern 1
  increaseDataCount(sensorId, date, increment, moveToPattern2) {
      this.ddb.updateItem({
      TableName: 'SensorDigest',
      Key: {
        SensorId: {N: String(sensorId)},
        Date: {S: date}
      },
      ConditionExpression: 'attribute_exists(DateUsed)',
      UpdateExpression: 'SET DataCount = DataCount + :increment',
      ExpressionAttributeValues: {
        ':increment': {N: String(increment)}
      }
    }, (error, data) => {
      if(error) {
        if(error.code === 'ConditionalCheckFailedException' && 
           moveToPattern2) {
            // レコードは存在しない、go to pattern 2
            save(sensorId, date, increment)
        } else {
            // 失敗
        }
      } else {
        // 成功
      }
    });
  }

  save(sensorId, date, increment) {
    this.ddb.putItem({
        TableName: 'SensorDigest,
        Item: {
            DeviceId: {N: String(sensorId)},
            Date: {S: date},
            DataCount: {N: String(increment)}
        },
        ConditionExpression: 'attribute_not_exists(Date)'
        }, (error, data) => {
        if(error) {
            if(error.code === 'ConditionalCheckFailedException') {
                // 重複、go to pattern 3
                increaseDataCount(sensorId, date, increment, false);
            } else {
                // 他のエラー
            }
        } else {
            // 先生
        }
    });
  }

pattern 1のときに更新と同時にConditionExpressionを追加し、レコードがないとすぐ失敗になります。 callbackの中でもし条件に満たさないエラー(レコードが存在しない)なら、pattern 2に移します。

pattern 2でレコードを追加します。ここもConditionExpressionがあり、複数クライアントの場合ひとつのクライアントだけ成功になります。失敗したほかのクライアントは条件満たさないエラーで、pattern 3に移します。

pattern 3はpattern 1のコードそのまま利用し、パラメータのmoveToPattern2をfalseに設定しただけです。パラメータを用意するのは無限ループにならないためです(基本ならないと思いますが)。確率からすると、pattern 3になるクライアントはほとんどいないはずです。

ようやくcallbackスタイルのコードをPromise/async/awaitに変わる部分に入れるようになりました。 上のコードを見ると、同期スタイルのコードの変えるのは難しそうですね。

try {
    await increaseDataCount(sensorId, date, increment)
} catch(e) {
    if(e.code == 'ConditionalCheckFailedException') {
        try {
            await save(sensorId, date, increment)
        } catch(e) {
            if(e.code == 'ConditionalCheckFailedException) {
                await increaseDataCount(sensorId, date, increment);
                return;
            }
            throw e;
        }
        return;
    }
    throw e;
}

(ここで、increaseDataCountのcallbackからsaveをコールしなくなるので、パラメータmoveToPattern2を削除しました。) しかも、同期スタイルに変えても読みにくそうです。 ではPromiseスタイルはどうでしょう。

await increaseDataCount(sensorId, date, increment)
    .catch(e -> 
        if(e.code == 'ConditionalCheckFailedException') {
            return save(sensorId, date, increment)
        }
        return Promise.error(e)
    )
    .catch(e -> 
        if(e.code == 'ConditionalCheckFailedException') {
            return increaseDataCount(sensorId, date, increment)
        }
        return Promise.error(e)
    );

同期コードよりよさそうです。わかりやすいです。 increaseDataCountとsaveをPromiseスタイルに変えるのはとくに難しくないのでここで割愛します。

私が古いコードをPromise/async/awaitに変える中でこれは一番難しかったです。フローはそうだし、エラーハンドリングもすべてasync/awaitに寄せるではなく、Promiseも使うのもいいでしょう。 実際、こういうPromiseのchainのような書き方は関数型プログラミング言語HaskellでMonadといいます。Monadを用いたReactiveX(RxJava、RxSwiftなど)もエラーハンドリングなどに優れています。

いかがでしょうか。DynamoDBで集計テーブルを操作するのも、callbackスタイルのコードをPromise/async/awaitも難しかったんでしょうか。普段こういうコードあまり見れないと思いますので、参考になると幸いです。

ルクミーMIMAMORIフォントを作ってみる

Webエンジニアのほんまです。

今回ですが、紹介できるネタがなくなってしまったので、ちょっとお遊び的な内容になっています。 エンジニアの方には物足りないかもですが、非エンジニアの方は楽しめる内容になっていると思いますよ。

今回は、「ルクミーMIMAMORIフォントの作成」にチャレンジしてみました。

続きを読む

ユニファのシステムの移り変わり(後編)

おはこんにちばんは! ユニファのインフラ見てますすずきです。

とりあえずユニファシステム遍歴の最後、後編です。
今現在では新サービス、新サブシステムが増えたり、微妙に構成変更したりしていますが一旦は昨年のフォトリニューアルのタイミングまでを後半としようと思います。

前編、中編はこちら

tech.unifa-e.com tech.unifa-e.com

さっそく後編始めます。

続きを読む

ユニファのシステムの移り変わり(中編)

おはこんにちばんは! ユニファのインフラ見てますすずきです。

先週に引き続き書いています。(ホントはこれも先週出す予定…)
引き続きなので今回はユニファのシステム変化の中編(後編では収まらなさそうだ…

前編はこちら

tech.unifa-e.com

さっそく中編始めます。

続きを読む

RSGT2020 参加レポート

こんにちは。スクラムマスターの渡部です。

まだまだスクラムマスターとしての経験が浅い私ですが、幸運にもRSGT2020に参加させていただくことができたので、ここにまとめたいと思います。

あらゆるセッション、あらゆる出会いと体験が素晴らしいものでした。

ですので、その全てに触れるのはここでは諦めます。 今回の記事では、特に印象に残ったこといまのチーム・組織に活かせそうなこと考える必要がありそうなことにフォーカスします。

社外の方はもちろんですが、社内の方にも見ていただきたい内容に、きっとなっていると思います。

RSGT2020とは

RSGT2020と略称を連呼していますが、正式にはRegional Scrum Gathering Tokyo 2020(リージョナル スクラム ギャザリング 東京)と言います。

2020.scrumgatheringtokyo.org

アジャイル / スクラムに関わる方が400人ほど集まり、スクラム開発の成功 / 失敗体験の共有や、スクラムを理解する助けになるようなお話、理想に向かって進むときの問題と必要なアプローチなど、様々なテーマのセッションが行われます。

また、参加されている方はみな、各々の現場でより良いチーム、良いプロダクト、良い組織のために悩み抜いていらっしゃる方ばかりなので、セッション外の時間でのコミュニケーションからも多くの学びを得られる、ワクワクが満載のイベントでした。

因みに私は、(恐らく無関係な)複数の方から「まだまだコントロールしようとしている」という趣旨の指摘を受けています。「難し〜!!」と心の中で叫びつつも、その通りだと理解しているので、今後の課題の一つとして認識しています。

…これでもちょっと意識しているつもりなんですが、本当に難しいんですよ!!!(大泣)

雰囲気

会場

f:id:unifa_tech:20200203184429j:plain
会場となったソラシティカンファレンスセンター

f:id:unifa_tech:20200203162932j:plain
セッションが行われる場所

前回までは別の開催場所だったようですが、今回は御茶ノ水にあるソラシティカンファレンスセンター*1で開催されました。

会場は、写真ではちょっと伝わりにくいのですがかなり広く、多分400名くらい入ると思います。また、写真の部屋の他にもう一会場ありますので、かなり規模が大きいことがわかるかと思います。

積極的な採用活動

f:id:unifa_tech:20200203184518p:plain
一緒に保育をハックするプロダクトを作りたい方、募集中!!

ロビースペースにはジョブボードが張り出され、皆さんの会社が一言コメント付きで募集をかけていました。当然弊社も参加しています。

興味がある方はぜひ一度ご連絡ください(笑)

herp.careers

おすすめのAgile / Scrum本アンケート

ロビーの壁に投票用紙があり、参加者のおすすめ本アンケートが行われていました。

iwakiri.hatenablog.com

詳細な結果は上記リンクをご覧いただければと思いますが、良書と言われている本はやはり強いですね。「アジャイルサムライ」は全参加者層から1位を獲得していたようです。

他の本もほとんどは見聞きしたラインナップでしたが、「Joy,Inc.」のみ知らず…個人的にAmazonで購入したのはまた別のお話です。

以下、セッションについての感想です。

良い悪いではない。あなたはいま、どの段階なのか?

confengine.com

1日目のKeynoteでは、スクラムを「禅の入門書」に照らし合わせて、(言い方に語弊があるかもしれませんが)初心者から熟練者までのそれぞれのフェーズを解説する内容でした。

その中で都度「いまのあなたはどの段階ですか?良い悪いではありません、いまの段階を知ることが重要です」と話されていたことが、非常に印象的でした。

「段階をすっ飛ばして一気に理想を追い求めるべきではない、まずは正しく現状把握をせよ。そして次の段階へ進むために必要な適切な行動をすべし。」

そう言われているように感じ、プレッシャーとともに、勇気をもらえたような気がしました。(そう思えるようになったのは2日目以降でしたが)

何をするにしても、理想と現状の間にはギャップがあります。それはスクラムに限らず、個人のスキル、チームの状態、あらゆることに当てはまります。

大事なのは、いま私たちがどの段階にいるのかを理解すること。そして、次の段階に進むために何が不足しているかを考え、一歩一歩前進していくことなのです。

会社やチームのことを、めっちゃ嬉しそうに語れるか?

Tadahiro Yasuda - 日本にJoy,Incを創る!ぼくらのジョイインクジャーニー3年間の軌跡

www.slideshare.net

おすすめのAgile / Scrum本で見事第二位を獲得した、話題のあの本が関連するお話です。

発表されていた会社は、昔は会議が暗い雰囲気で、チーム間でいがみ合いがあったり、業績も良くなかったそうです。(そんなことを声を大にして発表するなんて、非常に勇気がいることだったと思います)

そんな時にJoy,Inc. *2に出会い(恐らく憧れを抱き)、チームを、会社を変えるために行ってきた施策とその効果が紹介されていました。

その中でも比較的簡単に導入できて、特に面白そうだったものを紹介したいと思います。

ふるふるリレートーク

輪になって順番にリレートークするのですが、誰か一人を「ふるふると震えるほど」褒めまくるとのこと。

単純に面白そう、ということもありますが、忘れられがちな良いところを言語化して話すことで、聞いてる人もその人を知ることができますし、言われる側も改めて何が貢献できているかを理解できるのは素晴らしいですね。

チームの他の人が自分に対して何を期待しているのか、何をしてもらったら嬉しいのかは、わかっているように思えて、実は少しずつずれているものです。

そういったミスコミュニケーションを減らせるので、日々の仕事の効率も少しずつ上がっていきそうな気もします。やってみたいですね。

雑談

合間合間で積極的にコミュニケーションできるよう心がけていたのですが、まさか「雑談だけの時間」を作ってしまうとは驚きでした。

改めて考えてみると、「効果がある」とされていることを、業務時間で取り入れていけない訳が無いんですよね。

ただ、続けていくと話す人が偏ったり、ネタ切れになったりと課題はあったらしく、こころかるた*3というものを使って解消されていたそうです。

詳しいやり方、やってみた効果のまとめはこちらにあるので、よければご覧になってみてください。 www.creationline.com

カルチャーと業績は連動する

私たちは同じ会社で一緒に働いてはいますが、意識的に相手を理解しようと努めないと、仕事のアウトプット以外はよく知らないなんて状態になります。 そうなると、心理的安全性*4も低い状態になり、生産性も低下していくのでしょう。

セッションでは、「繋がりができて仕事がスムーズになった」、「心理的安全性が高まった」ということが強調されていました。

心理的安全性はパフォーマンスに繋がりますからね、素晴らしい!

そして、上記のような施策を実施し、カルチャーをカイゼンし続けた結果、従業員のエンゲージメントスコア会社の業績が連動して上昇したという結果が出たそうです。

「ふるふるリレートーク」も「雑談」も、時間さえ確保できればカンタンに実践できるので、私も早速取り入れてみたいと思います。皆様も是非!

最後に

参加された方のブログもたくさん拝見したところ、みなさん「ブログでは書ききれない」「学びの多い時間」「勇気をもらえた」と口を揃えて仰っていますが、本当にその通りでした(笑)

今も昔も悩みだらけですが、一つ違うのは、「自分だけではなく、みんなも同じように理想と現実とのギャップに悩みながら、明日が少しでも幸せになるように足掻いていることを知った」という点です。

RSGTに参加したからと言ってすぐさま劇的に状況が変化することはありませんが、今スプリントよりちょっとだけ素敵な来スプリントへ向かって、一歩一歩着実に、楽しく、勇気を持って進んでいきたいと思います。そんなチームに、会社になっていけるよう、引き続き精進していきたいと思います。

最後の最後になりましたが、RSGT2020参加者・関係者・スポンサー企業の皆様、そして、快く送り出してくれたチームのみんなに感謝を。

f:id:unifa_tech:20200203175248j:plain
スポンサーロゴ