閉包テーブル(closure table)でツリー構造を表現する

この記事は個人ブログと同じ内容です

www.ritolab.com


www.oreilly.co.jp

SQL アンチパターン」という書籍を読んでいて、ナイーブツリー(素朴な木)という、ツリー構造(再帰的な階層構造)の表現について書かれた章があり面白かったので試してみました。

隣接リスト(adjacency list)

よくある(んだけどツラいよねっていう)パターンとして「隣接リスト」というものが紹介されていました。これは、コメントのテーブルに親を識別するための parent_id をカラムに追加するというものです。

DDL

create table comments
(
    comment_id   bigint unsigned auto_increment primary key,
    parent_id    bigint unsigned null,
    bug_id       bigint unsigned not null,
    author       bigint unsigned not null,
    comment_date datetime        not null,
    comment      text            not null,
    constraint comments_author_foreign
        foreign key (author) references accounts (account_id),
    constraint comments_bug_id_foreign
        foreign key (bug_id) references bugs (bug_id),
    constraint comments_parent_id_foreign
        foreign key (parent_id) references comments (comment_id)
)

f:id:ro9rito:20220228143752p:plain

たしかにお手軽な方法ですが SELECT がつらい。

自身のコメントと、子を 1 件だけ使いたいのであれば join しても取り回しはできます。

-- コメントと直近の子を取得
SELECT
    c1.*,
    c2.*
FROM comments  c1
LEFT JOIN comments c2 ON c1.parent_id=c2.comment_id;

ですが、階層構造が無制限であることを考えると join が増えるだけでなく、その結合数を制御することは困難です。

-- この流れで直近の子から更に子を取ろうとすると JOIN が増えていく
SELECT 
    c1.*,
    c2.*,
    c3.*
FROM comments  c1
LEFT JOIN comments c2 ON c1.parent_id=c2.comment_id
LEFT JOIN comments c3 ON c2.parent_id=c3.comment_id;

併せて結合は列が増えるので取り回しの効率も悪く、結局該当コメントを全件、行として取ってくるという選択に落ち着くことになりそうです。(そして comments テーブルに parent_id がある意味とは..という気持ちになる)

-- 該当バグのコメントを全て取得
SELECT
    *
FROM comments
WHERE bug_id = 1;

ちなみに、INSERT は簡単。そして子孫関係の更新も簡単。parent_id を更新すれば良いので、コメントの付け替えは容易です。

ただ、書籍にも書いてありましたが、私は隣接リストの最大に辛い点は削除だなと思いました。

外部キー制約を設定しているので、好き勝手にコメントを削除することができません。少なくとも削除したいコメントの子孫として紐付いているコメントは全て削除する必要があり、しかも制約のおかげで最下層から順に削除していかなければなりません。

もし指定のコメントだけを削除したい場合は、削除したいコメントの子の parent_id を別のコメントに紐付けてからでないと指定のコメントのみを削除できません。(指定のコメントだけを削除して、その子らを昇格させたい場合は親の comment_id に付け替えれば良いので問題は無い。)

色々とツラいのは体験できました。

閉包テーブル(closure table)

書籍では隣接リストに変わるパターンの提案として、3 つのパターンが示されていました。 その中で個人的に最もしっくりきたのは「閉包テーブル」です。

閉包テーブルは、comments テーブルには子孫関係の情報を持たせず、子孫関係の情報は別のテーブルに持たせます。

DDL

create table comments
(
    comment_id   bigint unsigned auto_increment
        primary key,
    bug_id       bigint unsigned not null,
    author       bigint unsigned not null,
    comment_date datetime        not null,
    comment      text            not null,
    constraint comments_author_foreign
        foreign key (author) references accounts (account_id),
    constraint comments_bug_id_foreign
        foreign key (bug_id) references bugs (bug_id)
);

-- comments の子孫関係を表現する
create table tree_paths
(
    ancestor   bigint unsigned not null,
    descendant bigint unsigned not null,
    primary key (ancestor, descendant),
    constraint tree_paths_ancestor_foreign
        foreign key (ancestor) references comments (comment_id),
    constraint tree_paths_descendant_foreign
        foreign key (descendant) references comments (comment_id)
);

f:id:ro9rito:20220228144003p:plain

それぞれのコメントレコードに関して、その親と子の関係を収録しているのが tree_paths のレコードです。

階層構造の最上階、最も親のコメントに関しては自身が子孫のどちらにも自身を指定しているようなレコードになっています。

こちらも INSERT は簡単。子孫関係の更新も、対象コメントの親や子は簡単に判別できるので、こちらもコメントの付け替えは容易です。

隣接リストでツラみポイントであったコメントの削除はどうかというと、tree_path から該当のレコードを削除すればその子孫も含め構造から切り離されるため、制約を受けることなく容易に行うことができました。 さらに切り離したコメント以下の子孫関係や、コメントレコード自体も全て残るため、その後の処遇も柔軟に行えそうです。

コメントの削除時に子孫のコメントたちを昇格させるのも簡単で、子レコードの親カラムの値を変更してあげるだけです。

set @DELETE_COMMENT_ID = 4;
select @ANCESTOR := `ancestor` as `id` from `tree_paths` where `descendant` = @DELETE_COMMENT_ID;

-- 削除対象のコメントの子らを一段上へ昇格させる
update tree_paths
set ancestor = @ANCESTOR
where ancestor = @DELETE_COMMENT_ID
and descendant in (
    select x.id from (
        select descendant as id from tree_paths where ancestor = @DELETE_COMMENT_ID
    ) as x
);

