初めてのグループワークを成功させるたった一つの秘訣

初めてのグループワークを成功させるたった一つの秘訣
複数名で何か実施したい。しかし、どう動けばいいか分からない…そんな課題をお持ちでしょうか?この記事ではグループワークの成功率を上げるための自分が行ったことについてまとめました。同じ悩みを持つ人の参考になれば幸いです。

ことの始まり

ことのきっかけは事業部内の方針を形にする必要があり、集まって顧客理解につながる活動をするでした。 この話に関しては別記事の「方針を形にする。ユーザー理解Day始めました」をご確認ください。

自己紹介

ロジクラでiOSエンジニア担当をしてる川上です。エンジニアですが、部署のいちメンバーとして部署活動の企画、運用を率先したりサポートしています。ベンチャーのような社員数の少ない企業では、掛け持ちはあるあるですね。

初回実施に向けた準備

初回のユーザー理解Dayに向けた初動について説明します。チーム内でグループワークを実施したいけど準備が心配な人の参考になれば幸いです。

ほぼ完璧なタイムスケジュールで実施

結論を先に述べるなら、約8時間の長丁場ながらもほぼ予定通りに進められることができました。

これはとても重要な結果です。

なぜなら、初回のグループワークで複数名の時間を使ったのに想定より成果が出なかった場合、

上長からの印象は良い方向にはならず、メンバーからのグループワークに対するモチベーションや期待値が下降 してしまい、むしろグループワークがチームにとってネガティブに働いてしまうことがあるからです。

徹底した備えが成功の鍵

ざっくりとした目的・計画では、それだけ成功確率がブレてしまいます。

運良く事が進むこともあれば、何も良い効果が生まれず終わってしまうという経験をした方もいるのではないでしょうか 事前の準備を徹底さえすれば、グループワークの成功確率をかなり上げることができます。

これは初回イベントに向けて用意した裏方用の一部タスクになります。

オフィス組用にUberEatsを頼む時間さえも組み込んでいました。

また注文自体も前日に参加者から募っています。

グループワーク準備資料

実施環境

  • リモートワーク組とオフィスワーク組がいる
  • 途中2チームに分かれる時間がある
  • 人数は10名
  • 運用は二人

準備の流れ

  1. 方向性の認識合わせ
  2. プログラムのたたき台を二人で作成
  3. 私の方で細かい部分まで肉付け
  4. 当日の流れを脳内シミュレーションし不明点や不足点をなくす
  5. 当日調べないと分からない問題は、代替案も考えておく
  6. タスク出し
    1. 前日までに決めておくこと
    2. 当日の開始前に実施すること
    3. 当日開始後に実施すること
  7. 当日参加者用の資料用意
  8. 裏方用の資料も用意

ハイブリットワークならではの問題

弊社では木曜のWeeklyオフィスとリモートワークを活用し、ハイブリットワークで日々勤務しています。

ユーザー理解Dayは全員が集まる想定でしたが、必ずしも全員が集まれるとは限りません。

そのためリモート組が困らないために設備や運用を考える必要がありました。

その中で一番重視した点は、全員が見えるだけでなく、雰囲気が見える環境を作ることです。 当日の会場ではiPhoneからビデオ会議に接続し、それを全体を俯瞰できる場所に配置しました。こうすることで、会議のプログラムとプログラムの間や、休憩からの再開時になかなか始まらない場合に今どういう状況?という疑問を解決できる環境を作りました。

↓下図は事前に考えていた必要設備とその配置です。私一人に情報が依存してしまうと準備作業において私がボトルネックになるため、このように誰でも作業を手伝えるように作業情報を裏方資料に準備しておきました。

グループワーク当日のデスク上の設備配置図

大きい用紙とそれに印刷する方法

みんなで集まって作業ってなると、方眼用紙(A1)やA2のような大きな紙にみんなで付箋を貼ったり書き込んでいくイメージがあります。

しかし実際問題オフィスの複合機などにA1やA2といった大きい用紙はありません。文房具店などに行かないとありません。しかも用紙に事前に印刷などしたいってなるとA1が入るプリンタが必要です。でも大抵のオフィスにそんな大きい紙が入るプリンタは設置していないと思います。

弊社の同様で、紙を買うにしてもオフィス近くにそういった場所がなく、プリンタもありませんでした。

この問題の最適な解決方法はポスター印刷を使うことです。

ポスター印刷とは拡大分割して印刷することで、A4用紙をつなぎ合わせるとA1の印刷ができる方法です。この機能であればオフィスにおいてある大体のプリンターがサポートしているかと思います。

印刷したA4はテープで繋ぎ合わせます。

プログラム毎に想定時間を出す

予定した時間帯で終わらせるには予定プログラムに何分かかるかが必須情報です。

用意したプログラムを当日ぶっつけ本番で実施するのは、テストせずリリースするのと同じことです。コーディング同様に想定通りうまくいくかテストは大切です。

その見積もり確度を高めるために実際に喋る内容を用意したり、一連の流れを頭の中でシミュレーションを何度も繰り返しました。

当日トラブった事

当日に想定外に起きた問題がありました。

それは私自身がめったにオフィスに出社することがなかったため、

通勤時間を見誤って、予定してた到着時刻に遅れて準備時間がほとんど取れなかったことです… 😅

しかし、当日は別の裏方メンバーや当日手伝ってくれた方が裏方資料を見て作業を進めてくれたことで、開始までには無事間に合いました。

まとめ:グループワークは準備が大切

初めてやる人でも入念な準備と対策を練っておくことが大切です。

また初回に豊富な準備資料が用意されてることで次回開催時にその資料をベースに話を進められるため次回作業が円滑になります。

最後に

ロジクラでは顧客目線に立ち一緒に開発してくれるメンバーを募集しています! 興味がある方はぜひこちらの募集をご覧ください!

方針を形にする。ユーザー理解Day始めました

部署の行動方針を掲げたが、それ以降は方針を見る機会もなければ思い出す機会もなく、考え方や価値観もメンバーが分からないまま、気がつけば形骸化…なんて経験はないでしょうか?

