大学院を辞退し、初の新卒エンジニアとしてプレックスに入社した理由

はじめに

2024年4月に株式会社プレックスにエンジニアとして新卒入社した佐藤祐飛と申します。プレックスでは初の新卒エンジニアとなります。現在は、SaaS事業部にてサクミルという建設業界向けのプロダクト開発を行っています。

sakumiru.jp

タイトルにもある通り、私は学部4年生の夏頃までは大学院へ進学予定でしたが、インターンを通してプレックスでのファーストキャリアを歩みたいと考えるようになりました。

そんな私の体験談を共有しながら、読者の皆様にプレックスについて知ってもらいたいと思い、この記事を書きます。

学生時代について✏️

横浜国立大学情報工学EPを卒業しました。学生時代は学業に力を入れており、学科内で首席になった学期もありました。

首席の表彰状

海外で働いてみたいという夢があり、オンライン英会話やTOEICなどの英語学習にも力を入れていました。日々の学習の甲斐あって学部3年生の春頃にTOEIC915点を獲得することができました。

弊学科は約9割の学生が大学院へと進学するので、私も当然のように学部1年生の頃から大学院進学をする予定でした。

プレックスとの出会い🌸

学部4年生になるまで、私は開発経験が全くなく、授業でC言語Javaを触った程度でした。春頃に配属先の研究室で行われた就活座談会にて、WEB系のエンジニア就活ではインターン経験がないと話にならないということを知り、インターンを探すことになります。インターン募集に手当たり次第応募し、面接をしていく中でプレックスと出会いました。今思うと、ここが人生の分岐点だったなと感じます。

初めてテックリードと面談した際には、 「君は僕を超えるエンジニアになるかもしれない」や当時、インフラエンジニアを志望していた私に対して「君がインフラエンジニアだけで可能性を閉じてしまうのは勿体無い」など有り余るような評価をしていただきました。

その後、インターン選考としてRuby on RailsとNext.jsを利用した企業データ管理アプリケーションを作成する技術課題が出題されました。WEBアプリケーションを1から作るのは初めてでしたが、テックリードと仕様検討をしながら課題を進めるという形式だったので楽しく取り組むことができました。

他にもインターンの合格をいただいていましたが、開発未経験であるにも関わらず自分が評価されていることや、フルスタックの開発を通じて自分の可能性を広げてみたいという理由からプレックスでのインターンを決めました。インターン採用方針などについては、テックリードの記事に詳しく記載されています。

product.plex.co.jp

プレックスでのインターン🌊

インターンは学部4年生の8月から開始しました。この時点では既に横浜国立大学大学院の合格をいただいており、大学院を蹴ることになるとは夢にも思っていませんでした。

印象的なタスクについて

最初に任されたタスクはAPIからリソースをフェッチしている間のローディング画面にスケルトンを表示させるというものでした。ドキュメントを読みながら実装して、プルリクを出しました。その後のレビューにて私が書いたコードは丸っと修正されており、細部までこだわったスケルトンが実装されていました。その際に、「プロ意識が足りていない」というフィードバックをいただきました。求められている水準を再認識し、タスクに対して真摯に思考する能力が必要だと強く実感しました。

インターンを始めて2ヶ月目には、サクミルの社内管理画面を作成するタスクを担当しました。当時はサクミルを利用するユーザーのアカウントを発行する際に、ビジネスサイドの方が認証基盤にユーザー情報を登録し、サクミルのDBにそのユーザー情報を登録、最後にアカウント発行のメールを送信するという3ステップを手作業で行っていました。これを管理画面上で一度に行いたいという要望がありました。エンジニアになって2ヶ月目にも関わらず大きな裁量をいただき、PdMと仕様を検討しながら開発を進めました。管理画面が完成し、初めて利用していただいた際には、サクミルチームが湧いたことをよく覚えています。このタスクを通じてビジネスサイドと距離が近い環境で開発を行いたいと考えるようになりました。

きっかけはPdMの一言⚡️

学部4年生の10月に私が「SaaSの立ち上げ期に携われるのは楽しいけど、エンジニアなら大規模サービスにも関わってみたいですよね」という話をPdMにしたところ、PdMから 「1つのサービスの立ち上げから、大規模になるまで自分でやっちゃえばいいんだよ」 という一言をいただきました。あ、そっか。自分でやればいいんだと思いましたね笑。単純ですが、個人的にはコペルニクス的転回でした。

この時点まで、私は大学院進学をするつもりでしたが、この一言がきっかけで学部卒でプレックスに就職した方がよくないか?という疑問が湧いたのです。もちろん、大学院に進学しそのままサクミルでのインターンを続けるという選択肢もありましたが、正社員という当事者として急激な成長期のサクミルに携わる経験が長期目線で自身の成長につながると考えました。そして、学部4年生の2月に内定を承諾しました。

プレックスに入社した理由🔥

プレックスに入社した理由をもう少し分割したいと思います。

豊富な打席に立ち続けることができる

前述した通り、サクミルは急激な成長期にあるため、エンジニアには様々な打席(タスク)があります。そのため、たとえ新卒1年目であっても、難しい打席で裁量を持ってバットを振ることができます。

これは技術力が高いとされる企業と比較した際のメリットであると考えています。プレックスでは常に自分の能力値ギリギリの打席に立つ事ができるので、他社と比較してより成長できる環境だと考えました。

面白い打席については随時、開発者ブログに投稿していきます。

優秀なメンバーと事業を成長させることができる

プレックスには、優秀なメンバーが数多く在籍しており、メンバー1人1人が事業成長に対して熱量を持っています。業務時間中にプロダクトに関して熱く議論を交わすのは日常茶飯事です。議論のレベルが高いので、私はついていくのでやっとです汗。

また、サクミルが対象とする建設業界の国内における市場規模は経済センサス‐活動調査(METI/経済産業省)によると 約112兆円で超巨大です。さらに、建設業界はレガシー業界でIT化がそれほど進んでいない業界でもあります。そのため、発足1年程度のサクミルにはとても大きな伸び代があります。

私は、優秀なメンバーとこんなにも大きな挑戦ができることにワクワクを抑えきれませんでした。そのため、仮に大学院へ進学しても研究を放り投げてプレックスのインターンに時間を投下することは目に見えていたので、それならいっそ入社してしまったほうが良いと思いました。

最後に

プレックスではエンジニアを募集しております。特に我こそは新卒2人目のエンジニアになりたいという方は是非連絡をください! dev.plex.co.jp

最後になりますが、入社するにあたり、プレックス関係者や研究室の先輩・OBの方々に何度も相談に乗っていただきました。皆様のおかげで迷いのない意思決定をすることができました。本当にありがとうございました。

プレックス初の新卒エンジニアの名に恥じないように日々精進して参りたいと思います!

立ち上げ期のSaaS事業におけるエンジニアインターンの採用について

SaaS事業 テックリードの石見です。

2023年4月からSaaS事業を立ち上げており、エンジニアインターンを採用しました。
結論、最高の結果になったため、誰かの助けになればと思い詳細に記載することにしました。

SaaS事業の立ち上げの変遷は前回の記事をご覧ください。 product.plex.co.jp

前提

当時の状況を記載します。

4月から立ち上げを行い、事業が急成長していました。
6月には新規開発、既存機能の改善、事業における技術的な意思決定を1人で進めていくのが物量として限界でした。

