気ままに気ままのエンジニアブログ

定期的に得た知見を気ままに発信中

6ヶ月間取り組んできたアジャイルチームの改善活動を紹介します

こんにちは、フォースタートアップス株式会社のエンジニアの八巻(@hachimaki37)です。主にタレントエージェンシー支援システム(SFA/CRM)のシステム開発を担当し、フルスタックに開発を行なっております。

半年間に渡りチームの業務改善(以下、改善と呼ぶ)をリードして参りました。今回の記事では、具体的なHOWを中心に、どんな課題がありどのような狙いを持って改善に取り組んできたのかについて紹介します。

目次

8000文字を超える記事です。各項目で内容は完結しているため、一から熟読する必要はありません。気になる目次から飛んで拝読頂ければと思います。

以下の方々を対象としております

  • 改善をはじめて行う方
  • 何から手をつけていいか模索している方
  • 改善のHow Toを知りたい方
  • 具体的にどんな改善案があるのか知りたい方
  • 改善の勘所を知りたい方
  • 改善を始めた経緯・モチベーションを知りたい方

チームについて

メンバー

  • PDM: 1名
  • Engineer: 3名
  • SRE: 1名
  • Designer: 1名

計6名のチームです。小規模なチームのため、PDMが開発Issueを取ったり、エンジニアがプロジェクトリードしたり、SREがフロントエンドのIssueを取ったりと、ポジションはあれど横断的に開発を進めています。

開発スタイル

スクラムを採用しております。ただし、スクラムイベントをすべて行うのではなく、デイリースクラム、リファインメント、レトロスペクティブなど、必要なイベントをかい摘み実施しています。スプリントの期間は2週間です。

プロジェクト管理ツール

ZenHubを使用し、Issuesを管理しております。

改善に対するアンケート

半年間に渡り行ってきたチームの活動に対して、アンケートをとりました。アンケートは6項目になっています。そのうち、特に改善に対する結果がわかりやすかった3項目を紹介します。

※私を除く、5名のアンケート集計です。

アンケート結果に対する感想

今回の改善を通じて、チームが「良い方向に変化した」と感じられたメンバーが多くいたことはポジティブに捉えています。一方で、個々人について考えた際、仕組み化による負荷・新たに露見された課題など、まだまだ改善できることはあるなと思いました。

これまでに至ったストーリーを紹介します。

なぜ改善に取り組んだ?

「スプリントごとに価値のある有用なインクリメントをもっと生み出したい」と思ったためです。

チームには、EM・スクラムマスターはおらず、チーム全員で意見を出し合い、改善サイクルを回しております。価値あるインクリメントをもっと生み出していきたい、そんな想いをチームで持ちながら、現実は日々の業務に追われ、なかなか改善に手が回らないという状況でした。日々ルーティン化されたイベント、意義や目的を失ったミーティング、形骸化されたスクラムイベントなどに課題感を持ちました。

たとえば、デイリースクラム。本来の目的は、「スプリントゴール達成に向けた進捗を確認し、進捗にボトルネックがあれば、何かしらのリカバリーを図る」と認識しております。しかし、実際は「作業」をチーム内で共有するだけのイベントになっていました。

伸び代があるこの状況を改善すれば、もっと価値のあるインクリメントをチームで生み出せると思い、まずは半年間、積極的にチームの改善・チームビルディングを担い、取り組みました。

ベロシティの変化

あくまで参考値ですが、改善指標としてチームのベロシティ推移を見ていきました。

注記

ベロシティ増加を目的としておりません。図の波形は個々のスキルアップを始め、人数比や入れ替わり・CD改善なども起因していると考えるため、あくまで参考値としてご覧頂ければと思います。

結果的に、改善実施前から数ヶ月が経過した今、チームのベロシティ平均は上昇傾向になってきました。

やったこと

改善の存在意義を決める

最初に存在意義を定義します。それは、活動を進めていくにつれ、迷いや目的を見失うシーンが発生すると考えたからです。なぜ時間を割いて改善を行う必要があるのか?そのような状況下で支えになるのが、この存在意義です。物事の判断や方向性を示す羅針盤を作り、走り出しました。

アジャイル宣言の背後にある原則 では、「顧客満足を最優先し、価値のあるソフトウェアを早く・継続的に提供すること」を思想としております。これを目指していくためには、「安定的にパフォーマンスを出せるチーム構築」が重要だと考えました。そこで、これを改善の存在意義としました。

チームの課題感を探る

チームのボトルネックを探り、何を改善すれば水の流れは良くなるのかを考えました。

改善の2つのキーワード

組織改善についていろいろ調べていると、2つのキーワードに辿り着きました。1つは「自己組織化」、もう1つは「バーナード組織の3要素」です。

  • 自己組織化とは

    物質や個体が、系全体を俯瞰する能力を持たないのに関わらず、個々の自律的な振る舞いの結果として、秩序を持つ大きな構造を作り出す現象のことである。

引用元:自己組織化 - Wikipedia

つまり、変化し続ける外部環境や発生する課題に対して、組織を構成するメンバー一人ひとりが、主体的・自律的に適応することができる組織・チームを指します。

  • バーナード組織の3要素

    アメリカの経営学者チェスター・バーナードが提唱した、組織が成立するために必要な3つの条件のことを言い、(中略)「コミュニケーション」「貢献意欲」「共通目的」の三要素が不可欠であり、どれか一つでも欠けている場合には不完全な組織として、組織が健全に機能しなくなると定義づけました。

引用元:バーナードの組織の三要素とは?組織に必要な3つ要素を理解し導入するためのポイントを解説 | オンライン研修・人材育成 - Schoo(スクー)法人・企業向けサービス

つまり、「コミュニケーション」「協働の意欲」「共通の目標」の3つを一定水準満たした組織・チームを指します。

ご自身のチームは、どの領域にプロットされる?

これらについて、私の見解を以下にまとめました。

私の考えでは、チームが第4領域に達してから改善をスタートすべきです。自己組織化が目的ではありませんが、自己組織化されたチームでなければ改善を行ったとしても改善の効果を実感しづらいと考えます。第4領域は、主体的・自律的に適応することができる組織・チームです。第4領域にプロットされるチームの場合、改善の方向性を決め、ボトルネックを解消していくことで、大きく水の流れを改善できると考えます。

ご自身のチームがどの領域にプロットされるかにより、課題は異なり、改善内容も大きく変化します。チームの日々の振る舞いや観察、メンバーとのコミュニケーションからご自身のチームがどの領域にプロットされるかを考え、仮説を立てることがまずは重要だと考えます。

改善方針を決める

私たちのチームは、ある程度自己組織化されたチーム状態にあると考えたため、第4領域にプロットし、改善を進めていく形をとりました。改善方針としては、開発プロセスで弊害となる問題や課題を中心に「発見と解決」を繰り返すことがチームを前進させる上で最も重要だと考えました。新たに何かを取り組むよりも、まずは今行っているイベント群を徹底的に改善する方針としました。

具体的に行ったこと

※実施結果を書いておりますが、定量的に計ったものではありません。

※以下、抜粋した改善例です。これら以外にも小さな改善を含め、さまざまな改善サイクルをチームで回しております

ストーリーポイントの基準を再定義する

  • 課題

    • ポイントの基準が不明確
    • ポイントの基準理解が個々人で異なっていた
  • 影響

    • ポイントが大きいストーリーを見積もるときほど個々人でポイントの基準がズレていた
  • 打ち手

    • ポイントが2, 5, 13の課題となるストーリーポイントの基準を定義した

  • 結果
    • 複数の基準を定義したことで、ポイントの認識齟齬が減少した。それによってリファインメントのスムーズな進行が可能となった。また、見積もり時にメンバー間でポイントに差が生まれても基準の認識について議論する時間が減り、作業内容に着目した議論がよりできるようになった