この記事では、掲げた方針を腐らせないために具体的な形へ一歩進めた話についてまとめました。同じ悩みを抱える部署マネージャーにとって一つの参考になれば幸いです。

自己紹介

ロジクラでiOSエンジニア担当をしてる川上です。エンジニアですが、部署のいちメンバーとして部署活動の企画、運用を率先したりサポートしています。ベンチャーのような社員数の少ない企業では、掛け持ちはあるあるですね。

活動で交流することで方針の認識をあわせる

ロジクラのプロダクトチーム内の顧客理解を高めるために、オフィスで丸1日使って顧客理解に繋がる活動を実施しています。

方針を方針で終わらせない活動の一環

ロジクラのプロダクトチームには、「チーム方針」と呼ばれる、チーム内で共通の認識があります。

  • Respect
  • Collaborate
  • Go beyond customer
  • Be a professional

これら方針の陥りやすい状況として、方針が方針で終わって、中身がないことがよくあります。

会社で見かけるバリューや社訓、ミッションといった類で、実態と格差が開いてる会社を見かけたことがあると思います。

ロジクラも例外なく完璧とは言えない状態です。

このような共通の認識は初めから備わってる人とそうでない人に分かれます。そうでない人が中身がないままにしないために、格差をなくす活動が必要です。

今回この中の「Go beyond customer」にフォーカスした活動として、顧客理解するグループワークを開催に関する話です。

チーム内で月1実施のユーザー理解Day

プロダクトチームは、エンジニア、PM、QA、デザインとロジクラサービスを作るメンバーで構成されています。

月に1回オフィスに集まり、11時から18時までメンバー全員(緊急対応者除く)で顧客を理解するために用意されたプログラムに沿って活動を開始します。

初回の大プログラムは次の流れで実施しました。

  1. 集合・説明
  2. アイスブレイク
  3. 対象となるユーザーの説明
  4. ヒアリングレポート共有
  5. お昼
  6. 導入事例勉強
  7. カスタマージャーニーマップ実施前の説明
  8. CJMワークショップ
  9. 各チームCJM共有
  10. ドックフーディン
  11. アンケート
  12. 談笑

目的は顧客理解であり、顧客を知るだけでなく顧客の立場になって考える事がより効果的に理解へ繋がるという考えのもと、メインディッシュをカスタマージャーニーマップの作成とし、その前段に情報収集という流れで実施しました。

アイスブレイク

普段リモートワークが中心の会社なため、実際に顔を合わせる機会も少なく、初回活動ということもあって、アイスブレイクを実施しました。

アイスブレイクネタはネタ探してる時ちょうどよくTwitterで流れてきたMiroを使ったキャラ作成を参考にしました。

https://twitter.com/tyasuma/status/1516228968625901569

リモートメンバー交えたアイスブレイクの様子

情報収集

今回メインのターゲットに近しい顧客のヒアリングレポート共有や導入事例勉強で、カスタマージャーニーマップ作成時の材料を集めました。

リモートメンバー交えてヒアリングした情報の共有

2チームに分かれてペルソナ定義とジャーニーマップ作成

大勢でワークショップに取り掛かると発言率や観点が狭まる理由と、スペースの関係で2つのチームに分かれてワークショップを実施しました。

Team A

Team Aのペルソナとジャーニーマップ

Team B

Team Bのペルソナとジャーニーマップ

成果物としては不完全ですが、目的は成果物を作ることではなくユーザー目線で考える事なのでOKです!

チーム毎に作ったジャーニーマップやペルソナを発表

各チームで作成したCJMやペルソナを発表し、観点を共有しました。

作成したペルソナやCJMを各チーム発表

カスタマージャーニーマップの目的を作成ではなく作成行為に重みを置く

本来であればカスタマージャーニーマップはもっと早い段階で作成されていたり、定期的にメンテンナンスされて、常にユーザーの動きを視覚化されていることで本領発揮します。

今回は作成することが目的ではなく、「作成するために調べる・触る・考える行為が顧客目線に合わせるトレーニング」ということを実施前に説明しました。

アンケート結果は上々

イベント最後にイベントのアンケートを参加者に書いてもらいました。

結果は好評で、良いスタートとなりました。

グループワークのアンケート結果

まとめ:方針の落とし込みは大切

今回は「Go beyond customer」に関する話でしたが、別の方針にも施策がまだなかったりします。

チーム方針などチームの共通価値観や認識は、そのチームの存在理由や行動に影響を与え、その行動の統一性により不要な議論の衝突を避けれたり、ハイコンテキストな議論につなげられたりして、結果としてチームの生産性や安定性に繋がります。

最後に

ロジクラでは顧客目線に立ち一緒に開発してくれるメンバーを募集しています! 興味がある方はぜひこちらの募集をご覧ください!

ロジクラ開発チームの取り組みを紹介します!

はじめに

はじめまして!株式会社ロジクラでエンジニアをしている甲斐と申します。

今回は弊社でエンジニアを取り巻く開発環境や施策の中で、今でも続けているチーム全体の取り組みを一部紹介しようと思います。

早いもので、ヒヨッコのつもりでいてもスタートアップで2年在籍すると相対的に古株になってしまうわけですから月日の経過というのは恐ろしいものですね。在籍している間にもどんどんと仕組みが変わっていって今振り返ると懐かしさを感じます。

1. パフォーマンス改善モブプロ

弊社ではプロジェクトの進行に関してはスクラムベースでissue作成やスクラム運用にはPMと連携して実施しますが、インフラやパフォーマンス周りではエンジニアが主体となっています。

毎週時間を取ってエンジニアで集まり、DatadogやBugsnagとにらめっこしてスロークエリ探したりN+1を探したり、ケアできていないエラーをピックアップしたりしてその場でコードを共有しながら修正するか、手こずりそうな場合はissue化してスプリントに追加するようにしています。