コード品質と仕組み化には、こだわっていたため育成環境はありましたが、事業はまだまだ不確実な状況でした。

正社員の採用活動と同時にインターンの採用活動も行うことにしました。

採用要件

要件は以下のようにしました。

必須項目

プログラムによる実装経験がある

if, for とはなんですか?からの育成はコストが大きすぎるため。

※就業・実務の経験は問わない。

週3日以上の出社(研修期間中)

初心者は「何がわからないかわからない」ので、テキストコミュニケーションが困難であるため。

また「社員の時間を奪いたくない」「できないと思われたくない」などの質問への萎縮も発生しやすいので、様子を見てメンターから声をかけたいため。

そして、ドメイン知識のキャッチアップのため。

推奨項目

事業で結果を出すことに興味関心・意識が向いている

立ち上げ期は、意思決定の前提となる条件が大きく変わることが多いです。

また、事業責任者・顧客・競合でさえも正解がわからないことも多いです。チームのひとりひとりが自身の専門性と価値観から意見をぶつけることが重要です。

技術としての成功、タスクとしての達成を超えて、事業に意識が向いていることを推奨としました。

採用フロー

募集

伝えたこと

企画、設計、実装のすべてのプロセスに関われること

立ち上げ期であり、やるべきことは大量にあります。

企画、設計、実装から、それがどう事業に影響を与えるかのすべてのプロセスに関われることが強みとなります。

成長プロセスの明記

立ち上げ期は不確実性が高いため、不安に感じる場合もあります。そのため、プロセスを明記しました。

以下のスプリントを1~2週間で1周します。

・課題と企画と仕様の把握
 ・課題に対して、企画が妥当であるか。企画に対して仕様が妥当であるかを整理します。
・設計
 ・整理した仕様を元に設計を行います。シンプルで良い設計になるまで議論します。
・実装
 ・設計通りに実装をします。シンプルな実装になるようにレビューします。
・振り返り
 ・開発が終わるごとに、学んだこと、疑問だったことなど、成長を言語化して共有します。

学生のキャリアに貢献すること

学生の時間は本当に貴重です。その時間をいただくからには責任を持ってキャリアに貢献することを明記しました。

選考

前提

最初に事業立ち上げにおいて、優秀なエンジニアを定義しました。

  • 技術力が事業の決定打になることは、ほぼない。
    • 当たり前の機能の検証が多いため、大抵は入力 -> 保存 -> 表示で十分である。
  • 性能の最適化が事業の決定打になることは、ほぼない。
    • パソコン、スマホなどデバイスの性能が上がった時代。
    • 事業がスケールしない限りは、最適化が必要な状況はほぼ起こらない。
  • 言語、フレームワークの経験値が事業の決定打になることは、ほぼない。
    • 1週間あれば入門して、アウトプットを出せる時代。
    • 当たり前の機能の検証が多いため、大抵は入力 -> 保存 -> 表示で十分である。
  • Copilot・ChatGPTの登場した現代。
    • 正しい指示ができれば8割の精度で自動生成できる。

→ 課題を特定できて、解決策を提示できて、それを仕様や設計に落とし込めるエンジニアが優秀となる。逆にこれらができれば実装はシンプルになり、自動生成も可能となる。

※もちろん技術のプロとして技術力は非常に重要です。ただ、立ち上げ期は技術力以外の比重が大きくなると考えています。

1. 面接

大きく3点を確認しました。

気持ち

  • なぜ応募したのか
    • 直近の将来像、求めているもの
  • なぜエンジニアなのか
    • 長期的な将来像、欲望、野望

行動

  • (気持ちの回答に対して)そのために何をやったか、やっているか
    • 具体的な行動内容と意図
  • 上手くいったこと、上手くいかなかったこと、その理由
    • 課題と要因
    • 自責と他責の認識
  • アウトプット
    • インプットだけでなく、アウトプットしているか
  • 毎日3時間以上技術に触れているか、触れることを習慣にできるか
    • 量を積めるか、失敗をできるか

対話

  • 抽象的な表現が出てきた際に質問する
    • 具体と抽象の思考と、その場で定義ができるか
  • トレードオフな質問をする
    • 状況と情報の整理と、意思決定ができるか

2. 技術課題

アウトプットを出していただければ、フィードバックをして育成はできると考えたため、今の本人の最大限をアウトプットする能力を確認できる内容にしました。

また、Copilot・ChatGPTでは回答が難しく、事業で結果を出すことに興味関心・意識が向いているかを確認できる内容にしました。

結果として「シナリオとヒントを伝えて自由にシステムを実装をしてもらう」という非常に抽象的な課題となりました。

シナリオ

ダミー社は、企業の経営情報を元にシステムの営業をする会社です。

今までは、スプレッドシートやエクセルやテキストなど、部署や個人で管理していた企業情報ですが、DXの流れもあり、社内の企業情報を1つにまとめた検索システムを構築することになりました。

あなたは、現状と課題を整理して、仕様と企画を定め、素敵なシステムの実装と運用をする必要があります。

レギュレーション

  • Rails + Next.jsで実装してください。
  • 質問は3回までOKです。
    • 複数の質問を1度に送ったら1回としてカウントします。
    • 課題の特定、実装方針の決定、成果の確認で利用するのがおすすめです。
  • 2週間以内にGithubで提出してください。
    • 合否にかかわらず必ずフィードバックをします。

3. フィードバック面談

学生の貴重な時間をいただくからには、合否にかかわらず「参加して良かった」と思っていただけるくらいのフィードバックをしたいと考えていました。

評価点

  • 情報整理
    • 課題を捉えているか
    • 質問をしたか
    • 質問の視点がエンジニアを超えて事業へ向かっているか
  • 実装力
    • 実装方法の妥当性
    • 実装内容の妥当性
    • 実装順序の妥当性
  • システム
    • 不具合がないか
    • UI / UX
    • データの正規化
  • その他

Githubを一緒に見ながらフィードバックをしました。 「ここはできている」「ここはもう少し」「この視点があると良い」「こう実装すると良い」と背景と意図を具体的に伝えることを意識しました。

また、学生の貴重な時間をいただくからには、必ずキャリアに貢献したいと考えていたため、インターン期間が中途半端に終わらないようにフィードバックの後に「Nヶ月いただければ成長にコミットできる」と伝えて、インターン期間の合意を得ました。

4. CTO面接

ここまでは私1人で行うため、独断と偏見にならないようにCTO面接を最終面接として行いました。

結果

実務未経験者を対象としたため、2週間という短い期間でしたが非常に多くの応募をいただきました。

  • 応募: 62
  • 書類選考合格: 34
  • 面接合格: 19
  • 技術課題提出: 9
  • 技術課題システム動作: 5
  • 最終合格: 3

最終合格となった3名は、システムの提案資料と技術資料を作ってプレゼンをした方や、「この会社の営業方式はオフラインですか?オンラインですか?」といった非常に具体的な業務プロセスを質問して課題を特定した方や、非常にミニマムな実装のみでシステムを実現した方など、本当に素晴らしい方々でした。

感想

まず、選考に関わってくださった学生の方々、本当にありがとうございました。

若い方から学ぶことは多かったです。

合否に関係なく、学生の夢や思考を聞いて一緒に議論して課題を進めた時間は本当に楽しく眩しかったです。