-- 削除対象コメントの関係を削除
delete from tree_paths
where ancestor = @ANCESTOR
and descendant = @DELETE_COMMENT_ID;

扱いやすさもそうですが、テーブルの表現として、コメントそのものとそれらの関係性が分離されているのはとてもシンプルで良いなと思いました。

ツリー構造のしやすさ

最後に、折角ツリー構造の話なので階層構造も作ってみます。

閉包テーブルと PHP を使ってツリー構造にしてみます。

SQL

select
    comments.comment_id,
    comments.comment,
    comments.comment_date,
    accounts.account_name,
    tree_paths.ancestor,
    tree_paths.descendant
from comments
    inner join tree_paths on comments.comment_id = tree_paths.descendant
    inner join accounts on comments.author = accounts.account_id
where comments.bug_id = 1;

この結果が array in stdClass で返ってくるとして、再帰関数を噛ませて階層構造を作ります。

PHP

/** @var object[] $commentList */
$addChild = function (array $comments, array &$treeData) use ($commentList, &$addChild)  {
    /** @var object $comment */
    foreach ($comments as $comment) {
        $treeData[$comment->comment_id] = (array)$comment;

        $children = array_filter(
            $commentList,
            fn (object $item) => $item->ancestor === $comment->comment_id && $item->ancestor !== $item->descendant
        );

        if (empty($children)) {
            continue;
        }

        $treeData[$comment->comment_id]['child'] = [];
        $addChild($children, $treeData[$comment->comment_id]['child']);
    }
    $treeData = array_values($treeData);
};

// 起点となる最上階のコメント
$parentItems = array_filter($commentList, fn (object $comment) => $comment->ancestor === $comment->descendant);

$treeData = [];
// 子孫を付与
$addChild($parentItems, $treeData);

return $treeData;

結果を表示(クリックで展開)

Array
(
    [0] => Array
        (
            [comment_id] => 1
            [comment] => このバグの原因は何かな?
            [comment_date] => 2022-02-26 01:45:40
            [account_name] => Fran
            [ancestor] => 1
            [descendant] => 1
            [child] => Array
                (
                    [0] => Array
                        (
                            [comment_id] => 2
                            [comment] => ヌルポインタのせいじゃないかな?
                            [comment_date] => 2022-02-26 02:45:40
                            [account_name] => Ollie
                            [ancestor] => 1
                            [descendant] => 2
                            [child] => Array
                                (
                                    [0] => Array
                                        (
                                            [comment_id] => 3
                                            [comment] => そうじゃないよ。それは確認済みだ。
                                            [comment_date] => 2022-02-26 03:45:40
                                            [account_name] => Fran
                                            [ancestor] => 2
                                            [descendant] => 3
                                        )

                                )

                        )

                    [1] => Array
                        (
                            [comment_id] => 4
                            [comment] => 無効なインプットを調べてみたら?
                            [comment_date] => 2022-02-26 04:45:40
                            [account_name] => Kukula
                            [ancestor] => 1
                            [descendant] => 4
                            [child] => Array
                                (
                                    [0] => Array
                                        (
                                            [comment_id] => 6
                                            [comment] => よし、じゃあチェック機能を追加してもらえるかな?
                                            [comment_date] => 2022-02-26 06:45:40
                                            [account_name] => Fran
                                            [ancestor] => 4
                                            [descendant] => 6
                                            [child] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [comment_id] => 7
                                                            [comment] => 了解、修正したよ。
                                                            [comment_date] => 2022-02-26 07:45:40
                                                            [account_name] => Kukula
                                                            [ancestor] => 6
                                                            [descendant] => 7
                                                        )

                                                )

                                        )

                                    [1] => Array
                                        (
                                            [comment_id] => 5
                                            [comment] => そうか、バグの原因はそれだな。
                                            [comment_date] => 2022-02-26 05:45:40
                                            [account_name] => Ollie
                                            [ancestor] => 4
                                            [descendant] => 5
                                        )

                                )

                        )

                )

        )

)

RDB への問い合わせ結果は行列の表なのでツリー構造にすること自体はアプリケーション(プログラム)側で行う想定のため、そこのコストに関しては隣接リストだろうが閉包テーブルだろうがあまり変わらないかなとは思いました。

まとめ

書籍では隣接リストに変わるパターンとして「経路列挙(Path Enumeration)」「入れ子集合(Nested Set)」「 閉包テーブル(closure table)」の 3 つのパターンが紹介されていました。

隣接リストを含め、全部でこの 4 パターンの良いところやツラい点、良いけどトレードオフになる点などが紹介されていたので、書籍の中でも閉包テーブルはエレガントだとしていたのはありつつも、どれを選択するかは実現するものと照らし合わせてメンテナンスしやすいものを適材適所判断していければ良いなと感じました。(まあでも個人の見解では優勝は閉包テーブルかな)

SQL アンチパターン」という書籍では沢山のアンチパターンとその解決策が紹介されていて読んでいてとても勉強になるしテーブル設計上の視野を広げてくれる良書だと思うので、これからもいつでも手に取れるところに置いておきたいと思います。

www.oreilly.co.jp


現在 back check 開発チームでは一緒に働く仲間を募集中です!!

herp.careers

herp.careers

herp.careers

herp.careers

herp.careers

herp.careers

herp.careers

RSGT2022 の動画が公開されたので、社内で視聴会をやりました

