Rubyの「=」について無限に紐解いてみる

はじめまして、Baseconnectエンジニアの御園と申します。 私は2022年の4月1日にBaseconnectにエンジニアとしてJOIN、実務として経験するのは初めてのRubyを使って開発業務を行うことになりました。

そして、入社してから1ヶ月にも満たない4月22日、無茶振りによって 社内向けにLTをすることになり、私はプログラミング言語オタクとしての本領を発揮すべく、Rubyにまつわる些細な疑問を深堀りしたLTを行いました。

結果として割と反応はよく、社内の皆さんに良い感想を沢山頂けたので、今回はそのLTの中から抜粋し、テックブログとして記事を書かせてもらうことになりました。どうぞ、よろしくお願いいたします。

introduction

さて、皆さんがRubyを学ぶ時一番最初に学んだ事を覚えていますか? Rubyでプログラムを書く準備、Rubyという言語について……様々な事を学んだでしょう。 そして、Rubyイカしたプログラムを書く時、誰もが最初に学ぶものがあります。 そう、それは変数に値を格納するための「=」ですね。

プログラマが一生付き合うことになっていく「=」。それを記述した数はもはや数え切れないでしょう。 その「=」がもつ働きは、実はRubyという言語の性質を追求するのに実はもってこいという事を知っていましたか? 一回立ち止まって、あるいは少し後ろを向いてみて、「=」にもう一度目を向けてみましょう。 さぁ、私と一緒にRubyの「=」について追求する旅に、一緒に出かけてみましょう。

motivation

一番最初の些細な疑問は、私がRubyでとある演算子を使おうと思ったときに生まれました。 以下のようなコードです。

value++

++ はインクリメント演算子と呼ばれ、JavaScriptPHPなど、Cに影響を受けた色々な言語で使うことが出来ます。 意味は単純で、"変数の値を1加算する"と言ったものです。よく for と一緒に使用されたりしますね。例えばC++のコードでは以下の様に使えます。

for (int i = 0; i < 10; i++) {
  // iは0〜9まで、1ずつ加算されながら、このブロックの中の処理は反復される
}

さて、Rubyの世界に戻りましょう。Rubyに堪能な読者の人ならば、既に「そんなもの、Rubyには無いよ」と鼻息を荒くしていることでしょう。 まさしくその通りです。Rubyには ++ が定義されていません。なので、私は残酷にもRubyにエラーを吐かれてしまいました。 ( ++を使った直後の行を対象に以下のエラーを出される )。

syntax error 

Rubyにはインクリメント演算子も、"変数の値を1減算する"ところのデクリメント演算子 -- もありません。 例えば1加算する、といった事を実現するためには、以下の様にする必要があります。

value = value + 1

ただし殆どの人がこう書かず、必ずこう書くでしょう。

value += 1

自己代入などと表現されるこの += は、 value = value + 1 と同じ意味を持っています。 これはある意味シンプルです。 変数の値を加算、あるいは減算するという事に対しては、全て同じ演算子を使えば良いということになります。 1を足す時も、何かそれ以外を足す時も+=。1引く時も、何かそれ以外を引く時も -=。 わざわざ ++-- という特別な演算子の事を学習する必要がありません。 しかし、JavaScriptPHPなどに見られるように、”あっても良さそう”という気持ちが生まれるのも確かです。

何故Rubyにはインクリメント、デクリメント演算子が無いのでしょう? プログラマは探求をするのが好きです。私もご多分に漏れずその性を持っていますから、こんなちょっとした興味で、Rubyにこれら2つの演算子が無いことを調べ始めました。 そして、すぐさま、その疑問が解消される事については、Rubyという言語そのものの性質を知らなければならないことに気づくのでした。

奇妙なコード

Rubyを学習しようと文献を漁ると、高確率で目にしたり、聞いたりするものがあります。

Rubyでは全てがオブジェクト

これはどういう事なのでしょうか? この事を知るために、まずは次のようなシンプルなコードを実行してみます。

class Hoge
end

i = Hoge.new
puts i.object_id

object_id メソッドは、そのオブジェクトのIDを取得する事が出来ます。 Hoge クラスは new メソッドによってオブジェクトとして実体化され、 i に格納されました。 そして、その i に対して object_idメソッドを使用することによって、実体化したオブジェクトのオブジェクトIDが取得出来るというわけです。 例えば私の環境では、以下のように出力されました。

60

では、もう一つ、次のようなコードではどの様に出力されるでしょうか?

class Hoge
end

puts Hoge.new.object_id
puts Hoge.new.object_id
puts Hoge.new.object_id

このコードは、例えば私の環境では以下のように出力されます。

60
80
100

おそらくどのような人の環境でも、3つとも違うオブジェクトIDが出力されるでしょう。 通常、Rubyではオブジェクトが作成された時、他のオブジェクトと衝突しないようにユニークなオブジェクトIDが割り振られます。 上記のコードでは、3回 Hoge クラスのオブジェクトの作成をしており、それぞれユニークなオブジェクトIDが割り振られた結果、3回とも違うオブジェクトIDが出力されたという事になっています。

今、私達は object_id でそのオブジェクトが持つオブジェクトIDを知る事が出来る事を理解しました。 では、次のようなコードはどうでしょうか?

puts 20.object_id

一見これはとても奇妙です。 もし、これが奇妙に見えない人がいたら、ニヤニヤしながらこの先に進んでください。 しかし、JavaPHP、他のオブジェクト指向の特性を持つプログラミング言語をやったことのある人であればあるほど、このコードがとても奇妙であるということは共感できるはずです。 そして、Rubyではこのコードは問題なく解釈でき、例えば次のような出力を生むはずです。

41

なんと 20 という数字に対して、 object_id メソッドが使えてしまいました。 Rubyでは全てがオブジェクト という言葉がここで脳裏を反芻します。 もう一度先のコードを見てみましょう。

puts 20.object_id

このコードは何が奇妙なのでしょうか? 先程 object_id メソッドは、オブジェクトのオブジェクトIDを取得できるものという説明をしました。 その「オブジェクト」とは、潜在的に「new メソッドを使ってクラスを実体化したもの」という印象があったはずです。ですが、このコードでは new メソッドはおろか、クラスの定義すら出てきません。

プログラマ、特にRubyをやったことのないプログラマ達にとっては、20という数字は「ただの20という数字」でしかありません。それ以上でもそれ以下でも無く、20という数学的性質を表す分量以外の何者でも無いのです。 しかし、Rubyでは、それに対して直接 object_id を使うことが出来てしまいました。 一体何が起きているのでしょうか?