ほぼ1人で採用活動を行いましたが「事業の成長と学生への貢献」の両立を実現する結果となり、嬉しかったです。

次回は、採用後の育成について執筆をできたらと考えています。

... 最後に

「事業で勝てるエンジニア」「事業と技術の両軸」を志している方は、ぜひ連絡をください。

一緒に闘いましょう。

【入社エントリ】あえて入社しました

はじめに

はじめまして、プレックスの栃川と申します。

2023年12月に株式会社プレックス(以下、プレックス)にエンジニアとして入社しました。

早くも入社して3ヶ月経ったので、自己紹介を兼ねて入社経緯や入社してからの感想などをまとめておきたいと思います。

一人でも多くの方にプレックスに興味を持っていただけると嬉しいです。

自己紹介

改めて栃川と申します。27歳の社会人4年目になります。

私の経歴は下記の通りです。

年月 経歴
2020年4月 私鉄企業へ新卒入社 。情報システム担当してました。
2021年7月 スマサテ株式会社にエンジニアとして入社。
2023年12月 プレックスへエンジニアとして入社。

実は、新卒入社した会社ではエンジニアではなく情報システム担当として働いていました。

情報システム担当と聞くとあたかもシステム関連の仕事をしていそうですが、 基本的には議事録作成や人事異動に伴うPCのセットアップなどの雑務中心の業務をしていました。

そんな畑違いの業界から急にエンジニアへの転向したのは、たまたま一緒に働いていたエンジニアの方から、Progateというプログラミングの学習サイトについて教えてもらったことをきっかけでした。

プログラミングを通じて物を作る楽しさを知ってから「自分もエンジニアになってなんか作りたい!」と強く思うようになりました。

その後、エンジニアになるべく新卒入社した会社を1年で退職し、前職にあたるスマサテ株式会社にエンジニアとして入社しました。

スマサテ株式会社では、フロントエンド・バックエンド問わず幅広く開発に携わりました。 当時はマンションの一室がオフィスで、まさにスタートアップ!という感じで働かせていただきました。

今でもこの会社で働けたことは誇りです。

2年半ほどお世話になった後、プレックスに転職しました。

プレックスではSaaS事業部に所属し、建設業に特化した業務管理クラウドサクミルの開発に従事しています。

プレックスに入社した理由

「この環境に飛び込んだら、とんでもない苦しみと引き換えに大きな成長を得られるかも!」

それが決め手となり、プレックスに飛び込むことを決めました。

私が転職活動で大事にしていたことは2点です。

  • 事業が成長していること
  • プロダクトが面白いこと

これは自身の働くモチベーションになるので重要視していました。 しかし、これらを満たす企業は何社かありました。

その中でプレックスを選んだのは、配属される部署のテックリードの方とお話しした時にこう思ったからです。

「やばい....この会社に行ったら過去の経験は何も通用しないな.....絶対苦しむな.....うん、逃げよう!」と(笑)

プレックスのエンジニアは、「正しい問いが何かを徹底的に考え、正しい意思決定をすること」が求められます。

例えば、PdMから機能開発の要望があった場合、エンジニアはその機能が「いつ・どこで・誰が・何を・どのように・なぜ」必要になるのか全て洗い出し、現状と理想のギャップから生まれる根本的な問題を正確に把握します。

その結果、問題に対してより最適な答えがあれば、エンジニアが主体となってPdMやセールスの方へ提案を行い、正しい意思決定を導き出します。

今まで「求められた機能を忠実に再現していく」ことをしてきた自分にとっては、 エンジニアが企画段階から意思決定をする環境は未知の領域です。

もし、この未知の領域に飛び込む決断をすれば、相当苦しむだろうなと全身は拒絶反応を起こしまくっていました。

一方で「この環境で働いた1年後の自分はどうなっているんだろう」と、心がとても高揚したのを今でも覚えています。

とても悩んだ末、まだ見ぬ自分に会ってみたいというワクワク感を抑えきれず、新たな挑戦に身を投じることを選びました。

入社してからの感想

徹底して思考しきる「やばい」文化

入社して3ヶ月目までは脳が溶けそうなくらい脳をフル回転させていました。 (ちなみに今も溶けそうです)

なぜなら、設計レビューしてもらうと下記のようなことを普通に質問されます。(ごく一部です)

  • タスクの背景にあるものはなんだろうか。
  • 背景に対してどんな課題があるのか
  • 課題に対してどんな意思決定をしたのか。
  • その意思決定で得られたことはなにか
  • その意思決定で失うことはなにか
  • その意思決定は、どの立場にたった意思決定なのか
  • その意思決定の前提が崩れるときはどんな条件か

こんな感じで、どんなに小さいタスクでも徹底して思考しきります。 入社してすぐのフィードバックで「それ、仕事したとは言えませんよ」と指摘を頂いたことも記憶に新しいです(笑)

最初はとても苦労するかもしれませんが、ただの作業員ではなく、意思決定できるエンジニアを目指す人にとってはこれ以上ない「やばい」環境なのではと思います。

当たり前が「すごい」環境

プレックスではインターン生を含め優秀な方が本当に多いです。

ゆえに「当たり前の基準」が極めて高いです。

入社当初は、この当たり前の基準の高さに「すごい」を連呼していました。

例えば、仕事の進め方についても「キーボードから手を離した時点で負け」と言われるほど生産性にこだわっていますし、 「言葉にできないことは全て0点」と言われるほど、言語化や解像度の高さにこだわっています。

他にも、息をするように自己研鑽し、情報共有していくことも当たり前として根付いています。

エンジニア界隈で話題になるような話題は、どのSNSよりも早く、誰かがSlackでシェアしてくれます。 勉強会やエンジニアイベントも定期的にあります。

そのため、自分だけでは広げきれない興味・関心の範囲を一気に広げることができます。

こうした当たり前の基準が高いというのは、プレックスならではの良さだと思います。

まさに、「すごい」環境だと思います。

成長が「激アツ」な事業

事業成長半端ない」です。

入社して何よりも驚いたことです。

「記録達成!」や「連続受注!」といった嬉しいワードが毎日のように社内で聞こるほど事業が伸びています。

事業が急成長していく中で当事者として参加できることは滅多にないので、このタイミングで入社できた運の良い自分に感謝しかありません。

事業が成長していると、やらないといけないタスクも刺激的で面白い仕事ばっかりなので、かなり「激アツ」です。

最後に

入社してから3ヶ月しか経っていませんが、プレックスはとても恵まれている環境だと心底感じています。

プレックスについて語り出すと、かなり長文になりそうなので続きは他の新人の入社エントリーにバトンタッチしたいと思います。

最後にはなりますが、プレックスではソフトウェアエンジニア、フロントエンドエンジニアを募集しています。

もしこの記事を読んで、一緒に挑戦してみたいと思った方がいましたら是非ご連絡をお待ちしています!!

dev.plex.co.jp

ユーザー向けプロダクトだけが全てじゃない、テクノロジーで事業にレバレッジを掛けるプレックスのコーポレートエンジニア

eye-catch

こんにちは、プレックス コーポレートエンジニアチームの石塚です。

はじめに

みなさんは「コーポレートエンジニア」という単語を聞いたことがあるでしょうか? 情シスや社内SEのようなイメージを持たれる方が多いかと思いますが、わざわざ別の単語を用いて表現しているからには、若干違うニュアンスが込められているはずです。