この施策によって数々のN+1クエリを取り除くことができたほか、勘所をエンジニア全員で共有することによって事前にN+1を回避するような対策が行われるようになりました。

PMが事業を伸ばす施策を考え、エンジニアがアプリケーションの健全な運用を考える、この両輪でプロダクトチームは成り立っています。

2. ハッカソン

通常業務から離れてテーマを定めて1日ガッツリ実装する日です。現状はロジクラに直接関係しているけど普段なかなか関われなかったり、技術的にちょっとチャレンジがいるアプローチの検証の場として使用することが多いです。

私はデータ分析の準備用にデータの作りを調査したり、全文検索用にインデックスを改善したりといったテーマに最近は注力していますが枠の時間がネタの増加に追いつかなくなってきて贅沢な悩みが出てきました。

同僚はiOSバーコードリーダーを実装したり、ワークフローの改善を行ったり、普段後回しになりがちな実装をやるいい機会なのでどんどんロジクラに成果をフィードバックしていきたいですね。

3. DB勉強会

ロジクラの実装に日々携わって実感したのが、エンジニアとPMの知識に非対称性が非常に強いことです。

PMの視点から見れば事業レベルの話をどこまで伝えるか、という悩みがありますが、それと同時にエンジニアから見るとどのくらいシステムの構成や設計を伝えるべきか、ということが悩ましいのです。

とはいえ、あんまりブラックボックス化するとPMが作る仕様が開発の実情から大きく乖離して調整が大変になることがあり、少しでも格差を埋めようということで内部のデータについて知識を共有する機会をしばしば設けています。

特にPMに対する知識の共有という目的は満たされており、以前に比べて内部データに関する話題での齟齬が少なくなってきているのを実感しています。

CSメンバーなどにも参加してもらっています。将来的には顧客データの実態調査タスクを手離れさせたいとは思ってるのですが、レベル感を合わせきれずに置いてきぼりにしてしまったという反省もあり、機能面でもナレッジ面でも、まだまだ道は遠いなと実感させられています。

4. お披露目会

直近にリリースされた機能の紹介と、関係者にお疲れさまでしたを言う会です。

各社員の反応って様々で、広くチームをまたがって下準備していた機能もあれば仕様が明快で数人でさっと作れてしまう機能もありますし、話は聞いていても実際に動いているところを見ることで解像度が上がることもあるので、活動の共有として重要だと思っています。

開発者の中で直接お礼を言われることをモチベーションにしているかどうかというのは個人差がある内容ですが、そういう場を作ることによるチームの一体感醸成などの意味でも意義のある場です。

おわりに

施策面ではもがきながら試行錯誤を繰り返しています。色々やりすぎた結果本来のタスクを圧迫したり、スリムにしてみたらなんだか連携がぎこちなくなったり、ああでもないこうでもないと理想と現実の間で揺れ続ける毎日です。

株式会社ロジクラではそんな我々と一緒に物流業界の未来を切り開くエンジニアを募集しています! 興味がある方はぜひ こちら をご覧ください!

ロジクラ入社して1年経ったのでエンジニア環境を内部評価してみた

自己紹介

家では子供3人の父親で、ロジクラでiOSエンジニア担当をしてる川上です。 2021年3月から入社し、iOSを全般的に担当しています。

ロジクラ社員による職場をガチ評価

今回は私がロジクラに入社して1年経過したため、ロジクラに興味を持った方に向けて、働いて分かったエンジニア目線によるエンジニア環境について内部評価してみます。

この記事を書こうと思った理由

転職活動を経験した人は分かると思いますが、「ホントのところ」が見えにくい会社が多いです。

募集要項のみでそれ以外の技術ブログや業務内容が見えなかったりと、情報に鮮度がなくもっと知りたいけれど、他にも見る企業は多いから諦めざる得ないといった経験は多いと思います。

【注意】個人の意見です

なお一個人として独断と偏見の混じった評価となります。 会社としての正式ではありません。

良い点

  • 時代先取り!? 我社はハイブリットワーク
  • コーディングに集中できる
  • Slack(テキストコミュ)の欠点をoVice(音声コミュ)で補っている
  • チーム内の問題解決に本気で取り組んでいる
  • ユーザーファーストが当たり前!
  • サービスに対してみんな意見できる雰囲気
  • 変だと感じたら変だと言っても奇っ怪な目で見られない
  • 上からの言いなりではなく、チームの方針を自分たちで話し合い提案できる
  • 社長が超話を聞いてくれる

我社はハイブリットワークにチャレンジ

私個人はリモートワーク歴は5年目になり、リモートワークの利点と欠点を味わってきました。

ロジクラでは元々はオフィスワーク中心でしたが、約2年ほど前にリモートワークに切り替えました。その後オフィスワークの利点も取り入れたくハイブリットワークを目指して働き方を改善を継続しています。

オフィスで仕事したい人は出社し、そうでない人は自宅や近くのノマドなどでリモートワークを選択できるようになっています。 私自身は今執筆時点で新型コロナ(オミクロン)が流行ってるので9割近く自宅作業です。

最近では借りていたオフィスを引き払い、指定の1曜日だけ借りれるオフィスに移りました。 もちろん流行りだからではなく、コスト面も考えた結果となります。

コーディングに集中できる

ロジクラは他社と比べるとわりかし会議体の少ない会社です。 1スプリント2週間のタイムボックスの中で私が参加している定例と呼べる会議体は合計7.5時間になります。

私個人ではさらに少ない職場で健全に回ってた経験があるのでまだ改善余地はありますが、それでも大手や縦割り組織から見ると少ない方かもしれません。

Slack(テキストコミュ)の欠点をoVice(音声コミュ)で補っている

リモートワークにとってSlackなどテキストコミュは必須かつ重要環境です。 しかしながら、テキストコミュは人によって得意/不得意があり、必ずしも最適解ではありません。 テキストに書き起こせる=自分で情報を整理・構築できている状態であり、 それ以外の例えば

  • 何がなんだか分からん状態
  • 完結するには不足点が多い状態
  • 情報よりも情緒・感情・熱量を伝えたい状態