全てがオブジェクトということ

Rubyのリファレンスマニュアルを参照すると、オブジェクト の欄に以下のような記述が出てきます。

オブジェクトとは Ruby で扱える全ての値はオブジェクトです

これはとても興味深いことです。 この事をあえて、他の言語で擬似的に表現することにしてみましょう。 例えば、

hoge = 42

このようなRubyのコードがあったとして、C++では通常このコードと同じような事をするならば

int hoge = 42;

となるはずです。しかし、Rubyでは 扱える全ての値はオブジェクト ということでした。 つまり、実際にRubyで起こっていること を忠実に再現するならば、C++では以下のようなコードになると言えます。

auto hoge = new Integer(42);

42という数字を代入するために、Integer クラスのオブジェクトを42という数字で初期化し、それを変数に代入しています。 これが意味するのは、プログラム中に記述された

= 42

というものが、C++Rubyで決定的に違うということです。 この事を踏まえると、先程起きた奇妙な出来事について、紐解いていけそうです。 もう一度コードを見てみましょう。

puts 20.object_id

直感的に言えば、これはC++ではこのようなコードになりそうです。

std::cout << 20.object_id();

しかし、このコードは大方の予想通り通りません。20 という数字そのものに対して、 object_id などというメソッドは無いからです。 20はあくまで20という数字リテラルでしかなく、それ以上でもそれ以下でも無いのです。 ですが、 実際にRubyで起っていること をまたここでも忠実に再現してみましょう。

std::cout << (new Integer(20))->object_id();

これなら通りそうです。とどのつまり、Integer クラスのオブジェクトを20で初期化したあと、 object_id というメソッドをそのオブジェクトに対して呼び出し処理をしています。

Rubyでの 全てがオブジェクト という事がここまでで具体的に見えてきました。 実際にRubyのリファレンスマニュアルを見ると、Integer という整数クラスの存在を確認する事が出来ます。 その継承を追ってみると

Integer < Numeric < Comparable < Object < Kernel < Basic Object

となっており、これのうち Object クラスに object_id メソッドが定義されていることが確認できます。 Rubyでは、ただの数字に見えても、実際には Integer クラスのオブジェクトであり、それに付随して実装されているメソッドをダイレクトに呼び出せたという訳です。

これはRubyの大事な性質です。何気なくタイピングしている数字、文字列、それらはRubyの中では全てオブジェクトとして顕在化するということに留意しなければなりません。 しかし、この性質を意識していると、奇妙なコードも徐々に"Rubyでは当たり前"という見方に変わっていくでしょう。

もっと踏み込んでいく 〜Integerクラスの工夫〜

ところで、ここで一つ疑問が生まれます。 全てがオブジェクトなのであれば、

・数値を宣言する毎にオブジェクトが生み出されている ・同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか?

という疑問です。先程、Rubyでの次のようなコードを

hoge = 42

C++で擬似的に表すのに次のようなコードを用いました。

auto hoge = new Integer(42);

このコードのままであれば、 同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか? という疑問に対してはYESと答えるしかありません。 例えば、

hoge = 42
huga = 42
hugi = 42

というのはC++での疑似コードでは

auto hoge = new Integer(42);
auto huga = new Integer(42);
auto hugi = new Integer(42);

と表せるはずですから。

ですが、面白いことに実際にはRubyでは工夫されています。 まず、次のようなコードを見てみましょう。

puts 20.object_id
puts 20.object_id
puts 20.object_id

例えばこのコードは、私の環境では以下のように出力されます。

41
41
41

おや?これは少し興味深そうです。冒頭で私は

通常、Rubyではオブジェクトが作成された時、他のオブジェクトと衝突しないようにユニークなオブジェクトIDが割り振られます。

この様に説明しました。とすると、20というオブジェクトは、一回しか作成されておらず、そして同じオブジェクトIDが出ていることから、あたかもそれを使いまわしているかのような挙動をしています。 そのため、このコードのC++での擬似コード

std::cout << (new Integer(20))->object_id();
std::cout << (new Integer(20))->object_id();
std::cout << (new Integer(20))->object_id();

これは真に正解ではなさそうです。

これの種はとてもシンプルです。 Rubyでは全てのIntegerオブジェクトは、シングルトンパターンによってオブジェクト化されます。 つまり、1度登場した数字オブジェクトは、以降同じオブジェクトを使い回すということになります。 次のようなコードで確認してみましょう。

v = 20
puts v.object_id
puts 20.object_id

例えば私の環境では以下の様に出力されます。

41
41

変数 vに代入されている20という数字オブジェクトの object_id と、 そもそもの20という数字の数字オブジェクトの object_id が一致しました。

20というオブジェクトは、一回しか作成されておらず、そして同じオブジェクトIDが出ていることから、あたかもそれを使いまわしているかのような挙動

RubyのIntegerは、シングルトンによって工夫したデザインをする事によって、この挙動を実現しています。 つまり、同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか?に対してはNoと返答する事が出来そうです。 私達は、知らずIntegerの工夫によって、こういった細かいパフォーマンスを気にすること無く、コーディングが出来ているというわけです。

「=」が持つ 本当の意味

さて、ここまで読み進めた方は、一つの違和感を覚えるでしょう。 この記事のタイトルは「=」について紐解いてみるというタイトルでした。 しかし、実際言及したのはRubyという言語が持つ、全てがオブジェクトという性質についてでした。 冒頭で私は、

「=」がもつ働きは、実はRubyという言語の性質を追求するのに実はもってこいという事

などと述べました。ここまでの言及で実は先に、Rubyという言語の性質については追求できましたが、肝心の「=」がもつ働きについては追求できていません。 いやいや、と思うこともあるでしょう。 変数に値を格納 という表現を、同じく冒頭で私はしています。そしてあるいは多くの人が同じような認識を持っているかもしれません。 「変数に値を格納する事」こそが「=」の働きである。これは本当なのでしょうか?

まず、次のようなコードを見てみます。

a = "hoge"
b = a 

puts a
puts b