この記事は個人ブログと同じ内容です

RSGT2022 の動画が公開されたので、社内で視聴会をやりました


みなさん、こんにちは! ROXX で back check のエンジニア兼、スクラムマスターをやっています。ぐっきーこと山口壮太 (@Area029S)です。

さて、Regional SCRUM GATHERING Tokyo 2022(以下、RSGT2022)が終わって早2ヶ月が経ちました。 私も仕事の傍ら、 RSGT で知り合ったアジャイルコミュニティの方が開催されているイベントに参加したり、家族でキャンプしたりとバタバタと予定を詰め込んでいたらあっという間に時間が過ぎてしまいました。

RSGT の視聴会やったよ

ありがたいことに今年も RSGT のセッション動画が公開されましたね。 ROXX でも、 agent bank と back check の両事業部で、スクラム開発をおこなっているということで、両事業部みんなで集まって視聴会をしてみました。

会の概要

毎月開催している ROXX Dev Meetup という社内勉強会の1時間枠を使って、 RSGT のセッションの動画を、社内の開発メンバーみんなで試聴しながらわいわいする会を行いました。 題材にしたのは、両事業部どちらも参考にできそうなものということで、あなたのSprint Goalは、機能してますか?プロダクトバックログ Deep Diveを見ました。

超余談ですが、30分以上あるセッションを、1.5倍速でみることでなんとか1時間の枠で納めました。

視聴会の様子

Discord で動画を配信しながらチャットでわいわいしました。

視聴会をやってみた感想

セッションで出てきた、気になった箇所やわからない箇所をみんなで補足しあいながら視聴する体験がよかったです。 また、みんなでひとつの題材を取り扱うと、セッションででてきたワードをその後、共通言語として使うことができるというのもよかったと感じました。

逆に残念だったこととしては、今回1時間の枠に絞ってしまったため、視聴後にディスカッションができなかったことです。 視聴中に出てきたコメントをふりかえったり、そのまま OST をやったりできていたらより良かったと思います。

RSGT2022 のセッションの中で、チームで共有したいセッションはまだまだあるので、今後ちょこちょこ視聴会をやっていきたいと思います。

参考資料

youtu.be youtu.be

さいごに

さいごに少しだけ宣伝です。

現在 back check 開発チームは一緒にはたらく仲間を募集中です!! 私たちと一緒に、 back check を通して「信頼が価値を持ち、信頼によって報われる社会の実装」に挑戦してみませんか?

herp.careers herp.careers herp.careers herp.careers herp.careers herp.careers herp.careers

アジリティーを高めるために目的不確実性をコントロール下に置く

この記事は下記記事と同じです

note.com

最近PdM活動を行っている中で、アジャイル開発における対峙する不確実性の捉え方を変える事によって、生産性が大きく向上しそうな体験を感じることができたので、言語化してみる。

想定読者

  • リリースした後にCSから「これじゃ使えない」と言われる
  • 振り返りの場で顧客価値について言及されない
  • リリースしたのに使ってくれているのかどうかわからない
  • 本当に使ってくれるかわからない のに長期間開発してリリースしている
  • 施策を計画的にリリースすることに重きを置いている

アジャイル開発は不確実性を受け入れる開発

改めて12の原則を照らし合わせてみると本質が書いてあるなぁと思う。 https://agilemanifesto.org/iso/ja/principles.html

顧客満足を最優先し、 価値のあるソフトウェアを早く継続的に提供します。

要求の変更はたとえ開発の後期であっても歓迎します。 変化を味方につけることによって、お客様の競争力を引き上げます。

とある通り、変化することを受け入れるというマインドセットそのものがアジャイル開発における重要なピースの一つになっている。

ではどういった変化を受け入れるべきなのか。「エンジニアリング組織論への招待」では3つの不確実性がソフトウェア開発に含まれているという

  • 目的不確実性
    • 何を作るのかという不確実性
  • 方法不確実性
    • どうやって作るのかという不確実性
  • 通信不確実性
    • コミュニケーション上発生する不確実性

本書においてはアジャイル開発では「目的不確実性と方法不確実性の両方に対して段階的にアプローチする」と書いてあるが、これをしっかり回すことは難しい。

f:id:kotamat:20220130154039p:plain

通常の開発では方法不確実性、通信不確実性に目が行きがち

なぜ不確実性を減らしていくことが難しいのか。 それはそれぞれの不確実性が対象領域がかなり異なっているためだと思っている。

f:id:kotamat:20220130154105j:plain
不確実性の対象領域

  • 目的不確実性
    • ビジネス的な成長と顧客理解の合わさったところ
  • 方法不確実性
    • スケジュールの予測、ベロシティーの安定化
  • 通信不確実性
    • チーム内における情報の非対称性

また、それぞれの不確実性の対処方法も異なる。

  • 目的不確実性
  • 方法不確実性
    • 多面的な見積もりやベロシティ計測による予測可能性の向上
  • 通信不確実性
    • KPTなどでの振り返りによる情報非対称性の解消や、心理的安全性の確保

開発チームという枠組みの中での改善を行おうとすると、影響を及ぼせるのは方法不確実性と通信不確実性のみとなってしまうので、振り返りの場で上がってくるイシューも、方法不確実性や通信不確実性の話題に終止してしまう。

目的不確実性はマーケット環境や顧客理解、ビジネスサイドの動き方といった事業部全体を包含しなければ不確実性の探索が難しいため、目的不確実性をコントロール下に置くことに対して難しさを感じたり、はなから無理だと決めつけて管轄外と捉えるようになってしまう。