プレックスにおいても2022年中頃からコーポレートエンジニアというポジションをオープンし、採用を進めてきました。 その際、候補者やエージェントの方にコーポレートエンジニアのニュアンスが間違って伝わっていることが少なからずあり、今回のエントリーを書いてみようと思いました。 本ブログでは、他社のコーポレートエンジニアの事例を見ていった後に、プレックスにおけるコーポレートエンジニアの定義や実際に直近で取り組んでいる課題、技術スタックをご紹介します。

他社のコーポレートエンジニア事例

SmartNews

2017年からCorporate Engineering Teamが設立されているSmartNews。Corporate Engineeringは世界的には割とメジャーなポジションらしく、海外展開をされているからこそ、こんなにも早くこの領域への投資ができているのかと思います。

ミッションは、会社をhackすることで、会社全体の業務効率化や、改善活動をエンジニアリング的手法を用いて実施する組織となっています。

developer.smartnews.com

メルカリ

メルカリのコーポレート・エンジニアリングチームは、お客さまにサービスを届ける側である、ともに働く仲間にフォーカスし、「最高の従業員体験(Employee Experience)」を実現するというミッションを掲げています。

チームメンバーも2019年時点で40名と多くのメンバーが在籍しており、テクニカルサポートのようないわゆる情シスのような役割を担う「IT Service」チームから、社内プロダクトの開発を行う「Corporates Product」チームまで幅広い役割を持った組織になっています。

mercan.mercari.com

SmartHR

SmartHRのコーポレートエンジニアは、社内の課題をプログラミング技術で解決する役割を担っており、社内システムの導入・設計や社内システム開発がメインの業務となっています。

ヘルプデスクやオンボーディングを行うコーポレートITチームとコーポレートエンジニアチームが分かれていることからも、通常の情シスや社内SEとは少し変わったポジションであることがわかります。

www.talent-book.jp

techplay.jp

プレックスにおけるコーポレートエンジニア

これまで3社のコーポレートエンジニアの事例を見てきましたが、プレックスでは「オペレーションの効率化によって事業成長に貢献する」をミッションとし、社内の内製するシステムや導入するSaaSに関連する開発、運用を担当するチームと定義しています。

プレックスはインフラ産業において、人材紹介、ダイレクトリクルーティング、SaaSM&A仲介など多様なソリューションを用いて業界の課題を解決している会社です。インフラ産業では、会社や担当者によってITリテラシーにばらつきがあるといった業界ならではの特徴があります。単純にSaaSなどのプロダクトを提供しても、それをお客様が使いこなして価値を得るためには多くのハードルがあるため、弊社ではテクノロジーの力だけではなく、人の力を駆使することも重要視しています。そういった理由から採用活動も積極的に進めており、直近では正社員だけで250名を超える組織規模になってきました。そのため、純粋なユーザー向けのプロダクト開発を進める以外にも、社内のオペレーションを効率化していくことへのニーズが高まっていき、コーポレートエンジニアチームを発足させることを決めました。

組織図的には総務部などのコーポレート部門ではなく、開発本部付けの組織になっています。そのせいか、情シスやテクニカルサポートといった役割よりは、社内プロダクトやSaaS拡張の開発といった業務がメインとなっています。 また、扱う技術もWeb開発における汎用的な技術がほとんどで、エンジニア全体の定例や勉強会など他プロダクトのエンジニアとの交流も盛んです。

直近で取り組んでいる課題

より弊社のコーポレートエンジニアについて具体的なイメージを持っていただくため、直近で取り組んでいる課題についても簡単に箇条書きでご紹介します。

  • メンテナンスしづらいシステムの品質向上
    • プレックスでは過去に業務委託やインターンのメンバーに構築してもらった社内システムやコードが存在し、技術スタックや品質にばらつきがあったり、適切な監視が実施されていないという問題がありました
  • LINEチャットシステムの開発
  • 事業部を横断したMA(Marketing Automation)ツールの開発
  • CRMとして利用しているkintoneのカスタマイズやプラグインの開発
  • kintoneのつらみの解消
    • データ量が増えてきたアプリで速度面での問題が顕在化している、他の社内システムとの連携がしづらい、APIのリクエスト回数が上限に近づいてきているといった課題があります
  • 既存のユーザー向けプロダクトが成長する中で全社的にデータ分析のニーズが高まっている
    • 特に事業部間をまたいだデータの共有や活用の機運が高まっており、モダンデータスタックの構築を進めています
  • Slack通知を社内のさまざまなシステムから実行していてAPI制限がかかる場合があり、中央集権的にAPIリクエストを管理したい

技術スタック

最後に技術スタックについても軽く紹介しておきます。 コーポレートエンジニアがメンテナンスするシステムは規模は小さいものが多いですが、数が多くなるといった特徴があるため、メンバーが複数のシステムをメンテナンスしていけるように、なるべく技術スタックは統一していくという方針を取っています。 RailsやNext.jsといった会社のユーザー向けプロダクトチームでも活用している技術を用いているため、他のチームからのサポートが受けられる、全社で技術的なナレッジをシェアできるといったメリットもあります。

  • 言語
    • Ruby
    • Python
      • 推奨言語はRubyですが、古いシステムやクローリング、AirbyteのCustomConnectorで一部Pythonが使われています
    • TypeScript
    • JavaScript
      • 基本はTypeScriptを使っていますが、kintoneのプラグインやカスタマイズの開発にJavaScriptも使用しています
  • フレームワーク
  • インフラ
    • Heroku
      • 会社でRailsのテンプレートリポジトリを用意しており、PipelineやContainer Stackの機能を使用することを推奨しています
    • Vercel
    • terraform
    • GCP
      • Cloud Storage
      • Cloud Functions
      • Compute Engine
      • BigQuery
    • 監視
      • プラットフォームで提供する監視を使用することを推奨しています
        • Heroku ⇒ Heroku Metrics
        • GCP ⇒ Cloud Logging / Error Reporting
  • テスト・CI/CD
  • データ系ツール
    • Redash
    • Airbyte
    • dbt
    • Hightouch
  • その他ツール

終わりに

今回のブログでは他社のコーポレートエンジニアの事例から、プレックスにおけるコーポレートエンジニアの定義、直近で取り組んでいる課題、技術スタックを紹介しました。 この記事を読んで、コーポレートエンジニアについての理解が深まり、興味を持ってもらうきっかけとなってもらえたら幸いです。

プレックスでは「オペレーションの効率化によって事業成長に貢献する」コーポレートエンジニアを募集しています。 少しでも興味をお持ちいただけたら、カジュアル面談でちょっと話を聞いてみたいなくらいの温度感でも大丈夫なので、下記のリンクから応募いただけると嬉しいです。

dev.plex.co.jp

HerokuからGCPへのインフラ移行 〜ダブルライト検証編〜

アイキャッチ

こんにちは、Plex Job 開発チームの池川です。

Plex Job では従来、バックエンドのデプロイ先として Heroku を使用していましたが、2024年1月に GCP に移行しました。 移行にあたって、移行後しばらくはいつでも旧環境に切り戻せるようにしておく必要があったほか、切り戻し時に発生するサービスの休止時間もなるべく抑えたかったため、新旧 DB にダブルライトする方法が取れないか検証しました。 結論としてはダブルライトは不採用としたのですが、不採用とした経緯も含め検証して得た知見を今回はまとめたいと思います。