このコードの出力は自明です。どちらの putshoge を出力することでしょう。 しかし問題はもっと深い所にあります。 この時、 ab が参照している hoge という文字列は果たしてどうなっているのでしょうか? 「格納」という表現が合っているのならば、a に 「hogeを格納」した後、 b に 「a の内容を(コピーして)格納 」といった表現が出来るはずです。 これは直感的には ab にはそれぞれ別々に値が入っているイメージを作ります。 しかし、実際にはどうでしょう。次のようなコードを試してみます。

a = "hoge"
b = a
puts a.object_id
puts b.object_id
puts "hoge".object_id

このコードの出力は、私の環境では以下のようになります。

60
60
80

まず、文字列 hoge 自体に object_id を行った時、違うidを参照していることから、文字列はIntegerとは違い、同じ文字列でも新たな文字列オブジェクトが生成され、別のユニークなobject_idが割り当てられることがわかります。

では、 abはどうでしょうか?こちらは同じobject_idを指しています。 つまり、abが指している文字列オブジェクトは同じものであるという事なのです。 その事実を確かめてみましょう。

a = "hoge"
b = a
a.upcase!
puts a
puts b

upcase! は、格納されている文字列を大文字に変換するメソッドです。そして、このコードの出力は

HOGE
HOGE

となります。 b には何ら作用していないように見えるのに、 bの出力も大文字になってしまいました。 これは、

abが指している文字列オブジェクトは同じものであるという事

という事実を裏付けているものに他なりません。 そして、これは「格納」という表現が期待するものとは違うように思えてきます。 とすると

b = a

というコードが持つ意味は

  • aにbの内容をコピーして格納する

のではなく、

  • bはaと同じオブジェクトIDを持つようにする

という事に他ならないのです。 もっと、スッキリする言い方にするのであれば、

a = "hoge"

というのは、 - aという変数は、生成された文字列オブジェクト "hoge" を参照する

となり、

b = a

というのは - bという変数は、aの参照しているオブジェクトを参照する(=aと同じオブジェクトを参照する)

という言い方が出来る事になります。 つまり、「=」が持つ本当の働きは、右辺のオブジェクトの参照先を、左辺に”束縛”する という事なのです。 「=」は、変数にオブジェクトを格納するものではなく、ただ束縛する、結びつけるという働きを持っているのです。 これがRubyにおける「=」の本当の意味なのです。

インクリメント演算子が無いわけ

「=」の紐解きが完了しました。 今こそ、motivationで生まれた疑問を解消するときです。 まず、自己代入 += は何故許されているのか。 これはとても単純な問題です。

value += 1

これは、次のコードのシンタックスシュガーでした。

value = value + 1

このコードが意味することは、value が参照している数字オブジェクトに対して、「1を足した数字オブジェクト」を新たに生成し、それを新たに左辺の value に束縛している。 という事になり、これはRubyの性質から見ても筋が通っています。

value = 1
puts value.object_id
value += 1
puts 2.object_id
puts value.object_id

このコードは、私の環境では以下のようになります。

3
5
5

1という数字オブジェクトを保持していた valuevalue+1 によって新たに生成された2という数字オブジェクトを value = によって新たに参照するようになった。 この事を考えると、 2.object_id と2回目の value.object_id が同じ値を出力するのは、当然のことと言えます。

さぁ、かたや ++ はどうでしょう。 この ++ は "変数の値を1加算する" という意味でした。 では、次のようなコードを考えてみます

a = 1
b = a
a++

もしこれが許されればどうでしょうか? まず、aに数字オブジェクト1が束縛されます。そして、 b も、 a と同じオブジェクトを参照します。 その後、++ によって、 a が参照している数値に対して1を加算しようとします。この操作は破壊的です。 そうすると、どういった事が起きるでしょうか。

a = 1
b = a
a++
puts a
puts b

このputsは両方とも2を返してしまうわけです。もっと言うなれば、 1.object_id を束縛していたものは、全て「2」になってしまうわけです。1が2になる世界。意味が分からなくなってきました。

数値はプログラミングにとって欠かせません。このような意味が分からないことが起きないように、Rubyでは数値オブジェクトには、Immutableという「破壊的な変更」を出来ないようなルールが課せられています。

そして、このルールを破らないように、Rubyには ++ が無いというわけなのです。 同じ様に、"変数の値を1減算する" デクリメント演算子もありません。 これで疑問が解消できました。

outro

長い旅路でした。 私達は、Rubyにおける「=」の持つ意味、そしてその本質であるところのRubyでは全てがオブジェクトである、というものの片鱗を見ました。 これで明日から「=」を書く時も、実際に行われている事を思い浮かべながらコーディングが出来そうです。

いかがだったでしょうか? 生まれた些細な疑問は、時として実に深くプログラミング言語の性質を追求することになる訳ですから、プログラミングは面白いものです。 日常的に生まれた疑問は、是非とも疑問のまま終わらせず、深く追求していきたいですね。

Baseconnectでは、Rubyの「=」のようにデータを結ぶ(Musubu)事に興味があるエンジニアを募集しています。

herp.careers

気持ちをちょっと楽にする工数見積もりの技術

はじめに

こんにちは、Baseconnectでエンジニアをしている米丸です。みなさん、工数見積もりって難しくないですか?しんどくないですか?嫌いじゃないですか?

私も以前「見積もり、やだなーこわいなー」と強く思っていた時期があったのですが、工数見積もりが持つ特徴について学ぶことで、ちょっと気持ちが楽になった経験があります。同じように苦手意識を持っている人向けに、「これを知っているだけでも見積もりがしやすくなる」という、Tips的な話をしたいと思います。

なお、この内容はBaseconnect社内で行っているエンジニア共有会(通称:Developer Closer)でのLTをもとに作成したブログになります。社内エンジニアから役に立ったよーという声をいただけたので、ブログとしてまとめてみました。

工数見積もりはなぜしんどいか?

本題に入る前に、工数見積もりはなぜしんどいかを考えてみます。工数見積もりがむずかしい理由はいくつかありますが、エンジニアが工数見積もりをしんどいと感じる理由としては、「早くしなきゃと遅れちゃいけないの心理的ストレッチ」があると考えます。

早くしなきゃと遅れちゃいけないの心理的ストレッチ

一般論としてですが、ソフトウェア開発において機能のリリースは早ければ早いほど価値につながるため、エンジニアは管理者から「できるだけ早く開発してね!」とプレッシャーがかけられます。一方で、期限に遅れてしまうとユーザーや関係部署に迷惑がかかってしまう場合があるため「絶対に遅れないでね!」とも釘をさされます。