Issueのテンプレートを作成する

  • 課題

    • Issueの記述内容にばらつきがある
  • 影響

    • 私達のチームではリファインメント開催ごとに書記をランダムに決めている。これは良い点もあるが、一方でIssueの詳細や内容にばらつきが見られ、情報の過不足に繋がり、実装漏れや仕様確認が適宜発生する悪い点もある
  • 打ち手

    • 開発者が「やりたい(実現したい)こと」を理解できるよう、Issueのテンプレート化とルールを作成。「日付」「ref」「チケットの作成理由」「チケットの完了定義」など、記述内容をテンプレート化し、Issueのデフォルトに設定した
    • 設定方法:リポジトリ用に Issue テンプレートを設定する - GitHub Docs

  • 結果
    • テンプレート化したことで、従来発生していた仕様確認や実装漏れが減少。その結果、Issueアサインから着手までの時間やコミュニケーションコスト、アウトプットの認識齟齬減少に繋がった

Velocity Trackingをチームに導入する

  • 課題
    • ベロシティに波がある(以下、参考データ)

  • 影響

    • スプリントゴールを安定的に達成していくことがもっとも重要であると考えている。ベロシティに波があることは一定ペースで価値を提供しきれていないということ
  • 打ち手

    • ベロシティの推移をチームで認識・理解する必要があると考え、ZenHubのVelocityTrackingをチームに導入、説明の機会を作った

  • 結果
    • 運用自体を再検討する運びとなり、失敗に終わる。人数比を始め、突発的なIssue対応やチームへの共有範囲、ベロシティ増加が目的ではなかったことなど、懸念点が多くペンディングとなった。当時をふりかえると、ベロシティを上げることに目が行きがちで、品質よりもリリースを先行してしまう心理状態となってしまったことが反省点。別の打ち手を現在模索中

チームメンバーの相互理解を深める機会を作る

  • 課題

    • 新規メンバーが既存メンバーへの隔たりを感じている
  • 影響

    • チームでは、新しくジョインするメンバーの自己紹介の場を作っていたが、古株になるにつれ、既存メンバーについて知る機会(誰が何をやってきて、どんなことが得意不得意で、何に興味があるのかなど)がなく、新規メンバーからすると既存メンバーへ隔たりを感じていた
  • 打ち手

    • 自己紹介の場に目的を作り、コンテンツを実施。相互理解を深める機会をチームに作った

  • 結果
    • 以前よりも意思疎通をストレスなくおこなえるようになった。また副次的な効果として、対話量が増加したことで、ミーティングなどで議論が活発になる機会が増加した

Issues整理とプロジェクト管理ツールの運用ルールを再定義する

※Pipelineとは

Issueの流れを表すレーンで、そのissueが完了するまでのステータスを表します。私達のチームでは、New Issues→Sprint Backlog→In Progress→Review/QA→Closedとしています。

  • 課題
    • 2〜3年前に切ったIssuesが散乱し、緊急度・重要度・対応すべきIssueなのか否かが不明確だった
    • 使用していないPipelineが多数存在する
    • プロジェクト管理ツールにルールがなく、スクラムボードが「スクラムボードの役割」を果たしていない

過去のPipelineの全体像

  • 影響

    • Pipelineの形骸化、一向にCloseされないIssuesが散乱し、無駄な確認作業が適宜発生する。Issueの優先順位や緊急度、および重要度をスクラムボードで理解することが難しく、適宜PDMに確認する必要があった
  • 打ち手

    • 既存Issues(150ほど)をIceBox or Closeに振り分けるスクラムボード大掃除会を数回に渡り実施
    • スクラムボードで「実行可能性を瞬時に伝達する」ことを目的に、Pipelineの改善と運用ルールを定義した

改善後のPipelineの全体像

以下運用ルールを定義

  • New Issues

    • 新しいIssueを配置
    • 次のスプリント計画を立てるまでに、極力Issueをレビューし、優先度(Icebox, Product Backlog, Sprint Backlog)を振り分ける
    • リファインメントを通じて、このパイプラインを極力空にする
  • Icebox

    • やった方が良いが着手する優先度は低いIssueを配置
    • スプリント中に想定されたIssuesが完了した場合などに着手する
  • Product Backlog

    • 今スプリントでは対応しないが、次回以降にやるべき優先度の高いIssuesを配置
    • 次のスプリント計画に使ってもOKとする
  • Sprint Backlog

    • スプリント期間中に終わらせる優先度が高いIssuesを配置
    • 次のスプリントまでに空になっていることが望ましい
  • In Progress

    • 進行中のIssueを配置
    • 必ずアサインされた担当者(このIssueを完了にする責任を負う)が設定される
  • Review/QA

    • EngineerによるReview期間にあるIssueを配置
    • 開発はすでに完了している状態とする
  • Design Review

    • Engineer Reviewが完了し、DesignerによるReview期間にあるIssueを配置
    • UI周りを中心にReviewがなされる
  • Closed

    • PRがmain branchにMergeされたIssueを配置
    • 対応不要だったIssueなどを配置
  • 結果

    • スクラムボードで、Issuesをマネジメントできるようになり、自律的な判断が可能となった。結果、主体的にIssueに取り組める仕組みができ、(体感ではあるが)Issueアサインから着手までの時間短縮に繋がった

ふりかえり

半年間にわたり、チームを巻き込みながら改善を進めて参りました。チームの協力がなければ進めることはできません。

また、チームの時間を使うということは、チームにとって良い影響を生み出していかなければなりません。もっとこうしたら良いのではないか?こうするべきではないか?メンバーからさまざまなアイディアを頂く中で、結局成し遂げたいことは何なのか。

それは、「安定的にパフォーマンスを出せるチーム構築」です。改善の存在意義を決めていたことで、これを達成するためには何が必要か、何を優先とすべきかが明確になったように思います。

たとえば、OpenAPIの更新負荷や使用ツール代替案の検討という提案がありましたが、改善の存在意義の『安定的にパフォーマンスを出せるチーム構築』とは違う問題だと判断し、実施を見送りました。やらないことを判断することができ、改善の本質に時間を割くことができたと考えます。

チームとしてどうあるべきか、どういうバリューを持って突き進むべきか、まだまだ改善できることはたくさんあると思います。

うまくいかないという名の成長痛を感じながら、チームでアイディアを出し合い、より良いチームへの変貌を遂げられるよう、これからも日々精進していきたいと思います。

ドメイン駆動設計の中核は「Design」である。近い未来に訪れる組織変化に「DDD」は最適なソリューションになり得るのか

こんにちは、2022年4月にフォースタートアップスにジョインしたエンジニアの八巻(@hachimaki37)です。主にタレントエージェンシー支援システム(SFA/CRM)のシステム開発を担当しております。現在所属するチームでは、サーバサイド(Ruby,RoR)、フロントエンド(Vue.js)の役割を分けず、2週間のスプリントを切って開発を行なっております。

少し前から興味が湧いていたドメイン駆動設計(以下、DDDと呼ぶ)、ありがたいことに外部研修の参加を募るアナウンスがあったため、DDD Boot Campという外部研修を受講してきました。

詳細は後述しますが、きっかけは、近い未来に訪れる当社の一課題をDDDで解決できないか?と思ったことです。また私自身、DDDについて言葉や概念をなんとなく知っている程度であったため、実践に役立つ知識を養いたいという思いで参加してきました。

今回のテックブログは、近い未来に訪れる組織変化を考えながら、DDDとは何かを初め、DDD Boot Campを受講して見えた学びや見解、そしてアクションなどについて執筆していきたいと思います。

※注記:タレントエージェンシー支援システム(SFA/CRM)とは?

時より、この言葉を使用しておりますが、社内のさまざまな部門の方々が使う業務システムとご認識頂ければと思います。

DDD Boot Campとは?

本題に入る前に、DDD Boot Campとは何かについて簡単に説明します。

講師情報:和智 右桂さん

  • 独学ではなかなか全容がつかみにくいドメイン駆動設計についてわかりやすく学べる
  • ワークショップ形式での体験を通じ業務にいかせる実践力を身につける

業務フローやシナリオは多くの現場で使われていますが、漫然と書いていても、今ひとつぴんと来ない、ということになりがちです。 単に手を動かして成果物を作るだけでなく、きちんと理解してそれを共有するためにはどうすればよいのか、ドメイン駆動設計(DDD)のバイブルでもある『エリック・エヴァンスのドメイン駆動設計』(翔泳社刊)翻訳者でもある和智右桂氏を講師に迎え、DDDにヒントを得ながら、ワークショップ形式でポイントを学ぶ講座です。

引用元:https://event.shoeisha.jp/cza/ddd