検証環境

設計

ダブルライト実行の大まかな流れは下記です。

  1. 新環境 DB に該当レコードの書き込みを行う
  2. 書き込み結果のレコードを取得する
  3. 非同期タスクのジョブとしてキューを作成する
  4. Worker によりジョブが実行され、旧環境の DB に 2.が書き込まれる

ダブルライトの実行対象は /graphql エンドポイントへの Mutation クエリとしました。 また、新環境への影響を考慮して旧環境 DB への書き込みは非同期タスクとし、非同期タスクのジョブとしてキューを登録するタイミングは ApplicationRecord の after_commitコールバック時としました。 なお、非同期タスクの実行にあたっては Delayed Job を使いました。

設計時に考慮した点がいつくかあったので紹介します。

1. コールバックは順番が保証されているか

同一のトランザクションの各操作に対するコールバックの順番が保証されているかどうか検証しました。 例えば下記のように新規登録したデータを元に別のモデルのデータを更新するケースです。

ActiveRecord.transaction do
  user = User.create!(name: params[:new_user_name]) # ①

  product = Product.find(params[:product_id])
  product.update!(price: params[:new_product_price], updated_by_user_id: user.id) # ②
end

after_commit使用して旧環境 DB への書き込みした際にも ① → ② の順番が正しく担保されるか検証した結果、順番通りに実行されており問題ありませんでした。 ただし、同一のトランザクション内で同じレコードに対して書き込みを行うと、後続のレコード更新時にコールバックが実行されない問題があるため、場合によっては下記の記事で紹介されているような対応が必要となります。

zenn.dev

2. コールバックがスキップされる ActiveRecord のメソッドがある

ActiveRecord のメソッドの中にはコールバックがスキップされる、つまりafter_commitが呼ばれないメソッドがあります。 例えば下記のようなメソッドです。(一覧はこちらにまとめられているので参照ください)

  • insert_all
  • update_all
  • delete_all
  • update_column
  • update_columns

上記のメソッドを使用している箇所はそれぞれ下記の対応を行いました。

  • insert_all などを使用しているところで扱う件数が少ない箇所
    • create, save, update を使って一件ずつ登録・更新するように変更してafter_commitを使用する
  • insert_all などを使ってバルクインサートした方が良い箇所
    • after_commitは使用せず、バルクインサートした後に個別に非同期タスクのキューの登録処理を実装する
  • update_column などを使っていてコールバックを敢えて使用したくない箇所
    • after_commitは使用せず、update_columnした後に個別に非同期タスクのキューの登録処理を実装する

3. Active Storage に対応可能か

Active Storage にファイルを保存する際、active_storage_attachments テーブルと active_storage_blobs テーブルにデータが書き込まれていたため、対応する必要がありました。 それぞれのテーブルの役割は下記を参照ください。

railsguides.jp

upload_file = ActiveStorage::Blob.create_and_upload!(
  # 省略
)

user = User.find(params[:id])
user.file.attach(upload_file)

attachment_attributes = user.file.attachment.attributes # active_storage_attachments テーブルの値
blob_attributes = user.file.blob.attributes # active_storage_blobs テーブルの値

# キュー登録の処理を実装する

ActiveStorage の一連のアップロード処理で作成されるレコードは上記のような形で取得できるため、項目 2 で紹介したバルクインサートのケースと同様に、ファイル登録処理のタイミングで個別にキューを作成するようにしました。

次にどのような実装を行なったかコードを元に説明していきます。

実装

1. 設定の追加

旧環境 DB への接続情報を database.yml に追加します。 今回は replica_db という名前としましたが任意の名前を設定可能です。

development:
  db:
    <<: *default
    url: <%= Settings.db.url %>
  replica_db: # 以下、追加
    <<: *default
    url: <%= Settings.replica_db.url %>

合わせて、Rails 標準で実装されている水平シャーディングを使って、レプリカ DB への接続設定を ApplicationRecord に追加します。 replica_dbの部分はdatabase.ymlで設定した名前と合わせます。

class ApplicationRecord < ActiveRecord::Base
  # 省略

  connects_to shards: {
    default: { writing: :db, reading: :db },
    shard_one: { writing: :replica_db, reading: :replica_db }
  }
end

2. モジュールを作成する

ダブルライト用のモジュールを作成します。

module DoubleWrite
  extend ActiveSupport::Concern

  included do
    after_commit :double_write, if: :on_cloud_sql? # 新環境 DB(cloudSQL)に書き込みされた時のみDoubleWriteが実行されるように設定
  end

  private

    def double_write
      operation = determine_operation
      attributes = previous_changes.transform_values(&:last) # 変更内容を取得

      DoubleWriteJob.perform_later(self.class.name, attributes, operation, id) # 非同期タスクのキューを登録
    end

    def on_cloud_sql?
      ActiveRecord::Base.connection_pool.db_config.name == 'db' # database.yml にある新環境 DBの名前を設定
    end

    def determine_operation
      return 'destroy' if destroyed?

      previous_changes.include?('id') ? 'create' : 'update' # idが含まれていれば更新処理、含まれていなければ新規登録処理
    end
end

新環境 DB に接続した場合のみ after_commit が実行されるようにします。 非同期タスクには今回は下記のような値を渡すようにしました。

名前 内容
self.class.name データの登録・更新があったモデルのクラス名
attributes 登録・更新内容
operation 処理名(create or update or destroy)
id 更新・削除の場合、対象のレコードの ID

3. ApplicationRecord でダブルライト用のモジュールを読み込む

モジュールは各モデルに継承されている ApplicationRecord でインクルードするようにします。

class ApplicationRecord < ActiveRecord::Base
  # 省略

  include DoubleWrite # ダブルライト用のモジュールの読み込み
end

4. ダブルライトを実行するジョブを作成する

非同期に実行するジョブを作成し、旧環境 DB に書き込む処理を書きます。 ActiveRecord::Base.connected_to を使って旧環境 DB にコネクションを切り替えした中で operation(create や update など)に応じた処理を行うようにします。

class DoubleWriteJob < ApplicationJob
  queue_as :double_write

  def perform(model_name, attributes, operation, record_id = nil)
    ActiveRecord::Base.connected_to(shard: :shard_one) do
      model_class = model_name.constantize

      # operation(create or update ...)ごとの処理を実装する
      # 例:create
      ActiveRecord::Base.connection.reset_pk_sequence!(model_class.table_name)
      model_class.create!(attributes)
    end
  end
end

旧環境 DB への書き込み時に注意する点として、新規登録時には ID の自動採番の不整合が生じないように reset_pk_sequence! を使ってデータを登録するようにしました。 reset_pk_sequence!を使えば該当テーブルの最大IDを取得し、次に挿入されるレコードのIDとして設定することができます。

apidock.com

主な実装は以上です。 最後に検証してみた結果をまとめます。

検証してみて