この「早くしてね」「遅れないでね」というメッセージングがあるため、工数を短めに見積もりたくなる気持ちと、長めに見積もりたくなる気持ちという相反する想いが同時に生じ、心理的ストレッチがかかってしまいます。

くわえて、工数見積もりは非常に難しく、実装経験さえあればできるというものではありません。心理的ストレッチがある中で見積もりを大きく外してしまい、トラウマになっているエンジニアは少なく無いのではないでしょうか?

個人的には、工数見積もりは技術を要するものだという認識をもっと広めたいです。

気持ちをちょっと楽にする工数見積もりの技術

ここから本題です。工数見積もりに苦手意識を感じていた私が、知るだけでもだいぶやりやすくなった見積もりの技術を紹介します。

1.ぶれ幅の特徴を掴む

工数と確率のベータ分布

工数見積りにはぶれ幅があります。

ソフトウェアの開発期間のばらつきを、縦軸に確率、横軸に時間軸を置いたグラフに表すと、このようなベータ分布になるといわれています。うまくいけば左端の短い期間(楽観値)で開発が終わるが、問題が起きれば右端の期間(悲観値)まで開発は長引いてしまう、という開発期間のぶれ幅を表しています。山になっている最頻値は、もっとも着地する確率の高い期間です。

まずは、見積もりには幅があるものなので、神様でも点での見積もりを正確に出すことはできないのだと、安心してください笑。また、自分が出した工数見積もりが、楽観値、最頻値、悲観値のどれにあたるのか意識するだけでも精度は上がると思いますし、受け取り手にも、それが伝わるようにしてみてください。

工数と確率のベータ分布 - 50%ライン

さらに、このグラフが面白いのは、左右対象ではなく後ろに尾をひく形になっていることです。非対称の理由としては、開発期間の短縮には限界があるが、開発を遅らせる問題には制限がないためと言われています。

グラフに赤線でひいていますが、50%の確率でここまでには開発が終わるという、50%ラインというものが存在します。左右非対称のため、この50%となるラインは、最頻値よりも少し右側にあります。そのため、一見妥当そうに見える最頻値の見積もりを基準にスケジュールを引いてしまうと、確率としては 2回に1回以上、スケジュール遅れになってしまうことになります。必ずしも最頻値の見積もりを出せばよいというわけではないのです。

もう少し言うと、大きなプロジェクトでは、50%ラインをベースにスケジュールを引きつつ、「各タスクの50%ラインと悲観値の差の標準偏差」をバッファとして持つ、といった方法もあったりします。ここでは詳細は言及しませんが、楽観値、最頻値、悲観値から50%ラインを割り出す計算式もあったりします。

2.場合分けする

いつもきれいなベータ分布になるとは限らない

先程のベータ分布のグラフはあくまでモデルであり、実際にはいつもきれいなベータ分布となるとは限りません。 例えば、ライブラリ更新だけで終わると思っていたら、思ったように動かずスクラッチでつくることになった・・・という悲しい経験が個人的にはあるのですが、そのような事例では、うまくいく場合といかない場合とで、開発工数の差は大きく乖離ができ、確率と工数のグラフもフタコブラクダのようになります。このような見積もりを大きく左右する要素が最初から分かっている場合は、それがうまくいく場合とそうでない場合とで、場合分けをして見積もりを立てるのが一つの手だとおもいます。

3.相対的に見積もる

「相対的に見積もる」とは、とあるタスクを基準として2倍/3倍/4倍といった比較値をだしたり、アバウトにS/M/L/XLといったTシャツのサイズでタスクの大きさを表す手法を指します。このとき比較するのは、過去に実施したタスクとの比較、あるいはこれから実装するタスク同士で比較をします。

個人的には、絶対時間の見積もりが必要な場合も、まず相対的な見積りをだして考えるようにしています。なぜ相対的な見積もりを出すかと言うと、まず、絶対時間の見積もりよりも相対的な見積りの方が人間は出しやすいからです。実際に、人は10倍以内のものならうまく見積もれる、という研究結果もあるそうです。あとは、絶対的な見積もりは、実装者のレベルなど諸条件によって変わるところがありますが、タスク間の相対見積もりは比較的影響をうけないので、管理がしやすいことがあげられます。

フィボナッチ数列での見積もり

アジャイルの文脈では、相対見積もりであるストーリーポイントとして、「1, 2, 3, 5, 8, 13, …」というフィボナッチ数列が使われることがあります。ソフトウェア開発は規模が大きくなるにつれて不確実性が増していくため、数が大きくなるにつれて間隔が大きくなっていくフィボナッチ数列は見積もりを表すのに相性がよいと言われています。

個人的な理解では、「うーん、このタスクの見積もりは、10、11、いや11.5かな…」などと細かい数字の違いに気をとられることなく、大きく外さないレベルまでの見積もりを意識せず出せることが、フィボナッチ数列を使うメリットだと理解して、アジャイルな開発ではなくても使える技術だと思っています。

4.理想時間と現実時間を区別する

理想時間と現実時間とは?

  • 理想時間

    • 何者にも邪魔されない、精神と時の部屋のような理想の環境で作業した場合の、開発にかけられる時間
  • 現実時間

    • 予定されていない差し込みのタスクや、切り替えのオーバーヘッドの影響を受けた、実際に開発にかけられる時間

例えば、とあるタスクを3日程度でできそうだと見積もった時に、その3日は8時間フルでタスクにあてた場合の見積もりでしょうか?カレンダーに入っている予定は考慮して差し引いているかもしれませんが、差し込みで入る小タスクや、slackで送られてくる連絡や質問へのリアクション、カフェスペースでの同僚との雑談などにかかる時間は考慮されていないことが多いのかなと思います。特にマルチタスクをしているような場合は、タスクを切り替えるときのスイッチングコストもそれなりに掛かっています。

理想時間と現実時間の活かし方

見積もりを出す時に、理想時間と現実時間を明確に分けずに考えると、失敗しがちです。理想時間での換算なのか、現実時間を考慮したものかを区別する事がまずは大事です。

ただ、現実時間をとらえることは難しいです。計測することも一つの手ですが、細かいタスクの積み重ねやスイッチングコストがどれくらいかかっているかは計測しにくいところです。一つの手として、極力理想時間と現実時間の差分が無くなるように予定を組むのは有効です。ミーティングをまとめたり、複数のタスクを持たないようにしたり、差し込みをシャットアウトする集中時間を設けたり、などです。