私が参加した回は、参加者20名のオンライン実施。講義に参加してみて、DDDの概念を一から知れたこと、ワークショップがあったことで、知識が一人歩きせず、業務との接続イメージが沸き、全体を通して非常に学び多い時間となりました。

近い未来に訪れる変化と課題

それは、システム利用ユーザーの変化によるコミュニケーションの肥大化です。

以下、当社 2023年3月期 通期決算説明資料から情報を抜粋しています。

引用元:https://pdf.irpocket.com/C7089/bU43/fyL8/CPUq.pdf

入社から現在にかけて、タレントエージェンシー支援システム(SFA/CRM)のシステム開発を担当しておりますが、組織拡大に伴い、今後様々な問題が出てくることが想定されます。その一つが社員数純増に伴う問題です。上記資料を見てわかるように、社員数が前期末比 +51名純増であること、そして4月以降更なる純増が見込まれます。

そこで、現在もなお顕在化しつつある問題の一つが、入社比率の変化です。具体的には、業務システムに慣れてない新人の方々が社員数全体の約40%となる近い未来が見えており、生産性の低下が大きな課題となってきています。

引用元:https://pdf.irpocket.com/C7089/bU43/fyL8/CPUq.pdf

現行システムでは機能しないのか?

決してそう言うことではありません。この先を見据えた進化、それはもっと未来にフィットさせたプロダクトへの進化が必要なのではないかという事です。

あくまで個人的な考えですが、タレントエージェンシー支援システム(SFA/CRM)は、ローンチされてからすでに数年が経っております。当初のプロダクトビジョンと今向かうべきビジョンにギャップが出始めているのではないかと考えております。それもそのはずで、会社としてのフェーズ・組織デザイン(人数・体制)が変われば、システムの課題や価値も自ずと変わっていくことでしょう。

未来に向けた新たなプロダクトビジョン

実は最近、「シンプル」と「安全」をキーワードにした新しいプロダクトビジョンが定義されました。つまりそれは、次の未来へ繋げる「意志」と「価値提供」がプロダクトに吹き込まれたという事です。そして、私どものチームでは、以下テーマを設定し、開発を進める形となりました。

  • 人員数の増加(業務システムに慣れてない新人比率の上昇)に対応できる環境
  • 業務プロセスの改善(効率的な業務進行)

これらの問題と課題を鑑みた上で、方向性を決め、システム開発に繋げていく必要があります。

どう解決していくのか

とある日、Bizからアイディアを募る連絡が所属チーム内にありました。

エンジニアチームで見える、タレントエージェンシー支援システム(SFA/CRM)の改善ポイント、ブレストレベルで構いませんので、ぜひアイディアをよろしくお願いします!

的外れなアイディアも多々ありますが、いくつか考え、Bizへ提案してみました。

テーマ:情報を「探す」プロセスを改善する

数ヶ月前に情報の「集約」と「透明化」を目的とした、比較的大きいリリースが行われました。次に課題となるのは「情報伝達」だと考えました。情報を人へ伝え届けるためには、「探す → 見つける → (自身が)理解する → (論理や体系を立てる) → 伝える → (相手が)理解する → 伝わる」といったプロセスがあると考えており、そもそも「探す」行為に相当なコストがかかっている(当社ではUXリサーチを実施しており、そこで得た情報から見えてきた課題感)のではないかと考えました。

情報収集のプロセスを改善することで、業務プロセスの改善(効率的な業務進行)に繋がると考えました。

テーマ:ユビキタス言語を使う

人の増加や入社時期の違いから、コミュニケーションコストが以前に比べ、倍以上に増加することが想定されます。なぜなら、バックグラウンドが人によって異なるため、言葉の定義や理解の違いから意思疎通が困難になるからです。

つまり、本来使うべき時間的コストが失われるため、共通言語化を図り、コミュニケーションコストを極力減らしていく策が良いのではないかと考えました。

など、さまざまなアイディアを考えてみました。

うっすら見えてきた課題感と解決の光

Bizとの共通認識である課題、それはユビキタス言語(共通言語)の活用です。「共通言語が少ない、もしくは合っていないか」そういった課題をBizは抱えておりました。私どもの新たなプロダクトビジョンであるシンプルとは何か。詳細は割愛させて頂きますが、一つの意味合いには「言葉の意味整理と統一(ユビキタス言語)」という概念があります。

少し糸口が見えてきた気持ちでした。そして、その課題解決を実現する手段の一つがDDDであると考えました。ようやく、DDDの話です。

ここからは、DDDとは何かを初め、参加してみての気づきや学びについて書いていきたいと思います。(具体的な方法論は述べていません)

重要な観点

ドメイン駆動設計は、「Design」である。よく見かける〇DD(例えば、TDD, MDD, UCDD…)は、Development(開発やテスト)の話ですが、Domain-driven design(DDD)は、開発手法ではなく、デザイン手法・設計の話であることを念頭に置く必要があります。よく出会う問題とそれにうまく対処するための設計であり、将来の変更や発展性に耐え得るかというアーキテクチャ的観点が必要です。

学んだこと

大きく下記4点です。

1. ソフトウェアには、業務知識を反映させる

DDDとは、エリック・エヴァンス氏により提唱され、一言で言うと「ソフトウェアには、業務知識を反映させましょう」という概念です。具体的には、頭の中に構成している業務知識を抽象化して反映させることです。ただし注意点があり、単なる業務知識を反映させるのではなく、不要な概念や知識が省かれ、「選び抜かれている」点がポイントです。

2. 隠れた概念を見える化し、ドメインモデルに(境界線を)反映させる

「どういう業務であるか」という概念の見える化を行う手法を様々知ることができました。例えば、業務フロー図、ユースケース図、ロバストネス図などです。ここでのポイントは、選び抜かれた業務知識の業務構造に関する理解を、「そのままソフトウェアで表現する」ことです。 業務知識を表現することで、業務理解とプログラム設計を直接的に関連づけられることで、プログラムがわかりやすく整理させ、ソフトウェアを柔軟に拡張したり、安全な変更を可能とします。

3. 1と2を実現するには、ユビキタス言語(共通言語)の認識と理解が重要である

Devだけで、完結するものではありません。Bizが選び抜いた業務知識を反映するだけでは物足りません。DevとBiz、双方がドメインモデル(選び抜かれた業務知識)への共通認識と理解が重要なのです。そのためにはまず、業務フローの中で使われている言葉の定義と認識を合わせることが、ファーストステップとして必要です。

4. 大切という感覚は、抽象化した自分の解釈における価値観である

言葉スケッチというアイスブレイクを冒頭に行いました。1対Nの関係で、1がお題となる写真を言葉だけでNに伝え、その情報を元にNがスケッチするといった内容です。私は、Nの立場で参加しましたが、思いの外難しい。1の立場からすると、そもそも描かれている全ての情報を説明することは困難であり、目に映るモノの中で、大切だと思うモノを自身で選択して説明しています。つまり、伝えている情報は、そのモノ自体ではなく、自分の理解(=モデル)なのです。

ユビキタス言語の認識と理解を合わせるためには、言葉だけのコミュニケーションではなく、上記で紹介した〇〇図などを使い、双方の概念やモデルの見える化を行い、共通のイメージを合わせる過程が必要です。どの言葉を選択し、どのように相手の頭の中を整理しながらモノごとを伝えていくのか、このような「言葉で作りたいモノを伝える」ことは非常に重要なスキルです。

実践してみようと思うこと

1. 言葉の意味整理と統一(ユビキタス言語)

ここは欠かせないと感じました。DDDをやるやらないに関わらず、言葉の共通理解の重要性を改めて感じました。まずはチーム内から、そしてユーザーとのコミュニケーションをユビキタス言語で図れる世界に近づけるよう、ファーストステップを踏んでいきたいと思います。

2. 業務フロー図、ユースケース図、ロバストネス図、ホワイトボードなどを使って、図でイメージを伝える

私自身、(例えばロジックなど)テキストや言葉だけで結構伝えていたなぁと痛感しました。つまり、イメージの理解がおそらく合っていないということです。伝え方もあるかとは思いますが、根本的に各々が見ている視界や視点(DevとBizなども含めて)が異なるため、捉え方・理解の仕方がバラバラになっていると感じました。共通のイメージや認識を持てるよう、図や表現でイメージを伝え、見ている世界がより鮮明になるようにしていきたいと思います。