対処方法も対象領域も異なりすぎるため、短絡的に考えれば開発チームに閉じる方法不確実性と通信不確実性のみを扱うのが気が楽である。ただ、目的不確実性もコントロール下に置ければ難易度は上がるものの、開発チームにとっても事業部ひいては顧客にとっても良いのではないかと思っている。

目的不確実性をコントロール下に置くとどうなるか

ざっと下記のメリットがあると思う。

  • どこまで作れば完成なのかがアウトカムベースでわかる。
  • まずは最低限何を作ればいいかがわかる
  • 顧客への価値提供を身にしみて感じることができる
  • 最も重要なことに取り組んでいる事がわかる。
  • BizとDev双方協力して顧客への価値提供していることを一体感を持って感じることができる

アジャイル開発においては、目的不確実性と方法不確実性を双方合わせて解消していくプロセスを踏んでいくことになるが、これを前提に開発することを念頭に入れることで、初手は完璧に作りすぎる必要がないことを理解出来、最もコアで不確実性の高いものを先に作る重要性を理解できる。 そうすると、「これって無駄に作りすぎているんじゃないか」とか、「このまま長期で開発していくとダレてしまう」などの不要な懸念や不安を感じることなく、目的に向かって迷いなく進んでいくことができる。 また、全てを完璧に作る必要がないため、思考のリソース的にも「考えを遅延させて重要なことだけを考えればいい」状態が継続されるため、常に重要なことに取り組んでいるという状態を作ることができる。

目的不確実性をコントロール下に置かない、計画駆動な開発スタイルでいったほうが確かに「モノが作れるスピード」は段違いで早くなるかもしれない。ただそれが顧客に価値を届けられたかどうかをベースに考えていないことで、「これってなんのために作ったんだろう」という、作った後に大きな手戻りが発生する危険をはらみ、中長期的にモチベーションが下がっていってしまう。

小さく答え合わせをしながら、手段と目的の不確実性を言ったり来たりして、少し遠回りしてでも最終的には遠くの地点にたどり着ける。そういった感覚を得られるのではないかと思っている。

目的不確実性の解消には技術が必要だがすべてをやらないといけないわけではない

ではどうやれば目的不確実性をコントロール下におけるのか。それはプログラミングスキルとは違った、技術が求められる。

  • ビジネス理解
  • プロダクトゴール、プロダクトビジョンの策定
  • 仮設構築力
  • 開発外の部署の理解
  • 顧客理解
  • アセットとPLの次元の違い
  • etc..

これらはプロダクトマネジメントに求められるスキルであり、非常に多岐に渡る。これらを一朝一夕で身につけるのは非常に難しい。自分自身もまだこのスキルを身につけられたと自信を持って言える状態ではないし、果たしてそんな日が来るのだろうかという不安を覚えるくらい、非常に幅広く、深いスキルが要求される。

ただ、全てが完璧にならないとコントロール下に置けないかというとそうではないと思う。目的不確実性は0 or 100の世界ではなく、次第に少しずつ下げていくものであるため、そこに5でも10でも関与し下げられる事ができるのであれば、それだけでもとても価値のあることであるし、上記に述べたメリットを享受することができる。

まずは技術を習得する前に、できることからやっていくのがいいかなと思う。

目的不確実性を下げるために開発の観点からやれること

目的不確実性は下記の様に、ある特定のフェーズを通過するごとに下がっていく。 f:id:kotamat:20220130154141p:plain

このような活動に積極的に関与していくことだったり、もしここに上がっているフェーズをすっ飛ばしてものを作ろうとしている場合は、一旦立ち止まって「なんのために作ろうとしているのか」というのを考え、たとえ間違ってもいいので、後から検証できるような形に持っていくというのでもいいかもしれない。

また、「1秒でも早く価値を届けるためにどうすればいいか」というのを考え、やれることをやっていってもいいかもしれない。

特におすすめなのは 施策をリリースした後にCSと顧客に展開するのではなく、施策の出来上がりの解像度が少しでも上がったらすぐにFBを求めに行く というもの

  • まずは動くものを作るというアジャイルの原則を否が応でも実現する必要が出てくる
  • 顧客へのヒアリングより圧倒的に速いスピードでFBを貰える。
  • CSが「これだとお客さんに使ってもらえない」という意見がリリース後に出てくることがなくなる。
  • CSに事前に展開しておくことで、リリース後に顧客に使ってもらえるような準備をリリース前に実施してもらえる
  • 例えばCSが顧客と商談するときに、施策に対して顧客からFBをもらい、リリース前に調整ができるかもしれない。

というところで、少なくともリリース時には 「CSが顧客に使ってもらえる」状況まで目的不確実性を下げる ことができ、かつその プロセス自体も対して難しいことを要求しているものでもないため、すぐに取りかかれる難易度でもある。

また スプリントゴールを設定後に事業部に展開する というのも良い。質は多少劣るもののいくつか同様の効果を得られる。

これ以外にもあるかもしれないが、まずは自分のできるところからやってみるとよい

まとめ

目的不確実性をコントロール下に置く重要性と、まずできることからやっていこうという話をさせてもらった。 「もっとこうしたほうがいいんじゃないか」「こういう観点もありそう」みたいな意見があればコメントにいただけると嬉しいです。 あと、プロダクトマネジメントや開発手法などに関して、一緒にお話していただける方も募集中です。 https://twitter.com/kotamats にDMいただければと思います。