5.複数人で見積もる

プランニングポーカーというチームで見積もる技法があります。

  • プランニングポーカーの手順
    • 開発に携わる全員で集まる
    • それぞれで見積もりを考え、数字が書かれたカードを伏せた状態で自分の前に出し、せーので公開する
    • 自分が立てた見積もりの根拠を議論する。ギャップがあればすり合わせる

この方法の良いところは、一人よりも数人で考えた方が正確性が増すことと、開発内容について認識ずれがあった時に見積もり段階ですり合わせができることにあります。

そうはいってもチームで毎回集まるのは難しい場合があります。プランニングポーカーまでは行かなくとも、コードレビューのように見積もりをレビューしてもらう方法は、効果もあり取り入れやすいと思います。

実際にプランニングポーカーをおこなった経験では、人によって出てくる見積もりはおもしろいほど異なり、正解は無いものだと実感することができました。

むすび

工数見積もりと仲良くなるための技術をご紹介しました。どうでしょうか。ちょっとでも工数見積もりへの苦手意識がなくなったら幸いです。

本来、工数見積もりとは不確実な未来に対して道筋を建てるための有効な武器のはずですが、出す側も受け取る側もコミットメント(期限の約束)と捉えてしまうことで、エンジニアを苦しめる足かせになっていることがあります。

正確な見積もりを出すことは不可能だと認めた上で、見積もりの特性を理解し精度を上げ、足かせではなくプランを建てる武器としてうまく使って行きたいものです。

Baseconnectでは不確実性と戦うエンジニアリングマネージャーを絶賛募集しています。興味を持たれた方は、是非カジュアル面談で一度お話しましょう。お気軽にご応募ください。

herp.careers

SCRUM FEST Osaka 2022 に参加しました!イベントレポート2

メガネひげ面、のび太型サブマネージャーの、富田と山本が「SCRUM FEST Osaka 2022」に行ってきたよ!

ほう、なんとこなれたオン・オフハイブリッド型カンファレンス! ちょっとまて、これって会場に来る必要あったのか?(伏線)

印象に残ったセッション(山本版)

フルリモート下でのチームビルディング

推進するフルリモートの中で、カルチャーを大事にしながら、ポテンシャルが発揮できる場をどう醸成するのだろうか...最近はずっとそんなこと考えています。

参考 → Scrum Fest Osaka 2022 フルリモート下でのチームビルディング

チームメンバーを巻き込んでロードマップを作成・アップデートする

メンバー全員が現在地を認識し、目的地を共有しながらロードマップを調整する。そのことでオーナーシップが生まれそうだ。

組織作りと個人の成長の両方に役立つワークシップ

相互理解を深めるワークショップ、たとえば「バリューカード」や「スキルマップ」。

実は弊社でも「※オフサイトMTG記事」のアイスブレイクでバリューカードをプレイしてみました。 むちゃくちゃ盛り上がったし、メンバーの意外な一面を発見することができました。

うむ

チームという土台の上に「成果」を積み上げていく... チームビルディングとは、チームで最大の成果を出すための土台作りである

私たちの土台とはなんだろうか?

モブとソロを織り交ぜてハイアウトプットなチーム開発

ソロワーク、モブワーク...組み合わせて、良いとこ取りをしてアウトプット増大! 言うには容易いが、その運用の知恵には「ほうほう、なるほど」

参考 → モブとソロを織り交ぜてハイアウトプットなチーム開発

ソロワークとモブワーク

担当したタスクに単独で集中して成果を出していく「ソロワーク」しかやったことがない私は、モブプロやモブワークに実体験がなく、頭では目的やメリットを理解しつつも、うまくやる知恵が足らない。(やってみればいいんだけどね)

ソロワークとボブワークそれぞれに、向き不向きがあるって事。 そこで両方を織り交ぜたハイブリッドな運用で、フロー効率とリソース効率のバランスを取って行こうぜ、というアイデアに注目したい。

うむ

モブかソロ、どちらで取り組むかを最初に宣言することが大事(ソコがScrumらしさかと感じた)。 そして、モブで取り組んでいることが単純作業などのソロ向きに移行した時に、ソロに移行したことを宣言し、残りのリソースは別のタスクに着手する。ソロが完了したとき、モブに合流するという流れもある。

モブワークとソロワークは、柔軟に使い分ければよい でも、いま取り組んでいることがどっちなのかを共有しないと、デメリットが出てきてしまうだろう

面白いなぁ

実践!勝手に育つチームの作り方

自律したチームとなるには、どこまで権限があるのかを明確にしておく必要がある。 実際に「デリゲーションポーカー」を体験して、認識を合わせ、未来像まで描くことが出来た面白い体験

参考スライド

チームを強くする

必要なスキルに対して、チームメンバーの現状のスキルレベルを明らかにする。そこから、成長すべき方向や採用計画を明らかにする。

デリゲーションポーカー

権限を委譲するレベルを段階に分けて。マネージャーの期待値とメンバーの希望を話し合い、両社で決定する。 そして、将来はどういう状態が望ましいかを共有し、方向性を共有する。

うむ

意思決定のあり方について「責任と権限の委譲」の課題は、なかなか進捗しない、あるあるだ。

「デリゲーションポーカー」ってのを初めて知ったが、ワークショップで良い体験ができたことは、むっちゃ楽しかった。

権限移譲は Baseconnect でもよく話題になる課題。 それを明快に表現できる手法なので、ぜひやってみたい。

本編は懇親会にあり

会場に足を運んでみると、セッションの内容はオンライン... 確かに熱量やムードを感じ、パワーを受け取ることができるのだけど...

と、おもいきや、オマケと思っていた懇親会では、「濃い面々」の興味深い話が... 「スクラム、良さそうだからやってみたくて」なんて漏らそうもんなら、嵐の禅問答が!

勉強不足でしたぁ

Scrum とは何なのだ?

面白いことに、スクラムを知る人ほど、この問いで遠目になる。 それはどういう事象なんだろうと思案していると、散歩中にふとシナプスが繋がった。

私はデザインを学んだが、「デザインとは何ですか?」という問いに似ているんだろう。しかし未だに、デザインの本質を射抜く言葉は持ち合わせていない。そして、デザインを知れば、良いプロダクトが生み出せるというわけでもない。あえて、デザインは何かと言えば、それを探し続けるマインドそのものなのかもしれない。

チームメンバーの成長に期待するから、ScrumというKATAを学ぶのだ