3. ソフトウェアには、業務知識を反映させる

共通言語の認識を合わせた上で、設計してみるといった内容です。今までは、Dev視点でDevにとってわかりやすいER、クラス、命名などにしておりましたが、少なからず業務フローで使用している言葉を使い、共通認識を持って進めていきたいと思います。その前のファーストステップは、ユビキタス言語やコンテキストマップ作成などから計画的に進められると良いかもしれません。

まとめ

これら3点が少しずつ浸透し、形になって行くだけでも、前述した組織の一課題解決に近づいていくのではないかと考えます。

講義を聞いていて、DDD=オブジェクト指向ではないか?と思った節はありましたが、オブジェクト指向との違いは、やはり「ドメインモデルがエンティティに反映されているかどうか」が大きな違いであることを知りました。何度も言いますが、DDDとは、ソフトウェアに業務知識を反映させることが重要です。つまり、「現実 → モデル → ドメインモデル → コード」を実践することで、ソフトウェアの価値を高めることを目指すものなのです。

ただし、全てDDDにすればいいじゃん!というものでもありません。不向きなシステム、不向きな開発サイクル、場合によっては損益分岐点を下回る可能性もあります。つまり、ドメイン駆動設計は、「Development」ではなく、「Design」であることを忘れてはいけないということです。

何をしたいのか、そのために何が必要なのかを考え、どう実現するかの順番が重要です。

参考資料

コードレビュー自動化 Siderのサービス終了に伴い、GitHub Actionsで実行できるreviewdogの調査・導入をしてみた

こんにちは、2022年4月にフォースタートアップスにジョインしたエンジニアの八巻(@hachimaki37)です。主にタレントエージェンシー支援システム(SFA/CRM)のシステム開発を担当しております。

コードレビュー自動化サービス Siderがサービス終了となった背景から、移行先となるサービスを調査し導入をしました。今回のテックブログでは、調査過程で出てきた疑問、そして調査結果、導入方法などを合わせて執筆していきたいと思います。

背景

タレントエージェンシー支援システム(SFA/CRM)では、コードレビュー自動化サービスとしてSiderを利用し​​ておりました。そんなSiderですが、sider.reviewサービス終了のお知らせとして、2022年12月31日に全てのサービスと技術サポートの提供が終了となりました。

sider.reviewサービス終了のお知らせ

このサービス終了を受け、Siderの移行先となる代替案を検討する運びとなりました。なお所属のチームでは、CI/CDはGithub Actionsを使っております。

目的

普段当たり前に行っているコードレビューですが、導入調査を機にコードレビューの目的ってなんだ?とふと疑問に思い、改めて調べてみました。

コードレビューの目的とは

レビューを通して、欠陥の早期発見による手戻りコストの低減を行い、ソフトウェアの品質を高めることです。

つまり、ソフトウェアの品質を高めるためにコードレビューを行っているのです。とは言え、どのような観点でコードレビューを行なえば、ソフトウェアの品質を高めることができるのだろうか。手動でやる場合、これらを一から明確にし定義しなければなりません。なかなかの手間と時間ですね。

コードレビュー自動化の目的とは

目的にいく前に、まず2つの疑問を持ちました。一つ目は、コードレビュー自体を、そもそも自動化するメリットがあるのだろうか?という点です。この疑問は、目的を理解することで、調査早々に解消されることとなりました。

コードレビュー自動化サービスを導入することで、問題点や気をつけるべき点などを自動的に検知し、開発者に報告してくれます。つまり、コードレビューにかかる時間や人的な見落としを削減することができるのです。とは言え、導入・運用コストに見合うのだろうか?という点が二つ目の疑問でした。

コードレビュー自動化には、以下のような目的も兼ね備えております。

  • コードレビューにかかる時間を削減
  • エンジニア個人や開発チームの成長
  • ソフトウェアの品質向上
  • など

つまり、コードレビュー自動化サービスを導入することで、コスト面以外にも様々なメリットがあることを今回の調査を通じて理解することができました。導入しない選択肢はありませんね。

Siderの概要について

引用元:コードレビュー自動化サービスSiderのご紹介 Sider代表取締役社長 浅原明広氏

タレントエージェンシー支援システム(SFA/CRM)の設定解析ツール

解析ツールの概要

  • RuboCop
    • Rubyの静的コード解析ツール。規約を守れていないコードに警告をだすだけではなく、インデントや改行位置の修正、使用NGなメソッドの置換なども自動で行う
  • Brakeman
  • Reek
    • Rubyソースコードを静的に解析することで、「コードの臭い」を検出するツール

※Reek と RuboCop では、検出する問題の種類に違いがあります。

代替案の調査

そこで代替案の観点として、以下を定義しました。

  • 導入コストが低い
  • ドキュメントが豊富
  • 解析結果をPull Requestのレビューコメントに表示できる
  • 既存の解析ツールのサポートがある

1. reviewdog

概要

代替観点としての評価(※あくまで個人的な意見です)

  • 導入コストが低い △
    • 各種linterを設定ファイルに記載する必要があるため
  • ドキュメントが豊富 ○
    • 概要や導入方法などをGoogle検索した結果、多数ヒットするため
  • 解析結果をPull Requestのレビューコメントに表示できる ○
    • 各種linterの実行結果を渡すことで、Pull Requestなどの差分に関して、警告された行をレビューコメントに表示できるため
  • 既存の解析ツールのサポートがある ○
    • タレントエージェンシー支援システム(SFA/CRM)で使用する解析ツールを始め、様々サポートがあるため

料金