Github Actions でのプルリク作成時に特定のファイルの存在を知らせる

この記事は個人ブログと同じ内容です

www.ritolab.com


Github Actions を用いて定期的にプルリクエストを作成する際に、特定のファイルの存在を知らせたかったのでやってみます。

特定のファイルの存在を知らせる

知らせるのは「通知」という意味ではなくて、プルリクの説明欄にこれらのファイル名を記載して、いちいち確認しなくてもこれらのファイルが存在していることをわかるようにします。

定期に自動でプルリクを作成しているような状況なのでフィーチャーブランチのプルリクというよりはリリースのプルリクやローカルに向けたプルリクなどを想定しているとして、例えば PHP フレームワークの Laravel だと、マイグレーションファイルとかサンプルの環境変数ファイルの変更とかは、プルリクの中身を確認しなくても追加や変更の存在がわかるといいな。みたいな状況感です。(プルリクを取り込んだけど環境変数追加あったの見逃しててエラーになるみたいなのを防ぎたいモチベーション)

プルリクの説明欄への記載

hub コマンドでプルリクを作成する場合は、 -m オプションでプルリクのタイトルと説明を指定できます。

最初の 1 行目がプルリクのタイトルになり、空行を挟むとそれ以降が説明欄の文章になります。

MESSAGE="ここがプルリクのタイトルになる

## これ以降が説明欄の文章になる
* テスト
* test

markdown で書ける。
"

hub pull-request -m "$MESSAGE" -b main -h dev

上記コマンドで作成されたプルリクは以下のようになります。

f:id:ro9rito:20220125182046p:plain

特定のファイルを抽出する

差分から検索して抽出しようと思います。

MESSAGE="プルリクのタイトルです。\n\n"

# 変更ファイルを取得
DIFF=`git diff main dev --name-only`

# 探したいファイルのパスや名前で該当の行を抽出
ENV_FILES=`echo "$DIFF" | sed -n '/\.env\./p'`
MIGRATION_FILES=`echo $DIFF | sed -n '/database\/migrations\//p'`

# 説明欄のコメントを追加
if [ -n "$ENV_FILES" ]; then
  MESSAGE+="環境変数ファイルの変更が含まれます。\n$ENV_FILES\n\n"
fi
if [ -n "$MIGRATION_FILES"  ]; then
  MESSAGE+="マイグレーションファイルが含まれます。\n$MIGRATION_FILES\n\n"
fi

echo -e "$MESSAGE"

git diff で差分を持ってきます。

ここでは main ブランチと dev ブランチの差分を、--name-only オプションでファイル名のみを取得しています。(変更があった、新規に追加されたファイルの一覧が入ってくる。)

入ってきたファイルリスト($DIFF)に対して sed -n で指定の文字列を含む行のみを抽出します。

あとはコメントを組み立てて出力です。

上記を実行するとこんな感じで出力されます。

プルリクのタイトルです。

環境変数ファイルの変更が含まれます。
.env.example

マイグレーションファイルが含まれます。
database/migrations/2022_01_22_000000_create_users_table.php
database/migrations/2022_01_22_000000_create_books_table.php

hub コマンドでプルリクを作成するとこんな感じです。

f:id:ro9rito:20220125182150p:plain

Github Actions で作成

上記を踏まえてこれらを Github Actions 側に実装します。

.github/workflows/xxxx.yml(メッセージ・プルリク作成部分のみ抜粋)

# ブランチ名作成
- name: Make branch name
  id  : setting
  env:
    TZ: 'Asia/Tokyo'
  run : |
    DATE=`date +"%Y%m%d_%H%M%S"`
    BRANCH_NAME="release/$DATE"
    echo ::set-output name=branch_name::$BRANCH_NAME

# プルリクのメッセージ作成
- name: Make pull request message
  id: message_making
  env:
    BRANCH_NAME: ${{ steps.setting.outputs.branch_name }}
  run: |
    MESSAGE="$BRANCH_NAME\n\n"
    
    DIFF=`git diff main dev --name-only`
    ENV_FILES=`echo "$DIFF" | sed -n '/\.env\./p'`
    MIGRATION_FILES=`echo "$DIFF" | sed -n '/database\/migrations\//p'`
    if [ -n "$ENV_FILES" ]; then
      MESSAGE+="環境変数ファイルの変更が含まれます。\n$ENV_FILES\n\n"
    fi
    if [ -n "$MIGRATION_FILES"  ]; then
      MESSAGE+="マイグレーションファイルが含まれます。\n$MIGRATION_FILES\n\n"
    fi
    echo ::set-output name=message::$MESSAGE

# ブランチ作成
- name: Create Branch
  uses: peterjgrainger/action-create-branch@v2.1.0
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  with:
    branch: ${{ steps.setting.outputs.branch_name }}

# プルリクエスト作成
- name: Create Pull Request
  id: create_pull_request
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    BRANCH_NAME: ${{ steps.setting.outputs.branch_name }}
    MESSAGE: ${{ steps.message_making.outputs.message }}
  run: |
    PULL_REQUEST_MESSAGE=`echo -e "$MESSAGE"`
    hub pull-request -m "$PULL_REQUEST_MESSAGE" -b main -h $BRANCH_NAME

1 点だけポイントで、組み立てたメッセージはエスケープ文字を使用している(改行部分)ため、hub pull-request で指定する前に echo -e で出力してエスケープ文字を有効にしておきます。