Scrumは、近代的な開発手法、あるいはフレームワークだと思っていたが、実は、チーム自身が成長するための鍛錬でありKATA(型)なのである。

SCRUM FEST Osaka 2022に参加しました!イベントレポート1

6/17(金)、6/18(土)に開催された「SCRUM FEST Osaka 2022」に、サブマネージャーの山本と一緒に参加しました。

今回のscrumfesはオンラインがメイン、現地会場からの参加も可能なハイブリッド開催でした。

6/17(金)

この日は、16:30からオープニングトーク、17:00から角 征典さんの基調講演でした 私達は、 業務後に会社から鑑賞しました。

基調講演「クリーンスクラム―基本に立ち戻れ―」

感想

DS(どうかしている)というパワーワードは、しばらくアジャイル界隈で語られそう…

尖った人もいるチームはそれだけイノベーションが起こりやすく、そんなチームを取りまとめるのがスクラムマスターの役割の一つ。多様性大事。

スクラムの要素を新しい角度から一つ一つ分析していく内容は圧巻でした。

F1大阪グランプリ22

感想

基調講演と各コミュニティからの紹介セッションの後は、「F1大阪グランプリ」が開催されました。

「F」はフィードバック。とのことです。

各地で活躍しているアジャイルコーチの方々が、お題に対してフィードバックしていく「大喜利」的な内容です。

ワイワイしながらな感じですが、各マシン(参加者の方々)の走り(フィードバックの内容)は、エッジの効いたものばかりでした。

6/18(土)

2日目は、大阪本町の現地会場に移動して参加しました。

日本全国のアジャイルコミュニティがトラックを持ち、同時に幾つものセッションがオンラインで進行します。 タイムテーブル

大阪の会場では、各コミュニティ(トラック分)のipadが設置され、ipadにイヤホンを刺すことで、そのトラックで行われているセッションに参加出来る形です。 なかなか斬新…

どのセッションに参加するか悩んだのですが、下記のセッションに参加しました

組織の崩壊と再生、その中で何を考え、感じたのか。そして本当に必要だったもの

感想

現在、どんどん組織が拡大中の弊社ですが、いつか「うまくいかなくなる時」がやってくると思っています。

Rettyさんは、今でこそLeSSや自己組織化の浸透が進んでいるとのことですが、数年前まではツラい時期があったとのことです。

「崩壊しない組織より、適度なスクラップ&ビルドを乗り越えることが出来る組織の方が良い」という言葉は印象に残りました。

Managing for Happinessまもなく出版!プラクティス欲張り全部盛りジェットコースターワーク

感想

カンファレンスといえばワークショップ!ということで参加してみました。 90分でManagemnet3.0のワークをガンガンやっていくセッションです。

先日、オフサイトミーティングを開催し、メンバーがお互いに理解しあうアクティビティを行いましたが、このワークショップでは色々な角度からチームを掘り下げる方法を学ぶことが出来ました!

機会があれば、メンバーとManagement3.0のワークショップに参加して、弊社にも取り入れることが出来れば…!と思いました。

プロダクトってなに? マネジメントってなんなの? ゼロからプロダクトマネジメントを明らかにするぞ

感想

マネジメントとは?プロダクトとは?という根源的な問を、分析・明らかにするセッションでした。

スタートアップはプロダクトがすべてのところがあるので、再度、動画をじっくり見させて頂こうかと思います。

各登壇のスライドはスクラムマスダーさんがブログにまとめられているので、ご参考下さい!

他にも聞いてみたいセッションが山のようにあるので、動画が公開されるのが楽しみです。 しばらくはscrumfes漬けの日々になりそうです…

CSSフレームワーク「xstyled」について

こんにちは、Baseconnectのエンジニアインターンの大島です。 この記事では私が主に開発を行っているMusubuのフロントエンドの技術スタック、特にCSSフレームワークに注目して紹介したいと思います。

Musubuのスタイリング

Musubuの開発ではCSSフレームワークに、styled-componentsxstyledを用いて開発を行っています。

今回は聞き馴染みがない方が多いであろうxstyledについて注目し、Baseconnectでの使われ方も合わせて紹介したいと思います。

xstyledとは

xstyledはReact開発におけるpropsベースのCSS in JSフレームワークです。 styled-componentsやemotionに互換性があり、すでにこれらのCSS in JSを用いて開発を行っているプロダクトには簡単に導入ができます。

コンポーネント

import { x } from '@xstyled/styled-components'

type ButtonProps = typeof x.button.defaultProps & {
  leftIcon?: React.ReactNode
}

const Button = ({children, disabled, leftIcon, ...props}: ButtonProps) => {
  return (
    <x.button
      w="60px"
      fontWeight="bold"
      backgroundColor={disabled ? 'blue-500' : 'blue-400'}
      {...props}
    >
      {leftIcon && <IconWrapper>{lefIcon}</IconWrapper>}
      {children}
    </x.button>
  )
}

xstyledを使うことによるメリット

  1. utility first
  2. 命名コストがない
  3. presentational componentなのか、styled-componentなのか迷わない
  4. レスポンシブ対応や擬似クラスも容易に対応可能
  5. 開発速度の向上

などが挙げられます。一つずつ見ていきます。

1. utility first

xstyledでは、各タグに<x.*>をつけることでpropsでスタイルを当てることができます。そのため、事前にスタイルを当てるためのCSSを宣言する必要はありません。

また、カスタムCSSを一行も記述することなく、完全にカスタム化されたコンポーネントデザインを実装することができます。 たとえば、spaceXpropsであれば、通常以下のCSSを記述する必要がありますが、xstyledなら、1行で済みます。

--x-space-x-reverse: 0;
margin-right: calc({space} * var(--x-space-x-reverse));
margin-left: calc({space} * calc(1 - var(--x-space-x-reverse)));

2. 命名コストがない

これは私としてはかなり大きいメリットだと感じています。 styled-componentでは、それぞれstyleに対して命名する必要があるかと思います。これは開発者にとって脳に無駄なリソースを割くことになるため精神安定上良くないと思っています。 xstyledでは先の例で示したとおり、propsに渡すことでスタイルを当てることができるため命名の必要がありません。

これが解決できるだけでも十分なメリットがあると感じています。

3. presentational componentなのか、styled-componentなのか迷わない

styled-componentを使って開発を行っていると、これがstyled-componentなのか、presentationalなコンポーネントなのかがわからないということが頻繁に起こります。そのたびに、そのコンポーネントを参照してどちらなのかを確認することが必要になります。