などは音声コミュや直接話すことで情報齟齬は起きにくく効果的にコミュニケーションできます。 ロジクラではSlackに加えてoViceを使っており、それらを上手に活用しています。 会議体ではmeetやzoomを使っています。

またoViceに拘らず他にも類似ツールで良さそうなものがあれば「とりあえず試してみる」姿勢とそれを許容するマインドがあり、 最近だと月末のTGIFでは SpatialChat を使ってたりします。 こういった挑戦する文化が整っているのは成長促進する良い姿勢だと思います。

チーム内の問題解決に本気で取り組んでいる

私が入社してマインド共有とやり方を提案した経緯もありますが、 不要な会議体を減らしている一方で、チームが抱えている問題を深堀りする時間を2週間に1回2時間半使い問題の深堀りと解決策を出していっています。 これにより問題に向き合い続ける取り組みを維持できているので企業成長して問題が変わっても、同様に解決していける自己組織化された強いチームになると思っています。

ユーザーファーストが当たり前!

企業である以上利益追求は義務だと思います。 しかしながら利益には対価を払っても良いと思ってくれるユーザが存在することで成り立ちます。

ロジクラのプロダクト開発ではユーザーの意見を軽視せず、ユーザー目線の意見を出しても厄介者扱いしないことは、常にファンを獲得し続ける良い姿勢だと評価できます。

またエンジニアでも希望者はユーザーの倉庫へ訪問もでき、現場のオペレーションを教えてもらったり、抱えてる課題や苦悩・苦痛を教えてもらえることでユーザーの気持ちが理解しやすい点はユーザー目線を大事にしている行動だと思います。 訪問時は一人エンジニアが出向くのではなく、PMやCSと一緒に訪問し、基本的なやり取りを担ってくれて、部分的に質問できたりするので、訪問慣れていないエンジニアでも安心です。

サービスに対してみんな意見できる雰囲気

エンジニアでも業務知識を覚え、課題を知れば改善案を思い浮かびます。 しかし大手などでは職種偏見などによりエンジニアの提案は軽視される組織もあります。

ロジクラは誰であっても提案できる雰囲気となっています。 もちろん提案のために案を整理したり論点があってたり、ロジックが整ってる必要はあります。

実際私も何個も新機能や改善を提案していますが、みんな快く話を聞いてくれます。 ときには称賛してくれるほど心地よい雰囲気となっています。

変だと感じたら変だと言っても奇っ怪な目で見られない

過去経験した組織とは非効率なやり方をロジクラがやっていても不思議なことではありません。

その時に全体に対して「基本的な質問」「前提覆す本質ついた質問や意見」を投げかけてはいけないみたいな暗黙の了解はなく、 むしろロジクラでは最初に質問したことに感謝されることすらあります。

上からの言いなりではなく、チームの方針を自分たちで話し合い提案できる

組織によってはマネージャーや上長・経営層からの指示が絶対な会社は当然あります。

理由を聞いても「上が言ったから」と返ってきたり、直接聞くとめんどくさそうな顔されることに辟易している人は、ロジクラは良いと思えます。

なぜなら上へ直接聞くことも出来れば、出た組織方針を自分らで話し合いどう進むか提案することが許されています。 当然一方がすごく強いといった偏りではなく、双方の意見を交換できることが当たり前となっています。

社長が超話を聞いてくれる

会社によっては社長と話すには雲の上の存在でコネが必要だったり、人の話を全く聞かず持論ばかりを押し付ける社長だったりと、関わるだけ無駄な社長は残念ながら存在します。

ロジクラの社長では学生起業で右も左も分からないながらも失敗を繰り返し今までこれた実績があり、その挑戦する文化をすごく重要視しています。

まだこの規模ならではかもしれませんし、これからも継続できるかもしれませんが、 社員が個人として社長と話す機会もあり、社長も常にウェルカムになっており、社内からの情報を常に欲している状態です。

ロジクラに必要な改良点

一方で当然ながら良い点とは言えず、成長チャンスを隠し持った改良点もあります。

  • 資料がまだまとまっていない
  • 発言する人が偏ってる
  • (個人的に)まだ不必要な会議体が多い
  • 情報の交通整理が部署毎で稼働していない

資料がまだまとまっていない

スタートアップあるあるです。 初期スタートアップ時は本当にリソースが足りないため価値提供から離れた作業は優先順位が下がります。 ドキュメントも同じです。

しかし、弊社はそのフェイズは抜けエンジニアの数も当初よりも倍以上に増えました。 そこでお待ちかねの資料不足となります。

これは「なぜ資料がないんだ!」という感情的なものではなくただの通過儀礼的なものだと思っています。 選択と集中によりトレードオフされた結果であり、その結果が今も会社が継続していると分かっているためです。

もちろん「全然ない」ってことはなくある所はしっかりと整備された資料はあります。 しかし、人が増えてスケールアップしたことに対する伝達方法が口頭だった部分に関しては資料不足が出てきている状態です。

これに対して前述した問題解決に全力の会議で、問題となっている部分毎に対応を施す動きで解決へ進んでいます。

発言する人が偏ってる

これは問題が大きく3つあると思っています。

  1. 発言が不慣れな性格、話題、ついていくので精一杯のため発言が出来ない人
  2. 話題に興味が薄い、発言力に偏りを感じているため発言をしない人
  3. 発言したくても何らかの要因で発言できない人

これらは原因特定までは出来ていません、あくまで経験からくる仮定です。 もし2と3、特に3である場合は解決必須です。

これは個々の問題で難易度が高めで時間がかかってますが、メンバーに依頼して個人毎に話を聞いてもらってまずは特定をしている段階です。

(個人的に)まだ不必要な会議体が多い

私が入社してマインド共有と同時に会議体を減らした経緯もありますが、

  • 顔を合わせる必要がある
  • 同期的でなければならない
  • 対話性の高い議論となる