# エスケープ文字を有効にする
PULL_REQUEST_MESSAGE=`echo -e "$MESSAGE"`

Github Actions を回して実行してみます。

f:id:ro9rito:20220125182338p:plain

抽出されたファイルのリストが説明欄に書き込まれてプルリクが作成されました。

まとめ

定期のプルリクエスト作成に限らず、手動でのプルリク作成時にもこういったファイルを検出してコメントするみたいな事に応用できそうだなと思いました。

任意のファイル抽出やコメントへの書き込み(プルリク作成も含め)は、探せば GitHub Marketplace にワークフローが公開されているかもしれないので探してみるのも良いかもしれません。

Regional SCRUM GATHRING Tokyo 2022 速報(3日目)

こんにちは! ROXX で back check の開発エンジニア兼、スクラムマスターをやっている、ぐっきーこと山口 (@Area029S)です。Regional SCRUM GATHRING Tokyo 2022 3日目のレポートをさせていただきます!

OST(Open Space Technology)

discord とオンサイトそれぞれの会場を交えて、ハイブリットな OST を実施しました。 私はオンラインでの OST に慣れていた分、オンサイトではお目当てのテーブル探しに一苦労しましたが、楽しく参加してきましたので一部始終をお届けします。

来週からはじめること、やめること

A 枠 ost-table-1

いきいきいくおさんの「みなさん、いきいきしてますか?せーの、いきいき〜!!」の掛け声にのせて、来週からはじめること、やめることを発表しあう時間になりました。 個人的に好きだったのは「ふりかえりをうまくやろうとすることをやめる!」でした。会場で一番いきいきしたテーブルだったと思います。

フルリモート環境でのいい感じなオンボーディング気になっている

B 枠 ost-table-11

オンボーディング、チームビルディングのために、四半期に一回のペースで相互理解のためのワークショップをやっているというお話が印象的でした。バリューズカードゲーム、ドラッカー風エクササイズ、ストレングスファインダーなどを活用しているというお話を聞けました。

ネガティブワードを妥当な表現に変えたい!!!

C 枠 ost-table-1

チームの雰囲気が悪くなるネガティブワードを妥当な表現に変えたい!!!ということで日頃のネガティブワードあるあるをどう改善できるか考えました。 気遣いの行き届いた素敵な表現がたくさんでたので、私も積極的に使っていきたいと思います。

  • ~べき → ~の方が好き
  • 結局なにがしたいの → そういえば、この議論の目的ってなんでしたっけ?
  • それじゃない。◯◯だろ。→ それだったらこれは?
  • それでいいです → それがいいです
  • もうちょっとできるでしょ → 満足してる?
  • とくにないです → 完璧やん!
  • xxとか、zzとかっていう逃げ方はありますけど… → xxとかzzとかの選択肢を取れますね!

Closing Session

Closing Session は @Tsuyoshi Ushio さんによる、アメリカの超巨大クラウドの中の人に転生したガチ三流プログラマが米国システム開発の現実をリークするというセッションでした。

Ushio さんは現在アメリカの Microsoft でシニアエンジニアとして働いています。ただ、特別エンジニアとして優秀なわけではありません。偶然、経歴が求められていたものとフィットしただけであり、どうやったら Microsoft で働くエンジニアになれるの?という問いに対しての Tips は、「面接を受けましょう」ということでした。

また、日本とアメリカ(特に Microsoft )との組織の違いとして、アメリカではウォーターフォールのメリットは0だから、100%の現場でアジャイルを導入している。また、チームのメンバーがインディビジュアルコントリビューター(IC: 個人事業者的な意味合いで使われている)として、管理されるわけではなく自分たちで行動やキャリアを選択していくいう点を挙げられていました。

おわりに

3日間行った RSGT2022 もついに終わってしまいました。 discord のみなさん、オンサイトのみなさん、3日間楽しくギャザリングできましたでしょうか? 私ぐっきー個人の振り返りとしては、新たな学びを得たとともに、多くの方と交流できたので有意義なギャザリングができたと感じております。

また、 RSGT2022 の1日目、2日目の速報もあわせてお読みください。

最後に少しだけ、弊社 ROXX の宣伝をさせてください!今回の RSGT ですが、私は業務として参加しています。 RSGT は参加費がお高めなカンファレンスですが、これらは会社負担で参加できます。弊社 ROXX では、エンジニアが技術力をつけるための勉強会については本人が業務に活かせると判断するのであれば、基本的に業務扱いにしてくれます。

もし、弊社にご興味を持っていただけましたら、弊社 ROXX 採用サイトよりお気軽にカジュアル面談のお申し込みください!

最後まで読んでいただき、ありがとうございました!

Regional SCRUM GATHRING Tokyo 2022 速報(2日目)

こんにちは! ROXX で back check の開発エンジニア兼、スクラムマスターをやっている、ぐっきーこと山口 (@Area029S)です。Regional SCRUM GATHRING Tokyo 2022 2日目のレポートをさせていただきます!

Keynote

2日目の KeynoteDiana Larsen さんです。 The Agile Fluency Model を用いて、どのようにチームをリードし、彼らの能力を発揮させるために投資をするかについてお話されました。

Fluency (流暢さ)なアジャイルチームになるには Focusing, Delivering, Optimizing, Strengthening の4つの習熟度のゾーンがあります。また、ゾーン毎にそれぞれ以下の指標を計測します。 - Focusing: チームの取り組みに対する、ビジネス価値の観点からの進捗を可視化できている - Delivering: 望まれたタイミングで、いつでも高い品質の機能をデリバリーできる - Optimizing: 自分たちの製品が市場でどのような位置にあり、どのようにその位置を向上(または確保)させるかを説明できる