命名規則をもたせることで解決できることもあるかもしれないですが、規則があったとしても命名コストはつきまとってきます。

xstyledならどのタグにどのスタイルがあたっているか一目瞭然となり見通しも良くなります。

4. レスポンシブ対応や擬似クラスも容易に対応可能

Musubuでは詳細な企業情報を扱ったり、営業リストを作成する機能を備えているため、モバイル端末をメインで扱っていません。 しかし、あらゆるユーザーを想定し、最低限のレスポンシブ対応を行っています。その際にxstyledは非常に容易に定義できます。

styled-componentの場合、メディアクエリを用いて以下のように定義する必要があるかと思います。

const Wrapper = styled.div`
  display: "block";
  background-color: "#fff";
    
  &:hover {
    background-color: "#000";
  }
    
  @media (min-width: 768px) {
    display: "flex";
  }
`
const ExampleComponent = () => {
  return (
    <Wrapper>
      <div>...</div>
      <div>...</div>
    </Wrapper>
  )
}

xstyledを用いることで簡潔に記述することができます。

// xstyledの場合
const ExampleComponent = () => {
  return (
    <x.div
      display={{
        _: "block",
        md: "flex"
      }}
      bg={{
        _: "#fff",
        hover: "#000"
      }}
    >
      <x.div>...</x.div>
      <x.div>...</x.div>
    </x.div>
  )
}

5. 開発速度の向上

xstyledはTypeScriptで開発されているため、型の恩恵を受けられます。そのため、styled-componentでよくあるtypoによってstyleが付与されないという問題を解決することができます。

また、styled-componentの場合、条件によって付与するスタイルが異なる場合、かなり冗長なコードになると思います。しかし、xstyledはpropsベースのため、条件分岐を簡単に書くことができます。

さらに、特に個人で開発を行う際など、colorやspaceなど全て自分で決めるのは少々面倒なこともあります。xstyledでは、v2以降TailwindCSSに影響を受けているため、デフォルトでいい感じのThemeファイルを用意してくれています。

xstyledのデメリット

ここまでメリットについて述べてきましたが、デメリットについてもいくつか上げたいと思います。

  1. コミュニティが他CSSライブラリと比べ活発ではない
  2. ドキュメントが少ない(Qiita, Zennでの検索ヒット数は合わせて1件)
  3. 複雑な疑似要素などを扱えない

これらの理由により、ある程度の規模のプロダクトでxstyled単体での利用は難しいと感じています。 そのため、styled-componentやemotionと併用して用いることで快適なフロントエンド開発を行えると思います。

Musubuでの活用事例

ここからは、Musubuでのxstyledの活用事例について紹介したいと思います。

theme file

xstyledではデフォルトでいい感じのthemeファイルを用意してくれています。 Musubuではデフォルトのthemeファイルを使わず、オーバーライドすることでデザインシステムを構築しています。

styled-component と xstyledの使い分け

Musubuではxstyledをベースに開発を行っています。しかし、先に述べたとおり、疑似要素を扱えないためbeforenot(:first-child)などを使いたいときにstyled-componentを使っています。 さらに、styled-componentを作成する際に@xstyled/styled-componentからimportすることで、xstyledの恩恵を受けながらstyled-componentで複雑な疑似要素を扱えるようになります。

import styled, { x } from '@xstyled/styled-component'

const Wrapper = styled(x.div)`
  &:before {
    ...
  }
`

const ExampleComponent = () => {
  return (
    <Wrapper w="1200px" bg="gray-100">
      ...
    </Wrapper>
  )
}

これにより最大限見通しを良くした状態でstyleを当てることが可能になります。

最後に

ここまで読んでいただきありがとうございます。Musubuのフロントエンド開発で用いられるCSSフレームワークについて書いてきました。

styled-componentemotionに辛みを感じている方にとって、何か役に立てましたら嬉しいです。

もし今後の技術選定に関わっていきたい方や、もう少し詳しい話を聞いてみたいという方、ぜひカジュアルにお話ししましょう! Baseconnectではエンジニアメンバーも絶賛募集しています。

herp.careers

エンジニアでオフサイトミーティングを開催しました

プロダクト開発チームの富田です!

先日、オフサイトミーティングを開催したので、今回はそのことについて書こうと思います。

なぜ開催したの?

現在、Baseconnect全社で「戦略的内省期間」として、全部門で取り組みを行っています。

「戦略的内省」とは、下記のようなことと位置付けていますが、具体的にどんなことをするのかは各部門に一任されています。

・バリュー
・顧客価値
・顧客との対話
・メンバー同士の関係構築
・プロダクト価値に向き合う
・存在意義を問い直して深めること
・戦略を深く理解すること

等の長期的な成長に欠かせないこと(重要だが緊急性がないこと)に取り組む期間を作りたい。

開発では「メンバー間の相互理解を深める」ということをテーマにしました。

このテーマとなったのは「メンバーの人数が増えてきたことで、チームがバラバラに動いているように思える」と問題感が出たからでした。

どんな人が参加したの?

合計22名が参加しました。

どこで開催したの?

京都の岡崎庵で開催しました。

普段は結婚式場らしいですが、会議室としても利用することができます。

近くに平安神宮があったり、高級そうな料理屋が立ち並んでいたり、オフィスと同じ京都にありますが、イイ感じで気分転換出来る場所だったと思います。

関東に住んでいるメンバーも、この日は京都に来ていただきました。 久しぶりにリアルに会話することが出来て、それだけで盛り上がっていました!

何をしたの?

ウェルカム ワークショップ

最初に今回のオフサイトミーティングの目的とタイムテーブルの説明が終わった後、DPAのワークショップを行いました。

DPA(Design the Partnership Alliance)

4つのチームに分かれて座ってもらい、それぞれのチームで下記について話してもらいました。

  • どんな雰囲気でオフサイトMTGを進めたいですか?
  • その雰囲気を作り出すために何をしますか?

その後、各チームの代表者がどんなことを話したのかを発表してもらいました。

ファシリテータが意見をまとめ

  • 和やかに
  • 楽しく
  • リラックスして

という雰囲気で進めるということで、全員に同意してもらいました。


Value Card

その後、各チームでValue Cardを行いました。

これは、トランプのようにカードを使い、各メンバーがどんな価値感を大切にしているか明らかするアクティビティです。 ゲームに近い感覚で楽しむことが出来ます。