Free(OSS

2. HoundCI

概要

代替観点としての評価(※あくまで個人的な意見です)

  • 導入コストが低い ○
    • HoundCIの公式サイトからGithubアカウントでログインし、監視対象リポジトリを設定するだけのため
  • ドキュメントが豊富 △
    • 概要や導入方法などをGoogle検索した結果、数件しかヒットしないため
  • 解析結果をPull Requestのレビューコメントに表示できる ○
    • コーディング規約違反のコードだった場合、警告された行をレビューコメントに表示できるため
  • 既存の解析ツールのサポートがある △寄りの○
    • タレントエージェンシー支援システム(SFA/CRM)で使用する解析ツールはあるものの、reviewdogと比べるとサポート数が少ないため

料金

  • Chihuahua:Up to 50 private reviews $29 month
  • Terrier:Up to 300 private reviews $49 month
  • Labrador:Up to 1,000 private reviews $99 month
  • Husky:Up to 3,000 private reviews $199 month

その他 候補

調査結果

コードレビュー自動化サービスは、これに決めた!

上記観点を鑑みてSiderの代替案は、reviewdogを採用する形にしました。以下、静的解析ツールをreviewdog(Github Actions)を通して、Pull Requestに表示するよう設定を加えました。

  • Rubocop
  • Brakeman
  • Reek
  • Stylelint

最終的なコード

.github/workflows/reviewdog.yml

name: run Review Dog
on: pull_request
jobs:
  rubocop:
    name: RuboCop
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.1.1

      - name: RuboCop
        uses: reviewdog/action-rubocop@v1
        with:
          rubocop_version: gemfile
          rubocop_extensions: rubocop-rails:gemfile rubocop-rspec:gemfile
          github_token: ${{ secrets.github_token }}
          reporter: github-pr-review
          fail_on_error: true

  brakeman:
    name: Brakeman
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.1.1

      - name: Brakeman
        uses: reviewdog/action-brakeman@v2
        with:
          brakeman_version: 5.2.0
          reporter: github-pr-review
          fail_on_error: true

  reek:
    name: Reek
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.1.1

      - name: Reek
        uses: reviewdog/action-reek@v1
        with:
          reek_version: gemfile
          reporter: github-pr-review
          fail_on_error: true

  stylelint:
    name: StyleLint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 16

      - run: yarn install
      - name: StyleLint
        uses: reviewdog/action-stylelint@v1
        with:
          reporter: github-pr-review
          stylelint_input: '**/*.scss'
          fail_on_error: true

設定がうまく行けば、下記のようにGitHub Actionsから各種解析ツールが実行されるようになります。

Successfulの場合

Failingの場合

調査過程の紹介

RuboCop

調査対象として第一に行った解析ツールは、RuboCopです。自動コードレビューの結果をGitHubのコメントに出力するよう設定を加えました。

公式ドキュメント:https://github.com/reviewdog/action-rubocop

公式ドキュメントを参考に、.github/workflows/rubocop.ymlに定義します。あるメソッドを’return if -> if else’に修正したところ、GitHub Actionsから自動レビューコメントが付くようになりました。

その後、残りの解析ツールを公式ドキュメントを参考に設定しました。

Brakeman

ドキュメント:https://github.com/reviewdog/action-brakeman

Reek

ドキュメント:https://github.com/reviewdog/action-reek

Stylelint

ドキュメント:https://github.com/reviewdog/action-stylelint

まとめ

Siderの代替案として、reviewdogを採用しました。reviewdog導入前の印象は、他のツールに比べて、GUIでの設定ができないため、心理的に「大変そう...」でした。ただ、実際にドキュメントを読みながら設定してみると、思いのほか簡単に設定することができます。

実際一番の悩みどころは、Siderの代わりとなる静的解析ツールは何がベターなのかという点です。今回、reviewdog以外のツールを導入しておらずですが、reviewdogは気軽に試せるので、一旦導入してみて運用しながら改善していく方針でも良いのではないかと思いました。

参考資料

Figma APIを使用し、svg形式のアイコンを /figma/images 配下にインポートするrake taskを実装してみる

こんにちは、2022年4月にフォースタートアップスにジョインしたエンジニアの八巻(@hachimaki37)です。入社から早半年間が経ちました。引き続き、社内向けプロダクト「タレントエージェンシー支援システム(SFA/CRM)」のシステム開発を担当しております。

はじめに

百聞は一見にしかず!ということで、まずはどんなモノか動画をご覧ください(34秒)

ご視聴ありがとうございました。今回のTech Blogは 、「Figma APIを使用し、svg形式のアイコンを/figma/images配下にインポートするrake taskを実装してみる」です。開発の背景や目的、苦労話などを交えながら執筆していきたいと思います。

事前に様々な記事を拝見させて頂きましたが、アイコンをFigmaで管理する上で、どこも同じような課題感があるんだなと所感を持ちました。

  • アイコンが大量に増えて、管理がめんどくさい
  • 毎度更新されたアイコンをsvgに書き出すのが手間
  • わざわざFigmaを見に行くのがめんどくさい
  • デザイナーがfigma上でアイコンを編集していたことが開発者に伝わらずに、アウトプットが予想と違うものになった
  • アイコンの更新漏れが発生した
  • などなど・・

開発の背景

所属チームでは、2022年7月に新しくUI/UXデザイナーの方を迎え(2022年4月からUI/UXデザイナーが不在)、タレントエージェンシー支援システム(SFA/CRM)のDesign System構築が本格的にキックオフされました。

以下、デザイナーの@Minmi303より得た内容です。

Design System構築に至った経緯

  • 既存のDesign Systemが使われていない(使いづらい)状態だった
  • サービスの詳細、機能、画面全てを網羅している人がいない(網羅するのが難しいほどの画面数)
  • デザインのテイストが2〜3種類ほど混在していた (例:アイコンがデザイナーが作ったものとFont Awesomeが混在していた)
  • 機能追加や削除など、開発のペースが早い
  • デザイナーの効率化を図る仕組みが必要だった(デザイナーが1人のため、作業時間の短縮を図りたい)
  • エンジニアサイドの仕組み化も必要だった(同じパーツでもコードがまとまっていない)
  • などなど・・

Design System構築前に抱えていたUI/UX周りの課題

  • UXを検討するフローが抜けていた
  • デザインレビューのフローがなかった(デザインを担保できない)
  • デザインのトンマナが整っていない
  • 多くの画面がUIを深く考慮されておらず、機能だけがある形の仕様になっていた
  • などなど・・

上記のような背景からDesign System構築が進むにつれ、Colors, Text Rule, Iconsなどが刷新され、開発者用にFixデータがFigmaに格納されるようになりました。

開発の目的

そこで課題に上がったのが、アイコンの格納方法です。

▼議論されたイメージ図

当初、「Figma -> Notion -> タレントエージェンシー支援システム(SFA/CRM)」という格納フローを検討しておりましたが、デザイナー/エンジニア間、双方に課題感を持っておりました。

  • デザイナー: svg書き出しの手間、更新されたアイコンの適応状況 など
  • エンジニア: svgの配置、適用ディレクトリの検討、アイコンの更新漏れ など

結果、Notion管理を不要とし、Figma APIを活用する方針となりました。よって、「Figma -> タレントエージェンシー支援システム(SFA/CRM)」という格納フロー構築を目指し、開発スタートに至ります。

Figma APIとは?

What can I do with the Figma API?

The Figma API supports read access and interactions with Figma files. This gives you the ability to view and extract any objects or layers, and their properties, so you can render them as images outside of Figma. You can then present your designs, connect them to other applications, or use them to expand on your vision. Future versions of the API will unlock even greater functionality around Files.

How does it work?

The Figma API is based on the REST structure. We support authentication via access tokens and OAuth2. Requests are made via HTTP endpoints with clear functions and appropriate response codes. Endpoints allow you to request files, images, file versions, users, comments, team projects and project files.

引用元 公式ドキュメント:https://www.figma.com/developers/api

端的に言うと、Figma API を使用することで、Figma上の様々なデータ取得が可能になります。こちらを用いて、格納フロー構築を行いました。

やったこと

概要

rakeコマンドを実行することで、Figmaからsvg形式のアイコンを/figma/imagesディレクトリにインポートする

大まかな実装イメージ

  • rake taskに以下処理を実装
    • Figma API コールの定義
    • svgダウンロードURLを取得する
    • svgをダウンロードする
    • 生成したファイルにsvgを書き込む

コードの紹介

※アイコンは以下ページから取得することとします

そしてfigma.rakeはこのようになりました

namespace :figma do
  desc 'import svg icons of Figma API'
  task :import_icons do
    # Figma API コールの定義
    file_key = Settings.FIGMA_FILE_KEY
    x_figma_token = Rails.application.secrets.x_figma_token
    figma_url = "#{Settings.HTTPS_PROTOCOL}://#{Settings.FIGMA_HOST_URL}/files/#{file_key}"
    header = { 'X-Figma-Token' => x_figma_token }

    # svgダウンロードURLを取得
    body = get(figma_url, header)
    body['document']['children'].each do |children|
      next unless children['name'] == 'New Icons'

      children['children'][0]['children'].each do |child|
        id   = child['id']
        name = child['name'].gsub('/', '-')
        next unless name.include?('icon-')

        # svgをダウンロード
        svg_download_url = "#{Settings.HTTPS_PROTOCOL}://#{Settings.FIGMA_HOST_URL}/images/#{file_key}?ids=#{id}&format=svg"
        child_body = get(svg_download_url, header)
        child_res = res(child_body['images'][id], header)

        # 生成したファイルにsvgを書き込む
        file = "app/src/javascripts/spa/assets/figma/images/#{name}.svg"
        File.open(file, 'wb') do |f|
          f.write(child_res.body)
        rescue SystemCallError => e
          puts "class: [#{e.class}] message: [#{e.message}]"
        rescue IOError => e
          puts "class: [#{e.class}] message: [#{e.message}]"
        end
      end
    end
  end

  private

  def get(url, header)
    res = res(url, header)
    JSON.parse(res.body)
  end

  def res(url, header)
    url = URI.parse(url)
    Net::HTTP.get_response(url, header)
  end
end

ソースコードの説明を加えていきたいと思います。

Figma API コールの定義

file_key = Settings.FIGMA_FILE_KEY # ※1
x_figma_token = Rails.application.secrets.x_figma_token # ※2
figma_url = "#{Settings.HTTPS_PROTOCOL}://#{Settings.FIGMA_HOST_URL}/files/#{file_key}" # ※3
header = { 'X-Figma-Token' => x_figma_token } # ※4

Figma APIを使用するにあたり、「File Key」と「Access Token」が必要になります。

svgダウンロードURLを取得

body = get(figma_url, header)
body['document']['children'].each do |children|
next unless children['name'] == 'New Icons'

    children['children'][0]['children'].each do |child|
       id   = child['id']
       name = child['name'].gsub('/', '-')

Figma APIをコールすると、

{"document"=>
  {"id"=>"0:0",
   "name"=>"Document",
   "type"=>"DOCUMENT",
   "children"=>
    [{"id"=>"0:1",
      "name"=>"Page 1",
      "type"=>"CANVAS",
      "children"=>
       ・
  ・
  ・
    }]
  }
}

のようなJSON形式で指定したファイルの値が返ってきます。今回Figmaから「New Icons」というページ(上の画像を参照)からアイコンを取得したいため、'next unless'で'New Icons'の値を取得、eachで返ってきた値を変数idとnameに格納します。今回、アイコン名に階層('icon/icon-hoge'のような)があったため、gsubメソッドで置換した値を変数nameに格納する形にしました。

svgをダウンロードする

svg_download_url = "#{Settings.HTTPS_PROTOCOL}://#{Settings.FIGMA_HOST_URL}/images/#{file_key}?ids=#{id}&format=svg" # ※1
child_body = get(svg_download_url, header)
child_res = res(child_body['images'][id], header)

※1 本来は、'https://api.figma.com/v1/images/#{file_key}?ids=#{id}&format=svg"'のようなHTTP Endpointになります

HTTP Endpointには、一つ前で格納したそれぞれのidを、そしてsvg形式で値を取得したい場合、’ format=svg ’を末尾に加え、APIコールをします。すると、以下のような値がレスポンスとして返ってきます。

=> "<svg width=\"108\" height=\"62\" viewBox=\"0 0 108 62\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M53.9998 61.5632C52.0642 61.5632 50.1289 60.8241 48.6532 59.3491L2.21553 12.911C-0.73851 9.95695 -0.73851 5.16748 2.21553 2.21464C5.16838 -0.738212 9.95689 -0.738212 12.9112 2.21464L53.9998 43.3057L95.0887 2.21607C98.0427 -0.736777 102.831 -0.736777 105.783 2.21607C108.739 5.16892 108.739 9.95839 105.783 12.9124L59.3464 59.3506C57.87 60.8258 55.9347 61.5632 53.9998 61.5632Z\" fill=\"black\"/>\n</svg>\n"

これで、svg形式のアイコンを取得することができました。そして、この値を変数に格納し、次のステップへいきます。

生成したファイルにsvgを書き込む

file = "app/src/javascripts/spa/assets/figma/images/#{name}.svg"
File.open(file, 'wb') do |f|
  f.write(child_res.body)
rescue SystemCallError => e
  puts "class: [#{e.class}] message: [#{e.message}]"
rescue IOError => e
  puts "class: [#{e.class}] message: [#{e.message}]"
end

最後に、一つ前で取得したsvg形式のアイコンをファイルに書き込みます。1行目(file = xx)には、変数nameを加えたファイルを定義し、書き込みにはFileクラスのopenメソッドを使用しました。あとは、取得したsvg形式のアイコンをwriteメソッドを使用して、指定したファイルに書き込めば完了です。

はまった点

チケット完了のゴールは定まっていたものの、そもそも実装イメージが皆無だった

  • Figma APIとはなんぞや...
  • どのようなTODOで進めて行けば良いのだろうか..
  • Figma自体さほど触っておらず、理解が浅い...

そんな状況からスタートしました。やったことは、開発着手前に「Figma APIの公式ドキュメントを一読する」、「現時点で思いつくTODOを洗い出す」ことから始めました。今までの経験から、プログラミングで右も左もわからなくなるケースは、大体何をすべきか理解できていないことが多かったため、着手前に思いつくTODOを洗い出し、都度TODOを追加していくようにしました。一例ですが、「Figmaにテストデザインを作成し、そのデザインをFigma API をコールして取得する」ことをTODOに含め実施したことで、「Figma APIの理解が進んだこと」、「やるべきことが都度明確になっていったこと」で、実装イメージが膨らみ、進めることができました。

svg形式のアイコンを取得したかったが、HTTP Endpointを叩くとレスポンスの値がnilになる

以下のようなHTTP Endpointをコールすることで、svgに置換された値がレスポンスされるはずでしたが、なぜかnilになっていました。

'https://api.figma.com/v1/images/#{file_key}?ids=#{id}&format=svg

pry(main)> child_res.body
=> nil

調査を進めていくと、’ ids=#{id} ’に格納した id の値が誤りであることがわかりました。APIのレスポンス値である’ components id ‘を格納していたことがnilの原因であったため、各アイコンに紐づく id に変更することで解消されました。

工夫したこと

今後の運用を考慮したこと(なるべくシンプルに保つ)

運用についてデザイナーと議論し、以下のことを決めました。

  • アイコンの命名規則
    • 基本的に「icon-」の形式とする
  • アイコンのツリー構造
    • 基本的にノード同士は親子関係にはせず、並列とする
  • アートボードの運用
    • 基本的に1アートボードにアイコンを集約する。もしアートボードを追加する際は、エンジニアに一報を入れる。(複数のアートボードからアイコンを取得する場合、データ構造を見直す必要の可能性があるため)
  • exportされるページへの注意喚起
    • 後任者などが見ても理解できるようFigmaのページ上に注意喚起を追記する

ソースコードの汎用性や可読性を考慮したこと

  • 汎用的に使用できるよう、Figma APIコールの定義をsettingsにまとめました。また、X_FIGMA_TOKENは作業者で異なるキーを運用するため、「gitの管理対象ファイルにして、間違えてコミットしてしまう可能性」を考慮し、.gitignoreにあるファイルで管理するようにしました

  • 処理のメソッド化、直感的に理解が進むよう必要最低限のコメントを追記、またソースコードを処理ごとにまとめ実装しました

改善余地があること

Access Tokenの運用方法

APIコールに使用するAccess Tokenに関して、チームで1つのTokenを運用するか、もしくは個人個人のTokenで運用するか、別途議論の余地がありました。所属チームの現時点での運用は、個人個人での運用とし、必要に応じて議論する形にしました。

image optimizerの検討

画像圧縮についてです。理想は、APIコールからsvgをダウンロードした際、プログラム上で画像圧縮が実行できればベストでしたが、優先度の兼ね合いから今回のチケットではスコープ外とし、対応可否を別途検討する形にしました。

Github Actionsでの定期実行

手動でコマンドを叩くこと自体が課題になるかもしれません。こちらも今回の対象チケットには含めなかったため、定期実行をScheduleに組込み、自動実行できる仕組み構築が今後必要になってくるかもしれません。

まとめ

実装から無事リリースに至り、これから運用へとプロセスを踏んでいきます。運用しながら課題が新たに発生するかもしれませんが、ひとまず元々感じていた双方の課題を解消できる開発になったのではないかと思います。

あとがき

私自身、今回初めてFigma APIに触れましたが、思いのほか手軽に実装することができました。というか、初めてでもわかりやすい仕様だったということでしょう。デザイナー、エンジニア双方の課題感を知り、実装を進められたこと、非常に楽しくワクワクしながら開発できました。

最後に採用情報です。 当社では、まだまだ採用募集中です。ぜひ一緒に成長していきませんか。 ご興味ありましたらぜひ一度カジュアルにお話できたらと思います。

採用ページはこちら。(冒頭のTwitterにDM頂いてもOKです!)

参考資料

スプリントレトロスペクティブ本来の目的とは?初めてファシリをやって体感した「難しさ」と「学び」

こんにちは、2022年4月にフォースタートアップスにジョインしたエンジニアの八巻(@hachimaki37)です。主に社内向けプロダクト「タレントエージェンシー支援システム(SFA/CRM)」のシステム開発を担当しております。

今回は、スクラムのイベントの一つである「スプリントレトロスペクティブ(以下、レトロスペクティブ)」について書いていきたいと思います。

早速ですが、レトロスペクティブをやっていて、こんなこと思ったことありませんか?

  • 最近なんとなくやってるなー
  • テーマを出したいけど、これで大丈夫かな..(迷う..)
  • TRY実行してるけど、何が改善されたんだ?
  • チームで議論すべきテーマってこれでいいの?
  • ふりかえりやだなぁ..

みたいな・・

ぜひこの機会に1つ考えて頂きたいことがございます。 それは、「あなたは、なぜレトロスペクティブ(ふりかえり)を行っていますか?

そんな「なぜ」を解消する内容にしてみました。

レトロスペクティブとは?

スクラムチームは、個人、相互作用、プロセス、ツール、完成の定義に関して、今回のスプリントがどのように進んだかを検査する。多くの場合、検査する要素は作業領域によって異なる。 スクラムチームを迷わせた仮説があれば特定し、その真因を探求する。スクラムチームは、スプリント中に何がうまくいったか、どのような問題が発生したか、そしてそれらの問題がどの ように解決されたか(または解決されなかったか)について話し合う。

スクラムチームは、自分たちの効果を改善するために最も役立つ変更を特定する。最も影響の大きな改善は、できるだけ早く対処する。次のスプリントのスプリントバックログに追加することもできる。 スプリントレトロスペクティブをもってスプリントは終了する。スプリントが1か月の場合、スプリントレトロスペクティブは最大 3 時間である。スプリントの期間が短ければ、スプリントレトロスペクティブの時間も短くすることが多い。

引用元:スクラムガイド

これを読んで考えたことは、「スプリント中に何がうまくいったか、どのような問題が発生したか、そしてそれらの問題がどのように解決されたか(または解決されなかったか)について話し合う」ことはあくまで手段であり、「自分たちの効果を改善するために最も役立つ変更を特定する」ことが最も重要だということです。

後述しておりますが、当初私が考えていた重要なことは、「スプリントであった出来事を整理し、チームの課題を改善すること」でした。ここに大きなギャップがありました。

所属チームのふりかえり手法と決め方について

所属チームでは、2週間のスプリント最終日にレトロスペクティブを開催し、経験年数に関わらずサイコロを使いランダムにファシリテーターを決めています。ファシリテーターに決まった方は、自身でふりかえり手法や段取りを考え実施します。

当社のレトロスペクティブの歴史としては、特定のファシリテーターがおり、基本的にKPTにてふりかえりを行っていたみたいですが、日々改善される中で今の形へと変わっていきました。

実施したレトロスペクティブの紹介

デイリーハッスル

デイリーハッスルは、「日常の慢性的なわずらわしい出来事を解消していくふりかえり手法」です。ストレスマネジメントの権威であるLazarusとFolkmanが1980年代に提唱した概念で、ストレス心理学におけるストレッサーの区分モデルのひとつです。

ラクティスとしてのデイリーハッスルは、この記事を参考に実施させていただきました。

引用元:ふりかえり手法のおもちゃばこ(前編)

  • 実施結果

Time Line

Time Lineは、「事実と感情の両方を合わせて書き出していき、全員で共有するふりかえり手法」です。書き出した事実を時系列に並び替え共有することで、チームの持つ情報を整理し、カイゼンのためのアイデアを出しやすくします。

  • 実施結果

工夫したこと

チーム状況に合った手法を選択

  • デイリーハッスルをやった時期は、新しいメンバーが加わったこともあり、身の回りの問題に焦点を当てられるふりかえり手法を選択しました。また合わせてメンバーの価値観(どんな場面でデイリーハッスルを感じるのか)を共有できるチャンスでもあったため、目的に加えて実施しました。
  • Time Lineを行った週は、イベントが多いスプリントであったため、日々やったことをチームで整理できるふりかえり手法が良いかと考え、Time Lineを実施しました。

Notionを活用

チーム内に出社組・リモート組がいたため、場所に囚われないようNotionを活用しました。ホワイトボードを活用する機会が多かったですが、ふりかえり手法によってはNotionも使用可能だと成功体験を得ることができました。

付箋の色分け

Time Lineを行う際に、付箋の色を分けたことで「事実」と「感情」の判別がしやすくなり、スムーズな進行が可能になりました。

体感した「難しさ」と「学び」

議論すべきテーマの選択

ふりかえりの議論となったテーマが、チームに対する共有事項であったり、影響範囲がチーム外の事柄、個々人によりすぎたテーマとなってしまいました。良し悪しというよりは、何のためにレトロスペクティブをやるべきなのか?を考えるきっかけになりました。

本当にそのTRYでProblemを解消出来るのか

つまり、TRYの精度です。TRYのアイディアが出されるも「本当にProblemを解消出来るTRYになっているのか?」を考えすぎてしまいました。正直やってみないことには何もスタートしません。そのため「決めたTRYを実行し、改善を繰り返す」という目線に変え、TRYを決めていきました。

レトロスペクティブの目的とは?

私が考えていたレトロスペクティブの意義

  • チーム課題を改善すること?
  • チームで洗い出したProblemを改善すること?
  • スプリントであったことを整理すること?
  • スプリントをふりかえること?
  • チームで決めたTRYを実行すること?
  • ...

私が考えていたレトロスペクティブの目的(意義)は、「スプリントであった出来事を整理し、チームの課題を改善すること」だと考えていました。しかし、スクラムガイドを再読した際に、レトロスペクティブ本来の目的を理解していなかったことに気づきました。

レトロスペクティブ本来の目的

スプリントレトロスペクティブの目的は、「品質と効果を高める方法を計画すること」である。

引用元:スクラムガイド

つまり、「チームがもっと効率を高めることができるTRYを計画できたか」です。

重要なファクター

スクラムガイドでは、以下2点が重要であると述べられているように思います。

  1. スプリント中に何がうまくいったか、どのような問題が発生したか、そしてそれらの問題がどのように解決されたか(または解決されなかったか)について話し合う。 ◯所属チームは、現状できている
  2. 自分たちの効果を改善するために最も役立つ変更を特定する。 ✖️所属チームは、現状できていない

これらを通して思ったことは、目的を意識することが大切だということです。「目的を理解していない = いつまでたってもゴールにたどり着くことはできない」。つまり、品質と効果を高められるTRYを計画できるにはほど遠いという事実を知ったことが、今回感じた最大の学びでした。

今回得た学びを実践した結果をふりかえる

今回の学びを通じて、個人的に決めた3つのTRYを実行してみました(3に関しては、時間の都合でできず..次回のTryとして実施していきたいと思います)

  1. レトロスペクティブの目的をチームへ周知する
  2. 自分たちの効果を改善するために最も役立つであろう変更を議論のテーマとする
  3. 品質と効果を高められるTRYを計画できたかをふかりえり後にチームに確認する

まず1に関しては、事前にタイムスケジュールを組み、レトロスペクティブ開始前に実施しました。やったことは、今回得た知見をチームへアウトプットするといった内容です。

共有自体はさほど難しいことではありませんが、やはり最大の課題は2の「最も役立つであろう変更を議論のテーマとする」ことでした。一人3票、議論したいテーマに各々投票する形式にしましたが、普段よりも投票に時間がかかった印象でした。チームとして、何を改善(議論のテーマと)すれば、「チーム効率がもっと高まるのか」を考える必要があるため、いつもよりも俯瞰的に物事をみる必要があったかと思います。

ご参考までに今回議論となったテーマです。

  • 朝会の運用をもう少し改善できないか?
  • 開発途中で仕様変わってない?

改善活動1回目のふりかえりの場でしたので、新たな思考のきっかけを今回作れたのではないかと思っております。引き続き継続していきます。

まとめ

Q1. レトロスペクティブ(ふりかえり)の目的は?

A.「品質と効果を高める方法を計画する」ことです。

Q2. レトロスペクティブ(ふりかえり)では、どんな議論をすべきか?

A.「スプリント中に何がうまくいったか、どのような問題が発生したか、そしてそれらの問題がどのように解決されたか(または解決されなかったか)について話し合うこと」です。

Q3. その理由はなぜか??

A.「自分たちの効果を改善するために最も役立つ変更を特定する」ためです。つまり「チームがもっと効率を高めることができるTRYを計画する」ために必要なファクターなのです。

あとがき

当社開発チームは、現在2つのチームで開発を行っています。今回ブログを執筆する前に共通のLT会でレトロスペクティブに関する発表をさせて頂きました。

所属チームは、2週間のスプリント最終日にふりかえりを行い、改善活動を行なっています。

良いスプリントだったとふりかえることもあれば、改善多数とふりかえることもあります。さまざまな変数があるかとは思いますが、効率を高められた!良いTRYが実行できた!素敵な価値をユーザへ提供できた!というようなポジティブな状態をチームで作っていけるよう、日々の改善活動を継続して行なっていきたいと思います。

GithubActions未経験者がcreateトリガーでブランチのフィルタ条件を追加してみた話

こんにちは、2022年4月にフォースタートアップスにジョインしたエンジニアの八巻(@hachimaki37)と申します。主に社内向けプロダクト「タレントエージェンシー支援システム(SFA/CRM)」のシステム開発を担当しております。

今回は初めてGithubActionsの改修チケットに関わることになり、途中ヒイヒイ言いながらも試行錯誤して解決に至った話について書いていければと思います。

本題に入る前に、まずは「GithubActionsとは?」と「GithubActionsの導入経緯について」簡単に述べていきたいと思います。

GithubActionsとは?

GithubActionsはGithubが2019年に出したCI/CDサービスです。

GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。

引用元:https://github.co.jp/features/actions

当社の導入経緯について

弊社のユースケースですとGithubActionsの方にコストメリットがあることがわかったため「コスト削減」を目的にCircleCIからGithubActionsへ移行しました。

導入されたがこんな問題があった

当社の場合、pushトリガー + ブランチのフィルタ条件を用いて発火条件を制御しております。

サンプルコード

name: staging Build and Deploy

on:
  push:
    branches:
      - staging_axx**
      - staging_bxx**
      - staging_cxx**

※サンプルコードをベースに以下話を進めています

1. Github上からブランチを作成するとGithubActionsが発火しない

pushを発火条件としているため、Github上でのブランチ作成(=pushなくGithub上にブランチができあがる)がこの条件にヒットせず、発火されない

2. ブランチのpush順序をミスるとGithubActionsが発火されない

▶︎正しい順序

  1. ローカルPC上で、作業用ブランチをmasterから作成する(feature/hogehoge)
  2. ローカルPC上で、stagingブランチを作業用ブランチから作成(staging_axx/hogehoge)しpushする
  3. 作業用ブランチ(feature/hogehoge)をpushする

▶︎誤った順序

  1. ローカルPC上で、作業用ブランチをmasterから作成する(feature/hogehoge)
  2. 上記3を先に実行する(作業用ブランチ(feature/hogehoge)をpushする)
  3. 上記2を次に実行する(ローカルPC上で、stagingブランチを作業用ブランチから作成(staging_axx/hogehoge)しpushする)

当社では、github-flowに沿った運用を採用しております。誤った順序でpushした場合、pushトリガーの条件にヒットせず、GithubActionsが発火されない

上記の体験から非常に使い勝手が悪い!!そんな声が開発メンバーから上がっておりました。

どんな状態を目指したのか

シンプルに「必要な時に、適宜GithubActionsを発火させたい」ということです。

最初のアイディアは、pushトリガーのようにブランチフィルタを使えば簡単に実現できる!と思っていたのも束の間、実現にあたってこんな問題を抱えておりました。

The create event does not support branch filter and tag filter.つまり、pushトリガーのようなブランチのフィルタ条件が、createトリガーにはサポートされていなかったのです。悲しい...

調査と試したこと

ここからヒイヒイ言いながら試行錯誤の日々が始まりました。

  • createトリガーを追加したらどのような動作が走るのかまず試してみる
  • ん?createトリガーってブランチフィルターをそもそもサポートしてないんじゃ無理くないか?と思いながら、いい解決方法ないかなぁとググりまくる。あった!→https://github.com/orgs/community/discussions/26286
  • jobs自体に条件を追加してみる
  • stepsに条件を追加してみる
  • 良い記述方法を模索する
  • 条件を追加してもSyntax errorがたくさんでる..そもそもどうやって記述すればいいのだろうか

などなど、試行錯誤をしながら進めておりました。

苦労したこと

  • 適切な箇所(jobs, stepsなど)への条件追加を模索したこと、追加したはいいもののSyntax errorが多発するなど、最初は構文理解に苦労しました。「コードを追加してみてpush」「Github上からブランチをcreateする」を繰り返すことで、徐々に理解に繋がりました。
  • 全てのブランチでjobsが発火してしまう。当たり前ですが、createトリガーを追加すると全てのブランチを作成したタイミングでjobsが発火してしまうため、頭を悩ませました。ここは記事(https://github.com/orgs/community/discussions/26286)を参考にコードを書いてみて、特定のブランチ名だと「success」「skipped」になる方法で、解決に至りました。

実装したサンプルコード

createされたブランチが、以下のブランチ名にマッチする場合に発火するよう、条件を追加しました。

name: staging Build and Deploy

on:
  push:
    branches:
      - staging_axx**
      - staging_bxx**
      - staging_cxx**
  create:

jobs:
  build:
    name: Build and Push
    runs-on: ubuntu-latest
    timeout-minutes: 30
    environment: staging

    if:
      contains( github.ref_name, 'staging_axx/' ) ||
      contains( github.ref_name, 'staging_bxx/' ) ||
      contains( github.ref_name, 'staging_cxx' )

    env:
      省略

    steps:
    - name: Checkout
      uses: actions/checkout@v2

- name: hogehoge
      run: test1
- name: foofoo
      run: test2
- name: fugafuga
      run: test3

createされたブランチがマッチした場合

ブランチ名:staging_axx/fix-hogehoge

createトリガーが発火し、GithubActionsが実行される

createされたブランチがマッチしなかった場合

ブランチ名:staging_zxx/fix-hogehoge

createトリガーが発火せず、skipされる

あとがき

正直結構な時間を費やしてしまいチームにご迷惑をおかけしてしまいましたが、リリース後、チームの方からは嬉しい嬉しいフィードバックがありました!!!

今回記述した設定ですと、対象ブランチの数が増加するとコードが​​冗長になるので、リファクタリングができないか?と考えておりますが、問題自体は無事解消に至りました。

私自身エンジニア歴は3年ほどで、今まではサーバーサイドをメインに経験を積んできました。次のステップを考えた際にフルスタック(コーディング以外も含む)に動けるエンジニアへの成長を目指し、フォースタートアップスにジョインしました。

今回初めてGithubActions周りのチケットに携わりましたが、直近はVue.jsなどフロントエンドにも関わる機会が増え、嬉しい成長痛を日々感じ業務にあたっております。

なかなか経験のないチケット着手はワクワクと不安の感情が入り混じりますが、当社では初めてのチケットの場合「good first issue」を付け、初めてでも取っ掛かりやすい環境を構築しております。

まだまだやるべきこと、やりたいことがたくさんあります。ぜひ一緒に成長していきませんか? 当社は採用大募集中です。ご興味ありましたらぜひ一度カジュアルにお話できたらと思います。

採用ページはこちら。(冒頭のTwitterにDM頂いてもOKです!)

【Rails】rake taskの作成とnamespace/task名の一手間プラクティス

こんにちは。

気がついたらもう冬です。気がついたらもう1年が終わります。
仕事もプライベートも紆余曲折を経て、成長を実感した1年でした。

どうもハチマキです。

はじめに

毎度悩む命名。rake taskを実装して感じた一手間プラクティス(こう書いたらわかりやすいかな)について書いていきます。サクッといきます!

rake taskの実装方法

こちらの記事を参考にさせて頂きました。非常にわかりやすかった。
RailsでRakeタスクの作成 - Qiita

namespace/task名の付け方ポイント

lib/tasks/hoge.rake

namespace :hoge do
  desc 'hoge の値を一括更新する'
  task : hoge_update_all do

file名(tasks/〇〇.rake) = namespaceは、シンプルかつ抽象度の高い命名にする

なぜか?

file名(tasks/〇〇.rake) = namespaceとtaskは、親子関係のような立ち位置だと考えたため

つまり、親要素にあたる file名(tasks/〇〇.rake) = namespaceを具体的な命名(例えば:hoge_update_all)にすると、複数taskが存在した場合に、命名の可読性が一気に落ちる。
よって、抽象度の高い命名の方がベターだと考えた

task名は、シンプルよりも具体的な命名にする

なぜか?

rake taskが複数にわたって作成された際、直感的に理解できる命名だと、深くコードを読む必要がなくなる。つまりコードリーディングの時間を削減できるから

ポイントは、「シンプルよりも具体的にする」ということです。つまりtaskを実行するとどんな処理が走るのか、詳細がわかる命名がベターと考えた

個人的には、この一手間があるかないかは、非常に重要だと感じた

                                            • -

最近のマイブームは、読書になりました。日々勉強です。

以上、ハチマキでした。