チームの習熟度はどのゾーンにいるのかを考え、今すぐ行動しましょう。 そして、効率ではなく、投資に対する効果を求めましょう。効果にもっとも価値があります。といった内容でした。

Fluency なアジャイルチームとして、より価値を届けられるチームを目指しましょう!

プロダクトバックログ Deep Dive

午後一発目は Ryutaro YOSHIBA (Ryuzee) さんのセッションを聞きました。

プロダクトバックログとはなにか。プロダクトバックログの管理、分割などを通して、プロダクトバックログをきっかけにスクラム全体の思想を感じられた、学びの深いセッションでした。 印象的だった言葉は「生煮えプロダクトバックログアイテムを食べると腹を下す」という言葉で、「これを避けるために、火が通って調理済みのプロダクトバックログアイテムを食べていくことが大原則です。検査と適用で自分たちにあった調理済みの状態を探していきましょう。」とワンセットにして、準備完了の状態の大切さを伝える際にぜひ活用させていただきたいと感じました。

QAなんなん?~みんなが納得できるQAのスタイルを見つけよう~

続いて、Kaori Tokiwa さん、 Naoki Kojima さん、 Yasuharu Nishi さん、 やすよ おおの さんの合同セッションです。

開発と QA が同じ方向に進む組織になるためのハッピーな QA のスタイルづくりについて、独自に考案した QA スタイルファインダーを使って考える。というお話でした。

QA スタイルファインダーの点数づけは左右の軸との相対値であって、チームの中でどこを大事にするか共通認識で持つことが大切です。自分たちで QA スタイルファインダーの採点をする中で、チーム自身で気づきを得られることが QA スタイルファインダーの強みということでした。 オーディエンスからは、 QA だけでなく、アジャイルチームにも活用できそうとの声もありました。 私個人的にも、可視化しづらいチームの状態というものをチーム全員が共通で認識できる。という意味でとても有効なツールだと感じました。

LeSS もスクラム。プロダクトオーナーがスクラムマスターとともに取り組んだ LeSS チームの合体から融合まで

2日目最後に聴いたセッションは、Yahoo Japan から Saiko Nakai さんです。 Saiko Nakai さんの体験から、スクラム less な LeSS チームと、ゆるふわスクラムチームがいかにして融合できたのかということについてのお話しでした。

融合の過程で学んだこととして、 - プロダクトオーナーはチーム全体に対してプロダクトが目指したい状態を明らかにする - スクラムマスターは、そのためにチームがどうあるべきかを明らかにする

これらを実践するためにも、プロダクトオーナーとスクラムマスターはたくさんおはなしをするべし。

また、スクラムで大事にしていたことは LeSS でも大事にしましょう。 規模は大きいけれど、やっていることはスクラムと一緒なので怖がる必要はありません。 基本はスクラムガイドに立ち返ろう。 LeSS もスクラムですよ。とのことでした。

プロダクトオーナーである Saiko Nakai さんの言葉で、スクラムチームとして大事にしたい価値観を言語化して、日々チームに伝えているというお話が印象的でした。 個人的には、プレゼンの内容から伺えた Saiko Nakai さんの現場推進力に終始圧巻されていました。

おわりに

さて、 RSGT も折り返しを越えまして、明日が 3 日目、最終日となりました! 楽しみな気持ち、寂しい気持ちが半分ずつありつつ、明日は最後までたくさん楽しんできたいと思います!

また、私ぐっきーも FIRST TIMER のリボンをつけて会場にいるので、この記事を読んだ方はお声がけしてくれると嬉しいです!

最後に少しだけ、弊社 ROXX の宣伝をさせてください!今回の RSGT ですが、私は業務として参加しています。 RSGT は参加費がお高めなカンファレンスですが、これらは会社負担で参加できます。弊社 ROXX では、エンジニアが技術力をつけるための勉強会については本人が業務に活かせると判断するのであれば、基本的に業務扱いにしてくれます。

もし、弊社にご興味を持っていただけましたら、弊社 ROXX 採用サイトよりお気軽にカジュアル面談のお申し込みください!

それではまた、明日 RSGT 3日目でお会いしましょう!

Regional SCRUM GATHRING Tokyo 2022 速報(1日目)

こんにちは! ROXX で back check の開発エンジニア兼、スクラムマスターをやっている山口 (@Area029S)です。Regional SCRUM GATHRING Tokyo 2022 1日目のレポートをさせていただきます!

Keynote

RSGT 初日の一日目の Keynote は、Manage It! 現場開発者のための達人式プロジェクトマネジメントの著者の Johanna Rothman さんから「Agile Program Management: Scaling Collaboration Across the Organization」です。

発表では Johanna さんが自身の経験(1980年台のコンピュータープロダクト開発)を題材にアジャイルなプログラムマネジメントについてお話しされました。 コラボレーションをスケールすることが大事であり、チームが自立して行動したり、 Small World Networks を作成することでこれが実現できるとのことでした。

計画については、ロードマップは4半期単位でも予定通りに進んだ試しがないのでつくらず、4週間分を1週間ずつにわけて計画を立てていき、1週間経過したらみえてきた課題に対しての改善をするために次の5週目の計画を立てる方法で計画しました。