実装後、テストをしたところ画面上で会員登録したり求人に応募したりする分には問題ありませんでしたが、データの一括インポートなど高負荷な書き込み行った際にダブルライトの処理完了までに大幅に時間がかかることが分かりました。 1,000 件程度の一括アップロード処理で、旧環境 DB への書き込みが完了するのに 1 時間程度かかるといった状況です。 旧環境 DB への書き込みに時間がかかってしまうと新環境と旧環境でデータに差分が生じ、当初の目的である障害発生時にスムーズに旧環境に切り戻すことができないため、今回ダブルライトの採用は見送りました。

また、時間がかかってしまった原因ですが、Heroku は us リージョン、 GCP は asia-east1 リージョンにそれぞれデプロイしており、物理的にサーバの距離が離れているため時間がかかったのかと考えています。 なので環境によってはインフラ移行時にダブルライトを採用するのはアリかもしれません。

さいごに

今回の記事ではインフラ移行時に検証したダブルライトについて、約1ヶ月間の検証を通して得た知見をまとめてみました。 ゼロから実装するにはいくつか詰まる点もあったため、何かの参考になれば幸いです。 今回取り上げたHeroku から GCP への移行は Plex Job にとっても一大プロジェクトだったため、インフラ移行に関する記事は後日改めて公開する予定です。

最後になりますが、プレックスではソフトウェアエンジニアフロントエンドエンジニアを絶賛募集中です! 少しでも興味を持っていただけた方は業務委託や副業からでも、ぜひご応募いただけると嬉しいです。

SaaS事業の1人目のエンジニアが1年目にやったこと

2023年4月にSaaS事業の1人目のエンジニアとして入社しました、プレックスの石見です。

SaaS事業の立ち上げにおいて、1人目のエンジニアが1年目に何をやったかをまとめます。

経歴について

DeNA(新卒) -> YOUTRUST -> プレックスとなります。

▼「なぜプレックスに入社したのか?」は、以下の記事をご覧ください。

note.com

SaaS事業について

現場作業が伴う事業者向けの業務効率化SaaS「サクミル」を運営しています。

2022年末からの大幅なピボットを経て、事業化に至ったタイミングで私が1人目のエンジニアとして入社しました。

sakumiru.jp

4月、SaaS事業は「機能がないと始まらない」

  • 入社
  • ドメインのキャッチアップ
  • 既存コードのキャッチアップ
  • 写真台帳機能のリリース
  • ChatGPTとCopilotを福利厚生に追加

入社して最初にドメイン(市場・顧客・競合)について学びました。良い設計、良いソフトウェア、良い実装は、特定の前提条件において「良い」ものになります。ドメイン(市場・顧客・競合)を知らなければ良いエンジニアにはなれないため、特に重視しました。

そして、プロトタイプのサービスがあったため、既存コードのすべてに目を通して理解をしました。現状はすべて過去の「当時の最善」の意思決定によって作られているので「なぜこの技術選定なのか」「なぜこの実装なのか」までを過去の議事録に目を通してキャッチアップしました。

また、写真台帳機能という大きめの新機能をリリースしました。

最後に、ChatGPTとCopilotを福利厚生に加えるように会社に提案をして受け入れていただきました。

5月、3ヶ月後の未来に備える

ピボットの関係で、現状と合っていないデータ構造が見られたため、テーブルスキーマを整理しました。テーブルスキーマが正しければアプリケーションコードはシンプルなものだけで良いため、事業検証の速度と開発の速度が担保できます。そのため、当たり前な機能の開発が主となるSaaS事業の立ち上げでは「テーブルスキーマの正しさ」は特にこだわった方が良いと考えています。

合わせて、アプリケーションコードのリファクタリングも行いました。

そして、事業進捗の速度を考えた時に私1人での限界が予想できたため、インターン生を採用することにしました。「なぜ業務委託でなくてインターン生なのか?」については、立ち上げ期の混沌においてはスタンスが固まり切っていない学生の方が適応力が高くパフォーマンスすると考えたためです。また、シンプルな機能実装でも学生にとっては貴重な機会となるため、学生のキャリアに貢献できるのも良いと考えました。

インターン生を採用するに当たって「なぜ採用するのか?」「どのような学生を採用したいか」「どうやって確かめるか」「何をお互いに合意した上で採用とするか」を決めました。

6月、変化を予期して、起こす

  • インターン生の採用面接
  • インターン生の技術課題のフィードバック
  • パフォーマンスの改善
  • フロントエンドのリニューアル計画
  • カレンダーの実装

インターン生の採用をはじめました。育成前提で経験を問わなかったこともあり、結果として60名ほどの応募が来ました。ほぼ全員と面接を行いました。

事業と技術の両軸に関心がある学生を見極めるために、抽象的な技術課題を設けました。抽象的な課題であることから質問を可能としていたため、質問対応を行いました。技術課題を提出をしていただいた学生には必ずフィードバックをすると決めていたため、提出された技術課題を読み込んでフィードバックを行いました。

また、サービスのパフォーマンスの改善を行いました。

そして、プロトタイプから脱却をするためにフロントエンドのリニューアルの計画をしました。私がフロントエンドの専門性が高いエンジニアではなかったため、フロントエンド周りの技術のキャッチアップを行いました。技術の変遷を理解するのは非常に面白かったです。

最後に、スケジュール機能のためのカレンダーの実装をしました。

60名の方との面接を1人で行ったこともあり、喉を激しく痛めてしまいました。人事の方をより尊敬しました。

7月、「正しさ」を正解にする

  • フロントエンドのリニューアル
  • インターン生の採用決定

フロントエンドのリニューアルを行いました。Next.js 13(App Router)を採用して、アーキテクチャにはBulletproof Reactを採用しました。「事業立ち上げ期で機能の境界が固まり切ってないことから、featuresはroutingごとに切る」などの規約も多く策定してESLintで担保されるようにしました。

github.com

リニューアルやリファクタリングは「本当に必要なの?」と言われることもありますが、自分だけが見えているリスクやコストを整理して、提案と説得をして、結果として「正しさ」を事業の正解にするのがアーキテクトやテックリードの責務だと考えています。

また、インターン生1名の採用を決めました。本当に素敵な学生と出会えて運が良かったです。採用方針や技術課題の内容も非常に良かったことを実感しました。

既存のサービスの開発をしながらリニューアルに向けて、とにかく意思決定をして手を動かしていました。

8月、1人からチームになる

  • フロントエンドのリニューアルのリリース
  • インターン生の受け入れ
  • スプリント開発とチケット管理の仕組みの構築

ついに、フロントエンドのリニューアルをリリースしました。サービスとしても非常に見栄えが良くなりました。開発体験としても適切な切り分けと規約によって意識する箇所が減り、開発速度が大きく向上しました。

そして、インターン生が来てくれました。1人でなくなった喜びもありつつ「未経験の学生をどうやって伸ばしていくか」というマネジメントと向き合いました。開発体験が向上してキャッチアップが容易になっていたこともあり、月末には1人のエンジニアとして活躍していただいていました。

また、1人でなくなったため、スプリント開発とチケット管理の仕組みを構築しました。

9月、プロダクトチームになる

  • スケジュール機能のリリース
  • チケット管理の仕組みを改善
  • ドキュメントの整備

スケジュール機能をリリースしました。少しだけ大変でした。

チケット管理の仕組みにPdMの領域を取り入れました。これによって、要望 -> チケット -> 仕様 -> 進捗 -> PRまでが可視化されるようになりました。可視化されたことで、開発速度が向上しました。

最後にドキュメントの整備を行いました。

