TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

935

目次 目次 1. はじめに 解決したかった課題 2. アーキテクチャ 3. プレビュー環境の作成・更新・削除 作成・更新フロー 削除フロー パターンA: PRクローズ or ラベル削除 パターンB: TTLによる定期クリーンアップ プレビュー環境へのアクセス PRコメント例 4. 実装のポイント Pull Request Generator の実装 PRごとに異なるValuesの命名規則 GitHub Actions Argo CD 再コミット時の自動イメージ更新 仕組み 環境数の上限制御 ResourceQuota によるリソース使用量の制御 5. おわりに 参考 1. はじめに こんにちは!SRE課のモリモトです。 今回は、 プレビュー環境基盤 をKubernetes上に構築した話をご紹介します。 解決したかった課題 弊社のあるサービスでは、フロントエンド開発において以下のような課題がありました。 現状では、ある程度修正がまとまったタイミングでないと検証環境に上げることが難しいため、ローカル環境以外で気軽に動作確認できる環境がない デザイナーや企画/要件を詰めるチームに画面仕様を確認してもらうのに検証環境にデプロイする必要があるが、上記の理由からリードタイムが発生している 検証環境にデプロイせずに確認してもらうためには画面のスクショを送ったりする必要があるが、それだと正確な仕様を把握してもらいにくい PRレビュー時に、レビュアーがローカルでアプリケーションを起動して動作確認する必要があり、無駄な時間が発生している そこで、 PRにラベルを付けるだけで自動的にプレビュー環境がデプロイされる 仕組みを構築することにしました。 具体的には以下のような体験を実現しています。 開発者がPRを作成し、PRに対して preview ラベルを付与 数分後、PRにプレビューURLがコメントされる そのURLにアクセスすると、PRの内容が反映されたUIを確認できる PRをクローズすると、環境が自動削除される 2. アーキテクチャ アーキテクチャ図 本構成では、PRを起点にGitHub ActionsとArgo CDがそれぞれ独立して動作します。 GitHub Actionsはイメージの生成と通知を、Argo CDはKubernetesリソースの生成と削除を担います。 両者を疎結合にすることで、責務を明確に分離しています。 ポイントは、Argo CD ApplicationSetの Pull Request Generator を活用している点です。 ApplicationSet は、同一構成の Argo CD Application を動的に複数生成するための仕組みです。 Pull Request Generator を使うことで、GitHub の PR 情報を元に Application を自動生成できます。 これにより、 preview ラベルが付いたPRを自動検知し、対応するKubernetesリソースを動的に生成・削除できます。 3. プレビュー環境の作成・更新・削除 すべての操作がPRに連動しており、開発者が意識的に環境管理を行う必要がないように設計しました。 作成・更新フロー 作成・更新のシーケンス図 作成・更新の流れは以下のとおりです。 開発者がPRを作成し、 preview ラベルを付与 GitHub Actionsがワークフローを起動 まず、すでに作成済みのプレビュー環境数( preview ラベルが付いたPR数)を確認します 上限(1リポジトリにつき最大10環境)に達している場合は、その旨をPRにコメントし、 preview ラベルを自動で外します 並行して2つの処理が実行 GitHub Actions側: コンテナイメージをビルド・プッシュし、プレビューURLをPRにコメント Argo CD側: preview ラベルの付いたPRを検知(3分おきにポーリング)し、プレビュー環境のNamespaceとリソースを自動作成 同一PRに対して再コミットが行われた場合は、GitHub Actions側で再度イメージがビルド・プッシュされ、Argo CD側でデプロイされるイメージタグが自動更新されるため、 追加の操作なしでプレビュー環境が最新化 されます。 削除フロー 削除は2つのパターンがあります。 パターンA: PRクローズ or ラベル削除 Argo CDがPRのクローズまたはラベル削除を検知し、Namespaceごと環境を自動削除します。 パターンB: TTLによる定期クリーンアップ プレビュー環境が溜まり続けることを防ぐために、TTLを設けて更新から10日間経過した環境を削除するようにしています。 削除のシーケンス図 GitHub Actionsのスケジュール実行(毎日0:00)で以下を実行します。 preview ラベルの付いたPR一覧を取得 最終更新日から10日以上経過したPRから preview ラベルを削除 不要になった古いコンテナイメージも合わせて削除 Argo CDがラベル削除を検知し、Namespaceごと環境を自動削除 プレビュー環境へのアクセス プレビュー環境のURLは以下の形式で動的に発行され、PRにコメントされます。 https://pr<PR番号>-<サービス名>.preview-example.com 例): PR #123 の場合 → https://pr123-serviceA.preview-example.com なおプレビューURLは社内ネットワークの範囲でのみアクセス可能とし、外部公開はしない前提で運用しています。 PRコメント例 PRコメント例 4. 実装のポイント Pull Request Generator の実装 Argo CD ApplicationSetでPull Request Generatorを使用することで、 preview ラベルが付いたPRを自動検知し、動的に環境を作成できます。 apiVersion : argoproj.io/v1alpha1 kind : ApplicationSet metadata : name : serviceA-preview-applications-applicationset namespace : preview-applications spec : goTemplate : true goTemplateOptions : [ "missingkey=error" ] generators : - pullRequest : github : owner : "my-org" repo : "serviceA-frontend-repo" appSecretName : app-secret # GitHub Apps認証用のSecret名 labels : - preview # previewラベルがついたPRのみを対象にフィルタリング requeueAfterSeconds : 180 # イメージビルドに3~4分かかることを考慮して3分に設定 template : metadata : name : 'pr{{ .number }}-serviceA-preview-applications' spec : project : preview-project source : repoURL : https://github.com/my-org/manifest-repo.git path : preview-frontend targetRevision : develop helm : releaseName : 'preview-frontend' valueFiles : - "values/develop.yaml" valuesObject : namespace : "pr{{ .number }}-serviceA" image : repository : 'ghcr.io/my-org/serviceA-frontend' tag : 'preview-pr{{ .number }}-{{ .head_sha }}' ingress : app : hosts : - host : 'pr{{ .number }}-serviceA.preview-example.com' paths : - path : / pathType : Prefix destination : name : "dest-cluster-name" namespace : "pr{{ .number }}-serviceA" syncPolicy : automated : prune : true selfHeal : true preview-frontend/ に汎用的に利用できるフロントエンドのhelm chartを実装し、それをプレビュー環境としてPRの数だけ複製するようなイメージになります。 その際に、イメージタグやプレビューURLなど、PRごとに異なるValuesを渡したくなりますが、 valuesObject から {{ .number }} や {{ .head_sha }} などの値を渡すことでPRごとに別々の値をhelm chartに渡すようにしています。 PRごとに異なるValuesの命名規則 命名規則は以下のようにしました。 イメージタグ: preview-pr<PR番号>-<コミットSHA> Namespace名: pr<PR番号>-<サービス名> ドメイン: pr<PR番号>-<サービス名>.preview-example.com 特にイメージタグとドメインについては、GitHub Actions側とArgo CD側で整合性を保つためにルールを決めておく必要があります。 このルールに沿って、GitHub Actions側でのイメージビルドやPRコメント、Argo CD側でのアプリケーションデプロイを実施しています。 GitHub Actions # イメージタグ生成側の例 - name : Set up parameters id : set-params run : | IMAGE_TAG=preview-pr${{ inputs.pr-number }}-${{ inputs.pull-request-head-sha }} # PRコメント時のプレビューURL生成の例 - name : Comment preview URL on PR env : GH_TOKEN : ${{ github.token }} run : | PREVIEW_URL="https://pr${{ inputs.pr-number }}-serviceA.preview-example.com" Argo CD # イメージタグ使用側の例 valuesObject : image : repository : 'ghcr.io/my-org/serviceA-frontend' tag : 'preview-pr{{ .number }}-{{ .head_sha }}' # プレビューURL指定の例 valuesObject : ingress : app : hosts : - host : 'pr{{ .number }}-serviceA.preview-example.com' 再コミット時の自動イメージ更新 同一PRに再コミットした場合、同一URLのままで自動でプレビュー環境が更新される仕組みになっています。 仕組み 再コミットすると、GitHub Actionsが新しいイメージをBuild&Push(タグ: preview-pr<PR番号>-<新コミットSHA> ) Argo CD ApplicationSetが {{ .head_sha }} を参照しているため、新しいコミットSHAでDeploymentのイメージタグが自動更新され、新しいPodがデプロイされる これにより、開発者は再コミットするだけで最新の変更がプレビュー環境に反映されます。 環境数の上限制御 1リポジトリあたりの環境数を10に制限し、環境が作られすぎないようにしています。 制御はGitHub Actions側で行い、上限に達した場合はPRにコメントで通知し、 preview ラベルを自動で外すようにしています。 # GitHub Actions Workflow内での制御イメージ - name : Check preview environment count limit id : check-limit env : GH_TOKEN : ${{ github.token }} run : | # previewラベルが付いているオープンPRの数を取得 PREVIEW_PR_COUNT=$(gh pr list \ --repo ${{ github.repository }} \ --state open \ --label preview \ --json number \ --jq 'length' ) echo "プレビュー環境数:" echo " - 現在: $PREVIEW_PR_COUNT" echo " - 最大値: ${{ inputs.max-preview-environments }}" # 上限チェック if [ "$PREVIEW_PR_COUNT" -gt "${{ inputs.max-preview-environments }}" ] ; then echo "[FAIL] 環境数上限に達しています。" echo "should-continue=false" >> $GITHUB_OUTPUT else echo "[PASS] 環境数は上限内です。" echo "should-continue=true" >> $GITHUB_OUTPUT fi - name : Comment on PR if limit reached if : steps.check-limit.outputs.should-continue == 'false' env : GH_TOKEN : ${{ github.token }} run : | gh pr comment ${{ inputs.pr-number }} \ --repo ${{ github.repository }} \ --body "## ⚠️ **プレビュー環境数の上限に達しています!** 現在、プレビュー環境の最大数(${{ inputs.max-preview-environments }}環境)に達しているため、新しいプレビュー環境を作成できません。 他のPRのプレビュー環境が不要になった場合は、そのPRから \`preview\` ラベルを削除してください。" - name : Remove preview label if limit reached if : steps.check-limit.outputs.should-continue == 'false' env : GH_TOKEN : ${{ github.token }} run : | gh pr edit ${{ inputs.pr-number }} \ --repo ${{ github.repository }} \ --remove-label preview ResourceQuota によるリソース使用量の制御 プレビュー環境は多数同時に立ち上がる可能性があるため、 ResourceQuota でリソース使用量を制限しています。 これにより、1つのプレビュー環境が過剰にリソースを消費することを防いでいます。 apiVersion : v1 kind : ResourceQuota metadata : name : preview-frontend-resource-quota spec : hard : requests.cpu : 20m requests.memory : 64Mi limits.cpu : 100m limits.memory : 128Mi pods : 2 # (...省略...) 5. おわりに GitHub ActionsとArgo CDのApplicationSetを活用することで、 ラベル付与だけで自動的にプレビュー環境が立ち上がる 仕組みを構築できました。 本記事では詳しく説明できませんでしたが、GitHub ActionsのワークフローをReusable Workflow化するなど横展開が容易な形で設計しているため、社内にどんどん展開して 複数チームで共通利用できる基盤 となっています。 現在はNginxを前提にプレビュー環境の仕組みが構築されていますが、今後の展望としては他のミドルウェアにも対応を広げていきたいと考えています。 プレビュー環境の構築を検討されている方の参考になれば幸いです! 最後まで読んでいただき、ありがとうございました。 参考 tech-blog.monotaro.com
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp これまで組織やマネジメントの話を書くことが多かったのですが、以前から少し気になっていたことがあります。 年末年始の休みに買い物をしている際、やはりそのことが気になったので、今回はそれについて書いてみようと思います。それはセルフレジについてです。 目次 「セルフレジのUIは、なぜあんなに不自由なのか?」 リアルUIとWEB UIは、そもそも戦っている場所が違う 利用者の「状態」の違い コンビニのセルフレジUIに見る「運用者優先」の設計 ファーストフードのセルフオーダーUIが示す別の最適解 なぜリアルUIでは「運用者視点」が強くなるのか この構造は、そのまま toB SaaS に当てはまる toB SaaSでよく見る失敗 セルフレジUIと toB SaaS UI の対応関係 プロダクトチームとして、この話をどう使うか AIが入ることで、ネットのUI/UXはどう変わるのか UIから「操作」を減らし、UXを「対話」に移す それでも消えない「運用者視点」 toB SaaSにおけるAI時代のUI/UX AI時代だからこそ、セルフレジの学びは生きる おわりに 「セルフレジのUIは、なぜあんなに不自由なのか?」 初めてセルフレジを使ったとき、そう感じたことがある方は多いと思います。操作の順番は決められていて、戻ることはできず、選択肢も少ない。 WEBサービスやアプリに慣れているほど、この不自由さは違和感として強く残ります。しかし、この違和感こそが、 toB SaaSのUI/UXを考える上で極めて重要なヒント になるのではと思っています。 私は現在、ラクスというSaaS 企業で、プロダクトマネージャーとプロダクトデザイナーが所属するプロダクト部のマネージャーを務めています。日々の業務の中で繰り返し立ち返る問いがあります。 それは、 「良いUI/UXとは誰にとってのものなのか」 という問いです。 プロダクト開発の現場では、UI/UXという言葉がしばしば「使いやすさ」「分かりやすさ」「シンプルさ」と同義で語られます。しかし、toB SaaSにおいてそれだけを追い求めると、運用が破綻する、問い合わせが増える、結果として顧客価値を下げてしまう──そんな経験をした方も多いのではないでしょうか。 今回、セルフレジやファーストフードのセルフオーダーといった リアルのUI/UX を整理して、私は「これはそのまま toB SaaS に当てはまる構造だ」と強く感じました。本記事では、セルフレジUI/UXの具体例を交えながら、 リアルUIとWEB UIの違いがどこから生まれ、なぜ toB SaaS のUI設計にとって重要なのか を整理していきます。 リアルUIとWEB UIは、そもそも戦っている場所が違う セルフレジやセルフオーダー端末に触れると、WEBやSaaSとは明らかに違う設計思想を感じます。この違いは、UIデザインのトレンドやデザイナーの好みではありません。 UIが置かれている環境と、背負っている責任構造の違い から必然的に生まれています。 利用者の「状態」の違い リアルUI(セルフレジ・飲食店端末)の利用者は、次のような状態に置かれています。 立ったまま操作している 後ろに行列ができている 周囲が騒がしく、集中しづらい 初見利用者が多い 操作ミスが即トラブルにつながる 一方、WEBやSaaSのUIはどうでしょうか。 座って操作できる 基本的に一人で使う 自分のペースで進められる 戻る・やり直しが可能 学習しながら使う前提 この違いが、設計思想を真逆の方向へ引っ張ります。 リアルUIは「考えさせない・迷わせない」ことが最優先 であり、 WEB UIは「ある程度の自由や探索」を許容 します。ここを混同すると、UIは破綻します。 コンビニのセルフレジUIに見る「運用者優先」の設計 セブンイレブン  セルフレジ ローソン  セルフレジ ファミリーマート  セルフレジ コンビニのセルフレジには、実は複数のUIパターンが存在します。 レジ袋選択 → 商品スキャン → ポイント → 決済 決済方法選択 → レジ袋 → 商品スキャン → 完了 一見すると「なぜこんな順番なのか分かりにくい」と感じる方も多いでしょう。しかし、これらはすべて 運用上の事故を防ぐための設計 のように思います。 例えば、 レジ袋を最後に聞くと、袋代金の取り忘れが発生する 決済直前に袋選択を挟むと、操作ミスが起きやすい ポイント提示を忘れると、店員呼び出しが発生する セルフレジでは、 一人の迷いが行列全体を止める という前提があります。そのため、UIは「直感的であること」よりも「事故が起きないこと」を優先します。これは決してユーザー軽視ではありません。 店舗全体のUXを守るための設計 だと思います。 ファーストフードのセルフオーダーUIが示す別の最適解 一方、ファーストフードのセルフオーダー端末では、ほぼ例外なく次のような流れが採用されています。 商品選択 セット・サイズ・カスタマイズ 注文内容確認 決済 ここで重要なのは、 決済が必ず最後に来る という点です。なぜなら、飲食においては 「何を食べるか決めるプロセス」そのものが体験価値 だからです。 マクドナルドのセルフオーダーを思い浮かべてください。画面には大きな写真、セット提案、期間限定商品が並びます。これはUXのためだけでなく、 客単価を最大化するための設計 でもあります。しかし同時に、 カスタマイズは段階的にしかできない 戻り操作は最小限 注文確定後の変更は難しい といった制約も多く存在します。ここでも、 運用者視点(厨房・提供フロー) が強く効いているように思います。 なぜリアルUIでは「運用者視点」が強くなるのか セルフレジやセルフオーダーは、UXツールであると同時に業務装置です。1人の操作ミスは、 行列停止 店員の割り込み対応 提供遅延 クレーム といった形で即座に表面化します。そのためリアルUIでは、 操作順序の固定 選択肢の制限 確認ダイアログの多用 が「正義」になるのでは推測しています。WEBの世界で嫌われがちなこれらの設計は、リアルでは 現場を守るための合理 です。 この構造は、そのまま toB SaaS に当てはまる ここで話を toB SaaS に戻します。toB SaaS もまた、 日々オペレーションを行う利用者 全体を管理する管理者・組織 という 二重構造のユーザー を持っています。 toB SaaSでよく見る失敗 「利用者にとって分かりやすいUI」を優先しすぎる 設定の自由度を上げすぎる 業務フローを柔軟にしすぎる 結果として、 管理画面が複雑化 設定ミスが多発 問い合わせが増加 運用ルールが守られない これは、 セルフレジにWEB的UI思想を持ち込んだ時と同じ失敗構造 ではと思っています。 セルフレジUIと toB SaaS UI の対応関係 ここで重要なのは、 制限=悪ではない ということです。制限は、運用を守るためのUXです。 セルフレジのUI/UXを観察して得た最大の学びは、UXとは「画面を触る個人」だけのものではないという事実です。 toB SaaSにおけるUXは、 その画面を触る人 そのデータを管理する人 その業務を回す組織 全体最適として成立して初めて良いUX になります。 UIを改善するとき、「もっと分かりやすく」「もっと自由に」と言いたくなったら、ぜひセルフレジを思い出してほしいと思います。その不自由さには、必ず理由があります。 プロダクトチームとして、この話をどう使うか ここまでセルフレジのUI/UXを題材に、リアルUIとWEB UI、そしてtoB SaaSの共通構造を整理してきました。最後に、プロダクト組織の責任者という立場から、この話をどう使ってほしいかをまとめます。 UXは「画面単体の使いやすさ」ではなく、「業務が止まらないこと」まで含めて考える 制限はUXの敵ではない。むしろ運用を守るためのUXであることが多い 利用者視点と運用者視点のどちらを優先しているのかを、常に言語化する これらは、UIレビューや仕様議論の場で、必ず立ち返るべき問いなのかなと思いました。 AIが入ることで、ネットのUI/UXはどう変わるのか 最後に、ネットのUI/UXにおいて AIが入ることで何が変わるのか について触れておきたいと思います。これは、セルフレジや toB SaaS の話とも深くつながるテーマです。 これまでのWEB UI/UXは、「人が画面を操作する」ことを前提に設計されてきました。ボタンを押し、入力欄に文字を入れ、選択肢の中から選ぶ。そのためUIは、 どの順番で操作させるか どこまで自由を与えるか どこで制限をかけるか を画面上で細かく設計する必要がありました。しかしAI、とりわけ生成AIや対話型UIが入ることで、この前提が少しずつ崩れ始めています。 UIから「操作」を減らし、UXを「対話」に移す AIが入ることで、ユーザーは必ずしも画面構造を理解する必要がなくなります。「こうしたい」「これをやってほしい」と自然言語で伝えれば、AIが裏側の操作や設定を肩代わりしてくれるからです。 これは、WEB UIを 画面中心のUX 手順中心のUX から、 意図中心のUX 結果中心のUX へと変えていきます。 それでも消えない「運用者視点」 ただし、ここで重要なのは、 AIが入っても運用者視点は消えない という点です。むしろ、AIが自由度を一気に引き上げるからこそ、 何をAIに任せてよいのか どこは人やルールで縛るのか AIの判断をどう制御するのか という設計が、これまで以上に重要になります。これは、セルフレジで「自由を削ることで現場を守ってきた」構造と非常によく似ています。AIはWEB UIを一気に自由にしますが、その裏側では 運用を守るための強いガードレール が必要になります。 toB SaaSにおけるAI時代のUI/UX toB SaaSにおいては、AIによって 設定画面が不要になる 操作フローが短縮される 専門知識がなくても使える といった変化が起きていくでしょう。一方で、 誤った設定をAIが自動でしてしまう 誰がどこまで責任を持つのか分からなくなる 組織ルールが崩れる といった新しいリスクも生まれます。だからこそ、これからの toB SaaS のUI/UXでは、 AIに任せる自由 人が守る制約 を意識的に分離して設計する必要があります。 AI時代だからこそ、セルフレジの学びは生きる AIはUIを消し去る存在ではありません。むしろ、UI/UX設計の難易度を一段階引き上げます。自由度が増えるほど、運用を守る設計が重要になる。この構造は、セルフレジやセルフオーダーがすでに私たちに示してくれています。AI時代のUI/UXを考えるときこそ、 誰のUXを守るのか どこまでをUXの責任範囲とするのか を問い続ける必要があります。 セルフレジ → toB SaaS → AI時代のUI/UX これらは別々の話ではなく、 同じ構造の延長線上にある と、私は考えています。 おわりに リアルのUI/UXは不自由です。しかしその不自由さは、現場を守り、業務を成立させるための合理です。セルフレジやセルフオーダーを観察すると、 なぜ手順が固定されているのか なぜ自由度が低いのか が腹落ちします。そしてそれは、そのまま toB SaaS のUI/UXにも通じます。UXを語るとき、「誰のUXか」「どこまでをUXの範囲とするか」を誤ると、プロダクトは壊れます。画面を触る人だけを見て設計すると、現場や組織が苦しみます。 セルフレジは、toB SaaSを作る私たちにとって、 現場視点と組織視点を同時に学べる、非常に優れた教材 です。もし今後、UI/UXの議論で迷ったときは、ぜひ近くのコンビニやファーストフード店に立ち寄ってみてください。その不自由なUIの中に、プロダクト設計の本質が詰まっています。 この記事を読んで、やラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp PdM(プロダクトマネージャー)って、企業によってやることがバラバラですよね。 「仕様書を書く人」みたいになってる会社もあれば、 「戦略を決める人」だったり、「なんでも屋」だったりする。その中で、よく出てくるモヤモヤがこれです。 顧客インタビューをしていないPdMって、本当にPdMなの? 逆に言えば、 エンジニアやデザイナーでも、顧客理解しながら動いていたらPdM的じゃない? この記事では、toB SaaS という文脈に絞って、この疑問をカジュアルに掘っていきます。 こんな方が対象: PdMを目指している人 いま PdM をやっているけどあまり顧客に会えていない人 「自分はPdMと言えるのか?」と不安になっている人 ✋ 結論から言うと… 🧩 PdMの仕事って結局こういう構造 💡 toB SaaSではなぜ顧客インタビューが必須級になるのか? 🧩 顧客理解なしのPdMが陥る罠 👀「インタビューをしているエンジニア/デザイナーはPdM行為をしている」という話 🧩 肩書きPdMと実質PdMの違い 🤔 では「なぜ顧客に会わないPdM」が生まれるのか? 🧩PdMの価値が最大になるポイント 🌱 とはいえ、「PdMが必ずインタビューすべき」とも限らない 👉私の経験上、国内toB SaaSでは“仕組みが成熟していてPdMが会わなくても回る”ケースはまだ多くありません。 🔚 最後に:PdMとは「顧客理解から逃げない人」 🧩 PdMとは何か?(一言で) 🌟 最後に PdMを目指すあなたへ ✋ 結論から言うと… toB SaaSでは、顧客接点がないと、PdMとしての意思決定が難しくなりやすい/PdM的になりづらい これは辛口でもなんでもなく、 プロダクトマネジメントの本質が “顧客理解に基づいた意思決定” だから です。 じゃあこれで決まり? いや、もちろん例外もあります。 ちゃんと後半で書きます。 🧩 PdMの仕事って結局こういう構造 上の段に行くほど “PdMである必要性が高まる” のですが、意思決定の源泉が 顧客の一次情報 である以上、 一次情報を持っていないPdMは、そもそも材料不足で判断できない。 となるわけです。 💡 toB SaaSではなぜ顧客インタビューが必須級になるのか? toCと違って、toB SaaSは 業務の中に深く入り込むタイプのプロダクト です。 つまり、顧客側の現実がめちゃくちゃ複雑。 ① 顧客の業務は会社ごとに全然違う フローも違う。 権限も違う。運用ルールも違う。 机に向かって仕様を書くより、5分の会話のほうが100倍早い。 ② 営業やCSの声は貴重だが「偏り」がある 営業:売りたい人  /  CS:困りごとを聞く人 なので、本質はその奥にあることが多い。 ③ 機能の一つが全体の業務に影響する toB SaaSでは、以下みたいなことがよくある。 承認フローが変わる 会計処理に影響する セキュリティに関わる 別部署の仕事まで変わる つまり、 「ちょっとした仕様判断」が全社レベルの業務変更につながる。 これは顧客理解なしには危険すぎる。 🧩 顧客理解なしのPdMが陥る罠 はい、負のスパイラルのできあがりです。 👀「インタビューをしているエンジニア/デザイナーはPdM行為をしている」という話 これも多くの現場で起きています。 デザイナーがユーザーに話を聞いて課題を整理してる エンジニアが仕様理解のために顧客にヒアリングしてる CS が課題を深掘りして問題の本質に迫ってる これ全部、PdMの本質的な行為です。 肩書きがなんであれ、 顧客理解を元に意思決定に影響を与えているなら、それはPdM的な動き です。 逆に、肩書きがPdMでも顧客理解がなく、意思決定していないなら、 PdM行為をしているとは言えない 。 🧩 肩書きPdMと実質PdMの違い 【肩書きPdM】 社内調整 仕様書作成 会議進行 タスク管理 → 顧客理解なしでもできてしまう 【実質PdM】 顧客理解(一次情報) 課題定義 仮説検証 優先順位の意思決定 → 顧客理解がないと成立しない あなたが見てきた「名ばかりPdM」が前者であることは多いです。 🤔 では「なぜ顧客に会わないPdM」が生まれるのか? 理由はいくつもあります。 ● PdMのロール定義が曖昧(仕様担当にされがち) ・PdMの歴史が浅い会社でよく起きる。 ● 組織として顧客インタビューの文化が薄い ・営業主導企業だと、とにかく営業が顧客を抱え込みがち。 ● 社内会議が多すぎて外に出られない ・PdMが“社内の渋滞ポイント”になっているケース。 ● リサーチャー不在でPdMが全部やる必要がある ・でも仕組みがないから後回しになる。これ、PdM個人の怠惰というより構造の問題です。 🧩PdMの価値が最大になるポイント 顧客理解 × 課題の定義 × 意思決定の責任 この3つの重なる部分が、PdMが最も価値を出せるゾーン。 顧客理解が抜けると、一気に価値が落ちます。 🌱 とはいえ、「PdMが必ずインタビューすべき」とも限らない 少し補足しておくと、例外は存在します。 ■ 顧客理解の仕組みが成熟している場合 UXリサーチャーが常に一次情報をとってくれる データ分析基盤が整っていて行動が全部見える CS Ops が定性情報を構造化してくれる こういう企業では、PdMは意思決定に集中できる。ただし、 👉私の経験上、国内toB SaaSでは“仕組みが成熟していてPdMが会わなくても回る”ケースはまだ多くありません。 だから、PdMが顧客に会う価値はやっぱり高い。 🔚 最後に:PdMとは「顧客理解から逃げない人」 肩書きがPdMかどうかより、 顧客を理解しているか 課題から逃げていないか 一次情報に触れているか 仮説と学習を回しているか ここがPdMを定義します。 🧩 PdMとは何か?(一言で) PdM = 顧客理解を起点に意思決定する人 だからこそ、 顧客に会うデザイナーはPdM的 顧客に会うエンジニアはPdM的 顧客に会わないPdMはPdM的ではない という状態が生まれる。 🌟 最後に PdMを目指すあなたへ 最後に一言だけ。 顧客と話すこと。それが PdM への最短ルートです。 経験年数より、肩書きより、スキルセットより、 一次情報に触れて意思決定していく行為こそが あなたをPdMにします。 以前にイベントで以下のような話をしましたので合わせて見てもらえればと思います。 speakerdeck.com この記事が、あなたの「PdMとは何か?」を考えるきっかけになれば嬉しいです。 ラクスの開発組織では『顧客志向』を大切にしています。 PdMはもちろん顧客を向いて業務をしていた方はラクスはオススメです。 また、本記事を読んでラクスのプロダクト部に興味を持ってくださったデザイナー / PdM の方は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 プロダクトマネージャー career-recruit.rakus.co.jp デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
明けましておめでとうございます。 こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 私は2014年から、仕事・プライベートを問わず、毎年個人で目標を立て、その目標をさらに3カ月単位に分解して取り組むことを続けてきました。大きな方向性を定め、短いサイクルで振り返り、修正しながら前に進む。このやり方は、今のプロダクトづくりや組織づくりにも強く影響しています。 新年最初の記事ということで、今回は昨年度の簡単な振り返りと、2026年に向けた抱負を書こうと思います。 はじめに 2025年に新しく始めたこと 2024年から継続して強化してきたこと 『穏跳』に込めた意味 PdMとして担うべきこと 1. 統合型ベストオブブリード戦略の実現 2. エンタープライズ強化 3. AIの製品活用 デザイナーとして担うべきこと 2026年に個人として取り組む目標 1. プロダクト成長・貢献 2. 組織強化・認知 穏やかに整え、次の3年へ跳ぶ はじめに 2025年のキーワードは『変革』でした。プロダクトと組織の両面で、これまでの前提を見直し、作り直す一年でした。振り返ると、主に以下のような取り組みを行ってきました。 2025年に新しく始めたこと YOUTRUSTの発信開始(フォロワー1,800突破) note「Rakus ProductManager」開始(全体20本/個人14本) デザイナー組織とPdM組織を統合し、プロダクト部へ再編 新規サービス「楽楽債権管理」を担当 2024年から継続して強化してきたこと Xフォロワー数:約1,800増、投稿数:約3,000投稿 登壇・記事露出:2024年比で約2倍(pmconf 東京・大阪に各1名登壇) PdM採用:2025年に3名採用、組織を2つに分割し新マネージャーが誕生 なお、ラクスの決算期は3月末のため、ここから書く2026年のキーワードや挑戦は、 あくまで私個人としての考え・目標 として受け取っていただければ幸いです。 2026年は、ラクスにとって 現在の中期計画から、次の中期計画(3年)へと切り替わる節目の年 です。 前回の中期計画策定時、私は入社直後ということもあり、十分に関与することができませんでした。ただ、プロダクトと組織がどのような意思決定で形づくられていくのかを、外側から見てきた3年間でもありました。 そして今回、 中期計画の策定に関与し、リードしていける立場 になりました。これは役割上の変化であると同時に、自分自身の覚悟の変化でもあります。 その決意を込めて、2026年のキーワードとして掲げるのが 『穏跳(おんちょう)』 です。 『穏跳』に込めた意味 穏やかであることと、跳ぶこと。 急激な変化や過度なジャンプは、短期的な成果は出せても、組織やプロダクトに歪みを残します。一方で、整えることだけに終始すれば、成長は止まります。『穏跳』は、 基盤を整えながら、跳ぶべきところでは確実に跳ぶ という姿勢です。 2025年の「変革」が作り直すフェーズだったとすれば、2026年は、 次の3年を見据えて、整え、意思を持って前進するフェーズ です。 PdMとして担うべきこと 次の中期計画において、PdMとして特に注力したいテーマは以下の3つです。 1. 統合型ベストオブブリード戦略の実現 単一プロダクト最適ではなく、 複数プロダクトが連携し、全体として最適な価値を提供する状態 を目指します。 それぞれが尖り続けながら、使う側から見たときには「一つの体験」として成立している。そんな統合を、戦略として実装していきます。 2. エンタープライズ強化 中堅・中小向けで培ってきた強みを活かしながら、 より複雑な業務・組織構造にも耐えられるプロダクト へと進化させます。 これは機能追加の話ではなく、 情報設計 権限設計 運用負荷の低減 といった、プロダクトの“設計思想”そのものを問う挑戦だと考えています。 3. AIの製品活用 AIを「試す」フェーズは終わり、 製品価値として組み込むフェーズ に入ります。 PdMとしては、 どこに使えばユーザー価値が最大化されるのか どこは人がやるべきなのか その線引きをし、継続的に改善できる形でプロダクトに落とし込むことを担います。 デザイナーとして担うべきこと デザイナーとしては、 UXを担い、より長く利用し続けてもらえるプロダクトづくり を担っていきたいと思います。見た目を整えることや、一時的な使いやすさだけではなく、 使い続けるほど理解が深まる 組織に定着し、業務の一部になる そんな体験を設計することが、次の3年でより重要になります。PdMと並走しながら、 課題設定 仮説検証 プロダクト全体の体験設計 に深く関与できる環境を作っていきます。 2026年に個人として取り組む目標 1. プロダクト成長・貢献 製品貢献実感:70%以上   ※アンケート内容や詳細については「 (2)製品貢献実感は、まだまだ伸びしろあり 」を参照してください。 プロダクト間連携リリースによるお客様への付加価値向上を複数件実現する 2. 組織強化・認知 社外認知 Xフォロワー数(個人):3,500 YOUTRUSTつながり数(個人):2,500 note公開数(プロダクト部):50本 社外登壇・外部媒体記事(プロダクト部):30件 穏やかに整え、次の3年へ跳ぶ 2026年は、「変革」で整えた地盤の上で、心は穏やかに、視座は高く。 闇雲に動くのではなく、静かなる自信と確固たる戦略を持って、大きく跳躍する。 組織体制やプロセス(AI活用など)で足元を「穏」やかに盤石にし、その上でプロダクトの連携や新規領域へ果敢に「跳」ねていければと思います。 一緒にこの 「穏跳」 の景色を見たい方、少しでも興味を持ってくださった  デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp おそらくこれが2025年最後の記事になるので、まずはこの一年を振り返ってみようと思います。 🗓 2025年の振り返り ■ 組織・採用まわりの変化 ■ 発信・外部登壇 外部登壇(モデレーター含む) note/ブログ記事 🏛 「文化」を構成するものは何か ハイレイヤーの意思決定の優先順位 なぜ「文化」が重視されるのか 🗣 文化は“言葉”に表れる ■ ラクスのカルチャー 🎁 採用で工夫している「4P」の話 📝 最後に 🗓 2025年の振り返り ラクスに入社して4年半。毎年さまざまな変化がありましたが、2025年は特に大きな転換点の年になりました。 ■ 組織・採用まわりの変化 デザイナー/プロダクトマネージャーが所属する プロダクト部が誕生 PdM組織が 1課→2課体制 へ、稲垣以外のPdMマネージャーが初めて誕生(内部昇格) 2025年採用(=入社) PdM +3名(リーダー・シニアクラス含む) デザイナー +7名(リーダークラス含む) ■ 発信・外部登壇 外部登壇(モデレーター含む) 稲垣単体:16回 組織全体:19回  自分はEMconfへ登壇 当社から2名が大阪・東京のpmconfへ登壇 note/ブログ記事 稲垣単体:12本(組織全体:25本)  ※2024年はほぼゼロからのスタート その他、取材対応の記事なども増加 このまま終わるのはさすがに寂しいので、「文化(カルチャー)」について、少し掘り下げて書きます。 🏛 「文化」を構成するものは何か 「文化(Culture)」=組織に根付いた考え方や行動の型。これをさらに分解すると、 風土 :文化が行動や感情レベルでどう体感されているか 人材 :文化・風土を形成する主体 この2つに行きつきます。 採用に長く関わってきましたが、特にエンジニア・デザイナー・PdMのハイレイヤーほど、 文化・人材・課題の3つ を重視し、しかもその優先順位は次のようになります。 ハイレイヤーの意思決定の優先順位 1. 誰がいるのか(People) 2. どんな課題があるのか(Problem) 3. どんな文化なのか(Culture) しかもレベルが上がるほど、 Culture > Problem >People の重み付けになっていきます。より「文化」が重要視されます。 なぜ「文化」が重視されるのか 採用の現場で多くの方と話す中で、次の理由が大きいのではと感じています。 一人では変えられない 変えるには年単位の時間がかかる 人や課題の進め方に強く影響する 文化は「知ってから入る」だけではなく、「入社してから痛感する」ものでもあります。 🗣 文化は“言葉”に表れる 私自身、これまで6社のIT企業で働いてきましたが、会社ごとに文化は全然違いました。長く働いた会社は、自分と価値観が近かったのだと思います。 tech-blog.rakus.co.jp そして文化は、 役員・マネージャーの発言、社員の口癖、日々の判断基準 に現れます。 たとえば、自分がいた会社でよく飛び交っていた言葉としてはこんなものがあります。 その目的は? 誰のため? 成果に対してやり切った? お客様はどう考える? Unless you know a better one Working Backwards 仕組化できる?誰でもできるようになってる? One-Way Door?Two-Way Door? Dive Deepしてる? Have Backbone; Disagree and Commit ラクスでも、同様に文化が明文化されており、役員やマネージャーだけでなく、社員一人ひとりが日々体現しています。 ■ ラクスのカルチャー ※ラクスの成長の源泉、「ユニークネス」・「リーダーシッププリンシプル」  career-recruit.rakus.co.jp より その中でも開発組織では「顧客志向」を価値観としておいています。 これを体現できるようにするために様々な取り組みがありますので、興味がある方は是非『「顧客志向」を実践する開発本部の取組み 』のnoteをご覧ください note.com 🎁 採用で工夫している「4P」の話 最後に、私が採用のオファー面談で意識していることを書いて締めようと思います。 面談で使う資料は、「 採用の4P 」(= 『THE TEAM 5つの法則』 にある「エンゲージメントの4P」) をベースに作っています。 Philosophy(理念・目的) People(人材・風土) Process(仕組み・方法) Performance(成果・実績) この中で「文化」は、 Philosophy と People の両面で表現できるため、明文化している内容をしっかり伝えるようにしています。 この構成に変えてから、 内定承諾率も少し向上した ように感じています。 もし採用に携わっている方がいれば、この「4P」をアジェンダとして使うのはおすすめです。 📝 最後に 本記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! それでは、良いクリスマスをお過ごしください! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp はじめに 1. 成果の大きさに限界がある 2. 成果を出すための「筋力」がつかない 【1年目:インプット期】「やり方」を学ぶ 【2年目:実践・改善期】「自分で回す」 【3年目:成果・貢献期】「型」ができる 3. 年収の伸びは“過去3年の積み上げ”で決まる 4. 採用側からの見え方がポジティブに映りにくい とはいえ、「無理に3年働くべき」と言いたいわけではない はじめに 今回のテーマはすごい反響のあるこの noteのテーマについて書こうと思います。 ※本記事は筆者個人の経験・価値観に基づくものであり、ラクスの評価制度やキャリア方針を示すものではありません。 note.com 自分もかなりうなずきながら読ませてもらいました。 このnoteを読んで思い出しました。 そう言えば自分は前職で、新卒や若手メンバーにたびたび 「3年は一つの場所で成果を出すべき」 伝えていました。 もちろん、世の中には本当に劣悪な環境もあるので「逃げるな」と言うつもりはありません。ただ、一般論として“3年はやってみるべき”という考えが自分のキャリア感にも強く根付いています。 なぜかというと、初期キャリアで以下のような価値観に触れて育ってきたからです。 「石の上にも三年」という日本的価値観 自分が入社した最初の会社でも、この価値観を強く求められた(同期200人中、3年で3割が離職) 当時は「長く働く=誠実」「短期離職=忍耐不足」という評価軸が強く、今とは少し状況が違います。 それでも今、自分は 「3年間で“成果”を出す経験はキャリアの基盤になる」 と感じています。ここからは、3年未満で転職したときに起こりやすいデメリットを、自分の経験も踏まえて整理します。 1. 成果の大きさに限界がある 先の note でも触れられていますが、「主体的に動いて実績を出す」には最低でも数年単位の時間が必要です。 偉そうに書いている私自身、実は過去に3年未満で2社を辞めています。 その2社でも「自分はこれをやったぞ!」という仕事はありましたが、正直かなり小粒でした。 その後の採用面接で「こんなことを成し遂げました」と胸を張って言えるレベルかというと、疑問が残ります。 特に入社時の職能や立場にもよりますが、 製品づくりや事業成長に携わる職種において、短期間で大きな成果を出すことは構造的にかなり難しい と感じています。 2. 成果を出すための「筋力」がつかない ここで言う「 成果を出す筋力 」とは、 仕事を構造化して進める力 課題の真因を特定する分析力 関係者を巻き込み、調整する力 自ら改善点を見つけて回すPDCA力 継続して成果を出すための習慣・再現性 こうした“型”のことです。 これらは本や研修では身に付かず、実務で失敗や改善を重ねながら形成されるもの。しかも、その形成スピードは 環境 × 個人の適性 × 労働条件 × マネジメント品質 によって大きく変わります。 とはいえ、平均的にはこんなイメージになるのではと思っています。 【1年目:インプット期】「やり方」を学ぶ 業務の全体像やルール、仕組みを覚える。 基本的には先人に教わりながら動く段階。 まだ「自分で考えて成果を出す」フェーズではない。 【2年目:実践・改善期】「自分で回す」 任される仕事が増え、失敗と改善のサイクルが回り始める。 ステークホルダーとの関係構築が進む。 ようやく「再現性のある成果」への第一歩を踏み出す。 【3年目:成果・貢献期】「型」ができる 自分の強みを活かした成果が出る。 年度をまたいだ継続的な改善が見える。 * ここで初めて「再現性のある価値提供」ができるようになる。 自分自身、3年未満で辞めた2社からは学びも成長も得られました。ただ、いま振り返ると 「成果を出すための筋力」までは育っていなかった と感じます。 3. 年収の伸びは“過去3年の積み上げ”で決まる これは完全に持論ですが「 今の年収は、3年前の努力の結果 」だと思っています。多くの企業では、評価や給与改定は過去の実績をベースに決まります。 スキルや実績には「蓄積効果」があり、行動してからそれが数字としての成果になり、評価され、給与に反映されるまでには1〜2年のラグが生まれます。 つまり、 年収の本質は「現在の価値 × 過去の蓄積」。 今の年収は、 過去3年(またはそれ以上)の投資に対するリターン なのです。だからこそ「短期間で転職を繰り返す=積み上げが途切れる」ことになり、年収の伸びを自ら止めてしまうリスクがあります。 また、同じ会社に5年以上いると“ 在籍ボーナス ”が効いてくるのも事実です。おそらくこれは以下のような“ 無形の価値 ”が積みあがるからです。 業務知識の深さ 上司からの信頼の積み重ね 高難易度の仕事のチャンス 組織内での reputational capital これらは「社内」では強力な武器になりますが、転職市場にそのまま持ち出せるわけではありません。 ちなみに私は、転職時の最低希望年収を「当時の年収の2-3年前の金額」に設定するようにしています。 新しい環境で、今の年収に見合う価値をすぐに出す自信がないからです。(エージェントには「実際に下がると税金面で苦労しますよ」とかなり止められますが……) 4. 採用側からの見え方がポジティブに映りにくい 最後に、採用する側(面接官)としての客観的な視点です。 書類選考で3年未満の退職が連続している場合、どうしても以下のような懸念を抱いてしまいます。 仕事や会社を理解する前に辞めているのでは? 本人側に理由があるのか判断しにくい 入社してもすぐ辞めるリスクがある スキルの積み上げが十分ではない可能性がある もちろん面接で事情を聞けば納得できるケースも多いのですが、複数回続くとポジティブには見られません。キャリアの一貫性がない場合は、さらに印象が悪化しやすいです。 とはいえ、「無理に3年働くべき」と言いたいわけではない ここまでデメリットについて書きましたが、3年未満で転職すること自体を否定したいわけではありません。無理に3年働こうとすることで、逆に以下のようなリスクも生じます。 合わない環境で心身をすり減らす キャリア最適化は早いほどよい(特に20代は貴重) 3年後には市場の状況が変わっている可能性がある つまり、 「3年未満で辞めるデメリット」と 「無理に3年を目指すリスク」 この両天秤をしっかりと理解した上で、ご自身の「3年間」をどうデザインするかを考えるきっかけになれば幸いです。 この記事を読んで、現在の場所を飛び出そうと思った方やラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
はじめに こんにちは。楽楽販売の開発を担当しているuemuraです。 楽楽販売では11月に、初のAI機能をリリースしました。 楽楽販売をご契約いただいたお客様が導入準備をスムーズに進められるように支援する、チャット形式の機能となっています。 プレスリリースはこちら 。 本機能の開発PJは楽楽販売にとって(また私自身にとっても) 初のAI機能開発 、 初のアジャイル×スクラム開発 となっており、新しいこと尽くめでした。 AI機能を開発する難しさもさることながら、アジャイル×スクラム開発にもなかなか苦戦したため、その学びを残しておこうと思います。 目次 体制紹介 どのような流れで開発が進んだか フェーズ1:立ち上がり フェーズ2:仮説ドリブンの機能開発 フェーズ3:品質改善とリリース準備 良くなかった点 自転車操業に陥った インクリメントの品質を担保できていなかった 生成AIによるコーディングとの付き合い方 それでもスクラム開発して良かったポイント 最後に 体制紹介 従来の楽楽販売の開発の流れはいわゆるウォーターフォール型です。 プロダクトサイド(組織図の"事業本部サイド")から要求が下りてきて、エンジニアがそれを実現します。 プロダクトサイドとエンジニアは組織構造的にも離れています。 関連部署だけを抜粋した組織図 ただ、市場に対して開発スピードのUPを図るためにも、本開発ではプロダクトサイドのメンバーとエンジニアで以下のようなクロスファンクショナルなスクラムチームを組みました。 プロダクトオーナー プロダクト戦略から1人 カスタマーサクセス(CS)から1人 エンジニア4人(私はココ) スクラムマスター(所属はエンジニアサイド) ※プロダクトオーナーが2人いる体制はスクラムの原則からズレているのでオススメしません どのような流れで開発が進んだか 開発期間は2か月半ほどでしたが、ざっくりと3つのフェーズで開発は進みました。 フェーズ1:立ち上がり ターゲット顧客の設定 楽楽販売はご利用いただいているお客様の業種や企業規模が多岐にわたります。 AI機能を開発するにあたって、全てのお客様に届く機能を作るというよりは、価値を提供したいメインターゲットを決めそのお客様に使っていただくことを第一に想定して開発を進めました。 CS業務の理解 ターゲットとなるお客様群に対して、楽楽販売の契約~導入までの間にCSがどんな支援を行っているのかをエンジニアがキャッチアップしました。 主に導入支援時のCSとお客様のやり取りの録画を確認することで、ターゲット顧客への解像度も上がりましたし、導入支援時のお客様、CSのペインを知ることができました。 とりあえず動くもの作成 ターゲット顧客にどんな体験を提供するかはこの段階では未定でしたが、まずはステークホルダーにこれから作ろうとしているAIチャット機能のイメージを持ってもらうために、最低限の会話と導入支援ができるシンプルなチャットを作ることを最優先しました。 フェーズ2:仮説ドリブンの機能開発 ターゲット顧客にどんな体験を提供すればいいかは探り探りだったので、主にCSの現場目線から必要な機能の仮説を立てプロダクトバックログ化していました。 エンジニアは仮説に対するフィードバックを高速にもらうために、バイブコーディング的にとりあえず動くインクリメントを毎スプリント積み重ねてスプリントレビューで披露しました。 ステークホルダーやCSの方々、実際のお客様からもフィードバックをいただき、実運用で課題になりそうな点を集め改善を繰り返しました。 最初は順調に進んでいるように見えていましたが、だんだんと、 プロダクトオーナーは集めた課題や新たな仮説のバックログ化 エンジニアはできたてのバックログを次のスプリントレビューに間に合わせるための短納期な開発作業 に追われることになりました。 このフェーズで高速にサイクルを回したおかげで探り探りだった機能のスコープを定めることができましたが、一方で開発内部ではバイブコーディングを繰り返したことによる負債がどんどんたまっていくことになりました。 フェーズ3:品質改善とリリース準備 これまでの負債を返すように、終盤はリリース品質の確保が中心になってしまいました。 フェーズ1,2と「スプリントレビューに間に合わせるためにとりあえず動くもの」を優先したことで、リリース品質に満たないインクリメントが積み重なってしまっていました。 そういった品質負債を解消し、リリースできる品質まで持っていくことに終始することになりました。 (スクラムを始める段階で、品質改善の予定は確保していましたが、当初の計画よりずっと大きなボリュームとなってしまいました。) 品質改善に追われつつも、何とかリリースを迎えることができました。 良くなかった点 開発を進める中でいかにもスクラム初心者がハマってしまう落とし穴にハマってしまったので、簡単に改善点を振り返ってみます 自転車操業に陥った 要件が探り探りだったため、開発期間中に 仮説⇒実装⇒検証 を繰り返すことになりました。 その結果、各スクラムイベントとスクラムメンバーは互いに首を絞め合うような負のサイクルに陥ってしまいました。 プロダクトオーナーは、 毎スプリント出てくるフィードバックの整理や新たな仮説のバックログ化に追われました。 結果、 将来のための計画や先を見据えたプロダクトバックログの整備ができない状態に陥りました。 スプリントプランニングでは、 整理され切っていないプロダクトバックログがスプリントゴールに入りました。 結果、スプリントバックログも具体性に欠けたものになってしまい、スプリントが始まってからプロダクトバックログを具体化する必要がありました。 エンジニアは、 「まず動くものを作らないとレビューに出せない」という意識のもと、短期開発に追われました。 結果、品質が犠牲になり インクリメント と リリース可能な品質 の乖離 が広がっていきました。 また、エンジニアはプロダクトオーナーへの支援として技術的な面からプロダクトバックログの整理を手伝うべきだったのですが、そこまで意識や手が回っていませんでした。 結果、未整理のプロダクトバックログが積み重なったまま次のスプリントへ…という負のスパイラルに陥ってしまいました。 原因と対策 PJ終了後にスクラムチームで振り返りを行ったのですが、いくつかの原因と対策が挙がりました。 要求があいまいだった アジャイル開発という名のもと、仮説と検証を繰り返す前提で要求があいまいなままスクラムが始まりました。 プロダクトオーナーは要求の整理をしつつも、エンジニアにバックログを用意しないといけないので将来よりも目先のことに時間を使わざるを得ない状況が続きました。 アジャイルとはいえ、スクラム開始前にターゲット顧客や届ける価値の方針を固めておき、スクラム中は方針実現のための仮説・検証に注力できるようにするべきでした。 1スプリントの期間が短すぎた 1スプリントを1週間で回していましたが、一度負のスパイラルに入ってしまうとリカバリーが難しかったです。 プロダクトオーナーやエンジニアに時間的な余裕を生むためにも次回以降は1スプリントの期間をもう少し長めに置くことを検討しています。 インクリメントの品質を担保できていなかった PJ終盤はインクリメントの品質向上に追われてしまいました。 これの原因は 完了の定義 を用意していなかったことに尽きると考えています 完了の定義とは 何をどこまでやれば「プロダクトバックログを完了とする」かを定義したリストです。 具体的には、以下のようなものが挙げられます コードレビュー 機能テストに合格 単体テスト合格 統合テストに合格 リグレッションテストが作成され合格している テスト環境にデプロイされている POはユーザーストーリーの完了を確認している 引用元: スクラムにおける完了の定義(Doneの定義)とは? 完了の定義を置いておくことでリリース可能な品質を保ちながらインクリメントを重ねる効果が見込めます。 今回のPJでは「とりあえず動くものを作ってスプリントレビューに臨んでいること」にエンジニアが疑問を持つこともできていなかったので、 そういった意識レベルの改善のためにも完了の定義は必要でした。 生成AIによるコーディングとの付き合い方 上記2点の改善点の根本的な原因は アジャイル×スクラム に対する知識や準備不足であることは間違いありませんが、 個人的には、そこに 生成AIの活用方法の未熟さ も重なり、結果として課題が大きくなったと感じています。 生成AIによるバイブコーディングは「とりあえず動くもの」を作るのには強力です。 今回のPJでは 仮説⇒実装⇒検証 を繰り返すタイミングでバイブコーディングの力を借りることで、 高速にサイクルを回し、探り探りだった機能要件を定めることができました。 一方で、高速に実装できてしまうが故に負債もどんどんたまってしまったとも言えます。 どこかのタイミングでバイブコーディング的なAI活用から 「リリースを見越し、仕様や内部設計をしっかり固めながら実装するAI活用(強いて名前を挙げるなら、仕様駆動開発が近いでしょうか?)」 に切り替えるべきでした。 今回はそういった生成AIの活用方法の未熟さもあり、結果的に開発の負荷を高めてしまったと感じています。 (ただし、 仮説⇒実装⇒検証 を十分繰り返せていなければ、そもそも未だにリリースできていたかどうかも怪しいので、バイブコーディングしたこと自体が間違いだったとは思っていません) それでもスクラム開発して良かったポイント 何かと大変な点もありましたが、クロスファンクショナルなチームによるスクラム開発には明確に良かった点もあります 顧客志向が付いた ビジネスサイドの方と同じチームで働くことで、ビジネス課題や中長期の市場への向き合い方、またお客様や導入プロセスへの解像度が上がりました。 AI機能を開発をする。と最初に聞いたとき、自分のエンジニア的好奇心から「いかにもAIっぽい機能を作りたい」気持ちも生まれたんですが、スクラムチームの人と会話し、顧客の解像度を上げることで、「作りたいものを作る」ではなく顧客の困りごとを解決するための手段を考えるマインドにシフトできました。 スピード感をもってリリースできた 初の アジャイル×スクラム に苦労はしましたが、楽楽販売の従来の開発スタイルではもっとリリースまでに時間がかかっていたと思います。 世に出さないと価値は生まないので、これからもスピード感は大事にしていきたいです。 最後に この文章を書きながら 追加機能のリリースも先日行いました 。 追加機能の開発の中でも新たな課題も生まれましたが、一方で上記に挙げていた課題を一定解消することもできました。 この記事が、スクラム開発を始めようとしている人に少しでも役立てば幸いです。
アバター
目次 目次 1. はじめに 前提条件 免責 2. Application Controllerの役割 3. Application Controllerのアーキテクチャ Application Controllerの起動処理(ctrl.Run()) App Refresh Processor App Operation Processor 該当箇所 Reconciliation Loop(内部メカニズム) Phase 1: Refresh 該当箇所 Phase 2: Sync Operation 該当箇所 4. Shardingの仕組み Shardingとは? Shardingのコアメカニズム ArgoCDで選択できるシャーディングアルゴリズム Legacy 特徴 該当箇所 Round Robin 具体例 特徴 該当箇所 Consistent Hashing 特徴 該当箇所 シャードIDの決定 パターンA: StatefulSet (静的割り当て) パターンB: Deployment (動的割り当て) 5. まとめ 1. はじめに こんにちは!SRE課のモリモトです。 ArgoCDはk8sエコシステムの1つであり、GitOps基盤として多くの会社で採用されています。 ラクスのk8s環境でも採用しており、私が所属するSRE課でも運用しています。 しかし、私を含め、Argo CDの内部構造を十分に理解しないまま使っている方も多いのではないでしょうか。 そこで本記事では、ややニッチなテーマではありますがArgoCDのApplication Controllerに特化して、ソースコードをベースに内部実装を解説してみたいと思います。 前提条件 ArgoCDの全体アーキテクチャや各コンポーネント間の繋がりについては、すでにある程度理解している前提で解説します。 もしあまり理解できていないって方がいらっしゃいましたら、他の方のブログではありますが↓が大変わかりやすいのでまずこちらを読んでいただければと思います! hiroki-hasegawa.hatenablog.jp 免責 本記事の内容は v3.2.1 時点の情報に基づいています。 一部、端折って解説している部分が多々あります。正確な挙動は実際にソースコードをご確認ください。 可能な限り正確な記述を心がけていますが、誤りがあればご指摘いただけると幸いです。 2. Application Controllerの役割 まずはArgoCD全体像における立ち位置をおさらいします。 ArgoCDにおいて、Application Controllerはまさに「心臓部」と言えるコンポーネントです。 以下2つの状態を継続的に 監視・比較 し、差異が検出された場合、設定に応じて 同期 を実行します。 GitHubリポジトリ上のマニフェストが定義する「あるべき姿 (Desired State)」 k8sクラスタ上の「現在の姿 (Live State)」 3. Application Controllerのアーキテクチャ アーキテクチャ構成図は以下のようになります。(だいぶ簡略化しています) ArgoCDApplicationControllerのアーキテクチャ 主要なコンポーネントには以下のようなものがあります。 カテゴリ コンポーネント名 役割・説明 Work Queues App Refresh Queue アプリケーションの状態を確認し、ステータス更新(Refresh)を行うイベントのためのWorkqueue App Operation Queue k8s クラスタに対して実際の同期操作(Sync)を行うイベントのためのWorkqueue Project Refresh Queue Project設定の変更を反映するイベントのためのWorkqueue Processors App Refresh Processor App Refresh Queue からイベントを取得し、アプリケーションの状態確認とステータス更新(Refresh)を実行するWorker App Operation Processor App Operation Queue からイベントを取得し、k8sクラスタへの同期操作(Sync)を実行するWorker Project Processor Project Refresh Queue からイベントを取得し、Projectの設定変更を反映するWorker Core Components App State Manager Controllerの脳にあたる部分。GitHub上のマニフェストとクラスタのLive Stateを比較したり、実際に kubectl apply による同期を実行したりする内部コンポーネント Live State Cache 管理対象クラスタ内の全リソース情報をメモリ上に保持する内部のキャッシュコンポーネント Cluster Sharding Cache コントローラーが複数台構成の場合に、自インスタンスがどのクラスタを担当すべきかのマップを保持する内部コンポーネント Application Controllerは、一般的なKubernetes Controllerと同様に、「イベントをためるWorkqueue」と「実際に処理を行うWorker(goroutine)」を内部に持っています。 Applicationリソースの変更などのイベントが発生すると、まずこのWorkqueueにタスクが積まれます。 そして、WorkerがWorkqueueからタスクを取り出して実際に処理を行います。 ここで重要なのが、 Refresh処理とSync処理がそれぞれ別のWorkqueue、Workerに分かれている点 です。 これにより、特定のアプリケーションの同期処理(Sync)に時間がかかっても、他のアプリケーションのステータス更新(Refresh)がブロックされることを防いでいます。 例えば、大規模なリソース作成やPre-Sync Hookの待ちによってSync処理が長時間ブロックされた場合でも、 別レーンで動作するRefresh処理は影響を受けず、UI上のアプリケーション状態は常に最新に保たれます。 また、負荷状況に応じて、それぞれの並列数(Processor数)を個別にチューニングできるというメリットもあります。 Application Controllerの起動処理(ctrl.Run()) Runメソッドは、Application Controllerのメインエントリーポイントであり、コントローラーの起動、初期化、そして各Worker(goroutine)の起動を行います。 重要な点はワーカーの起動です。 以下のように、 statusProcessors や operationProcessors で定義した数だけWorker(goroutine)を起動し、 processAppRefreshQueueItem() や processAppOperationQueueItem() の中でキューに積まれたタスクを実行していきます。 App Refresh Processor アプリケーションの比較・ステータス更新を担当。 for i := 0 ; i < statusProcessors; i++ { go wait.Until( func () { for ctrl.processAppRefreshQueueItem() { } }, time.Second, ctx.Done()) } App Operation Processor 実際の同期処理(kubectl apply)を担当。 for i := 0 ; i < operationProcessors; i++ { go wait.Until( func () { for ctrl.processAppOperationQueueItem() { } }, time.Second, ctx.Done()) } 該当箇所 github.com Reconciliation Loop(内部メカニズム) ArgoCDの「同期」は、大きく分けて Refresh と SyncOperation の2つのフェーズで構成されています。 ArgoCDApplicationControllerのアーキテクチャ(再掲) Phase 1: Refresh イベントの検知: 「Applicationリソースの変更」や「監視対象リソースの変更」等のイベントを検知し、 App Refresh Queue にアプリケーションキーをエンキュー キューからの取得: App Refresh Processor が、 App Refresh Queue から処理対象のアプリケーションキーを取得。 アプリケーションの取得: App Informer のキャッシュからApplicationリソースを取得。 状態の取得: App State Manager が、以下2つの状態を取得。 Target State (Git): Repo Server からマニフェストを取得。 Live State (K8s): Live State Cache から現在のクラスタ上のリソース状態を取得。 状態の比較: App State Manager が、取得した2つの状態を比較しステータスを計算。 Syncステータス: Synced か OutOfSync かを判定。 Healthステータス: リソースが健全(Healthy)かどうかを判定。 AutoSync判定: ApplicationリソースがAutoSync設定で、かつ OutOfSync の場合、Applicationリソースの .Operation フィールドをセット。( .Operation フィールドが同期処理のトリガーになる) ステータス更新: Applicationリソースの Sync ステータスと Health ステータスをセットし、 Kube API Server にPatchリクエストを送信して永続化。 Refresh処理が完了した直後に、必ず appOperationQueue にアプリケーションキーをエンキュー。 8については少し補足で、以下のようにworkerの処理に defer で App Operation Queue にappKeyをエンキューするようになっています。 defer func () { if r := recover (); r != nil { log.Errorf( "Recovered from panic: %+v \n %s" , r, debug.Stack()) } // App Operation QueueにappKeyをエンキューする ctrl.appOperationQueue.AddRateLimited(appKey) ctrl.appRefreshQueue.Done(appKey) }() 該当箇所 github.com Phase 2: Sync Operation キューからの取得: App Operation Processor が、 App Operation Queue から処理対象のアプリケーションキーを取得。 アプリケーションの取得: App Informer のキャッシュからApplicationリソースを取得。 最新状態の取得: 操作を二重に行ったり古い情報で判断したりするのを避けるため、直接 Kube API Server から最新のApplicationリソースを取得。 同期処理の実行: Applicationリソースの .Operation フィールドがnilではない場合、 kubectl apply 相当の処理を実行してK8sクラスタに変更を適用。 完了後のリフレッシュ: 操作が正常に完了した場合、UI上の表示(SyncステータスやHealthステータス)を即座に最新にするために App Refresh Queue にアプリケーションキーをエンキューし、強制的なリフレッシュをトリガー。 該当箇所 github.com このように、複数のコンポーネントが相互作用することで、同期操作を実現しています。 4. Shardingの仕組み Application数や管理対象クラスタ数が増加すると、単一の Application Controller Pod では Refresh / Sync のスループットが頭打ちになります。 Processor 数を増やすだけでは限界があり、このスケール問題を解決するために導入されているのが Application Controller の Sharding(水平分割) です。 弊社でもApplication ControllerのShardingを行っています。 このShardingの中身について見ていきます。 Shardingとは? k8s Controller における Shardingとは、複数のControllerで管理対象リソースを分担して処理する手法のことです。 通常、Controllerは単一インスタンスで全リソースを watch / reconcile しますが、Shardingでは何らかの値を元に管理対象を複数Controllerに分割し、各Controllerは「自分のshard」に属するリソースのみを処理します。 これにより、以下のようなメリットがあります。 大規模クラスタでのスケーラビリティ向上 Reconcile負荷・Kube API Server負荷の分散 ArgoCD Application ControllerでもShardingを設定することができます。 ※ より詳細が気になる方は公式ドキュメントをご参照ください argo-cd.readthedocs.io Shardingのコアメカニズム ArgoCDのShardingの大きな特徴は、Application単位ではなく「デプロイ先クラスタ単位」で担当シャードが決まる点です。 つまり、「あるクラスタAにデプロイされる100個のApplication」は、全て同じ1つのControllerシャードによって処理されます。 Application ControllerのメインのReconcileループでは、各Applicationを処理すべきかどうかを canProcessApp() メソッドで判断しています。 func (ctrl *ApplicationController) canProcessApp(obj any) bool { // ... (省略) ... // Applicationのデプロイ先クラスタ情報を取得 destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db) if err != nil { return ctrl.clusterSharding.IsManagedCluster( nil ) } // そのクラスタが自身の担当かどうかをチェック return ctrl.clusterSharding.IsManagedCluster(destCluster) } github.com ここで呼び出されている IsManagedCluster() メソッドが、割り当て判定の核心部分です。 // IsManagedCluster returns whether or not the cluster should be processed by a given shard. func (sharding *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool { sharding.lock.RLock() defer sharding.lock.RUnlock() if c == nil { // nil cluster (in-cluster) is always managed by current clusterShard return true } clusterShard := 0 // Shardsマップには、各クラスタに対して割り当てられたシャード番号が格納されている if shard, ok := sharding.Shards[c.Server]; ok { clusterShard = shard } else { log.Warnf( "The cluster %s has no assigned shard." , c.Server) } // クラスタに割り当てられたシャード番号が、自身のシャード番号(sharding.Shard)と一致するか確認 return clusterShard == sharding.Shard } github.com このように、ApplicationそのもののID等で判定しているのではなく、「そのApplicationが属するCluster」がどのシャードに割り当たっているかを確認していることがわかります。 ArgoCDで選択できるシャーディングアルゴリズム では、その「各クラスタが割り当てられるシャード」はどのように決定されるのでしょうか。 ArgoCDでShardingする際に利用できるアルゴリズムは以下の3つです。 Legacy Round Robin Consistent Hashing Legacy デフォルトで選択される 従来型のアルゴリズムです。 このアルゴリズムは「クラスタID」を基に、そのクラスタを担当すべき「シャード番号」を決定します。 具体的には、 FNV-1a ハッシュアルゴリズムを使用してクラスタIDを数値化し、 その数値をControllerのレプリカ数で割った余りを計算して担当シャードを決定します。 特徴 項目 内容 計算式 hash(cluster.ID) % replicas 特徴 単純な剰余計算のため、ハッシュ結果の偏り次第では特定のシャード(レプリカ)にクラスタが集中する可能性がある。均等分散は保証されない。 スケーリング時の影響 レプリカ数が変わると計算結果が大きく変化するため、スケールアウト/イン時に多数のクラスタ担当が入れ替わる。そのため再配置コストが高い。 該当箇所 github.com Round Robin このアルゴリズムは、均等な分散を保証するアルゴリズムです。 全クラスタをクラスタIDでソートしたリストを作成し、クラスタIDをキーとしたインデックスマップを作成します。 そして、そのマップ(各クラスタの順位)に基づいて担当シャードを割り当てます。 具体例 以下のように、全クラスタをIDでソートし、各クラスタに連番のインデックスを付与します。 そして、 インデックス % レプリカ数 で担当シャードを決定しています。 クラスタID Index 計算式 担当シャード cluster-a 0 0 % 3 = 0 Shard 0 cluster-b 1 1 % 3 = 1 Shard 1 cluster-c 2 2 % 3 = 2 Shard 2 cluster-d 3 3 % 3 = 0 Shard 0 cluster-e 4 4 % 3 = 1 Shard 1 特徴 項目 内容 計算式 clusterIndex % replicas (clusterIndexはソート済みリスト内の順位) 特徴 各シャードに割り当てられるクラスタ数が均等になることが保証される。Legacyと異なり、偏りが発生しない。 スケーリング時の影響 クラスタの追加・削除があるとソート順が変化し、多くのクラスタで担当シャードが再計算される。結果として、クラスタリストの変更時にシャッフルが発生しやすい。 該当箇所 github.com Consistent Hashing このアルゴリズムは、ほぼ均等な分散を実現しつつ、スケーリング時の影響を最小化するアルゴリズムです。 Consistent Hashing with Bounded Loads(負荷制限付き一貫性ハッシュ)アルゴリズムを使用し、他のアルゴリズムと異なり担当するクラスタのアプリケーション数も考慮したクラスタ割り当てを行います。 このあたりは自分自身も理解しきれていない部分があるので、ふんわりとした解説になってしまいますが、、、シャード割り当てのポイントは以下です。 各シャードをリング上に配置する(コンシステントハッシングの考え方) 負荷上限を考慮して、最も負荷の低いシャードにクラスタを割り当てる クラスタ割り当て後、そのクラスタのアプリケーション数分だけシャードの負荷を更新 func createConsistentHashingWithBoundLoads(replicas int , getCluster clusterAccessor, getApp appAccessor) map [ string ] int { clusters := getSortedClustersList(getCluster) appDistribution := getAppDistribution(getCluster, getApp) // クラスタごとのアプリ数 consistentHashing := consistent.New() // 各シャードをハッシュリングに追加 for i := 0 ; i < replicas; i++ { consistentHashing.Add(strconv.Itoa(i)) } for _, c := range clusters { // 最も負荷の低いシャードを取得 clusterIndex, _ := consistentHashing.GetLeast(c.ID) // シャードにクラスタを割り当てる shardIndexedByCluster[c.ID] = clusterIndex // シャードの負荷を更新(アプリ数を加算) appsIndexedByShard[clusterIndex] += appDistribution[c.Server] consistentHashing.UpdateLoad(clusterIndex, appsIndexedByShard[clusterIndex]) } } これにより、クラスタ数だけでなくアプリケーション数まで考慮して、シャードへのクラスタ割り当てが可能になります。 また、コンシステントハッシングアルゴリズムを利用することで、クラスタの増減が発生しても再割り当てのコストを抑えることができます。 特徴 項目 内容 計算方法 Consistent Hashingアルゴリズム + 負荷上限による再配置 特徴 各シャードに割り当てられるクラスタ数がほぼ均等になる。さらに、クラスタ単位ではなくアプリケーション数も考慮するため、実際の処理負荷がより均等化される。 スケーリング時の影響 レプリカ数やクラスタ数が変化しても、影響を受けるのは一部のクラスタのみ。Consistent Hashingの特性により、再配置コストが最小限に抑えられる。 該当箇所 github.com シャードIDの決定 最後に、各Application ControllerのPodはどのようにして「自分はシャードN番である」と認識するのかについて触れておきます。 これには大きく分けて2つのパターンがあります。 パターンA: StatefulSet (静的割り当て) デフォルトの設定で利用している場合、Application ControllerはStatefulSetでデプロイされ、シャード番号はPodのホスト名から推論されます。 InferShard() メソッドがそのロジックです。 func InferShard() ( int , error ) { hostname, err := osHostnameFunction() if err != nil { return - 1 , err } parts := strings.Split(hostname, "-" ) // ... (省略) ... // ホスト名の最後のハイフンの後ろの数字をパースする //  例) argocd-application-controller-0 → 0 shard, err := strconv.Atoi(parts[ len (parts)- 1 ]) // ... (省略) ... return int (shard), nil } github.com 非常にシンプルで、 argocd-application-controller-0 ならシャード 0 、というようにPod名から決定されます。 パターンB: Deployment (動的割り当て) Dynamic Cluster Distribution 機能を利用している場合、StatefulSetではなくDeploymentでデプロイされるためホスト名がランダムになり、パターンAの方法は使えません。 この場合、 ConfigMap を使った動的な割り当てとハートビートにより、各Controllerが自律的に担当シャードを決定します。 Controllerは、起動時および定期的に argocd-controller-shard-cm ConfigMapを確認します。 自身がまだシャードを担当していない場合、「空いているスロット」 または 「ハートビートがタイムアウトしているスロット」 を探します。 条件に合うスロットがあれば、そこに自分(Pod名)を登録して担当者となります。 担当が決まった後は定期的に HeartbeatTime を更新し、自分が生存していることを知らせます。他のControllerはこの時間が古くなると、そのシャードの担当がダウンしたとみなしてスロットを奪い取れるようになります。 github.com この仕組みにより、Argo CDはStatefulSetに依存せずとも、各コントローラが一意なシャードIDを持ち、役割分担を行うことができます。 5. まとめ 今回は、ArgoCDのApplication Controllerに特化し、内部の仕組みをソースコードを読み解きながら整理しました。 まだ理解しきれていない部分が多々ありますが、少しは理解が深まった気がします。 AIの進化でコードリーディングが格段にしやすくなりましたね。私もかなり助けられました。 長くなりましたが、最後まで読んでくださりありがとうございました!
アバター
この記事は ラクス Advent Calendar 2025 の20日目の記事です。 今回は、日々の開発や運用の中で「これ意外と便利だったな」と感じた小さな改善を紹介します。 背景・課題:CI 内でタグの定義場所が散らばる 解決策:タグ指定にGitHub Actions の出力値を使う 実際の構成:タグ生成ジョブ → 他ジョブが参照する 共通化:reusable workflow 化でタグ生成を完全に中央集約 タグ生成用 workflow(共通化) 呼び出す側 (補足)docker compose にも応用可能 .envファイルを使わない・使えない場合 まとめ 背景・課題:CI 内でタグの定義場所が散らばる GitHub Actions のジョブでコンテナイメージを指定する場合、たとえば以下のように container.image を書くことが多いと思います。 jobs : test : runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:v1.0.0 しかし、複数ジョブが存在するWorkflowだと、同じタグをあちこちで書くことになります。 更新時にすべて直すのが面倒で、修正漏れが発生しやすいのが悩みどころです。 また漏れが発生すると、テストやビルドが意図しないバージョンで動いてしまい、検知できるはずの不具合が見逃されるリスクもあります。 解決策:タグ指定にGitHub Actions の出力値を使う こうしたタグ管理の悩みを解決するために、さまざまな手段を探していました。 そんな中、別件の改善をするにあたってWorkflowドキュメントを読んでいると、 container.image にも変数が使えそうだ、ということがわかってきました *1 。 これまで漠然と container.image はベタ書きするしかないと思い込んでいたので、この発見は大きな転機でした。 実際に動作するか試してみたところ、問題なく動作したため、これを活用してタグ管理を一元化することにしました。 実際の構成:タグ生成ジョブ → 他ジョブが参照する jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - name : resolve tag id : resolve run : | TAG="v1.0.0" echo "tag=$TAG" >> "$GITHUB_OUTPUT" test : needs : resolve_tag runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:${{ needs.resolve_tag.outputs.tag }} これで test ジョブの image が resolve_tag ジョブの出力を使うようになります。 ジョブを追加するときも、 needs: resolve_tag から参照するだけです。 共通化:reusable workflow 化でタグ生成を完全に中央集約 Workflowファイル数が多い場合、resolve_tag ジョブを各ファイルにコピペするのも面倒です。 そこで、タグ生成部分を reusable workflow 化して共通化しました。 共通化という観点ではcomposite action にする方法もありますが、composite actionはジョブ内のステップとして展開されるような仕組みです。 container.image はJOBレベルでの指定なので、reusable workflow を採用しました。 ※ステップ内に展開されるcomposite actionでも対応はできますが、 outputs などJOBレベルの構造を利用側に毎回書く必要があり、共通化のメリットが薄い タグ生成用 workflow(共通化) # .github/workflows/resolve_tag.yml name : Resolve Image Tag on : workflow_call : outputs : tag : value : ${{ jobs.resolve_tag.outputs.tag }} jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - name : resolve tag id : resolve run : | TAG="v1.0.0" echo "tag=$TAG" >> "$GITHUB_OUTPUT" 呼び出す側 jobs : resolve_tag : uses : ./.github/workflows/resolve_tag.yml test : needs : resolve_tag runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:${{ needs.resolve_tag.outputs.tag }} こうすることで、タグは完全に一元管理でき、変更が楽になります。 (補足)docker compose にも応用可能 もしローカル開発用に docker compose でも同じタグを使いたい場合はタグの管理ファイルを作成すると、CIとの共通化が可能です。 例えば .env ファイルであれば、そこに記載すればdocker composeのコマンド引数を変更することなく反映できます。 .env IMAGE_TAG=v1.0.0 docker-compose.yml services : app : build : context : . args : - IMAGE_TAG=${IMAGE_TAG} Dockerfile ARG IMAGE_TAG FROM ghcr.io/ORG/REPO:${IMAGE_TAG} ... .github/workflows/resolve_tag.yml jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - uses : actions/checkout@v6 - name : resolve tag id : resolve shell : bash run : | source .env # .env から読み込み echo "tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT" .env ファイルを使わない・使えない場合 .env ファイルを使わない・使えない場合、docker composeのコマンド引数を変更しない、というのは難しいようでした。 私たちのケースではdocker composeをラップするような Makefile を用意し、その中で環境変数を設定する形で対応しました。 # Makefileの機能(include, export)で環境変数を設定 include xxx.env export IMAGE_TAG docker-build: docker compose build --build-arg IMAGE_TAG=${IMAGE_TAG} まとめ GitHub Actions の needs.<job>.outputs.* は container.image に使える タグ生成ジョブを 1 箇所に置くだけで CI 全体のタグ管理が楽になる ローカル(docker compose)に応用することも可能 小さな工夫ですが、導入すると CI の見通しがかなり良くなりました。 同じ悩みを持つ方の参考になれば幸いです。 *1 : Contexts reference - GitHub Docs
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 2025年12月4日(木)に当社ラクスの紀井が登壇し、『AI時代の「ジュニア不要論」に異議あり!未経験から戦力PdMを生み出すOJT戦略とは?』 2025.pmconf.jp というテーマで「PdM育成」について登壇しました。 そこで私も、現在、直下で1名のPdMを「再育成」していることもあり、 ラクスにおけるPdM育成のリアル を少しフィクションを交えつつお伝えします。 ■ はじめに メンバー概要 ラクスのプロダクトマネージャーの選考プロセス 採用時点の評価 ■ 入社後 → 再育成に至るまで ■ オンボーディングについて バディ体制 ■ PdM担当後に起きた課題 ■ 再育成の進め方 カリキュラム概要 1.課題図書 → レポート作成 → メンターと議論 2.社内e-learning受講 3.「自分のイシューは何か?」ワーク ■ 「自分のイシュー」課題の狙い チェックポイント(育成側) ■ 次の育成課題へ 最後に ■ はじめに メンバー概要 年齢:20代後半 PdM歴:約3年(ラクス入社前) 経歴: 新卒で大手IT企業のPdMとして入社。学生時代はベンチャーでエンジニアとして3年以上アルバイト経験。インバウンド向けのトラベル系プロダクトを担当。 ラクスのプロダクトマネージャーの選考プロセス 1. 書類選考 2. カジュアル面談 稲垣が15〜20分ほど会社と業務内容の説明 選考要素なし。その場で辞退してもOK 3. 一次面接(オンライン) 稲垣とリーダーPdMで実施 4.最終面接(対面) 部長が担当 このメンバーの採用時点での評価は以下です。 採用時点の評価 新卒PdMとして3年で一定成果を出していた ビジネス側・エンジニア側双方と連携経験あり 担当していた業務内容の整理・意義を高い解像度で説明できた (※言語化は今後の課題として認識) 行動力が非常に高く、インタビューでも多くのエピソードあり ラクスの開発組織が大事にする顧客志向で業務をしていた 複数のSaaS企業と比較した結果、 「サービスの成長性」「PdM組織の成熟度」「Biz/Dev バランス」 の3点を理由にラクスを選んでくれました。 ■ 入社後 → 再育成に至るまで 2024年夏 :入社(1か月オンボーディング) 2024年夏 :「楽楽明細」でPdM業務開始(当時は担当PdM 1名+本人) 2025年冬 :軽微な案件を担当開始。ステークホルダーとのコミュニケーション齟齬が発生  → 一部クレームもあり役割を縮小 2025年春 :リーダー判断で「一度プロダクト担当を外す」決定  → 再育成フェーズへ移行 2025年秋 :チーム規模拡大により、再育成担当者を私に変更 ラクスでは入社後1〜2ヶ月をオンボーディング期間としています。 入社半年以内に大きな成果を求めることはありません。特に入社1ヶ月目は 「会社の理解」「製品解像度の習得」 に集中してもらいます。 ■ オンボーディングについて ラクスでは「新入社員受入チェックシート(約50項目)」を進めてもらいます。 バディ体制 メインバディ :最初の業務フォローを担当 サブバディ :メイン不在時の相談先 また、1ヶ月間はマネージャーと週1〜2回の1on1を実施し、初期の6ヶ月目標もリーダー・メインバディで設定。 早期に「製品解像度」を上げる環境を整えます。 1〜2週目からはチャットや各種MTGにも参加し、 実務の情報もキャッチアップしてもらいます。多くのPdMは1ヶ月後半で 「そろそろ案件を担当したい」と意欲を見せる方が多いため、そこから軽微な案件をアサインする流れが一般的です。 今回のメンバーも同様でした。 ■ PdM担当後に起きた課題 担当後2〜3ヶ月は特に問題ありませんでしたが、3ヶ月目以降から以下のような課題が顕在化しました。 案件PRDの質 課題の解像度が粗い ステークホルダーとの認識齟齬が複数回発生 振り返ると、主因は以下の 「アサインミス」 だと判断しています。 それまでとは次元の違う難易度の案件を担当させてしまった その案件は、メインバディが担当しても難しかったレベルの内容だった PdMはステークホルダーとの信頼が生命線です。ここで信頼が揺らぐと、その後の業務が非常に進めづらくなります。そのため、本案件から外し、完全に再育成に入る決断をしました。 再育成を選んだ理由は以下です。 これまでの経験は「デリバリー寄り」で、「ディスカバリー」が弱かった 担当プロダクトは今後も高難易度案件が発生し続ける(OJTでは限界がある) ■ 再育成の進め方 ラクスでは、若手エンジニアをPdMへキャリアチェンジさせる際に 育成カリキュラム を作成しました。 今回はそれをベースに進めています。 カリキュラム概要 1.課題図書 → レポート作成 → メンターと議論 ■課題図書 『 問題解決――あらゆる課題を突破する ビジネスパーソン必須の仕事術 』 『 ロジカル・プレゼンテーション――自分の考えを効果的に伝える戦略コンサルタントの「提案の技術」 』 ■レポート内容 書籍を読んで印象に残ったことや気づき 自分ができている/できていない部分の整理 これまでの実務事例との紐付け※フォーマット自由 2.社内e-learning受講 ロジカルシンキング ラクスの事業理解 ラクスには豊富なe-learningコンテンツがあり、必須なものもあればそうでないものもあります。その中で、ピックアップしたものを受講してもらっています 3.「自分のイシューは何か?」ワーク ワーク:書籍『 イシューからはじめよ 』を読んだ上で「自分自身のイシューとは何か?」を提示せよ これは難易度が高く、平均して4〜5回のレビュー・議論を行います。 今回は当初リーダーがメンターとして実施しておりましたが、リソース逼迫により途中から私が直接担当しました。 ■ 「自分のイシュー」課題の狙い 狙いは以下の4点です。 1. ディスカバリーには「なぜ?」を問う力が重要 2. 多くの課題を見つけても「イシュー」を特定しないことにはディスカバリーできたとは言えない 3. 自分自身を対象にするため、他者への迷惑がかかりにくい 4. 成長の鍵である「メタ認知」を強化できる 育成側・育成される側双方に「評価基準」を最初から提示しています。  ※育成される側に徐々に開示している チェックポイント(育成側) 本人なりの「あるべき姿」が定義されている 単なる現状改善ではなく、自分の頭で考えた言語化になっている 「現状」と「あるべき姿」のギャップが構造化され、MECEになっている 課題と問題が区別できている 一次情報を元に事実と解釈を分離できている 最終的なイシューが以下を満たす 本質的な選択肢である(解決すれば価値が出る) 答えを出せる(解決策を示せる) イシューまでの流れがストーリーとして一貫している ここまで読んでお気づきの方もいると思いますが、 これはそのまま 「プロダクトマネジメント」 です。 つまり、 「自分自身」をプロダクトと見立てて、プロダクトマネジメントしてもらう というのが本課題の本質です。 ■ 次の育成課題へ このメンバーは10月に本課題をクリアしました。業務と並行しながら 約3ヶ月で完了 した点は素晴らしい進捗だと思います。 次に取り組んでもらっているのは—— 「PdM・デザイナーの業務をプロダクトマネジメントせよ」 具体的には、 PdM/デザイナー業務の中から課題を発見し、それを解決することでチームに貢献する というテーマです。 この課題の狙いは以下の3点です 1. 「自分のイシュー」より範囲が広くなり、難易度が格段に上がる 2. 成功すればチーム貢献となり、関係者からの信頼を獲得できる 3. チーム内でバラバラに取り組んでいたAI活用・生産性改善の棚卸しができる 私が育成を直接見ているため、難易度調整も可能で、成功確率を高められます。再育成を担当してまだ2ヶ月ほどですが、今の課題を確実にクリアし、2026年4月からは高いレベルで実案件に戻ってほしいなと思っています。 最後に 採用・オンボーディング・育成について紹介しましたが、 ラクスは「人への投資」を非常に大切 にしています。 また、記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
この記事は ラクスアドベントカレンダー2025 17日目 の記事です。 はじめに こんにちは! エンジニア3年目のTKDSです! 今回はgo-gitについて紹介します。 はじめに go-gitとは 基本的な操作 応用的な操作 実例 差分があったファイルのみSQL実行 まとめ go-gitとは https://github.com/go-git/go-git goで書かれたgit操作用ライブラリです。 git操作とGoコードを組み合わせかけるので、非常に汎用性が高く便利です。 基本的な操作 まず基本的な操作を紹介します。 今回はPublicリポジトリなので、認証部分は省いてます。 一部抜粋して載せてきます。 全文は リポジトリ に載せてあるので、main.goにコピペして実行してください。 clone リモートリポジトリをcloneする操作は以下のとおりです。 r, err := git.PlainClone( "poc/" , &git.CloneOptions{ URL: "https://github.com/tkeshun/go-git-poc" , Progress: os.Stdout, // 進捗を表示したくなければ nil でOK }) これを使って、cloneしてからlogを表示するコードを実行すると以下のようになります。 add addしてステータス確認 if _, err := wt.Add(addpath); err != nil { slog.Error( "Add Error: " + err.Error()) return } st, _ := wt.Status() print (st.String()) addして取り消すコードを実行すると以下のようになります。 なぞの文字列とファイル名が表示されてますが、Aがadd, ??がステージング前を表しているようです。 commit commitする操作は以下のとおりです。 if _, err = wt.Commit( "test" , &git.CommitOptions{ Author: &object.Signature{ Name: "go-git-invalid" , Email: "" , When: time.Now(), }, }); err != nil { slog.Error( "commit Error: " + err.Error()) } commitするコードを実行するして git log でみるとcommitが追加されてるのがわかります。 このcommitを消したいときはheadを親commitに戻して、indexをリセットすれば良いです。 ※ Errorハンドリング書くのめんどくさくなって省略してますが、実際はだめです!!! headRef, _ := r.Head() headCommit, _ := r.CommitObject(headRef.Hash()) parent, _ := headCommit.Parent( 0 ) wt.Reset(&git.ResetOptions{ Commit: parent.Hash, Mode: git.MixedReset, }) さっきのcommitが消えて親commitに戻ってます。 ここまででよく使う操作は紹介できました。 push(※番外) 一応公式のexampleにPushもあります。 現状私があまり必要としてないため飛ばします。 興味がある人は 公式example みて試してみてください。 このように、goコード上からclone, add, commit, pushなどの基本の操作が可能です。 応用的な操作 次に応用的な使い方です。 指定したcommitハッシュ間の比較 Hash値を指定して比較することで差分の比較が可能です。 ファイル差分を単純に比較することや差分内容を取得することができます。 commitが隣合わせでなくても差分は出すことができ、1commitで複数のファイルをcommitしていたとしても1ファイルずつ差分を取り出せます。 t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) この3つの処理で差分を取り出せ、 p.FilePatches() に差分が入ってるので、それをループで回すとファイルごとに2点のcommit間の差分を取り出せます。 for _, fp := range p.FilePatches() { } 色々試したサンプルを載せおきます。 package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } commit1Hash := plumbing.NewHash( "cacf82fd8a097b8266fb7a7c84f02a9a1a599a4c" ) commit2Hash := plumbing.NewHash( "b20da7389753009b3cd6be1ae0cc66e5551e8916" ) t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) print (p.String()) for _, fp := range p.FilePatches() { from, to := fp.Files() if from != nil { f := from.Path() fmt.Println(f) } if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Println(c.Content()) } } } ブランチ間の比較 Reference関数でブランチとハッシュが取れるので、Reference関数が要求する plumbing.ReferenceName を plumbing.NewBranchReferenceName を作ります。 Reference関数を使うと返り値で *plumbing.Reference が取得できます。 *plumbing.Reference.Hashを使うとハッシュ値が取得できるので、あとは同じようにcommitハッシュの差分をとれば、ファイル差分の取得ができます。 r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } ref1, err := r.Reference(plumbing.NewBranchReferenceName( "main" ), true ) if err != nil { slog.Error(err.Error()) return } fmt.Println(ref1) ref2, err := r.Reference(plumbing.NewBranchReferenceName( "test" ), true ) if err != nil { slog.Error(err.Error()) return } // あとは同じ 最新版と旧版の比較 fetchと組み合わせて使うことで、現在あるremoteの状態と最新のremoteの状態の比較が可能です。 コードは以下のようになってます。 package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/config" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } remoteRefName := plumbing.NewRemoteReferenceName( "origin" , "test" ) beforeRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } beforeHash := beforeRef.Hash() err = r.Fetch(&git.FetchOptions{ RemoteName: "origin" , RefSpecs: []config.RefSpec{ "+refs/heads/test:refs/remotes/origin/test" , }, }) if err != nil && err != git.NoErrAlreadyUpToDate { slog.Error(err.Error()) return } afterRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } afterHash := afterRef.Hash() if beforeHash == afterHash { fmt.Println( "diff: no changes" ) return } beforeCommit, err := r.CommitObject(beforeHash) if err != nil { slog.Error(err.Error()) return } afterCommit, err := r.CommitObject(afterHash) if err != nil { slog.Error(err.Error()) return } patch, err := beforeCommit.Patch(afterCommit) if err != nil { slog.Error(err.Error()) return } for _, fp := range patch.FilePatches() { _, to := fp.Files() if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Print(c.Content()) } } } 実行してみると下記画像のようになります。 2回目はもう更新済みなのでdiffがでません。 このように、単純なcommit間の差分、ブランチ間の差分、最新化した差分など様々な差分が取得できます。 実例 使用例を出してみました。 以前、登壇したことのある発表で出したツールの簡易版です。 差分があったファイルのみSQL実行 応用例で紹介したように、現状のHeadのcommit Hashを保持しておいてfetchをしたあとまたHeadのcommitを取得することで、更新差分が取得できます。 これを使うと、ブランチに新しく追加されたファイルを検知し、実行することができます。 コードは長いのでリポジトリをみてください。 差分検出にsql実行部分をくっつけます。 commitとpushして実行します。 init.sqlは以下のようになってます。 CREATE TABLE IF NOT EXISTS demo_items ( id bigserial PRIMARY KEY, name text NOT NULL UNIQUE , note text NOT NULL DEFAULT '' ); go run main.goで実行したあと、DBにつないで実行するとテーブルが作成されていることがわかります。 今回は単純な実行コードですが、もう少し作り込むならSQLファイルの内容チェックなどがあったほうがよいですね。 insertとselectも順番にやってみましょう 投入と検索もうまくいきました。 もし差分検知して自動実行したい場合は、これを定期起動すれば差分の自動実行もうまくいきそうです。 まとめ ここまでみていただきありがとうございました。 今回はgo-gitの紹介と簡単な実用例を紹介しました! 実際に社内用のプロダクトでも使われており非常に便利なライブラリです。 この記事でgo-gitに興味持った人は、ぜひgitを活かして色々やっててみてください!
アバター
こんにちは。株式会社ラクスで「楽楽債権管理」のPdMをしている柴、PMMの江良です。 「楽楽債権管理」は 請求(債権)データと入金データの照合・消込を自動化するクラウドサービスで、入金の過不足、まとめ入金、名義違いといった実務で頻発する複雑なケースにも対応します。消込後の仕訳データも自動生成し、手作業では時間のかかる債権管理業務を正確かつスピーディーに進められるようにします。 https://www.rakus.co.jp/rakurakucloud/saikenkanri/ 「楽楽債権管理」は2025年7月、「楽楽明細」のオプション機能からスピンアウトし、単独プロダクトとして販売を開始しました。単独化によって事業のポテンシャルは大きく広がりましたが同時に、ステークホルダーの増加や組織の拡大により、立ち上げ期のような少人数の阿吽の呼吸だけではスケールしなくなりました。 そこで必要になったのが、体系的で再現可能なプロダクトマネジメント、そしてチーム全員が「同じ地図」を持って進むための仕組みでした。この記事では、「楽楽債権管理」が新たなフェーズに向かう中で、どのようにその「地図」が整備され、プロダクトと組織が変わっていったのかをご紹介します。 このnoteは、プロダクトの立ち上げ期〜成長前夜にいるチームに向けて書いています。戦略や役割がまだ曖昧で、「自分たちはどこへ向かっているのか」「どうやって再現性のある組織を作るのか」に悩む方に、少しでも参考になれば幸いです。 立ち上げ期にあった課題感 役割を明確化し、組織の“ハブ”をつくる PdMは「開発・デザイン」のハブ PMMは「営業・CS」のハブ 戦略設計を「共通言語」に 顧客の声(VoC)の仕組みづくり データ活用による顧客行動の可視化 ロードマップを“見える化”する協働 おわりに 採用情報 立ち上げ期にあった課題感 単独化前後のチームには、次のような課題がありました。 データが整備されておらず、お客様の課題や利用状況を定量的に把握できない 戦略がなく、商談でよく出る要望を“積み上げ式”に対応していた チーム内の役割が曖昧で、意思決定の軸が人によって異なっていた こうした状況では、「プロダクトとしてどこへ向かうべきか」も「組織としてどう動くべきか」も不明瞭になります。ここから、再現性のある意思決定・役割分担・戦略共有に向けた取り組みを進めていきました。 役割を明確化し、組織の“ハブ”をつくる これらの課題に向き合ううえで、最初に取り組んだのが役割の整理でした。組織が拡大する中で、誰がどの領域をリードするのかを明確にすることも重要な課題でした。役割が曖昧なままでは情報が滞り、意思決定が遅くなってしまうためです。ラクスでは職能別組織体制を採用しており、それぞれが持つ専門性を最大限に活かしながらプロダクトを前に進めています。一方で、職能が分かれているからこそ、情報をつなぐためのコミュニケーションコストが一定かかるという側面もあります。そこで「楽楽精算」や「楽楽明細」と同様にPdMとPMMは役割を明確にし、“分断”ではなく“補完”として機能するよう再定義しました。 PdMは「開発・デザイン」のハブ エンジニアとの仕様検討 デザイナーとの構造・UI議論 技術的制約と顧客価値のバランス調整 PdMはプロダクトを「どう作るか」の中心に入りこみ、ビジネスの意図を開発に翻訳する橋渡し。 PMMは「営業・CS」のハブ 商談やデモからの学び CSが拾った利用実態や課題 市場動向・競合比較 PMMは顧客や市場のリアルをプロダクトへ返し、現場とプロダクトの温度差をなくす存在。 このハブ構造により、開発/デザイナー ←→ PdM ←→ PMM ←→ 営業/CSという情報の流れが生まれ、意思決定が早く、滞留しない組織へ変わりました。 戦略設計を「共通言語」に 最初に取り組んだのは PMF(Product-Market Fit)の定義 でした。当時は「PMFを目指す」という言葉だけが独り歩きし、何をもって達成と言えるのかチーム内で解釈がバラバラでした。そこで他プロダクトの状況や事業状況、プロダクトの利用データを整理し、いくつかの定量指標を設定。チーム全員が “PMFとは何か” を同じ言葉で語れる状態 にしました。さらに ICP(Ideal Customer Profile)を定義 し、「誰に価値を届けるのか」を明確化。業種、規模、利用システム、入金件数などの基準でターゲットを整理することで、ロードマップ、優先順位、マーケ施策の判断軸が一貫するようになりました。戦略を明文化したことで、議論のスタートラインが揃い、チームとして“同じ方向を向く”状態を作りました。 顧客の声(VoC)の仕組みづくり 次に取り組んだのが、顧客の声(VoC)を意思決定に活かす仕組み です。私たちはLookerによるデータ可視化に加え、VoCを毎月1回、PdMとPMMがあえて手動で整理する運用を取り入れています。自動集計ではなく全件に目を通すことで、数値化しづらいニュアンスや深層の課題を捉えられるためです。 VoCとPBIはすべて Notionで一元管理。PdM・PMMはもちろん、開発・営業・CSまで誰でもリアルタイムにアクセスできます。さらに、RICEスコアを参考にVoCを以下のような軸でスコアリングしています。 顧客規模 課題の頻度 代替手段の可否 その課題が事業成長に与える強さ(失注や解約につながる課題か) ICPとのマッチ度 定量的に整理することで、「大口顧客の1件の声」と「中堅顧客の複数の声」を公平に比較でき、単なる“要望リスト”ではなく戦略と一貫したロードマップに落とし込めるようにしています。 データ活用による顧客行動の可視化 VoCとは別に、実際の利用行動データから顧客の状態を捉える仕組みも構築しました。お客様ごとの利用ログを取得し、主要機能の利用状況を業務フロー単位で可視化。これによって、 主要業務がどれだけ実施されているか どの機能が活用されていないか どの工程で“詰まり”が生じているか といったプロダクトの実態が明確になりました。さらに、これらをスコアリングし利用が停滞しているお客様を自動検知。CSチームが能動的にフォローできる体制が生まれました。「ログの可視化 → スコアリング → CSアクション」の流れによって、プロダクトの価値をお客様の業務に定着させる循環を作りやすい仕組みにしています。 ロードマップを“見える化”する協働 ロードマップ策定は、PdM・PMMに限らず営業・CS・開発まで巻き込みながら進めています。 セグメントごとのニーズ整理 競合比較 商談だけでは見えにくい非定量な課題感の抽出 顧客価値の大きい領域を優先する判断 機能開発における規模や難易度 上記のような情報を現場から吸い上げ、プロセスを透明化することで、「このタイミングで、この顧客セグメントに、こういう価値を届ける」という共通理解が生まれ、プロダクトの方向性が揺らぎにくくなりました。 おわりに こうした取り組みの積み重ねにより、PdMとPMMが互いの領域に自然と“越境”しながら協働する文化が育ってきました。役割は分担しつつも、共通言語と共通の地図を持ち、チーム全体でプロダクトを育てる文化へと変化しました。 楽楽債権管理は今まさに成長フェーズへ踏み出しているプロダクトです。その中で積み重ねてきた仕組みと文化は、これからステークホルダーが増えても迷わず進んでいくための土台になると信じています。プロダクトが変わると組織が変わり、組織が変わるとまたプロダクトが変わる。この循環をこれからも回し続けていきたいと思います。 採用情報 career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp ■ 「Noを伝える技術」の感想 ■ イエスと言って「誠実に合意形成する」ための技術とは? ① まずは「強いイエス」を返す ② ただし、本当にやるのは「徹底的なリサーチ」 ③ そのまま“事実として”レポートする ④ 相手は自然と「今回はやめるか」と判断する ■この技術の良いところ 📝 まとめ:本気で向き合う姿勢が、結果的に“ノーを機能させる”あるいは“イエスのコミットメントをあげる” ■ 「Noを伝える技術」の感想 先日出版された 『 Noを伝える技術 プロダクトマネージャーが教える「敵を作らずに断れるようになる」作法 』 について触れたいと思います。 この書籍は、PdM界隈では知らない人はいないと言われる「Noを伝える技術 #pmconf2021」の登壇内容をもとにしています。 speakerdeck.com ただし登壇は20分、スライドは25枚ほど。 一方で書籍は、内容がより広く深く、そしてプロダクトマネージャーに寄り添った実践的な話が多く収録されています。 私は2021年8月にPdMを名乗り始め、pmconfを知ったのは2023年頃。登壇の存在もそれと同時でしたが、それだけ有名で、そして現場で刺さる内容です。本書で印象的だった一文があります。 『良かれと思ってやったこと』が積み重なった結果、まったく望まない方向に進んでしまうことがある 私はこれを何度も見てきましたし、自分自身も経験してきました。 だからこそ「Noを伝える」技術は、どの会社・どの立場でも必要で、多くの人が“心ではNO”と思っていることを言語化できるようになることは大きな価値があります。 ちなみに私は、読書の際に必ずメモを取るのですが、この書籍は2025年に読んだ約100冊の中でも、かなり上位のメモ量でした。 最近はこのメモをまとめて社内展開することで、書籍を読む文化を作ろうとしています。…と、思ったより書籍の話を書いてしまいましたが、ここからが本題です。 私自身が現場で使ってきた“イエスと言って「誠実に合意形成する」ための技術” を紹介します。 タイトルにもある通り、私がよく使うのは—— まずは「イエス」と言う。 そして、事実を積み上げることで"イエス”か"ノー"か相互に合意形成をしていく方法です。これを進めると自分から“ノー“と言う前に相手が“ノー“というケースもあります。 という方法です。 ■ イエスと言って「誠実に合意形成する」ための技術とは? 前職はわりとハードなベンチャーでは「イエスかハイかどっち?」と言われるような環境でした。ベンチャーでは珍しくない話ですが、この環境では「ノー」を直接言うと、話がややこしくなることがあります。 特に相手が強い自信を持っている提案や依頼に対して、正面からノーを言うのは得策ではありません。 ひどい場合、もしその後うまくいかないと「最初にノーと言ったせいだ」「本気でやっていなかった」とさえ言われかねません。 そこで生まれたのが、次の方法です。 ① まずは「強いイエス」を返す 相手が自信満々のときに正面からノーを言っても勝てません。なので私はまず、あえて 力強いイエス を返します。 ✔ 「いいですね、やりましょう」 ✔ 「確認します。まず真剣に調べます」 “乗り気である”姿勢を見せることで、相手はこちらを信頼し、前向きに期待してくれます。 ② ただし、本当にやるのは「徹底的なリサーチ」 イエスと言ったからには、最初に 本気でリサーチ します。 経営者の直感はまとはずれなことは少ないです。 調べてみると、想像以上に価値があることも多い。しかし、深掘りしていくと… 大きなリスク 想定外の障害 無謀さを示すエビデンス が出てきます。 ③ そのまま“事実として”レポートする ここが最重要ポイントです。私は、そのリサーチ結果を淡々と報告します。 ✔ 依頼を本気で実行しようとした際に見えてきたリスク ✔ プロジェクトを阻害する障害 ✔ 実行時の工数・コスト・競合状況 ✔ 成功確率が低い理由(エビデンス付き) “やらない理由”ではなく “本気でやるにはここが問題です” というスタンスで伝えるのが大事。 ④ 相手は自然と「今回はやめるか」と判断する ここまで来ると相手は、 「これは一度立ち止まったほうが良いかもしれない」 と 自分自身で判断 してくれます。つまり、私がノーと言うのではなく、 相手がノーと言ってくれる のです。 しかも、こちらは“最初にイエスと言っている”ので、関係性にマイナスが一切ありません。 むしろ信頼が高まります。また、仮にそれでもやるとなった場合には、最初に「ノー」と言わず進めるべきものだったのだと思います。 ■この技術の良いところ ① 相手との関係性が傷つかない 最初に前向きに受ける姿勢を示すので、否定の空気が残りません。 ② 事実ベースの議論になる 感情論ではなく、リスク・データ・実態という“事実”で判断できます。 ③ 意外な「価値の芽」にも出会える 本気でリサーチすると、 「これ、いけるじゃん」「思ったより価値あるな」というケースもあります。 ④ 結局は“やるべきこと”だけが残る 取り下げられなかったものは、本当にやる価値があるものだったと後からわかる。 📝 まとめ:本気で向き合う姿勢が、結果的に“ノーを機能させる”あるいは“イエスのコミットメントをあげる” 私はこの方法で、多くの場面を乗り切ってきました。 そして副産物として、依頼者からの信頼が下がることはありませんでした。 どんな提案でも本気で向き合う。そして事実で会話する。 この姿勢が、もっとも健全で、もっとも誠実な「ノーの伝え方」になると感じています。 ぜひ皆さんも、機会があれば試してみてください。 また、記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
この記事は ラクス Advent Calendar 2025 12日目の記事です。 はじめに こんにちは。ラクス フロントエンド開発課のたぐちです。 今回は、私たちフロントエンド開発課で取り組んでいる 学習文化の醸成に関する取り組み をご紹介します。 はじめに 取り組み内容 情報共有会の良いところ 1. 学習のハードルが下がる 2. メンバーの価値観や思考を知る機会が増える 3. 気になる技術をその場で試せる 今後の改善ポイント 1. ファシリテーターが固定化している問題 2. 記事を投稿するメンバーが偏りがち おわりに 取り組み内容 私たちのチームでは Notion を活用し、メンバーが日頃気になった記事や情報を随時ストックしています。 その中から、他のメンバーも気になった記事に「マーク」が付いたものを選び、 週次の情報共有会(1時間) で内容を確認したり議論したりしています。 記事共有の様子 以前は隔週開催としていましたが、最近は AI をはじめ技術の変化が非常に速いため、 「隔週ではキャッチアップが追いつかない」という声を受け、 毎週開催 へと変更しました。 また、Notion に追加された記事は Notion と Gemini の API を活用し、チャットツールへ要約を通知しています。 記事要約の例 情報共有会の良いところ 1. 学習のハードルが下がる 「エンジニアは日々の勉強が大事」というのは多くの人が感じていることだと思いますが、常に最新情報を一人で追い続けるのは、慣れていないと負担が大きいものです。 特にフロントエンドは移り変わりが激しいと言われていますが、AI はそれをさらに上回る速度で変化しています。追従しようとすると目が回りそうになります(私だけでしょうか…?)。 そこで、チーム全体で学習を共有する場をつくることで、 個々がキャッチアップし続ける負荷を抑えながら学習を進められる ようになっています。 2. メンバーの価値観や思考を知る機会が増える 業務中にも相談や議論はありますが、 開発とビジネスの距離をどう縮めるか 不要な機能の削減(いわゆる「機能の引き算」)をどう進めるか プロダクトの本質的な価値をどう捉えるか といった根源的なテーマを語り合う機会は意外と多くありません。 情報共有会ではジャンルを限定していないため、記事によってはこうしたテーマに踏み込んだ 普段は出てこない深い議論 が生まれることもあります。メンバーの考えを知る貴重な場になっています。 3. 気になる技術をその場で試せる 記事の確認が早く終わった場合は、紹介された内容を実際に触ってみる時間にしています。 たとえば、 新しい MCP Claude の Skills 機能 Playwright Test Agents など、多岐にわたります。 記事を読むだけでなく、その場で手を動かすことで理解が深まり、議論もより活発になる と感じています。 今後の改善ポイント 1. ファシリテーターが固定化している問題 現在は言い出しっぺである私がずっとファシリテーターを担当していますが、 私が休むと運用が止まってしまう メンバーがファシリテーションを練習する良い機会になる という理由から、 今後はローテーション制にしたい と考えています。 2. 記事を投稿するメンバーが偏りがち フロントエンドや AI 関連など、全員が興味を持ちやすい領域では記事が被りやすく、結果として毎週ほぼ同じメンバーが投稿してしまいがちです。 解決策としては、たとえば その週に投稿していない人の意見をファシリテーターが優先して聞く 投稿されていないジャンルを意識的に拾う といった工夫が考えられ、より多様で活発な議論につながるのではないかと思っています。 おわりに 以上、私たちの学習文化の醸成に関する取り組みをご紹介しました。 「受験は団体戦」とよく言われますが、 エンジニアリングの学習もチームで取り組むことでハードルが下がり、継続しやすくなる と実感しています。 もし技術の移り変わりの速さにキャッチアップが追いつかないと感じている場合は、ぜひチーム内で試してみてください。
アバター
はじめに 請求書の明細表をOCRによって自動で読み取ることができると、経理業務自動化の実現に役立ちます。 ところが実際には、多様なフォーマットの存在や OCR の誤読が積み重なり、AIモデルとルールベース後処理だけでは思った以上に精度が出ない、という壁にぶつかることがあります。 AI モデルそのものの改修となると、学習データの追加やモデル更新など、時間もコストも必要になります。また、ルールベース後処理を増やし続けるのは、将来の保守を重くする心配がつきまといます。 そこで、「大規模言語モデル(LLM)を後処理に加えたら、より柔軟に・より構造的に・まるで人間が行うように誤りを補正できるのではないか」という発想のもと、既存処理に GPT による補正ステップを追加し、精度改善ができるか検証しました。 はじめに LLM による後処理の追加 追加した処理の流れ GPT に与えたプロンプト 検証結果 まとめ LLM による後処理の追加 今回の検証では、ルールベース処理の“あと”に GPT による補正ステップを入れています。 使うのは画像入力に対応した gpt-4.1。表画像と OCR 結果(CSV)を突き合わせ、表構造と内容の整合性を取る役目を担います。 追加した処理の流れ 具体的にGPT が担うのは次のような作業です。 明細表の画像と既存処理の OCR 結果(CSV)を受け取る 画像と CSV を突き合わせ、 行・列のズレ修正 分割/結合ミスの調整 欠損・誤認文字の補正 修正済み CSV を返却 座標情報の扱いは精度を安定させるのが難しく、今回は見送りましたが、将来的には実装したい部分です。 GPT に与えたプロンプト 今回の補正処理は、次のような指示文(抜粋)を与えて実施しました。 表構造の統一、セル内容の整理、誤認識文字の補正など、発生しやすいゆがみを吸収するよう工夫しています。 refine_table_prompt = """表のOCR結果が次のようにCSV形式のテキストで与えられています。 {} 同時にOCR対象となった表の画像も与えられています。 表の画像を参照しながら、CSV形式のテキストを修正して下さい。 その際、以下の条件に従って修正を行って下さい。 条件: 1. 画像内の表の構造に基づいてCSV形式の行や列の構造を修正して下さい。 2. すべての行で同じ列数になるように修正して下さい。 3. 「||」は改行箇所を示しています。セル内の改行は「||」で表現してください。 4. 誤って複数行が1つのセルに入っている場合は、適切に分割してください。 5. ~ """ 検証結果 検証データ 165 件 を使い、 既存処理のみ GPT 補正を追加した処理 の比較を行いました。 結果として、 38 件で明確な精度向上が確認されました。 特に目視では、 列境界の誤りが正しく補正される 1 件の明細データが複数行に分離される誤りの統合 誤った行分割の修復 など、「人が見たらこう直したい」という形にかなり近づく改善が見られました。 一方でデメリットもあり、 コスト:検証データでは 1 リクエストあたり0.4~0.6 円(明細表の内容による) 処理時間:1〜5 秒 程度の追加(これも明細表の内容に依存) 後処理ルールをメンテナンスし続けるよりは負担が軽い?ものの、低コストの明細表読み取り実現やリアルタイム処理には少し工夫が必要かと思われます。 まとめ LLM を既存の OCR パイプラインに組み合わせることで、“ルールで吸収しにくい構造的なズレ” を補正でき、一定の精度向上が確認できました。 特に、 多様な書式の請求書を扱う ルールのメンテが重くなっている といった課題に対して、相性の良い手法かと思います。 一方で、「コストと処理時間の増加」という課題もあるため、実運用にはもうひと工夫が要りそうです。 今後は、座標情報を扱えるように処理を改修したり、より高性能なモデル(gpt-5.1のような推論モデル)を利用したりすることで、明細表読み取り処理をより優れたものにしていければと考えています。
アバター
※この記事は ラクスアドベントカレンダー 2日目の記事です はじめに こんにちは! エンジニア3年目のTKDSです! 今回はEKSでOtel Collectorを用いたContainer Insightsメトリクスの取得についてご紹介します。 ぜひ最後まで見ていってください! はじめに Container Insights Otel Collectorによるメトリクスの収集 Otel Collectorのマニフェスト例 まとめ Container Insights Container Insightsを使うとEKSクラスタからnodeやPodなどからCPU、メモリ、ディスク、ネットワークなどさまざまなメトリクスが取得可能です。 ※詳しくは AWSのドキュメント をご覧ください。 ※収集されるメトリクスについて知りたい方は こちらのドキュメント を見てください。 Otel Collectorによるメトリクスの収集 色々なメトリクスが取れるContainer Insightメトリクスですが、実はOtel Collectorでも取得することが可能です。 さらに、Otel Colectornにあるprocesserを使うことができるので、不要なメトリクスを落としてCloudWatchに送信することが可能です。 取得する流れは以下の図のようになってます。 まずAWS Container Insights Receiver(awscontainerinsightreceiver)がcollection_intervalに従って、定期的にメトリクスを取得します。 そのメトリクスに対して、processorでfilterを使い不要なメトリクスを落とします。ここで不要なメトリクスを落としておくことでCloudWatchの課金を抑えられます。 最後にexportersでawsemfを使って、 埋め込みメトリクスフォーマット というCloudWatch logsにメトリクスを取得するように指示する形式に変換して送信します。 Otel Collectorのマニフェスト例 次に実際に使ってるマニフェストの一部を下記に示します。 このマニフェストでは、 nodeとpodのCPUとメモリを取るようにしてます。 同じことをしたい人はぜひ参考にしてください。 apiVersion : v1 data : collector.yaml : | receivers : awscontainerinsightreceiver : collection_interval : 300s exporters : awsemf : dimension_rollup_option : NoDimensionRollup log_group_name : /aws/containerinsights/{ClusterName}/performance log_stream_name : performance/{NodeName} metric_declarations : - dimensions : - - Namespace - ClusterName metric_name_selectors : - pod_cpu_utilization - pod_memory_utilization - dimensions : - - NodeName - ClusterName metric_name_selectors : - node_cpu_utilization - node_memory_utilization namespace : ContainerInsights parse_json_encoded_attr_values : - Sources - kubernetes resource_to_telemetry_conversion : enabled : true debug : {} processors : batch : {} filter/drop_unused : metrics : metric : - name != "pod_cpu_utilization" and name != "pod_memory_utilization" and name != "node_cpu_utilization" and name != "node_memory_utilization" service : telemetry : logs : encoding : json level : error metrics : readers : - pull : exporter : prometheus : host : 0.0.0.0 port : 8888 pipelines : metrics : exporters : - awsemf processors : - filter/drop_unused - batch receivers : - awscontainerinsightreceiver CloudWatchでの保存先は以下の部分で指定しています。 自動作成されるので、事前の作成は不要です。 {ClusterName}と{NodeName}は自動で埋めてくれます。 ただ、"{NodeName}"のように書くとエラーで自動で埋めるのが機能しなかったため、適当に performance/ とつけました。 最後のnamespaceで指定した名前で、CloudWatchMetricsのnamespaceが作成されます。 log_group_name: /aws/containerinsights/{ClusterName}/performance log_stream_name: performance/{NodeName} namespace: ContainerInsights この設定で動かすと、CloudWatchに保存されます。 レコードは以下のような形で記録されます。(見せられない部分は伏せてます) { "AutoScalingGroupName": "xxxx", "ClusterName": "xxxx", "InstanceId": "xxxx", "InstanceType": "xxxx", "NodeName": "xxxx", "Sources": [ "cadvisor", "/proc", "pod", "calculated" ], "Type": "Node", "Version": "1", "_aws": { "CloudWatchMetrics": [ { "Namespace": "ContainerInsights", "Dimensions": [ [ "ClusterName", "NodeName" ] ], "Metrics": [ { "Name": "node_memory_utilization", "Unit": "Percent", "StorageResolution": 60 } ] } ], "Timestamp": 1764157664000 }, "kubernetes": { "host": "xxxx" }, "node_memory_utilization": 31.46575974804524 } 絞り込んだメトリクスだけ取れてるのがわかります。 この形式でロググループに入るとCloudWatch logsが自動でメトリクスに転送してくれます。 CloudWatchのページに行きContainerInsights→指定したDimension→メトリクスを選んで表示してみるとメトリクスを観察することができます。 ちなみにメトリクスとして取り込んだあとはemf形式のログは消しても問題ありません。 まとめ ここまで読んでいただきありがとうございました! 今回はEKSにおけるContainer InsightsメトリクスのOtel Collectorでの収集についてご紹介しました! filterでの柔軟なメトリクス削減は需要が高いのではないでしょうか? ぜひ興味ある方はお試しください!
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 2025年4月、ラクスではプロダクト部が新たに組成され、デザイナーとプロダクトマネージャーが同じ組織に加わりました。 そして2025年10月から、私はこの組織のマネジメントを担うことになりました。 実は7〜8年前にも、兼務という形でデザイナーのファーストラインマネージャーを経験しており、採用も含めて直接マネジメントをしていた時期があります。 その経験があるため、今回デザイン組織が含まれることに対する不安はありません。むしろ プロダクトマネージャーとデザイナーが同じ組織でコラボレーションできることにワクワクしている というのが今の正直な気持ちです。 この記事では、これまでの自分とデザイン/デザイナーとの関わり、そして今回のマネジメントに向き合うにあたって考えていることをまとめました。 ラクスでデザイナーとして働く未来を考えてくれる方に、少しでも私の考えが届けば嬉しいです。 目次 「デザイン」との出会い SI企業時代:管理画面の“画面設計”から始まった はじめて「デザイン」を意識した案件 デザイナー理解が一気に深まった転職先での経験 ●フラッシュセールサイト:国内にないコンセプトを形に ●ECプラットフォーム:UXとクリエイティブの両軸 デザイナーが入るだけで“世界が変わる”という実感 デザイナー採用への深い関与 エンジニア出身の自分がデザインを理解するためにやった2つのこと 1. デザインに関する本を“読みまくった” 2. デザイナーと“とにかく対話しまくった” 再びデザイン組織のマネジメントへ ― 私が今思っていること ― 「デザイン」の認識を変えたい 最後に:ラクスのデザイナー組織に興味を持ってくれた方へ 「デザイン」との出会い SI企業時代:管理画面の“画面設計”から始まった 新卒で入社したのはSI企業でした。案件の多くはWeb開発で、いわゆる管理画面のようなUIが中心。UI/UXの概念はほとんどなく、 『入力 → 確認 → 完了 』のような単純なフローが基本。 CSSやJavaScriptもほぼ使わず、デフォルトHTMLボタンさえ「その方がいい」と言われるような時代でした。 はじめて「デザイン」を意識した案件 転機となったのは、toC向け求人サイトのプロジェクト。大手メガベンチャーの案件で、お客様先に常駐し、自社チームで要件定義からリリースまで担当しました。 デザインはお客様側のデザイナーに依頼する形だったため、自分はHTMLベースの画面を紙芝居のように作成。それを見たデザイナーから絶賛されたのは今でも印象に残っています。 広告枠を削り、とにかくユーザーの機能に集中した“シンプルさ”を評価してくれた。 この時、 デザイナーは広告やノイズの少ない“純粋な体験”を好む傾向があるのだな と初めて理解しました。 デザイナー理解が一気に深まった転職先での経験 その後、toC系・ゲーム系の企業へ転職。ここでデザイナーとの連携は圧倒的に密になりました。私が関わったプロダクトは以下: ブログサービス フラッシュセールサイト ECプラットフォーム(ネイティブアプリあり) ここでは“当時も優秀だと思っていたが、今振り返ると本当に優秀だった”と思えるデザイナーたちと仕事をする幸運に恵まれました。 ●ブログサービス:感性と論理の狭間で学んだ スマホ・PC対応を進める中で、代表が細部までデザインにこだわるタイプだったため、私もデザイナーと代表の間に立ち大量のやりとりを経験しました。代表の感性的な指摘に対して、デザイナーはその意図を聞き出し、言葉にならないイメージを視覚に落とし込んでいく。 ヒアリング力・翻訳力・表現力のすべてを見せてくれました。 ●フラッシュセールサイト:国内にないコンセプトを形に 海外サイトを参考に、国内にはない体験をデザインするプロジェクト。 完成した UI は自分から見ても「これは素晴らしい」と感じられるものでした。 ●ECプラットフォーム:UXとクリエイティブの両軸 デザイナーは バナー・LPなどのクリエイティブデザイン EC購入導線のUI/UXデザインの双方を担当。 マーケやセールスの要望を視覚化する一方で、 「なぜこのデザインなのか?」 を 言語化し、理論で説明する姿 を見て、行動心理学、デザインパターン、言語化の重要性を強く理解しました。 デザイナーが入るだけで“世界が変わる”という実感 ブランド向け管理画面のリニューアルでは、機能は大きく変えていないのに、デザイナーが入ったことでブランド担当者の印象が一気に改善したことがあります。また、 ネイティブアプリ 国内でも未導入のオンライン決済の導入の体験 写真からサイズを測る機能 など、UX領域で共に多くの挑戦をしました。 デザイナー採用への深い関与 新卒・中途・マネージャー採用にも関わりました。 50名以上のデザイナーと話した 経験は、今考えても大きな財産です。 エンジニア出身の自分がデザインを理解するためにやった2つのこと 結局、自分がデザインやデザイナーを理解するためにやったことはシンプルです。 1. デザインに関する本を“読みまくった” エンジニアのための理論でわかるデザイン入門 デザイン思考が世界を変える〔アップデート版〕: イノベーションを導く新しい考え方 デザインのデザイン デザインはストーリーテリング 「体験」を生み出すためのデザインの道具箱 誰のためのデザイン? 増補・改訂版 ―認知科学者のデザイン原論 ジョナサン・アイブ 偉大な製品を生み出すアップルの天才デザイナ ピクサー流 創造するちから 小さな可能性から、大きな価値を生み出す方法 特に印象に残っている書籍は 「ピクサー流 創造するちから」はいまでも読み返す愛読書です。 プロダクト部のキックオフでも、この本に出てくる “ブレーン・トラスト” を組織に導入したいと話しました。 2. デザイナーと“とにかく対話しまくった” デザイナーが参加するMTGに同席 なぜその質問をしたのか、なぜその表現にしたのかをひたすら聞く ワークサンプルテストに参加して理解を深める 採用面談でキャリア観や課題感を聞き続ける 「多分、かなり鬱陶しかっただろうな」と思うほど聞きました。 でもこの対話の量が、デザイナー思考を理解することにつながりました。 再びデザイン組織のマネジメントへ ― 私が今思っていること ― 2025年、ラクスのデザイナー組織は再始動しました。 若く優秀なデザイナーが多く、プロダクトの成長に日々向き合っています。その中で、私がまず変えていきたいと思っていることがあります。 「デザイン」の認識を変えたい 多くの人にとって「デザイン」は 見た目を整える UI/UXの領域 といった“狭義”で認識されがちです。 しかし、本来デザインとは 「問題を解決するための設計・計画全般」 であると思っています。 ※本資料はプロダクト部の発足の時に「社内ラジオ」(毎月技術広報が取り組んでる開発組織内での定期イベント)でも紹介ししたスライドの抜粋 ※もともとのスライドを「Nano-Banana」で綺麗にしてもらった 私は職能をこう捉えています: デザイナー:人の体験をより良くするために課題を見つけ、形にする専門家 エンジニア:課題を仕組みで解決し、それを動かし続ける専門家 そして今はこうも思っています。 『デザイナー』『エンジニア』全員がプロダクトマネジメントのマインドを持てるなら、 プロダクトマネージャーは“1人いればいい”のかもしれない。 最後に:ラクスのデザイナー組織に興味を持ってくれた方へ ラクスのデザイナー組織は2025年に再始動しました。 これから人数も増え、 リードデザイナー デザイナーマネージャー といった役割も必要になっていきます。 もし少しでも興味があれば、ぜひ応募をご検討ください。 プロダクトの成長に本気で向き合い、『顧客志向』で一緒に良いものをつくれる仲間を心からお待ちしています。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
この記事は ラクス Advent Calendar 2025 1日目の記事です。 はじめに こんにちは! エンジニア3年目のTKDSです! 今回はCodex CLI SDKの入門記事を書きました! Codex CLI SDKはChatGPTの有料プランに契約していれば、特に追加費用不要で使用可能です。 そのため、さくっとMy AIエージェントを作るのには非常におすすめです。 では早速本題に入っていきたいと思います。 はじめに Codex CLIの概要 Codex CLI SDKについて 環境情報 基本的な操作 お天気エージェントを題材にした解説 シンプルなエージェント 最終版 まとめ Codex CLIの概要 Codex CLI is a coding agent from OpenAI that runs locally on your computer. 翻訳:OpenAIが提供するローカルコンピュータ上で動作するコーディングエージェント. Codex CLIはOpenAIの提供するモデルが使えるコーディングエージェントです。 Codex CLI SDKについて codex exec相当の処理をTypeScript(JavaScript)コード上から実行できるSDKです。 プログラムから使えるので、処理の流れを書きやすくなったり、TS(JS)ライブラリの使用・連携ができます。 今回このSDKを使ってエージェントを作ってみます。 環境情報 nodeは24.11.1を使いました。 $ node --version v24.11.1 codex、codex-sdkとmodelcontextprotocol/sdkを使いました。 細かいところは リポジトリ みてください。 mise install node@24.11.1 mise use -g node@24.11.1 npm i -g @openai/codex npm install @openai/codex-sdk npm install @modelcontextprotocol/sdk codexの設定は以下のようにしておきます。 今回用に余計なMCP設定は消してます。 [ sandbox_workspace_write ] network_access = true # 機能フラグ [ features ] web_search_request = true 事前に codex login しておいてください。 基本的な操作 // QuickStartにあるコード参考にしたコード import { Codex } from "@openai/codex-sdk" ; const codex = new Codex(); const thread = codex.startThread(); const turn = await thread.run( "東京の今日の天気は?" ); console . log (turn.finalResponse); console . log (turn.items); codexの操作関連について軽く解説します。 Codexとのやり取りをするオブジェクトを作成します new Codex() 1セッションを開始するのに相当する操作です codex.startThread() メッセージの送信操作です、これを繰り返すと同じセッションにメッセージを送信し続けられます thread.run() 逐次的にレスポンスを受け取りたい場合はこちらを使います。 codex.runStreamed() runStreamedを使う場合、 サンプルコード にあるようにawaitを使って非同期処理にすると良さそうです。 const { events } = await thread.runStreamed( "Diagnose the test failure and propose a fix" ); for await ( const event of events) { switch (event. type ) { case "item.completed" : console .log( "item" , event. item ); break ; case "turn.completed" : console .log( "usage" , event.usage); break ; } } お天気エージェントを題材にした解説 基本的な操作は説明したので、次にお天気エージェントを題材に実際にCodex CLI SDKを使ってみます。 追加のOpenAI API Keyの設定などは不要です。 codex login だけできてるか確認しておいてください。 シンプルなエージェント まずは単純にcodex cliをSDK経由で呼び出すエージェントを作ってみましょう。 シンプルにスレッドで天気を聞くだけです。 import { Codex } from "@openai/codex-sdk" ; const codex = new Codex(); const thread = codex.startThread(); const turn = await thread.run( "今日の東京の天気は?" ); console . log (turn.finalResponse) 何回か呼んでみると回答はぶれるものの大体ほしい回答を返してくれます。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts **東京の天気 (2025年11月23日)** - 現在: 快晴で約11 °C。冷え込む朝なので薄手のコートやニットが安心。 - 朝~昼: 07時頃10 °C→正午前後で17 °C近くまで上昇。日中は日差しがあり比較的穏やか。 - 夜: 深夜に向けて再び10 °C前後まで下がる見込み。帰宅が遅くなるなら上着を忘れずに。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts **東京の天気** - 現地時間2025年11月23日(日)の東京は晴れて気温11 °C前後。日中は次第に雲が増え、最高15 °C、最低9 °Cの予想です。 - 11月24日(月)は最高18 °C/最低9 °Cで晴れ間が広がり過ごしやすい見込み。 - 11月25日(火)は雲が多めで最高15 °C/最低9 °C、26日(水)は午後に風が強まるものの晴れて18 °Cまで上がる予報です。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts 2025年11月23日現在の東京は快晴で、気温はおよそ11°C(51°F)です。 未明から早朝にかけては晴天が続き、気温は9~11°C程度まで下がる見込みです。 午前になると晴れをキープしつつ徐々に気温が上がり、正午前後には16~17°C付近まで昇る予報です。 ただ回答形式がブレブレなので、適当にシステムプロンプト作って添付してみましょう。プロンプトはChatGPTでさくっと作ったのをベースに結構いじりました。 コツはoutput_templateを最後に持ってくることです。 user_inputが最後になると出力形式がどうしても安定せず思い通りの出力が得られないことが多いです(体感7割失敗)。 import { Codex } from "@openai/codex-sdk" ; const codex = new Codex(); const thread = codex.startThread(); const system_prompt = ` <system-prompt> # システムプロンプト あなたは天気情報専用アシスタントです。 - 常に「いつ・どこ・どんな天気か」を明示して、簡潔な日本語で答えます。 - 最新かつ信頼できる情報に基づき、不確実な点はその旨を明示します。 - 危険な気象では、公式機関の情報確認と安全への注意を必ず促します。 - 天気と無関係な質問には、天気専門であることを伝えて簡潔に断ります。 # 調査内容 1. 詳細 * 降水: 降水確率 {{降水確率}}%、予想降水量 {{降水量}}mm(あれば) * 風: 風向 {{風向}}、風速 {{風速}}m/s(強風時はその旨) * 湿度・体感: 湿度 {{湿度}}% 程度、{{蒸し暑く感じる/乾燥しやすい/体感は気温どおり}} 2. 行動の目安 * 服装: {{長袖/半袖/上着の有無などの簡単な目安}} * 持ち物: {{折りたたみ傘/日傘/帽子/飲み物/防寒具 など}} 3. 注意事項 * {{大雨/雷/強風/猛暑/大雪 など}} のおそれ: {{あり/なし}} * 必要に応じて: 「最新の警報・注意報は気象庁など公式情報を確認してください」 * 時刻はJSTを基準に調査 4. 補足(任意) * 洗濯・外出・運転などの簡単な一言アドバイス: {{例: 洗濯物は外干しで問題なさそうです など}} </system-prompt> ` const output_template = ` ユーザーへの回答には参考リンクなどは不要です。 # 出力テンプレート <output_template> * 日時: {{日付・時間帯}} * 地域: {{地域名}} * 天気: {{天気の要約}}(例: 晴れ時々くもり、にわか雨の可能性あり) * 最高/最低気温: {{最高気温}}℃ / {{最低気温}}℃ * 服装等のアドバイス: {{行動のアドバイス}} </output_template> <output_template_error> 天気専門なので答えられません </output_template_error> # 作業手順 1. <user_input>が天気についてか判断し、違った場合<output_template_error>を出力して終了。天気についての場合2へ遷移 2. お天気エージェントとして動作。作業が完了したら3へ遷移 3. output_templateに従って回答 promptにしたがって、作業してください。 ` // const user_input = "今日の東京の天気を教えて" const user_input = "一発芸して" const prompt = [ system_prompt, "<user-input>" ,user_input, "</user-input>" ,output_template ] const turn = await thread.run( prompt . join ()); console . log (turn.finalResponse) 何回か試してみます。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts * 日時: 2025-11-23(日) 終日(JST) * 地域: 東京都心 * 天気: 日中は晴れて夕方以降ゆっくり雲が広がる見込み。降水確率や降水量の公式値は未取得ですが現状は雨要素なしと見られます。風は弱い北寄りと推定、湿度も大きく変化せず体感は気温どおり * 最高/最低気温: 15℃ / 9℃ * 服装等のアドバイス: 長袖+薄手の上着で快適、朝晩は冷え込むため軽いコートがあると安心。日中は乾きやすいので飲み物でこまめに保湿。大きな荒天リスクはありませんが最新の警報・注意報は気象庁などでご確認ください $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts * 日時: 2025年11月23日(日) 日中~夜(JST) * 地域: 東京都心 * 天気: 午前は穏やかな晴れ、午後は雲が増える見通しで大きな崩れは今のところ見込まれていません;降水確率・降水量、風向・風速、湿度の詳細は今回取得できなかったため、必要に応じて最新の公式情報を必ずご確認ください。 * 最高/最低気温: 15℃ / 9℃(日中も空気はひんやりで体感は気温どおり) * 服装等のアドバイス: 朝晩は冷えるので薄手ニット+コートなど長袖の重ね着が安心、日中も羽織り物を携帯すると快適;雨具は不要そうですが念のため折りたたみ傘の最新情報をチェックしてください。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts * 日時: 2025-11-23(日) 日中(JST) * 地域: 東京都心 * 天気: 午前は快晴、午後は次第に薄雲が広がる見込み。降水: 降水確率データは取得できず、現状では雨の兆候なし・降水量情報なし。風: 風向風速データ未取得(体感的には穏やかな北寄りの風程度)。湿度・体感: データ不足だが朝はひんやり、日中は気温どおりのさわやかな体感になりそう。大きな警報級の現象は見込まれていませんが、念のため最新の注意報は気象庁公式で確認してください。 * 最高/最低気温: 15℃ / 9℃ * 服装等のアドバイス: 朝晩は冷えるので長袖+薄手アウター、日中はカットソー+カーディガン程度が快適。乾燥が進みやすいのでリップやハンドクリームと飲み物を持つと安心、日中の強い日差し対策に帽子もあると良いでしょう。 回答形式も安定し、良くなりました。 コメントアウトしてる部分を外して実行してみましょう // const user_input = "今日の東京の天気を教えて" const user_input = "一発芸して" $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts 天気専門なので答えられません $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts 天気専門なので答えられません しっかり断りを伝えられました。 ただ、実は問題点が残っており、記事執筆時、11/24(月)1:20なのですが、11/23(日)なってしまってます。これを修正してみましょう。 日付を作成して追加します。ついでに次の日の情報を聞くようにしましょう。 const ONE_DAY_MS = 24 * 60 * 60 * 1000; // 現在時刻から 1 日後の日時 const tomorrow = new Date(Date.now() + ONE_DAY_MS); // JST として日付部分だけを文字列にする const time_pormpt = tomorrow.toLocaleDateString('ja-JP', { timeZone: 'Asia/Tokyo', year: 'numeric', month: '2-digit', day: '2-digit', }); const prompt = [system_prompt, "<user-input>",time_pormpt,user_input,"</user-input>",output_template] 結果は以下のようになりました。 概ね思い通りの出力ができてます。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts * 日時: 2025年11月25日(終日・JST) * 地域: 東京都心 * 天気: 明け方は9℃前後で晴れ、午前から午後はおおむねくもり、夕方以降は再び晴れ間が広がる見込み。提供データに降水確率・風向風速・湿度は含まれていませんが、雨要素は示されていません。 * 最高/最低気温: 15℃ / 9℃ * 服装等のアドバイス: 朝晩は冷えるので長袖+薄手アウターが安心、日中は薄手ニット程度で快適。折りたたみ傘は不要そうですが、公式の最新情報も念のためご確認ください。 $ npm run dev > codex-cli-sdk@1.0.0 dev > tsx src/index.ts * 日時: 2025/11/25(火) 0:00-24:00 JST * 地域: 東京都心 * 天気: 夜明けは晴れ、日中はくもり優勢で雨シグナルなし(降水確率データ未取得・不確実)/風は北寄りの弱風が中心で体感は気温どおり/湿度データ未取得 * 最高/最低気温: 15℃ / 9℃ * 服装等のアドバイス: 朝晩は薄手のコートやカーディガンがあると安心、日中は長袖シャツで十分/乾燥しやすいのでリップやハンドクリームと飲み物を 降水量の具体値や湿度は取得できませんでしたが、現状は大きな雨雲の兆候は見当たりません。強い気象リスクの情報はありませんが、最新の警報・注意報は気象庁など公式情報でご確認ください。 最終版 import { Codex } from "@openai/codex-sdk" ; const codex = new Codex(); const thread = codex.startThread(); const system_prompt = ` <system-prompt> # システムプロンプト あなたは天気情報専用アシスタントです。 - 常に「いつ・どこ・どんな天気か」を明示して、簡潔な日本語で答えます。 - 最新かつ信頼できる情報に基づき、不確実な点はその旨を明示します。 - 危険な気象では、公式機関の情報確認と安全への注意を必ず促します。 - 天気と無関係な質問には、天気専門であることを伝えて簡潔に断ります。 # 調査内容 1. 詳細 * 降水: 降水確率 {{降水確率}}%、予想降水量 {{降水量}}mm(あれば) * 風: 風向 {{風向}}、風速 {{風速}}m/s(強風時はその旨) * 湿度・体感: 湿度 {{湿度}}% 程度、{{蒸し暑く感じる/乾燥しやすい/体感は気温どおり}} 2. 行動の目安 * 服装: {{長袖/半袖/上着の有無などの簡単な目安}} * 持ち物: {{折りたたみ傘/日傘/帽子/飲み物/防寒具 など}} 3. 注意事項 * {{大雨/雷/強風/猛暑/大雪 など}} のおそれ: {{あり/なし}} * 必要に応じて: 「最新の警報・注意報は気象庁など公式情報を確認してください」 * 時刻はJSTを基準に調査 4. 補足(任意) * 洗濯・外出・運転などの簡単な一言アドバイス: {{例: 洗濯物は外干しで問題なさそうです など}} </system-prompt> ` const output_template = ` ユーザーへの回答には参考リンクなどは不要です。 # 出力テンプレート <output_template> * 日時: {{日付・時間帯}} * 地域: {{地域名}} * 天気: {{天気の要約}}(例: 晴れ時々くもり、にわか雨の可能性あり) * 最高/最低気温: {{最高気温}}度 / {{最低気温}}度 * 服装等のアドバイス: {{行動のアドバイス}} </output_template> <output_template_error> 天気専門なので答えられません </output_template_error> # 作業手順 1. <user_input>が天気についてか判断し、違った場合<output_template_error>を出力して終了。天気についての場合2へ遷移 2. お天気エージェントとして動作。作業が完了したら3へ遷移 3. output_templateに従って回答 promptにしたがって、作業してください。 ` const user_input = "東京の天気を教えて" // const user_input = "一発芸して" // 1日分のミリ秒 const ONE_DAY_MS = 24 * 60 * 60 * 1000 ; // 現在時刻から 1 日後の日時 const tomorrow = new Date ( Date . now () + ONE_DAY_MS); // JST として日付部分だけを文字列にする const time_pormpt = tomorrow. toLocaleDateString ( 'ja-JP' , { timeZone : 'Asia/Tokyo' , year : 'numeric' , month : '2-digit' , day : '2-digit' , } ); const prompt = [ system_prompt, "<user-input>" ,time_pormpt,user_input, "</user-input>" ,output_template ] const turn = await thread.run( prompt . join ()); console . log (turn.finalResponse) まとめ ここまで読んでいただきありがとうございました! 今回はお天気エージェントを題材にCodex CLI SDKを使ってみました。 今回はmcpや構造化出力を使わなかったので、いずれ使うものを作ってみたいと考えてます。 今回のお天気エージェントも上記を使ってないので、改良の余地はまだあります。 Codex CLI SDKは codex login しておけば、追加でAPI利用料を支払わずに使えるのが大きな魅力です。 みなさんもぜひMyエージェントを作ってみてください!
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 先日、こんなイベントでモデレーターをしました。このイベントではプロダクトエンジニアをテーマに対談をしました。この『プロダクトエンジニア』という言葉はここ5年くらいででてきた名称です。 rakus.connpass.com プロダクト開発の現場では、 エンジニア・PdM・デザイナーという大きな分類だけでなく、 その内側でさらに細かな役割が生まれています。 グロースPdM / テクニカルPdM / AI PdM プロダクトデザイナー / UXデザイナー / サービスデザイナー フロントエンド / バックエンド / SRE / Platform / MLエンジニア こうした細分化は「仕事の複雑さに対応する進化」であり、 同時に「働く側の専門性の見える化」でもあります。しかし一方で、 細分化すればするほど“本当に良いことなのか?” という疑問も生まれます。 本記事では、これまで自分の経験やラクスの開発組織を例にしながら、以下のテーマを深掘りします。 職種内細分化のメリット・弊害 求職者と企業側、それぞれにとっての価値 「越境しなさい」という言葉への違和感の正体 ラクスは専門性と越境をどうバランスさせているのか 目次 ■なぜ職種は細分化されるのか?──良い面と本質的な価値 ■しかし細分化には“弊害”もある ① プロダクトごとの縦割りが強まる ② “越境しないと回らない”状況を生んでしまう ③ キャリアの固定化が起こりやすい ■では、ラクスはどちらのスタンスなのか? ラクスで活躍する人の共通点 ■まとめ:ラクスは「役割を尊重しつつ、越境も価値」と考える組織 最後に ■なぜ職種は細分化されるのか?──良い面と本質的な価値 専門性が深まり、プロダクト品質が上がる プロダクトは年々複雑化し、AI・データ基盤・クラウド・UX のように専門性が高度化しました。 1人がすべてを深く理解するのは不可能です。そのため、 AI PdM グロースPdM プラットフォームエンジニア UXリサーチャー など、 “深い専門領域”を担当するロールが自然発生 しています。 これは市場側から見ても、 専門性を言語化し、キャリア価値を高める良い流れ といえます。 自分の強みと合うポジションを見つけやすくなる 求職者にとっては、 「探索フェーズが得意」 「UI設計が強い」 「AI企画に興味がある」 といった“強み”と職種をマッチさせやすくなります。結果としてミスマッチが減り、 入社後の立ち上がりも速くなります。 組織側も「必要な人材」を明確にできる 採用・育成・配置の精度が上がるため、 中〜大規模の開発組織では特に細分化の恩恵は大きいです。 ラクスのように複数プロダクトが並行して走る企業では、    プロダクト固有の課題にフィットする役割を採用できる のは大きなメリットです。 ■しかし細分化には“弊害”もある 細分化は良いことばかりではありません。ラクスの文脈を踏まえると、特に以下の弊害が起こりやすいです。 ① プロダクトごとの縦割りが強まる 役割が細かくなるほど、「ここから先は担当外」という意識が生まれやすい。ラクスのプロダクトは独立性が高いため、役割の細分化がそのまま“壁”になるリスク があります。 ② “越境しないと回らない”状況を生んでしまう 人数が不足していたり、 立ち上げフェーズでロールが定まっていない場合、 PdMがUXもやる デザイナーが要件定義も見る エンジニアが仕様詰めも担当する という状況が発生します。これは本来、 マネジメント側が役割を設計しきれていない 場合に起こりがち。 ③ キャリアの固定化が起こりやすい 役割が明確になると、 「あなたはグロースPdMだから探索は任せない」 「UIの人だからサービスデザインは向いてない」 と“移動の壁”が生まれることもあります。ラクスは社内異動が比較的柔軟な組織だからこそ、 ここは課題になりやすいポイント。 「越境しなさい」が生む違和感の正体 求職者・現場メンバーの中には、「越境しろ」という言葉にモヤモヤを感じる人も多いです。その違和感の正体は、 ✔ 役割が曖昧なまま越境を強要されると、責任がぼやけるから ✔ 専門性を深めたい人からすると、広く浅くを求められるのは苦痛だから ✔ 越境できる人が“便利屋化”しがちだから 本来は、“越境”は役割を全うしたうえで自然に生まれる副産物 であるべきです。 専門性を深めるキャリアも、越境して幅を広げるキャリアも、 どちらも価値があり、どちらも正しい。 ■では、ラクスはどちらのスタンスなのか? ラクスは結論として、 「役割定義は進める」+「越境も推奨する」 ただし実態はプロダクトフェーズごとにバラつきがあるという “ハイブリッド型” の組織です。 役割定義は進めている ラクスでは近年、 PdMの役割整理 UXの期待値の明文化 エンジニア領域の Platform / SRE / アプリ の区分 AI PdM・データPdMのような新規ロール検討 など、「役割の明確化」が確実に進んでいます。 ただし越境が必要な場面もまだ多い 特に以下の領域は越境が自然に発生します: 新規プロダクト・AI領域 小規模プロダクト 立ち上げ直後のフェーズ UXリサーチのリソース不足領域 そのため、 役割があっても境界は柔らかい のが現状。 結果 ラクスの最適解は「専門性×越境」の両立型 ラクスは、 深い専門性を持ちつつ、必要があれば他領域にも関われるバランス型 を理想としていると言えます。そして、ラクスの開発組織としては、『顧客志向』を旨としているため、これを共通言語、見解として動くようにしています。 ラクスで活躍する人の共通点 職種に関わらず、ラクスで活躍しやすいのは次のタイプです。 ✔ 自分の専門性を持ち、それを武器にしている ✔ 他職種の視点を理解し、壁を作らず連携できる ✔ 必要なときは越境する柔軟性がある ✔ 役割定義が曖昧なら言語化し、組織に提案できる ✔ 利用するお客様を向いている “なんでも屋”ではなく、 専門性を軸にしながら、必要に応じて染み出すタイプ がもっともパフォーマンスを出しやすい環境です。 ■まとめ:ラクスは「役割を尊重しつつ、越境も価値」と考える組織 ラクスは、 役割をきっちり定義する 専門性を評価する 越境も歓迎する プロダクトごとに柔軟に運用する という、“いいとこ取りのハイブリッド型”のスタンスです。PdM・デザイナー・エンジニアのいずれにとっても、 専門性を磨きつつ、必要な範囲では越境できる柔らかな環境 といえます。 以前にイベントで以下のような話をしましたので合わせて見てもらえればと思います。 speakerdeck.com 最後に 職種の細分化も、越境も、どちらも本質的には プロダクトの価値を最大化するための手段 です。 ラクスはその両方をうまく取り入れながら、多様なスキルセットを持つ人たちが活躍できる組織づくりを進めています。 “専門性×越境” のバランスに興味がある方にとって、 ラクスはとても相性の良い環境になるはずです。 本記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー / PdM の方は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 プロダクトマネージャー career-recruit.rakus.co.jp デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
1. はじめに こんにちは、 id:rksbun です。私は40代の楽楽精算開発のPjMで、元エンジニアです。ラクスには今年2月に入社しました。 最近の開発案件で、通常よりもかなり短い期間でリリースできたので、PjMとしてやったことを書きたいと思います。この記事で、ラクスがただ言われた通りのプロジェクト管理だけでなく、考えたことに挑戦させてくれて、それを評価してくれる会社だということを伝えられたら嬉しいです。 1. はじめに 2. あったこと 3. 意識したこと 4. やったこと 1. DB設計は全員で重点的に 2. AIを使った効率化への挑戦 3. リスク管理 4. トラブル対応は迅速かつ大胆に 5. 結果 6. まとめ 2. あったこと PdMから、チャレンジングなスケジュールでのリリース打診がありました。通常の機能開発期間より大幅に短い期間でした。もちろん高品質は維持しなければならず、見るからに難易度の高い挑戦でしたが、チームと相談して、やることに決めました。 3. 意識したこと プロジェクト自体がチャレンジングだったこともあり、以下を特に意識していました。 自分の考えていることはいつも以上に意識して伝える チームのモチベーション高い協力は必要不可欠 目的とか何を考えているかは当たり前でも毎回伝える この意識を持っていても伝え漏れたりは全然あったと思います 設計には時間をかける 手戻りを最小限に なるべくシンプルな設計になるようにし、実装やテストを効率化する チームメンバーの「今」よりも、「少し先」を見て動く 障害になりそうなものを予め取り除いておく 他の部署との調整など時間がかかるものに早めに対処しておく その代わり、メンバーの今やっていることへの意識が薄くなりがちになるが、ポイントを絞って時間を使う メンバーを信頼し、時には勇気を持って任せる 4. やったこと 通常のプロジェクトマネジメント業務に加え、強化したり追加でトライしたことを書きます。今まで通りやっていては間に合わないことはわかっていたので、今までやったことのないものも積極的に取り入れました。なので当然ながら失敗もありました。 1. DB設計は全員で重点的に 要件が決まった後、テーブル設計の段階でかなり時間をかけました。AIも活用しながらチームのエンジニア5人全員で時間をかけて検討しました。ここで全員から意見を出し合い、認識を揃えられたことで、後の開発フェーズを安心して進めることができました。開発スピードを上げたいのに時間をかけるという、一見矛盾したアクションにも見えますが、結果的には時間をかけて良かったです。 2. AIを使った効率化への挑戦 やったことがなくても、効率化できそうな箇所は積極的にAI活用を試しました。結果的に成果があったのは一つだけでしたが、知見が得られたことや、今後も考えれば全部成功だし、やって良かったです。 実装 :いわゆるスペック駆動開発に近いもの(AIに設計書を読み込ませてソースコードをアウトプットさせる)を試しましたが、期待したほどの効果は出ませんでした。ベテランエンジニアと若手エンジニア+AIで同じ実装を先行調査してもらったところ、結果的にはベテランエンジニアがスピードも正確さも圧勝。既存コードが膨大な箇所のAI活用はまだ発展途上で、プロンプトの工夫が必要という結論になりました。 レビュー :Claude CodeとGitHub Actionsを使ったレビューの仕組みを作りたかったのですが、開発と並行しながらだったため実戦投入には間に合いませんでした。ただ、その後しっかり仕組み化できたので、今後の開発には活かせそうです。 テスト :これは大成功しました!PlaywrightMCPを活用し、テスト項目書からAIでテストコードを作成、テスト実施もAIがやってくれるように自動化しました。これにより、自動化できた箇所は50%の工数削減を実現できました。人間が苦手な繰り返しテストで特に効果が高かったです。 3. リスク管理 他プロジェクトとのコンフリクト対応 :同じ箇所を触っている別プロジェクトがあったので、コンフリクト対応を先回りしてスケジューリングしておきました。しかし諸事情でその別プロジェクトがリリースされないことになり、結局元に戻すことに。ただ、この可能性も事前に察知していたので、被害は最小限にできたと思います。 QA・CIグループとの調整 :遅延発生に備え、前もって調整することで、後続作業のチームからリカバリー期間を確保できました。調整は早めが鉄則ですね。とてもありがたかったです。 4. トラブル対応は迅速かつ大胆に 技術的な課題 :開発終盤でライブラリのバグを踏んで実装に遅延が発生しました。発覚したのは夜でしたが、その場にいたエンジニア全員で問題を整理し、必要な対応をリストアップ。バグを回避する実装の認識合わせと、その箇所の強化テストを整理できました。この時はチームに緊張感が走りましたが、すぐに対処できてよかったです。あの瞬間は若干ピリつきましたが、正直私は楽しかったです。(みんなで仕事するの大好きなので) 仕様レベルのバグ :結合テストで仕様レベルのバグが発覚した時は、すぐにエンジニアに調査を依頼して状況をまとめてPMMやPdMを緊急招集しました。判断しやすい状況を作れたおかげか、対応方針をすぐに決定できました。結果、結合テスト期間内で対応完了できました。緊急事態では臆せず関係者とコミュニケーションをとり、迅速かつ大胆に行動することが大事だと再認識しました。 5. 結果 要求通りのスケジュールでリリースできました。担当のPdMや効率化に挑戦してくれたエンジニアが部内のスピードアップ賞を受賞し、私やチームも上長から評価していただけました。上長からはもちろんFBもいただけましたので、次回以降の開発に活かせればと思っています。 6. まとめ 失敗もありつつ、色々挑戦したおかげで、最初は困難と思われたリリースまで無事漕ぎ着けることができました。私自身は難しい課題に挑戦してクリアするのはゲームのように楽しめるタイプで、この挑戦は楽しかったです。 ラクスでは、「失敗を許容する」という文化(他にも いっぱいある 。ちなみに冒頭で触れた「考えている事を言葉で伝える」とかもあります)がありますが、これは心の中の安心材料としつつも、「挑戦せずに失敗もないよな」と思い、色々挑戦させていただきました。結果的には評価もいただけてとても良かったです。 短期間でのリリースに協力してくれたエンジニア、デザイナー、QAチームの皆さん、その他ステークホルダーの皆さん、また、ここまでお読みいただいた方にはとても感謝しています。ありがとうございました。
アバター