また、進捗の見方としてベロシティの測定ではなくサイクルタイムで計測を行いました。これは顧客の求めているのは開発速度ではないこと。プロダクトが完成品にどれだけ近づいているかを確認するためです。また、顧客はフィーチャーを求めているので、バーンアップチャートを用いて確認することができます。

最後に、プロダクトがなんのためにあるのか、我々がどこに向かうのか、そのために我々は何をするのかの順で徹底的にプロダクトへフォーカスすると、メンバーが自律性を高めるための道筋が作れるとのことでした。

1980年代のアジャイル開発を実際に携わっていた人から聞くのは、スクラムのルーツを知れたような気がしてとても感動しました。

「いい感じのチーム」になるためにやること

@yohhatuさんからは"いい感じ"のチームになるにはどのような活動を行い、どのような関心を持てばいいのでしょうか?というお話しでした。 リーダーシップ、感情、実験の3つのテーマについて話しました。

リーダーシップ

みんなでリードしましょう。リーダーだけでなく、フォロワーシップも大切です。また、チームとしてのバランスも大切です。チームビルディングはスパイラルを登っていくことを意識しながら、継続的にやりましょう。

感情

感情を大切にすることで余白的な部分からでてくる気づきがあります。お互いのことを知り、理解することを心がけましょう。感情を大切にしてもらうためには、自分の弱みからオープンにしていくことがおすすめです。

実験

日常的に実験し続けましょう。 Tips として常になにかを変え続けていくことで変化に強くなります。また、継続的に行う中で、予想や仮説を持った上で一つ一つに対して本気で実験しましょう。

当たり前のことを当たり前に継続することが、いい感じのチームに近づけるヒントですとのことでした。

個人的には、普段の実験が習慣として定着していないことから、@yohhatuさんの「日常的に実験しましょう」という言葉が胸に刺さりました。

フリカエリ星人との邂逅 ~ふりかえりのお道具箱&お悩み相談~

@higuyumeさん / @viva_tweet_xさん

ここでは OST(Open Space Technology) 形式でお悩み+日頃のスクラムに対する Tips について、フーリーとカエリーの2人?が回答したり掘り下げたりしました。

最後にはこのセッションの振り返りも。とてもたくさんの振り返りがでました。

他のセッションは聴く側に回ることが多い中で、このセッションでは自分も一員になることができてとても楽しかったです。個人的には、頑張ってファしらない。 work together の一言でどこかほっとした気持ちになりました。

プロダクトゴールとは?あるいはプロダクトのゴールを設定するには何が必要か?

続いては、@tnagasawaさんによるセッションです。 セッション中に@tnagasawaさんがお話しされたプロダクトゴールの特性 FAST(Frequent discussions, Ambitiously, Specific metrics, Transparent) を元に設計されたプロダクトゴールのフォーマットは、ぜひ活用させていただきたいと思います。

また、プロダクトゴールには常に指標を計測して 検査・適用することが重要であり、プロダクトゴールと価値指標の検査は、経験主義に基づいたマネジメントフレームワーク - エビデンスベースドマネジメント(EBM)が参考になるとのことでした。 20分間のセッションではとても足りないほど学びの深い内容でした。

参考:EBM ガイド https://scrumorg-website-prod.s3.amazonaws.com/drupal/2021-02/2020-EBM-Guide-Japanese.pdf?nexus-file=https%3A%2F%2Fscrumorg-website-prod.s3.amazonaws.com%2Fdrupal%2F2021-02%2F2020-EBM-Guide-Japanese.pdf

あなたのSprint Goalは、機能してますか?

次に@KazuhideInanoさんによるセッションです。 スクラムガイドでのスプリントゴールと完成の定義の扱いは、今までは成果に付随するものでしたが、2020年からスプリントゴールを導入し、位置付けが明確になりました。よく聞く話でスプリントゴールは決めづらい。決めなくてもなんとかなる。との意見がありますが、実際は年々重要視されるようになってきているのです。

例えばスプリントゴールの効果として、チームの活動に軸を据える。ステークホルダーとのコミュニケーションの質を上げる。活動の透明性をあげ、検査し、適用を促す。などがあります。 具体的な例として、スプリントレビューの前に、スプリントゴールをステークホルダーに共有することで、スプリントレビュー時にはステークホルダーの理解度が深くなっており、より検査、適応にフォーカスしたコミュニケーションをとることができるようになります。

また、気をつけたい兆候としてスプリントゴールが単に行動の達成目標になってしまったり、ムーンショットすぎてどれも達成できなかったりなどが現れた場合は、振り返りをして改善できることはないか検討してみましょう。という内容でした。

私が所属するチームでも、スプリントゴールの扱いについては課題感がある中で、改めてスプリントゴールの重要性を確認できる内容のセッションでした。

おわりに

まだまだ RSGT は初日が終わったばかり! 2,3 日目のセッションも楽しみですね! 私もわくわくしながら明日を待ちたいと思います!

最後に少しだけ、弊社 ROXX の宣伝をさせてください!今回の RSGT ですが、私は業務として参加しています。 RSGT は参加費がお高めなカンファレンスですが、これらは会社負担で参加できます。弊社 ROXX では、エンジニアが技術力をつけるための勉強会については本人が業務に活かせると判断するのであれば、基本的に業務扱いにしてくれます。

もし、弊社にご興味を持っていただけましたら、弊社 ROXX 採用サイトよりお気軽にカジュアル面談のお申し込みください!

それではまた、明日 RSGT 2日目でお会いしましょう!