個人的には「プレイヤーとマネージャーの切り替え」に苦しみましたが、事業としては「パーツが揃った」と感じました。

10月、開発速度の躍進

フロントエンドのリニューアルから2ヶ月が経過して、一部の機能において想定と異なっていた箇所があったため、リニューアルを行いました。

また、不要な機能の削除を行いました。定期的な引き算は非常に大事だと実感しました。「また必要な時に考えて、再実装する」で良いです。

そして、前提が変わった箇所もあったためテーブルスキーマの整理とアプリケーションコードのリファクタリングを行いました。この際に日本語で表現する概念図とテーブルスキーマを表現するテーブル図を作成しました。これによって、サービス内の概念と表す単語と関係性の共通認識が取れるようになり、議論の質と速度が向上しました。

さらに、インターン生をもう1名採用しました。活発な2名の学生のおかげでチームに活気がつきました。学生から学ばさせていただくことは多いと実感しました。

最後に開発速度の向上によってリリース内容の把握が難しくなったため、リリースフローの整備を行いました。

開発速度が大きく向上しましたが、レビューや実装方針の決定など私がボトルネックになってしまうことが増えました。

11月、「個の最大値」をチームで超える

  • スケジュール機能の追加リリース
  • テーブルスキーマの整理
  • コミットとPRの仕組みの構築
  • ドキュメントの整備

スケジュール機能を拡張して追加リリースしました。こちらはインターン生の主導で開発をしていただきました。非常に良い機能になりました。

テーブルスキーマの整理を行いました。今回は概念の整理ではなくて、項目を増やしたりなど機能の拡張を行いました。

また、コミットとPRの仕組みを構築しました。これによって、レビュー工数が減り、リリースノートが自動で生成されるようになり、リリースフローの管理がより詳細になり、より容易になりました。

最後にドキュメントの整備を行いました。

チームとして最大値が出せるようになりました。

12月、次のステージへ

原価粗利管理機能をリリースしました。繊細な機能でしたが今までの仕組みと開発体験の向上のおかげもあり、楽に実装ができました。今後の複雑な機能も素早く丁寧に開発ができると確信しました。

テーブルスキーマの整理を行いました。今年中にすべてのテーブルの整理を終わらせるのが目標だったため、気合で進めました。

また、独自ドメインからのメール配信の対応を行いました。セキュリティの対応もあり、学ぶことが多かったですが、無事に対応ができました。

最後に、プロトタイプの完全廃止を行いました。

1年目を生き残っての感想

まず、本当に楽しい1年でした。人生の中で1番楽しかった。

個人としては「SaaSは1人でも実装できる。」を実感しましたが、事業の速度に追いつくには「1人では無理だ。」も実感しました。

この「事業に置いてかれないように、自分を拡張できるか。」という恐怖と「自分の理想を実現できるか。」という挑戦と、ずっと闘った1年でした。

そして「今の自分を乗り越える」「市場と顧客に貢献できる」「メンバーのキャリアを広げる」といったことが非常に楽しかったです。

来年はさらなる飛躍を控えているので、引き続き事業と技術に向き合っていきます!

「事業で勝てるエンジニア」「事業と技術の両軸」を志している方は、ぜひ連絡をください。

折れないで、一緒に闘いましょう。私はまだ闘い続けます。

Kaigi on Railsの参加レポートと管理画面用のAPIを統合した話

はじめに

この記事は「技術カンファレンス Advent Calendar 2023 | 2枚目」の12日目です!

こんにちは、Plex Job開発チームの種井です。

今回は10月27日・28日で開催されたオフラインイベント、Kaigi on Railsの参加レポートと、その中でのSMART BANK @ohbaryeさんの発表「管理画面アーキテクチャパターンの考察と実践」が同時期にPlex Job開発チームでも取り組んでいたリアーキテクティングと重なる部分があったため、発表内容を一部をお借りし、我々の取り組みについても紹介したいと思います。

Kaigi on Rails 2023の参加レポート

元々オンライン開催されていた時の登壇資料やアーカイブ動画を見たことがあったのでイベント自体は認知していました。 今回、初のオフライン開催であることや、会社でカンファレンスの参加費用補助制度もできたので、制度を活用して参加してみることにしました。

2日間、現地で講演を聞きましたが、どの発表も非常にわかりやすく、学びになるものでした。 特にRailsをプロジェクトで運用する中で発生するパフォーマンスや保守性の担保に関するものが多かった印象です。 Railsを採用していると立ち上げのスピードと引き換えに、システム規模の拡大とともにパフォーマンスや保守性の壁にぶつかることが多いです。 サービスのスケール時に技術選定から見直すこともあるかと思いますが、なるべくRailsで培った資産を有効に活用していきたい、そういった場合のノウハウが詰まっていました。

基調講演ではRailsコミッターであるzzakさんやbyrootさんの発表がありました。 お二人ともRailsへ携わるきっかけや、課題の特定と解決に向けた取り組み、OSSプロジェクトでの立ち回りで重要なことについて、経験を交えて紹介されていましたが、チームや個人としての成果を出す上では私達が普段プロダクト開発においても意識していることと重なる部分も多いと感じました。

また、イベントとしても初のオフライン開催とは思えないほどスムーズかつ丁寧な進行で、スポンサーブース、懇親会ともに終始楽しく参加することができました。 オフラインならではの熱気があり、私自身、刺激を多く受けて帰ってきました、改めてオフラインイベントのよさを感じた2日間となりました。

ちなみにESMさんのブースのガチャガチャでは#refineメソッドが当たりました。

Kaigi On Rails

さて、今回の講演の中で特に印象に残ったものの一つとして、SMART BANK @ohbaryeさん「管理画面アーキテクチャパターンの考察と実践」の発表があります。

プロダクト開発を行っていると社内向けの管理画面が必要になる機会が訪れます。 管理画面を開発する上で取られるアーキテクチャパターンを体系的化し、ビジネス要件とアーキテクチャ特性から採用したアーキテクチャと、それをRailsで設計・実装する上での工夫について非常にわかりやすく紹介されていました。

ちょうどKaigi on Railsの開催期間中に我々も管理画面のリアーキテクティングに取り組んでいたこともあったので、発表内容をお借りして、我々の取り組みについて紹介をしたいと思います。

社内管理画面のAPI統合

背景

私達も同じく、初期フェーズは仮説検証を優先し、管理機能を運用でカバーしつつ後追いで徐々に管理画面化を進める方法をとっていました。

我々の移行前のアーキテクチャパターンは以下です。

資料中の「マイクロサービスで共有データベースパターン」です。 エンドユーザー向けのサーバーと管理機能のユーザ向けのサーバーを分けるが、同一のDBを参照するというものです。 私達の場合もメリットとして挙げられている、「立ち上げ時の実装を楽にしたい」に加え「管理機能からの時間のかかるリクエストによって、エンドユーザー向けの機能に影響を与えないようにしたい」ことからこちらのパターンを選定した背景があります。

立ち上げ期は単純なCRUD操作を中心に機能要件としても複雑なものは必要なかったのですが、事業の成長とともに、複雑で事業上重要な要件を持つオペレーションの管理機能化や社内ユーザーの増加もあり、保守性や安定性がより求められるようになってきたところですが

  • デグレやすい
    • 依存モジュールの更新やCIの整備が放置されている
    • 単体テストやlinterの整備などが放置されている
  • 開発者のスイッチングコストが大きい
    • 同じドメインのモデル定義や実装を異なるコードベース間で同期させる必要がある
    • デプロイ対象のサーバーコンポーネントが多い