上記に当てはまらないなら会議体である必要はないと思っています。 例えば、議論や意見交換はされず、資料を会議体で読むだけの共有程度の会議体は集まる必要はなく、各自非同期に共有を確認し質問はSlackやoVice等でやれば良いと思ってます。 その質問・議論が長引くのであればそれは情報不足対話性不足関係者不足ということで後日それらを解決するためだけの会議体を開くほうが会議コストを抑えられます。

意思決定のない会議体は正味作業ではなく付帯作業であり、その時間が長いほどユーザーへの価値提供時間が遅くなります。

これに関しては引き続き形骸化してないかなど会議体の品質向上に努めていきたいです。

情報の交通整理が部署間で健全かつ安定して稼働していない

ソフトウェア開発、プロダクト開発、サービス開発、組織運営とどの粒度で見ても情報は流れるものであり、個から個もあればチーム(多)からチーム(多)もあります。 これらの情報の中身は他部署への質問・依頼であったり、共有だったりします。

  • 質問であればFAQや共有不足の改善で質問を減らしたり
  • 依頼であれば定期的に来るものは機能化(自動化やサービス化)で依頼を自己完結&高速化したり
  • 共有であれば最適なタイミングで、最低限の量で、必要とする人へ伝達し続けなければノイズ混じりの情報は見てもらえなくなります。

そしてこれらの情報の発生源はユーザー起因だったり影響するものが多く、最終的にはユーザーへの価値提供速度やCXに影響します。

これらはまだまだ改善余地があると思っており、時間を作って現地現物することで一緒に改善していきたいです。

【まとめ】ロジクラは不慣れながらも頑張る意思は強く、互いを尊敬しあうことを当たり前にしている会社

どちらもまだまだ書ける項目はありますが、量が多くなるのでこの辺でまとめます。

良い点は継続すべきマインドや行動であり、改良点は素直に受け止めそこからどうするか考える成長ポイントだと考えています。

以上が1年働いてみて分かったロジクラの開発事情です。 ロジクラに興味を持ってる方にとって少しでもイメージできる情報となればと思います。

結構ぶっちゃけた内容だと思います。もしこの記事が投稿されれば、それを良しとする企業体質だと言うことになります。

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!

本番データを使った検証を手軽にできる仕組みの構築

ロジクラでエンジニアをしている高梨です!

前回のstagingDev環境構築に引き続き、DevOps周りの改善を紹介します! 普段開発を進めていると、本番のデータを利用したいと思うことがよくあったりしますよね?

例えばロジクラでは

  • 顧客からの不具合問い合わせの検証
  • 新機能の仕様検証
  • 新機能のパフォーマンス検証
  • ...etc

などなど、色々なタイミングで本番データを利用したいと思うタイミングがあります。

ただし手動でやろうとすると、本番環境のデータベース周りを触ることになるのでセキュリティ上のリスクや誤操作のリスクがかなり高く、なかなか気軽にできません。

ロジクラではこういった課題をクリアし、誰でも本番データを利用できるような仕組みを作ったので今回はそちらを紹介します!

仕組みの概要

冒頭にいったような、セキュリティ上のリスクや誤操作のリスクを回避し、手軽に本番データが利用できるようにするため、開発者が簡単なアクションをするだけで本番データをマスキングしたデータが入ったデータベース ( 社内ではcloneDB と呼んでいます) を立ち上げ、stagingDev環境からそれを確認できるようにすることをソリューションとしました。

処理の実行には、いつものように github actoinsを利用しています。 github actionsでは以下のようなスクリプトを叩いています。

DBの複製

#!/bin/sh
export AWS_PAGER=""

usage () { echo "Usage : $0 -c <CLONE_DB_CLUSTER_IDENTIFIER> -i <CLONE_DB_INSTANCE_IDENTIFIER> -P <MASTER_USER_PASSWORD>"; }
while getopts c:i:p: OPT
do
  case $OPT in
    c) CLONE_DB_CLUSTER_IDENTIFIER=$OPTARG
      ;;
    i) CLONE_DB_INSTANCE_IDENTIFIER=$OPTARG
      ;;
    p) MASTER_USER_PASSWORD=$OPTARG
      ;;
  esac
done

if [ ! "$CLONE_DB_CLUSTER_IDENTIFIER" ] || [ ! "$CLONE_DB_INSTANCE_IDENTIFIER" ] || [ ! "$MASTER_USER_PASSWORD" ]
then
  usage
  exit 1
fi

DB_SUBNET_GROUP_NAME=logikura-xxx
VPS_SECURITY_GROUP_ID=sg-xxx
SOURCE_DB_CLUSTER_IDENTIFIER=logikura-xxx

# DBClusterのclone
echo "cloning database..."
aws rds restore-db-cluster-to-point-in-time \
  --source-db-cluster-identifier $SOURCE_DB_CLUSTER_IDENTIFIER \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --restore-type copy-on-write \
  --use-latest-restorable-time \
  --db-subnet-group-name $DB_SUBNET_GROUP_NAME \
  --vpc-security-group-ids $VPS_SECURITY_GROUP_ID

## DBClusterにDBInstanceを作成
echo "creating database instance..."
aws rds create-db-instance \
  --db-instance-identifier $CLONE_DB_INSTANCE_IDENTIFIER \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --db-instance-class db.t3.medium \
  --engine aurora-postgresql
aws rds wait db-instance-available --db-instance-identifier $CLONE_DB_INSTANCE_IDENTIFIER
aws rds modify-db-cluster \
  --db-cluster-identifier $CLONE_DB_CLUSTER_IDENTIFIER \
  --master-user-password "${MASTER_USER_PASSWORD}" \
  --apply-immediately

すでにあるDBをリストアしています。コマンドの詳細は公式のドキュメント をご覧ください!

ただしこのままでは本番のデータがそのまま利用できてしまうのでセキュアではありません。問題に対応するため、隠しておきたいデータをマスキングする処理を実行しています。

マスキング

#!/usr/bin/env ruby
require 'optparse'
require 'securerandom'