カードを捨てる毎に「それ大切にしないの?」「○○さんが捨てたその価値観、自分は重要だと思います!」というように、メンバーが何を大切にしているのか、逆に何を割り切っているのかコミュニケーションすることが出来ました。

ちなみに、私の場合は最終的にこんな価値観が残りました。

いつもの雰囲気そのままの結果の人もいるし、意外な結果な人もいるし、しばらく自己紹介の時にこの結果をネタに出来そうです。

ここまでで、午前中は終わりです。


インセプションデッキ(簡易版)

Value Card」で各個人感の相互理解が深まった(はず)ので、午後からは業務面に目を向けてみるアクティビティを行いました。

インセプションデッキは本来、プロジェクト毎の目線合わせや共通認識を作る手法かと思いますが、今回は、少し違った方向で行ってみました。

  • 作成するデッキは下記の4つです。(各内容は上記のリンクをご参考下さい)
・我々はなぜここにいるのか
・トレードオフスライダー
・夜も眠れなくなるような問題は何だろう
・やらないことリスト
  • 各受け持ちのプロジェクトではなく、Baseconnectのエンジニアとしてどうなのかという視点で考える

  • デッキを完成させるより、会話を楽しむことに注力する

また、デッキの内容毎にランダムに3つのチームに分かれ、下記のように進めました。

  • 質問についてチーム内で話し合う
  • 各チームから発表する
  • 全体でディスカッションする

1デッキ40〜50分、全体で4時間程となりました。

それぞれのテーマで白熱したディスカッションが行われ、最後のテーマでは全員ヘロヘロになっていました…

黙々と…

全体でディスカッション

印象的だった「愛」の付箋紙

ふりかえりと総括

インセプションデッキが終わった後、簡単に今日一日のふりかえりをしました

その後、部門責任者が締めの挨拶を行い、閉会となりました。


やってみてどうだったの?

直接的に何か業務に役立つことは難しいと思いますが、例えば

  • 新しいプロジェクトチームが編成された時、ValueCardをネタにコミュニケーションしてみる
  • SREやBI等、各チームでインセプションデッキを完成させてみる。また完成したデッキと、今回作ったデッキを比べてみる

等に利用出来るかもしれません。

いずれにしろ、自分自身や自分達について楽しくディスカッション出来たという「場の共有感」は、リモートメインの状況に於いては貴重だったかと思います。

一瞬マスクを外して記念写真

Baseconnectではエンジニアメンバーを絶賛募集しております。 もしご興味をお持ちいただけた方がいらっしゃいましたら、下記のリンクよりぜひカジュアルにお話しさせていただけましたら嬉しく思います!

herp.careers

Protocol Buffers でメッセージスキーマを管理する

こんにちは!エンジニアの大槻です。

Baseconnect ではたくさんのデータを保持していますが、単一のデータベースにそれらのデータを保存し、連携する各サービスからそのデータベースにアクセスする構成には、パフォーマンスその他多くの観点から問題があります。

それらの問題を解決するため、Baseconnect では複数のサービス間でデータを同期する方針をとっています。

データの同期には Amazon SQS や Amazon KinesisApache Kafka などのメッセージングシステムを用いますが、ここで問題になるのが、メッセージングシステムを介して送受信されるデータ(メッセージ)の形式をどう管理するかという点です。

候補としては Apache Avro や JSON Schema を挙げていましたが、エルデンリングのロード中にふと「Protocol Buffers が適しているのでは」と思いついたので、息抜きに試してみました。

実装の流れ

1. スキーマを定義する

まずは Protocol Buffers を用いてメッセージスキーマを定義します。

events/example.proto

syntax = "proto3";

package events;

option go_package = "github.com/ORGANIZATION/REPOSITORY/events";

message ExampleMessage {
    string id = 1;
    int64 timestamp = 2;
    string sentence = 3;
}

2. コードを生成する

先ほど定義したメッセージスキーマGolang その他のコードから扱うため、メッセージスキーマからコードを生成します。

$ protoc \
    -I ./events \
    --go_out ./events \
    --go_opt module=github.com/ORGANIZATION/REPOSITORY/events \
    ./events/*.proto

3. Kinesis Data Streams プロデューサーを作成する

先ほど Protocol Buffers で定義したスキーマからメッセージを作成し、メッセージングシステムにメッセージを送信します。

今回はメッセージングシステムに Kinesis Data Streams を用いますが、Amazon SQS など他のメッセージングシステムでも基本的な流れは変わりません。

cmd/producer/main.go

var client *kinesis.client

// ... 省略 ...

m := &events.ExampleMesssage{
    Id:        uuid.NewString(),
    Timestamp: time.Now().Unix(),
    Sentence:  "The quick brown fox jumps over the lazy dog.",
}

d, _ := proto.Marshal(m)

r := &kinesis.PutRecordInput{
    Data:         d,
    PartitionKey: aws.String(m.Id),
    StreamName:   aws.String("STREAM_NAME"),
}

res, _ := client.PutRecord(context.TODO(), r)

// ... 省略 ...

4. Kinesis Data Streams コンシューマーを作成する

最後にメッセージングシステムからメッセージを受信したら一連の流れは完了です。

cmd/consumer/main.go

var client *kinesis.client

// ... 省略 ...

res, _ := client.GetRecords(ctx, &kinesis.GetRecordsInput{
    ShardIterator: i,
})

for _, r := range res.Records {
    m := &events.ExampleMessage{}

    proto.Unmarshal(r.Data, m)
    
    fmt.Printf("%+v", m)
}

まとめ

Protocol Buffers でスキーマを定義し、Kinesis Data Streams 経由でメッセージを送受信してみました。

今回はメッセージングシステムに Kinesis Data Streams、プログラミング言語Golang を選択しましたが、他のメッセージングシステムやプログラミング言語でもほぼ同じ流れで実装できるかと思います。

Protocol Buffers は比較的馴染みがある、複数のプログラミング言語をサポートしている、後方互換性の担保をサポートしてくれるなど、メッセージングシステムと併用するうえで非常に便利な仕組みだと感じました。

今後はもっと実践的な内容で検証していきたいと思います。

Baseconnectではエンジニアメンバーを絶賛募集中です。 もしご興味をお持ちいただけた方がいらっしゃいましたら、下記のリンクよりぜひご覧いただけますと嬉しく思います!

herp.careers