上記のような、課題が大きくなってきました。 安定性や保守性の要件に対して、立ち上げ期ではなくなってきていることや、当初のパフォーマンス面での懸念事項も徐々に改善できていることから、アーキテクチャの変更に踏み切ることにしました。

変更後のアーキテクチャ

資料中の「既存Backendだけ再利用しつつ管理機能はPDS順守パターン」です。 エンドユーザー向けのサーバーと管理機能向けのサーバー、参照先のDBは同じで、フロントエンドをエンドユーザーと管理機能の利用者で分けるというものです。

これにより、以下のようなメリットを享受することができました。

  • 同じドメインを扱うモデル、コードの再利用が可能になった
  • コードベースを統合したことにより、比較的メンテナンスされているコードベースのテストやCIにのっかることができ、保守性やテスト容易性などが向上した
  • サーバーコンポーネントが減ることによって保守・運用コストが削減された

統合に向けてやったこと

今回の統合で上記のようなメリットは得られつつも、統合時に試行錯誤したこと、一部妥協し今後の課題として残っているところがいくつかあるため、その中の一部を紹介したいと思います。

Plex Jobの管理画面の構築には以下を使用しています。

  • Ruby (3.2.2)
  • Rails (7.1.1)
  • graphql-ruby (2.1.6)
  • react-admin (4.10.2)
  • ra-data-graphql-simple (3.19.5)

GraphQLスキーマを共存させる上で

Plex Job開発チームでは、管理画面へ提供するAPIを統合先のエンドユーザー向けと同じくGraphQLで作成されていました。 そのため、React AdminのData Providerである、ra-data-graphql-simpleの仕様に合わせて、GraphQLのスキーマを既存のスキーマに統合する必要があり、統合にあたって考慮が必要になるポイントの一つでした。

GraphQL上のスキーマを共存させる

ra-data-graphql-simpleでは、対象のリソースに対して以下のようなSchemaを提供する必要があります。 以下は、README.mdにあるPostリソースを例とした場合の期待するスキーマです。

type Query {
  Post(id: ID!): Post
  allPosts(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): [Post]
  _allPostsMeta(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: PostFilter): ListMetadata
}

type Mutation {
  createPost(
    title: String!
    views: Int!
    user_id: ID!
  ): Post
  updatePost(
    id: ID!
    title: String!
    views: Int!
    user_id: ID!
  ): Post
  deletePost(id: ID!): Post
}

type Post {
    id: ID!
    title: String!
    views: Int!
    user_id: ID!
    User: User
    Comments: [Comment]
}

input PostFilter {
    q: String
    id: ID
    title: String
    views: Int
    views_lt: Int
    views_lte: Int
    views_gt: Int
    views_gte: Int
    user_id: ID
}

type ListMetadata {
    count: Int!
}

scalar Date

上記のようなスキーマを既存のスキーマに統合するにあたって、私達のコードベースでは以下のような点に注意する必要がありました。

  • Object Type名の競合
  • Input Type名の競合
  • Operation名(QueryやMutation)の競合
Object TypeやInput Type名の競合への対応

GraphQLサーバーはその仕様上、/graphqlという単一のエンドポイントのみを持ち、リソースはREST APIのようにパスではなく、Typeを定義することで表現します。 そのため、今回のように扱うドメインは同じではあるが、ユースケースとしては異なる複数のクライアントに同じリソースを返す際に名前が競合してしまいます。 GraphQLではスキーマにnamespaceの概念の導入するProposalも作成されていますが、導入されるには至っておりません。 今回は、書籍「PRODUCTION READY GRAPHQL」を参考にプレフィックスで明示的にnamespaceを定義する方法をとることにしました。

以下は、同じUserというリソースを扱う場合の例です。

# エンドユーザー向け
type User {
  ...
}

# 管理画面向け
type AdminUser {
  ...
}
Operation名(QueryやMutation)の競合への対応

リソース名をプレフィックス付きで定義すると

  • all_admin_users
  • _all_admin_users_meta

Operation名もリソースを踏襲することになるため名前の競合を気にする必要はありません。

ただし、graphql-rubyを使用している場合はMutationのResolverを定義するクラス名に注意する必要があります。 graphql-rubyではPayload Typeがクラス名を元に自動で書き出されることから、両クライアント向けに同名のMutationクラスが定義されている場合にPayload Typeが競合する問題がありました。 冗長ではありますが、Mutationのクラス名を、PrefixつきのPayload Typeが書き出されるように定義することにしました。

以下、例

module Mutations
  module Admin
    class CreateAdminFeature < GraphQL::Schema::Mutation
      ...
    end
  end
end

コードを共存させる上で

元々管理画面用にあったコードベースを統合するにあたって、管理画面用の定義であることが把握しやすい作りにしておきたいです。 SMART BANKさんの取り組みでも紹介されていましたが、私達もnamespaceでの分離を行っています。

以下、例

app/graphql/types/admin/admin_user_type.rb
module Types
  module Admin
    class AdminUserType < Types::BaseObject
      ...
    end
  end
end

今のところ、使用するModelは同じにしており、主にapp/graphql配下でnamespaceによる分離を行うようにしています。 SMART BANKさんでは、同じモデルを利用する場合は継承やコンポジションを使用し、管理画面特有の振る舞いに拡張させるなど、同じnamespaceによる分離でもより分離レベルを高める取り組みをされているようで、非常に参考になりました。

今後に向けて

名前空間の分離レベルを上げたい

前述した通り、現状だとモデル内でエンドユーザー向け機能のための振る舞いか、管理画面のユーザー向けの振る舞いかが分かりづらくなっています。 SMART BANKさんの取り組みのように、振る舞いを分離するような工夫をしていきたいです。

GraphQLのスキーマを共存させるためにとった一時的な対応の改善をしたい

こちらも前述した通り、名前空間が競合する可能性があるものにPrefixをつけて対応していますが、コードベースの名前空間との兼ね合いで定義が冗長になってしまっています。 また、Mutationクラスの命名をはじめ規則性が弱く一部把握しづらいコードになってしまっており

  • より柔軟なGraphQL向けのData Providerを持っていそうなRefineに乗り換える
  • 自分たちで名前空間の切り分けを行いやすいData Providerを実装する

など、引き続き改善に向けて検討を行っていきたいところです。

実行プロセスの分離を行いたい

資料中でもコードベースは同じでありつつ、プロセスを分けることによるメリットについて言及されていました。 Plex JobではHerokuからGKE Autopilotへのインフラ環境の移行を計画していることから、サーバープロセスを柔軟に配置することができるようになる見込みなので、合わせてチャレンジしていきたいです。

終わりに

今回、さらっとKaigi On Railsの参加レポートを兼ねて、弊社Plex Jobでの管理画面用のAPI統合の取り組みについてお話させていただきました。

統合は完了したもののまだまだ課題は多く残っています。 このような課題にチャレンジしていただけるソフトウェアエンジニアフロントエンドエンジニアを募集しています。

少しでも興味を持っていただけた方は業務委託や副業からでも、ぜひご応募いただけると幸いです。

参考資料