clone_db_host = nil
opt = OptionParser.new
OptionParser.new do |opt|
  opt.on('--clone-db-host VALUE', 'clone db host name') { |v| clone_db_host = v  }
  opt.parse(ARGV)
end
raise "required clone_db_host" if clone_db_host.blank?

settings = YAML.load_file("config/settings/masking.yml")

raise "saftiy" if Rails.env.production?
raise "saftiy" unless clone_db_host.match(/clone/)

ActiveRecord::Base.establish_connection(
  ActiveRecord::Base.connection_config.merge(host: clone_db_host, database: "logikura_production"),
)
settings["tables"].each do |table_name, column_names|
  sql = "UPDATE #{table_name} SET"
  update_terms = column_names.map do |column_name|
    "#{column_name} = uuid_generate_v4()"
  end
  ActiveRecord::Base.connection.execute("UPDATE #{table_name} SET #{update_terms.join(', ')}")
end

ymlにtableとcolumnの定義を買いてuuidで埋めてるシンプルな実装です。

以上を実行するとこで、本番相当のデータを安全に利用することができます!

最後にgithub actionでdbのエンドポイントをコメントするようにしているので、そのエンドポイントを利用して検証などを進めています 🎉

f:id:logikura:20211229133344p:plain

エンジニア以外での利用

前回の記事 で検証環境( 通称stagingDev環境 )をbranchごとに手軽に作る仕組みを紹介させて頂きましたが、staginDevの立ち上げ処理には環境変数を設定できるフックポイントがあります。

test -f staging_dev.env && export $(cat staging_dev.env)
if [ -e "./deploy/staging_dev_env/${STAGING_DEV_BRANCH}.env" ]; then
  export $(cat ./deploy/staging_dev_env/${STAGING_DEV_BRANCH}.env | grep -v ^# | xargs);
fi

これを利用して、 branch_name.env

DB_HOST=xxx

のように書いておくと、立ち上げた環境が接続するDBホストの値をoverrideして変更することができます。 検証環境で手軽に本番データを扱うことができるので、開発者だけではなくカスタマーサクセスのメンバーやセールスのメンバーも、マスキングされた安全なデータを問い合わせの対応や機能検証の際に気楽に使うことができます!

まとめ

本番とほぼ同じデータをいつでも利用できるようにしたことで、より正確な検証を行うことができ、顧客への対応や機能リリーススピードが改善しました 🙌 ローカルやstagingだとレコード数が少ないので常に高速に動いてる感じになりますが、実際にお客さんの環境を再現することでボトルネックに気づけることがあります。

また、ロジクラの開発チームでは週に1度パフォーマンス改善の取り組みをチームで行っているのですが、その際の検証環境としても本番環境のデータをcloneしたデータベースが施策の検証のために役立っています!

スタートアップのフェーズでは機能開発に追われがちですが、このような基盤の改善を進めることでさらに開発スピードをあげていき、より良いサービスの提供を進めていっています 👍

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

マスキングスクリプト実行に時間がかかるので、もっと良いソリューションある方 & ロジクラに興味ある方はぜひ こちら をご覧ください!

検証環境をbranch毎に作ってリリーススピードを改善した話

自己紹介

ロジクラでエンジニアをしている高梨です!

前回ロジクラのインフラ構成を紹介 したのに続き、今回もロジクラの開発を支えている環境周りに関して、リリーススピードを数倍にした例を紹介していきます!

以前ロジクラではチーム開発を進めていく中で、機能検証がスタックすることによって開発のスピードが停滞するという課題が発生していました。

具体的な問題としては、検証環境がstagingしか存在しておらず、複数の機能開発が同時に進んでいる場合に、PMなどに仕様を満たしているか確認してもらう順番待ちが発生したり、手直しがあった際に巻き戻しコストがかかりリリースが遅れるということがあります。

結果として、大きめの機能が入ってくるとリリース頻度が週に1度ほどになってしまうことがよくありました。

仕組みの概要

上記の問題を解消するため、変更した機能ごとに開発者以外が簡単に検証することができる stagingDev という環境を用意することをソリューションとしました。

branchごとにサブドメインの割り振りとECSのタスクを立ち上げ、今まで通りstagingのDBにアクセスできるようにして社内メンバーなら誰でも検証できるというものです。

処理の実行には github actoinsを利用しています。

以下に実際の設定を貼ります。

name: StagingDev作成君

on:
  pull_request:
    types: [opened, synchronize]
env: ...

jobs:
  build:
    if: contains(github.event.pull_request.labels.*.name, 'StagingDev')
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: kayac/ecspresso@v1
      with:
        version: latest
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with: ...
    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    - name: Setup Environment
      run: ... # 必要な環境変数の準備

    - name: Build Image
      run: |
        ./deploy/docker_build.sh staging ${{ secrets.IMAGE_REPOSITORY_URI }} $BRANCH_NAME ${{ secrets.SIDEKIQ_CREDENTIAL }} $BRANCH_NAME
        docker push ${{ secrets.IMAGE_REPOSITORY_URI }}:$BRANCH_NAME

    - name: Setup
      if: env.SETUP_COMPLETED == 'false'
      run: |
        # priorityが重複するとエラーになるので最大値を取得して重複しないようにする
        PRIORITY=$(aws elbv2 describe-rules --listener-arn ${arn} | jq -r '.Rules | map(select(.Priority == "default" | not) | .Priority | tonumber) | max + 1')
        # cloudformationでターゲットグループ, リスナールール, レコードセットの作成
        aws cloudformation create-stack \
          --stack-name $STACK_NAME \
          --template-body file://$(pwd)/deploy/cloudformation/staging_dev.yml \
          --parameters ParameterKey=BranchName,ParameterValue=$BRANCH_NAME ParameterKey=ListenerPriority,ParameterValue=$PRIORITY
        # 完了するまで待つ
        aws cloudformation wait stack-create-complete --stack-name $STACK_NAME

        TG_NAME=$STACK_NAME
        export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn)
        export BRANCH_NAME=$BRANCH_NAME
        export IMAGE_TAG=$BRANCH_NAME
        export ENTRY_POINT='"./deploy/init.sh"'
        # ECS Serviceの作成
        ecspresso create --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml
        ecspresso create --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml

    - name: Deploy
      if: env.SETUP_COMPLETED == 'true'
      run: |
        TG_NAME=$STACK_NAME
        export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn)
        export BRANCH_NAME=$BRANCH_NAME
        export IMAGE_TAG=$BRANCH_NAME
        export ENTRY_POINT='"./deploy/init.sh"'
        ecspresso deploy --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml
        ecspresso deploy --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml

    - ... # issueへのコメントやslack通知処理

上記の処理の流れを簡単に説明します!

  1. PRに StagingDev ラベルを貼り、そのPRに変更をプッシュすることによってActionがトリガ
  2. 該当ブランチで利用する環境変数をセット
  3. 該当ブランチのイメージを生成
  4. 用意しているcloudformationの設定を利用し、リソースを構築
    • Route53
    • LB (target groupの設定など)
  5. タスクのデプロイ
  6. 完了したらurlをPRにコメント

f:id:logikura:20211129120805p:plain

最後にslack通知を行います🚀

f:id:logikura:20211129120831p:plain

ちなみに設定は紹介しませんが、PRをクローズすると環境が消されるactionも用意しており、環境が作られっぱなしになることが内容になっています 👌

リソースの構築について

ざっくりとした環境構築までの流れは理解いただけたかなと思うので、ここからさらにstagingDev環境を具体的にどう構築しているかを記載します。

前回の記事でも紹介したようにロジクラはAWS上でECSを利用して運用されています。

実際に検証できる環境を作るためには、branchごとの変更がdeployされたECSの新しいタスクに対して独自のurlからアクセスできるようにリソースを作らないといけません。

CFでTGとRecordSetの作成

まず設定から記載します。

AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Parameters:
  BranchName:
    Type: String
  VpcId:
    Type: String
    Default: 
  AlbArn:
    Type: String
    Default:
  Listener443Arn:
    Type: String
    Default: 
  ListenerPriority:
    Type: Number
Resources:
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 10
      HealthCheckPath: "/"
      Port: 80
      Protocol: "HTTP"
      HealthCheckPort: "traffic-port"
      HealthCheckProtocol: "HTTP"
      HealthCheckTimeoutSeconds: 5
      UnhealthyThresholdCount: 2
      TargetType: "ip"
      Matcher:
        HttpCode: "200"
      HealthyThresholdCount: 5
      VpcId:
        Fn::ImportValue:
          !Sub "${VPCStack}-VPC"
      Name: !Sub "logikura-tg-ecs-${Environment}"
      HealthCheckEnabled: true
      Name: !Sub "staging-dev-${BranchName}"
      Port: 443
      Protocol: HTTP
      TargetType: ip
      VpcId: !Ref VpcId
  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup
      Conditions:
        - Field: host-header
          HostHeaderConfig:
            Values:
              - !Sub "dev-${BranchName}.com"
      ListenerArn: !Ref Listener443Arn
      Priority: !Ref ListenerPriority
  RecordSet:
    Type: AWS::Route53::RecordSetGroup
    Properties:
      HostedZoneName: logikura.com.
      RecordSets:
      - Name: !Sub "dev-${BranchName}.com"
        Type: CNAME
        TTL: '60'
        ResourceRecords:
        - ***.elb.amazonaws.com

awscliで作っても良いのですが、削除が簡単にできるようにCFを採用しています。 terraformを利用している会社ではterraformで書き換えてもらってもよいかと思います!

最終的にbranch毎に割り振られたurlにアクセスすると、以下のようにbranch毎の変更が反映されたリソースにアクセスできます!

f:id:logikura:20211129120900p:plain

リソースの削除について

削除はCFで作ってるので簡単です。

githubactionsでPRが閉じた際にECSとELB等をまとめて消してます。

ecspresso delete --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml --force
ecspresso delete --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml --force

aws cloudformation delete-stack --stack-name $STACK_NAME
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME

一つ注意としてはPRの削除のたびに実行するとエラーになるので、stackが存在するかチェックする必要があります。

aws cloudformation describe-stacks --stack-name $STACK_NAME

注意事項

cloudformationのstackNameの制約で、 [a-zA-Z][-a-zA-Z0-9] にbranch名を限定しないとリソースの作成が失敗してしまうという問題があります。

branch名のルールが決まっていれば問題ないのであえて解決していませんが、もし命名に異なるルールがあるチームの場合は注意してください!

まとめ

ブランチごとに開発者以外が動作検証できる環境を作ったことで、それぞれの環境で仕様確認が同時並行で行うことができ、リリースがスタックすることがなくなりました 🙌

結果的に開発からユーザーに利用してもらうまでのスピードが数倍早くなり、顧客への提供スピードが上がっただけではなく、誰でも自由に検証できる環境があることで事前の社内周知やガイド作成など会社全体とし業務スピードをあげることにも成功しました!

今後も開発がスケールする仕組みを作っていき、さらに顧客へ価値を届けることができるようにしていきたいです 👍

最後に

強引にシェル芸で解決してる部分もあるのですが、もっとスマートにしていきたいので、 ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!

現状のインフラ構成とワーカーのオートスケール

現状のインフラ構成とワーカーのオートスケール

ロジクラでテックリードをしている高梨です! サービスが公式に公開される前から技術選定だったり、実際にコード書いたり、インフラやSRE的なことまで色々やってます。 最近は愛犬と遊ぶのにはまってます。

f:id:logikura:20211124171041j:plain

概要

ロジクラは今年、AzureからAWSへとインフラの移行を行いました 🎉

当初ロジクラはAzureを利用していました。利用していたAppServiceは十分要件を満たすことができ、Microsoftさんからのサポートも手厚かったことが主な理由で利用していたのですが、会社のフェーズが進み、今後さらに事業拡大し開発組織を大きくしようと考えたとき、リファレンスが充実しており、経験者が多いAWSに移行して採用につなげることを主な目的にインフラ移行を決定しました。 ここでは現状のロジクラのインフラ構成と、移行時に工夫した点などを紹介していきます!

今回移行したものはメインサービスである在庫管理のSaaSのロジクラです。

必要な仕組みとして大まかに説明すると、

  • メインのサービスを乗せているサーバー・DB
  • 非同期処理を行うためのサーバー
  • 非同期処理を行うためキューを貯めるRedis

というシンプルな構成になっています。

管理やスケールの容易さから今回メインのサービスを載せるサーバーについては、ECS・Fargateを利用することにしました。

構成紹介

載せきれない部分もあるのですが、大まかな構成としては以下のようなシンプルな構成になっています。

f:id:logikura:20211110165801p:plain

次にこの構成を作っていった中で、特に工夫していった点を紹介していきます。

デプロイについて

デプロイに関してはgithub actionをトリガーにしています。stagingとproductionでトリガーは異なるのですが、deploy処理に関しては以下のgithub actionの設定ファイル(不要な部分はカットしています)のように、buildしたimageを一度ECRに置き、そのimageを利用してdbのmigrationを行った後にdeployする流れとなります。

...
jobs:
  ...  
  build_image:
    runs-on: ubuntu-latest
    steps:
    - uses: nelonoel/branch-name@v1.0.1
    - name: Check out code
      id: checkout
      uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ secrets.AWS_REGION }}
    - name: Set environment
      id: set_environment
      run: {{環境変数を設定する処理}}

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    - name: Build Image
      run: {{deployするimageをbuild}}

  deploy:
    needs: [build_image]
    runs-on: ubuntu-latest
    env:
      IMAGE_TAG: ${{ needs.build_image.outputs.image_tag }}
    steps:
    - uses: kayac/ecspresso@v1
      with:
        version: latest
    - name: Check out code
      id: checkout
      uses: actions/checkout@v2
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ secrets.AWS_REGION }}

    - name: Database Migration
      run: {{DBのmigrate処理}}

    - name: Quiet Sidekiq Process
      run: {{Sidekiqプロセスの停止}}

    - name: deploy
      run: |
        ./deploy/ecs_deploy.sh -t web -e staging -i $IMAGE_TAG
        ./deploy/ecs_deploy.sh -t worker -e staging -i $IMAGE_TAG

実際のdpeloyに何しては、カヤック製のecspressoを利用しており、以下コマンドで $SERVICE_NAMEごとの設定(productionやstagingなどを入れています)を読み込んでいます。

ecspresso deploy --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml --rollback-events DEPLOYMENT_FAILURE
ecspresso wait --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml

詳しくはecspressoのレポジトリをご覧ください!

タスクに関してもgithub actionから実行したいコマンドと環境を指定して実行するよう設定しています。

以下がecspressoでの実行コマンドになります。

ecspresso --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml run --overrides="{\"containerOverrides\":[{\"name\":\"logikura-ecs-container-web-${APP_ENV}\", \"command\":[\"./deploy/run_command.sh ${COMMAND}\"]}]}"

最後にdeployとタスク実行ともに、完了するとslack通知が流れてきます。

f:id:logikura:20211110171022p:plain

以上が基本的なdeploy周りの設定となります!

オートスケールについて

構成の中で工夫した点としては、workerのオートスケールの仕組みを導入したことです。

ロジクラは、利用者の性質上、例えば商品登録や出荷データの登録など、大量のデータを一度に処理したいと言う要望が多く、非同期処理を行うことが必要になっています。

ただ、処理が集中した場合jobが詰まってしまうと言うことが発生し得ます。顧客も増えている ⇒ 安定的に全てのお客さんにシステムを使ってもらうために、job数に応じてオートスケールする仕組みを取り入れています。

credentials = Aws::Credentials.new(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_KEY"])
client = Aws::CloudWatch::Client.new(region: EMV["AWS_REGION"], credentials: credentials)
client.put_metric_data({
    namespace: "SidekiqJobCount",
  metric_data: [
    {
       metric_name: "job_count",
       dimensions: [
          {
            name: "app_env",
            value: ENV["RAILS_ENV"],
          },
        ],
        value: Sidekiq::Stats.new.enqueued,
        unit: "Count",
    },
  ],
})

落ち着いたらスケールインしたくなりますが、処理の途中で落としてしまうと設計によっては問題が生じる場合があるかと思います。

https://github.com/mperham/sidekiq/wiki/Signals#tstp

SidekiqはTSTPシグナルを送ると新しいジョブの取得を停止し、処理が終わると停止します。

この仕組を利用して、TERMをtrapしてSidekiqを安全に停止するようにしました。

function trap_term() {
  SIDEKIQ_PID=`ps -ef  | grep -v grep | grep sidekiq | awk '{ print $2 }'`
  kill -TSTP $SIDEKIQ_PID
  while :
  do
    SIDEKIQ_STATUS=`ps axu  | grep -v grep | grep sidekiq`
    echo $SIDEKIQ_STATUS
    if [[ $SIDEKIQ_STATUS =~ stopping ]]; then
      echo "stopped sidekiq"
      exit
    else
      sleep 1
    fi
  done
}

trap trap_term TERM

これで解決したように思えたのですが、処理に時間のかかるjobの場合stopTimeout(最大120秒)を超えるとKILLされてしまう問題が起こりました。

stopTimeoutはFargate環境では現状これ以上伸ばせないので、結局スケールインはできていません。

何か良い方法あれば教えて下さい 🙏

参考

https://aws.amazon.com/jp/blogs/news/graceful-shutdowns-with-ecs/

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html

まとめ

toBのサービスだと同じような課題を抱えている会社は多いと思います。ぜひ参考にしてみてください!

ロジクラではこの他にもインフラ面で工夫している点がいくつもあるので、また別の記事でも紹介していきます!

最後に

ロジクラでは一緒に開発してくれるメンバーを募集しています!

興味がある方はぜひ こちら をご覧ください!