TECH PLAY

株式会社ラクス

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

941

はじめに 楽楽勤怠開発部でPdM(プロダクトマネージャー)をしている @k0First です。 私は、 ラク スが提供する「 楽楽勤怠 」のプロダクトマネージャー(PdM)として、日々企画・要件定義・開発推進を担当しています。 ラク スは 「ITサービスで企業の成長を継続的に支援します」 をミッションに掲げ、 SaaS プロダクトを通じて企業の業務効率化・生産性向上に貢献する会社です。 私たち ラク スが大切にしているのは、 「顧客視点で価値を生む機能開発」。 ですが実際の現場では、さまざまなリク エス トや要望が飛び交い、目の前の「作るべきもの」に引っ張られてしまうこともあります。 たとえば、 事業部からの要望をそのまま実現する 競合がやってるから同じ機能を実装する そんなふうに開発された機能が、結果あまり使われない…という経験、ありませんか? だからこそ私たち楽楽勤怠開発部では、機能を"なんとなく"で作らないことを大切にしています。 「誰の」「どんな課題を」「なぜ」解決するのか。 これを要件定義の段階でしっかりと見極めることを意識しています。 この記事では、私たち楽楽勤怠開発部が実践している 「本当に必要な機能を見極めた上で開発を行うための顧客志向な要件定義」 についてご紹介します。 たとえば、 事業部からの要望にどう向き合うのか デザイナーとどう体験設計を進めるのか 開発メンバーにどう“目的”を伝えるのか こうした具体的な場面を通じて、単なる“仕様決め”にとどまらない、 顧客視点に立った「楽楽勤怠開発部流の要件定義」の考え方 を紐解いていきます。   はじめに 顧客志向とは何か なぜ顧客志向が必要なのか 本当に必要な機能を見極めるために大切にしていること 顧客要望を「課題」に翻訳する 顧客価値だけでなく事業価値も考える 顧客視点と事業視点を両立させる 「やる」「やらない」を決める基準 デザイナーも構想段階から巻き込む 体験を一緒に設計する コストの大きい開発案件は、ワイヤーフレームを作るところから 「コストと体験」のバランスも一緒に考える 開発メンバーには「Why」から伝える 仕様書ではなく、背景と目的から話す 開発メンバーからのフィードバックを歓迎する 仕様変更を恐れない姿勢 まとめ 「顧客志向な要件定義」は問い続ける姿勢     顧客志向とは何か 「顧客志向」という言葉をよく耳にするものの、「お客様の要望をそのまま実装すること」と誤解されているケースも少なくありません。 たとえば、 「勤務表の申請ボタンをもっと大きくしてほしい」 「打刻修正の理由欄を必須にしてほしい」 といった要望は一見もっともらしく聞こえますが、実際にはその裏に、 「毎月申請漏れが多く、管理者からの催促が発生している」 「修正理由が入力されていないことで 労務 チェックが止まってしまう」 といった、 業務上の根本的な困りごと が隠れていることが多くあります。 顧客志向の本質は、"要望に応える"ことではなく、 "困りごとの本質を見極めて、最も効果的な解決策を提供すること" です。 この視点に立つことで、「本当に使われる機能」「顧客の期待を超えた価値」を実現することができると私は考えています。 なぜ顧客志向が必要なのか 私たちが開発している「楽楽勤怠」は、全社員が日々の打刻や申請で使い、現場の責任者がそれを確認・修正し、さらに 労務 担当者が全体を集計・管理するというように、 複数の立場のユーザーに横断的に使われるプロダクト です。 この領域の難しさは、単に機能を実装することではなく、 ITに不慣れな現場の責任者にも、 直感的に使える操作性 部門や拠点ごとに異なる 就業ルールや申請フローへの柔軟な対応 毎日発生する リアルタイムな処理と確認作業の効率化 といった、 現場の実務に本当にフィットする体験をどう作るか にあります。 そんな中でお客様からの要望をそのまま実現してしまうと── 現場で本当に困っていることが解決されなかったり   別のユーザーには使いにくい仕様になってしまったり   ということが起こりがちです。 だからこそ私は、限られた開発リソースを本当に役立つ機能改善に使うために、 「要望」ではなく「課題」を見極めること を重視しています。 本当に必要な機能を見極めるために大切にしていること 楽楽勤怠開発部が大切にしているのは、 「要望」ではなく「課題」にフォーカスする姿勢 です。 お客様や事業部から「これが欲しい」と要望が上がってきたとき、PdMが最初にやるべきことは「なぜその機能が必要と感じているのか」を丁寧に聞くことです。 顧客要望を「課題」に翻訳する 楽楽勤怠開発部では、顧客からの要望を直接聞くことはなく、事業部から要望を収集して機能開発を行っています。 その際、事業部には次のような質問を投げかけます。 なぜこの機能が必要だと感じているのですか?(背景・文脈を ヒアリ ング) 今の運用でどんな手間がかかっていますか?(業務フローやオペレーションを理解する) この問題が解決されることで、どんな効果が期待できますか?(課題解決の確実性を確認する) 現状の代替手段や妥協点は何かありますか?(本当に"今"解決すべき課題かを見極める) この対話を通じて、お客様が本当に困っていること、解決すべき本質的な課題が明らかになります。 表面的な要望に引っ張られず、「その機能がなぜ必要なのか」を深掘りすることが、顧客志向な要件定義においては非常に重要です。 そうすることで、目的を逸脱した「使われない機能」の開発を防ぐことができます。 顧客価値だけでなく事業価値も考える 要件定義では、お客様のことを考えるだけでなく、事業部との調整も欠かせません。 事業部からは「売上拡大」「顧客獲得」といった目標を実現するための要望が上がってきます。 ここで大切にしているのは、「顧客価値」と「事業価値」の両方を論理的に整理し、合意形成することです。 顧客視点と事業視点を両立させる 楽楽勤怠開発部では、事業部と合意形成をするために、以下の内容を整理しています。 目的、背景、解決したい顧客の課題 なぜその機能を開発する必要があるのか それが事業に与える インパク ト 顧客単価UP、 顧客満足度 UP、解約防止など 必要な開発リソース 工数 ・コスト・スケジュール このように、感覚ではなく「数字と論理」で合意形成するようにしています。 最後に、その機能を実装するために必要なリソースと、見込まれるリターンのバランスを検討し、事業部と「もしこれをやるなら、どこに負荷がかかるか」「他の優先タスクにどんな影響が出るか」を具体的に議論します。 開発部と事業部では、それぞれ優先するものが異なるため、合意形成をスムーズに行うには、お互いのことを深く理解していなければなりません。 楽楽勤怠では、開発部と事業部の間に 信頼関係が築けている こともあり、お互いの立場を尊重した合意形成ができていると感じます。 「やる」「やらない」を決める基準 すべての要望を実現することはできません。 だからこそ、「やらないと決めること」もPdMの大切な仕事です。 私は以下のような基準で判断しています。 顧客にとって価値が高いか たくさんの顧客にメリットをもたらすか ある一部の顧客にとってしかメリットがないものになっていないか 今やるべきなのか 開発リソースは有限です。 「やるべき機能」を見極め、「やらないこと」を決断することこそが、真の顧客志向だと考えています。 こうして、開発ロードマップを作成し、機能開発の優先度を事業部と協議して決めています。 デザイナーも構想段階から巻き込む 楽楽勤怠開発部では、もともとデザイナーを「要件定義フェーズが終わってから参加してもらう」ようなスタンスで開発を進めていました。 ですが、現在では、 要件定義の構想段階から積極的に巻き込むことが重要 だと考えているため、一緒に要件定義を進めています。 なぜなら、顧客にとって「その機能がどう使われるのか」「どんな体験になるのか」は、仕様書だけでは表現しきれないからです。 体験を一緒に設計する 要件定義の段階で、デザイナーに共有するのは次のような情報です。 顧客の課題(なぜこれをやるのか) ユーザーがどんな業務フローの中で使うのか どうすれば迷わず、スムーズに使える体験になるのか この段階で、 PdMである私が頭の中で描いている「理想の顧客体験」を伝え、デザイナーの視点を加えてデザイン設計を行う ことが大切です。 例えば、 「この 動線 だと、ユーザーは何を期待してこのボタンを押すのか?」 「今の画面構成だと、どこでつまずくリスクがあるか?」 「業務の文脈を考えたとき、このタイミングでこの情報が必要か?」 といった様々な観点でディスカッションを重ねます。 コストの大きい開発案件は、 ワイヤーフレーム を作るところから 開発コストのかかる大きな案件は、まず、デザイナーが作る ワイヤーフレーム (下書き)をもとに議論を重ねます。 ここでいう ワイヤーフレーム は「完成品」として扱うのではなく、 “意思疎通のためのツール”として使う ことを意識しています。 PdMが考える「この機能で顧客はこう動くはず」という仮説 デザイナーが持つ「UXの原則」や「ユーザー心理」の知見 これらをPdM、デザイナー、事業部とすり合わせていくプロセス これが、顧客視点で価値を生む機能開発には欠かせません。 「コストと体験」のバランスも一緒に考える もちろん、理想的なUI/UXを追い求めるだけでは、開発リソースやスケジュールが追いつかないこともあります。 だからこそ、デザイナーとは「ここは妥協しても良い」「ここは死守したい」という優先順位を共有しながら、 “最適な着地点”を一緒に探ります 。 これが、楽楽勤怠開発部で実際におこなっている「顧客志向な要件定義」におけるデザイナーとの連携です。 開発メンバーには「Why」から伝える 次に重要なのが、開発メンバーとの連携です。 楽楽勤怠開発部では、開発チームを単なる“実装担当”として扱うことはありません。 PdMがやるべきは、 「なぜこの機能を作るのか」を開発メンバーにしっかり伝えること です。 仕様書ではなく、背景と目的から話す 私は開発メンバーに要件を伝える際、必ず次の順番で説明します。 背景 「この機能を要望された経緯」「顧客が直面している課題」を説明します。 目的 「この機能によって、どんな価値を届けたいのか」「どんな行動変化を期待しているのか」を共有します。 要件 そのうえで、「だからこういう仕様にしている」という理由付けをしながら要件を説明します。 これにより、開発メンバーは 「言われたものを作る」のではなく、「課題解決のために自分も考える」モード に入ってくれます。 私自身、過去に仕様書だけ渡してしまったことで認識ズレが生じ、開発がやり直しになった苦い経験があります。 その時痛感したのは、 「仕様そのもの」よりも「目的・背景」を伝える方が、はるかに重要 だということです。 なぜなら、「目的・背景」が伝われば、認識ズレを防ぐだけでなく、 私が考えた仕様よりももっと価値のある仕様を開発メンバーが考え付く可能性があるから です。 実際に、開発メンバーから「だったら、こうした方が早くて安全かも」という建設的な提案が自然と出てくるようになりました。 この双方向のコミュニケーションが、結果的に より良い顧客体験を実現する近道 だと考えています。 開発メンバーからのフィードバックを歓迎する 開発チームからは、仕様に対するさまざまなフィードバックが返ってきます。 技術的な実現可能性(もっと簡単な方法があるのでは?) 保守性やパフォーマンスへの影響 将来的な拡張性 セキュリティや障害対応の観点 これらをPdMが受け止め、 顧客価値を損なわずに、より良い形にブラッシュアップしていく のが理想の連携です。 仕様変更を恐れない姿勢 「Why」を共有したうえでのフィードバックであれば、仕様を変えることは決して「後戻り」ではありません。 むしろ、 開発チームが顧客志向で考えた結果としての改善提案 であれば、歓迎すべきことです。 楽楽勤怠開発部では、 スクラム 開発の中で、こうした対話を通じて 「チーム全体で顧客志向を実現する」 文化を根付かせていっています。 まとめ 「顧客志向な要件定義」は問い続ける姿勢 ここまで、楽楽勤怠開発部が実践する「顧客志向な要件定義」についてお話ししてきました。 お客様の“言ったこと”ではなく、“本質的な課題”を捉える 顧客価値と事業価値を両立させ、冷静に優先度を決める やらないことを決め、やるべきことに集中する デザイナーや開発メンバーと“Why”を共有し、一緒に考える これらは決して特別なことではありません。 重要なのは、 本当にこの機能は必要か? 顧客にとって価値があるか? を 問い続ける姿勢 です。 その積み重ねが、最終的に 「使われるプロダクト」「選ばれるプロダクト」 につながると、私は信じています。 楽楽勤怠開発部では、これからも愚直に「顧客志向」を追求し、企業の成長を支援するプロダクトを磨き続けていきます。
どうも、 稲垣 です。 先日、 株式会社翔泳社 ProductZine編集部主催のイベント に参加しました。 昨年に続き、今年も 神田明神 ホールで開催されました。 当日は晴天で、 神田明神 では『 神田神社 大祭』が催されており、活気にあふれていました。 神田明神 いつきても素敵だ 去年もここだったが良き 今日は 神田明神 でもイベントやってる #productzine pic.twitter.com/UWy89oT5tv — 稲垣 剛之(Takeshi Inagaki)| ラク ス (@ingktks7) 2025年5月15日 その中で以下のクロージングセッションが 【モデレーター】広瀬 丈[ログラス]/飯沼 亜紀[ウト]/斉藤 知明[ログラス]/横道 稔[Product People] ラスト始まった PdMのキャリア、悩ましい 自分についてもだし、メンバーに対してもなので組織ヅクリに活かしたい 3割くらいがPdMかなあ ★多様化し変化していくなかで目指すべき、これからのプロダクトマネージャーのキャリア #productzine pic.twitter.com/MSBVumfpha — 稲垣 剛之(Takeshi Inagaki)| ラク ス (@ingktks7) 2025年5月15日 という、プロダクトマネージャーのキャリアについてのセッションでした。 その中で 「キャリアは偶発的ですか?計画的ですか?」 という問いがありました。 登壇者全員が「偶発的」 と答えていて、非常に印象深かったです。 改めて、こういったセッションでは「よい問い」こそが素晴らしい議論を生み出すのだと感じました。 計画的なのは「 大谷翔平 さん」くらいではないかという話もあり、おそらく多くの方が頷いていたと思います。 また、会場にいたすべての人が「自分はどうだろう」と考えていたことでしょう。ポストしている方も多数いました。 キャリアは偶発的ですか?計画的ですか?という問い。偶発性を「いきあたりばったり」と捉えるなら、多分登壇者含めてじつはほんとはそうじゃないきはする。 北極星 (こだわり)をもって、目の前に現れる様々なことを縦横無尽に利用してやりたいことにむかってる人がPMは多い気がする #productzine — みやっち | 生成AI エバンジェリスト @ 🇪 🇽 🇵 🇱 🇦 🇿 🇦 (@miyatti) 2025年5月15日 自分もそんな一人だったので、イベントのテーマに触発されて、これまでの自分のキャリアについて書いてみようと思います。 自分のキャリアは偶発的ですか?計画的ですか? キャリアの変遷 前編 キャリアの変遷 後編 まとめ 自分のキャリアは偶発的ですか?計画的ですか? 自己紹介と略歴はこんな感じです。 新卒で大手独立系 SIer 企業に何とか滑り込み、そこを含めて、現在の ラク スまでIT企業を6社経験しています。 イベントの問いにあった「キャリアは偶発的ですか?計画的ですか?」については、ずるい回答をすると、  「偶発と計画の狭間」 にあります。 青の項目は、自らの意志で計画的にキャリアを変えたものです。 それ以外は、偶発的というか、働く中でチャンスがあったり、気がついたらそうなっていたキャリアです。 面白いのは、偶発的でも計画的でも、見事に失敗しているという点です。そのあたりについて、ここから書いていこうと思います。 キャリアの変遷 前編 「キャリア」(career)という言葉の起源は、中世 ラテン語 の「車道」にあり、英語では競馬場や競技場におけるコースや、そのトラック(行路、足跡)を意味するものだそうです。(登壇者のログラスの斉藤さんも「足跡」だと話されていました。) そんなわけで、自分のキャリア(足跡)を、いくつかのパートに分けて「出来事」と「その時感じたこと」という視点から振り返ってみることにします。 外部発信でここまで自己開示するのは珍しいと思いますが、読んでくださっている方の今後のキャリアに少しでも参考になれば幸いです。 ※なお、この「キャリア変遷」資料は本ブログのために作成したものではなく、前職で幹部育成の研修アドバイザーをしていた際、研修課題として提示されたものに対し、「面白そうだ」と思って自分も作成したものを、少し補正したものです。 これがキャリア前半の18年分です。ここでは、偶発的なキャリアを9年半で終え、初めての転職をしました(計画的)。 そして、この転職でひとつ大きな失敗を経験しました。その失敗の理由は、転職時に2つのことを同時に追い求めたことにあります。 この失敗を糧に、次の転職では「自分のやりたいことをとことんやろう」という軸で会社選びをしました。 このあたりから、「エンジニア」という枠組みをあまり意識しなくなり、「製品づくり」に直接関与し、製品の成長に貢献できれば、役割にはそれほどこだわらなくなりました。この考え方が、キャリア後半や現在のプロダクトマネージャーという役割につながっています。 キャリアの変遷 後編 これが、キャリア後半の7年間(2025年5月現在を含む)です。ここでは、大きな変化が2つあり、そのうちの一つは失敗でした。 最初の変化は、親会社のグループ化に伴い、携わっていた事業が分社化され、その会社の役員を務めることになったことです。 実は、役員就任前には当時の会社を辞めるか、別の事業への異動を希望していました。理由は、自分自身が成長を実感できなくなっていたからです。 事業立ち上げ直後から5年以上にわたって携わり、開発・企画・デザイン全般の責任者を務めていました。製品の成長は著しかったものの、その中での刺激が徐々に減っていきました(長くいると何かと楽ができてしまう)。また、各領域のリーダーたちも成長していたため、「そろそろ潮時かな」と思っていたところでした。 その最中で分社化の話が持ち上がり、代表と話し合った結果、少し迷いはあったものの、役員という貴重な経験ができる機会だと捉えて引き受けました。当初はプロダクト管掌の予定でしたが、急遽コーポレート部門も担当することになりました。 この2年間で以下のような経験をしました: 会社のビジョン・行動指針の策定 人事制度などの制度設計 オフィス移転に伴うオフィスづくり 全職種の新卒・ 中途採用 責任者 アメリ カ企業との提携交渉 訴訟対応や商標取得に関する弁護士・ 弁理士 との交渉 もしこのとき転職していたら、きっと経験できなかったことばかりだったと思います。 2つ目の変化は、役員退任後に「 外資 系企業」「技術サポートマネージャー」という、これまでまったく経験のなかった領域への転職です。 転職時には複数社から内定をもらっていましたが、そのほとんどは自社で製品づくりを行っている企業で、EM(エンジニア リングマ ネージャー)、システム企画、プロダクトマネージャーといった役割でした。なぜそれらの企業に行かなかったのか? 理由は、「そこそこうまくやれそうな気がしたから」だったように思います。 逆に、最終的に選んだ企業は、「うまくできるか全く未知」だったからこそチャレンジしたかったのです。 ただし、1点だけ懸念がありました。それは、「製品づくり」に直接関われない点です。その企業でもプロダクト開発は行っていましたが、基本的には アメリ カ本社での開発が中心でした。社内異動で関与する道もありましたが、ハードルは高いものでした。 結果的に、この懸念が的中し、退職を決断しました。 業務自体は面白く、同僚マネージャーやメンバーのレベルも非常に高かったですし、会社の仕組みや文化も独特で、短期間ながら多くを学ぶことができました。そのため、この転職に後悔はありません。 まとめ というのが、これまでの自分のキャリア変遷となります。 キャリアの中でブレなかったのは、「 製品をツクルことが好き 」という気持ちでした。 前職では、面接時に求職者の方に以下の質問をよくしていました。 これには当然、正解はなく、おそらくこれら全部が好きだからこそ、製品づくりに携わっているのだと思います。ちなみに、当時なぜこの質問をしていたかというと、純粋にその人のタイプが知りたかっただけで、深い意味はなかったように思います。 (当時のメンバーに聞いてみたところ、見事に意見が分かれて面白かったですし、チーム編成の参考にもなりそうだと感じました) ちなみに、自分の第一位は以下のように変化してきました: 2.考えたものを実際にツクルこと ⇒ 3.ツクったものがお客様に届き反応があること ⇒ 1.何をツクルか考えること 今はもう、めんどうなので順位はつけていません。 そして、キャリアについては、ここまで書いてきたように、偶発的でも計画的でも失敗します。だから今は、自分のキャリアについてはほとんど何も考えていません。 マネージャーとして組織の未来は描きますが、自分の未来を描くことはやめて、目の前のことを全力で楽しくやりきることにフォーカスしています。その代わり、「働く」ことに対してのビジョンは決めていて、そのビジョンに近づけるかどうかを軸にしています。 以上が、自分のキャリアの変遷や、キャリアについての考えです。 参考になったかはわかりませんが、少しでもこの内容を通じて、自分や自分が今所属している ラク スに興味を持ってもらえたら嬉しいです。 ちなみに、現在は以下の組織でPdM(プロダクトマネージャー)とデザイナーをリードしています。 tech-blog.rakus.co.jp 両方の役割とも採用募集中ですので、ぜひご応募ください! career-recruit.rakus.co.jp career-recruit.rakus.co.jp
はじめに こんにちは、エンジニア3年目のTKDSです! 今回はpg_query_goについて調べてみました。 業務で使用したこともあるのですが、改めて個人的に使ってみたいと思い、使い方をさくっと調べて試しました。 はじめに pg_query_goとは 簡単なサンプル 事前準備 中身をチラ見 実用例:SQLの操作を抽出 実用例:SQL実行種別のガードレール まとめ pg_query_goとは 実際の PostgreSQL の SQL パーサーを使って SQL クエリをパースして返してくれるライブラリです。 Goでも簡単に SQL の操作の取得などができてとても便利です。 github.com 簡単なサンプル 簡単なサンプルを使って、pg_query_goについて紹介します。 事前準備 事前準備をしておきます。 go mod init <プロジェクト名> 下記のサンプルコードをmain.goに保存します。 package main import ( "fmt" "log" "reflect" pg_query "github.com/pganalyze/pg_query_go/v6" ) func main() { sql := `SELECT id, name FROM users WHERE active = true;INSERT INTO テーブル名 (列名1, 列名2) VALUES ('値1', '値2');` treeStruct, err := pg_query.Parse(sql) if err != nil { log.Fatalf( "Parse failed: %v" , err) } // fmt.Println(treeStruct) fmt.Println(treeStruct.Stmts) fmt.Println( len (treeStruct.Stmts)) // SQL文が増えると列数が増える fmt.Println(reflect.TypeOf(treeStruct.Stmts)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 0 ].Stmt)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 0 ].Stmt.Node)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 0 ].Stmt.Node)) fmt.Println(treeStruct.Stmts[ 0 ].Stmt.Node) fmt.Println(reflect.TypeOf(treeStruct.Stmts)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 1 ].Stmt)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 0 ].Stmt.Node)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 1 ].Stmt.Node)) fmt.Println(treeStruct.Stmts[ 1 ].Stmt.Node) } pg_query_goは、Parseメソッドで引数として受け取った SQL を解析できます。 メソッド シグネチャ は func pg_query.Parse(input string) (tree *pg_query.ParseResult, err error) となっており、 pg_query.ParseResult 型のポインタ型とエラーを返します。 pg_query.ParseResult は github .com/pganalyze/pg_query_go/v6@v6.1.0/pg_query.pb.goに定義されている型です。 .pb.goの拡張子なので、おそらく プロトコル バッファから生成された型のようです。 サンプルではこの構造体をたどっていくように出力しています。   出力の一部を記載します。 これは一番最初の fmt.Println(treeStruct.Stmts) の出力 結果です。 [stmt:{select_stmt:{target_list:{res_target:{val:{column_ref:{fields:{string:{sval:"id"}} location:7}} location:7}} target_list:{res_target:{val:{column_ref:{fields:{string:{sval:"name"}} location:11}} location:11}} from_clause:{range_var:{relname:"users" inh:true relpersistence:"p" location:21}} where_clause:{a_expr:{kind:AEXPR_OP name:{string:{sval:"="}} lexpr:{column_ref:{fields:{string:{sval:"active"}} location:33}} rexpr:{a_const:{boolval:{boolval:true} location:42}} location:40}} limit_option:LIMIT_OPTION_DEFAULT op:SETOP_NONE}} stmt_len:46 stmt:{insert_stmt:{relation:{relname:"テーブル名" inh:true relpersistence:"p" location:59} cols:{res_target:{name:"列名1" location:76}} cols:{res_target:{name:"列名2" location:85}} select_stmt:{select_stmt:{values_lists:{list:{items:{a_const:{sval:{sval:"値1"} location:102}} items:{a_const:{sval:{sval:"値2"} location:110}}}} limit_option:LIMIT_OPTION_DEFAULT op:SETOP_NONE}} override:OVERRIDING_NOT_SET}} stmt_location:47 stmt_len:70] select_stmt , insert_stmt などの記述からSELECT、INSERTなどの区別がしっかりつくように解析されているのがわかります。 さらに、 fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 0 ].Stmt.Node)) fmt.Println(reflect.TypeOf(treeStruct.Stmts[ 1 ].Stmt.Node)) の実行結果からNodeの型が SQL 操作種別を表していることがわかります。 *pg_query.Node_SelectStmt *pg_query.Node_InsertStmt 中身をチラ見 次に中身をチラ見してみましょう。 単純に気になったのでみてみただけです。 pg_query.Parse( sql )からたどってみます。 func Parse(input string ) (tree *ParseResult, err error ) { protobufTree, err := parser.ParseToProtobuf(input) if err != nil { return } tree = &ParseResult{} err = proto.Unmarshal(protobufTree, tree) return } 中では、 parser.ParseToProtobuf 、 proto.Unmarshal を呼んでいるようです。 それぞれの関数の説明、 ParseToProtobuf - Parses the given SQL statement into a parse tree (Protobuf format) 、 Unmarshal parses the wire-format message in b and places the result in m. The provided message must be mutable (e.g., a non-nil pointer to a message). から前者は SQL をパースしてProtobuf形式で出力し、後者はそれを読めるようにして、 proto.Message に格納するようです。 まずは、 parser.ParseToProtobuf から見ていきます。 func ParseToProtobuf(input string ) (result [] byte , err error ) { inputC := C.CString(input) defer C.free( unsafe.Pointer (inputC)) resultC := C.pg_query_parse_protobuf(inputC) defer C.pg_query_free_protobuf_parse_result(resultC) if resultC. error != nil { err = newPgQueryError(resultC. error ) return } result = [] byte (C.GoStringN(resultC.parse_tree.data, C. int (resultC.parse_tree. len ))) return } 中身はCGOを呼び出すコードになってました。 C がCGOに紐付いたライブラリを呼び出しているコードのようです。 Cで扱えるようにGoの文字列を変換したあと、 C.pg_query_parse_protobuf で解析し、またGoで扱えるようにしていました。 ここでProtobufの形式でやりとりしていたので、後にUnmashalが必要になるのですね。 戻って、次は proto.Unmarshal です。これは受け取ったメッセージをtreeに書き込んでいます。 treeの引数の型部分にあった proto.Message はただのInterfaceなので、実装は ParseResult が持っていそうです。 ParseResultのあるファイルの中身を探しているとStringメソッドがありました。 func (x *ScanResult) String() string { return protoimpl.X.MessageStringOf(x) } 今回出力されてる結果はこのメソッドの出力だと検討がつきました。 満足したのでこの辺にしておきます。 次からは簡単な実用例を示します。 実用例: SQL の操作を抽出 以下のようにパースしたあとに型の判別を追加します。 前述のように ParseResult.Stmts.RawStmt.Stmt.Node の型が操作種別を表しているようです。 そこで、型によって処理を分岐させることで、操作を抽出してみましょう。 package main import ( "fmt" "log" "reflect" pg_query "github.com/pganalyze/pg_query_go/v6" ) func main() { sql := `SELECT id, name FROM users WHERE active = true;INSERT INTO テーブル名 (列名1, 列名2) VALUES ('値1', '値2');` treeStruct, err := pg_query.Parse(sql) if err != nil { log.Fatalf( "Parse failed: %v" , err) } var opeType string for _, stmt := range treeStruct.Stmts { node := stmt.Stmt.Node switch node.( type ) { case *pg_query.Node_SelectStmt: opeType = "SELECT" case *pg_query.Node_InsertStmt: opeType = "INSERT" case *pg_query.Node_UpdateStmt: opeType = "UPDATE" case *pg_query.Node_DeleteStmt: opeType = "DELETE" default : opeType = "UNKOWN" } fmt.Printf( "操作種別:%s, 型:%v \n " , opeType, reflect.TypeOf(node)) } } 実行結果は以下の通りです。 操作種別:SELECT, 型:*pg_query.Node_SelectStmt 操作種別:INSERT, 型:*pg_query.Node_InsertStmt みごと2つの文とも操作種別の抽出ができました! 実用例: SQL 実行種別のガードレール 前項で操作種別の抽出が可能になりました。 ということは、許可したクエリのみ実行できるように制限することも可能です。 さらにロジックを追加します。 今回はSELECTのみ許可するようにしましょう。 fmt.Printf("操作種別:%s, 型:%v\n", opeType, reflect.TypeOf(node)) を削除して、以下のロジックを追加してください。 ↓追加ロジック if opeType == "SELECT" { fmt.Printf("操作種別:%s, 型:%v\n", opeType, reflect.TypeOf(node)) } 出力は以下の通りで、SELECTのときのみ出力されるようになりました。 操作種別:SELECT, 型:*pg_query.Node_SelectStmt 今回はPrint文でしたが、 SQL などの実行処理の前にこの操作チェックをおけば、実行のガードレールとして使用可能です。 まとめ 今回はpg_query_goについて紹介しました! SQL の解析が簡単にできて便利です。 実用例の活用方法として、ユーザー入力の SQL を実行する MCP サーバーなど作る場合に使えそうです。 他にも地味にいいポイントが、Goなのでビルドしてしまえば簡単に配布ができます。 実行も go mod tidy → go run main.go だけなので非常に試しやすいです。 ぜひ興味もった方はご自身の環境でためしてみてください。 今後は、pg_queryにWASM版もあるらしいのでそちらも試してみたいです。 ここまで読んでいただきありがとうございました!
どうも、 稲垣 です。 先日、 PM、PdM向けイベントを0からみんなで作る会 に参加しました 参加のきっかけは、これまでとは少し違った雰囲気のイベントだったこと。それくらいの軽い気持ちでしたが、参加してみると、たくさんの気づきがありました。 せっかくなので、ブログにまとめてみようと思います。 思ったことを、感じたままに綴ります。読んでくださる皆さんにとって、何かしらの学びや気づきになれば嬉しいです。 前置きが長いですが是非!(イベントの内容については、ブログの後半でレポート) プロダクトマネージャーの解像度のあげ方 そもそもプロダクトマネージャーって何者? 「我々は何者か?」を明らかにすること ラクス?楽楽精算って聞いたことあるけど、どんな課題があるの? 「漫画」ができたら、次は? イベント登壇・対談・参加の新たな価値 まとめ 「PM、PdM向けイベントを0からみんなで作る会」の参加レポート 最後に プロダクトマネージャーの解像度のあげ方 現在、私は ラク スの「製品管理課」という組織でマネージャーを務めています。これまでの経歴としては、エンジニアを軸にしながら、さまざまな業務に携わってきました。 実は「PdM(プロダクトマネージャー)」という役割を知ったのは ラク スに入社したタイミングが初めてでした。私が入社した当時、 ラク スにはPdMという役割は、まだなく「製品企画」というポジションが事業部側に存在していただけでした。 では、私(稲垣)がどのようにPdMという役割を理解していったのかをお話しします。 正直なところ、入社当初はPdMという役割についてあまり意識していませんでした。 というのも、当時の製品開発には明確な課題があり、それを解決することが最優先だったからです。 その課題解決に、まずは1〜2年ほど集中して取り組みました。 その後、「 PdMの採用 」に向き合う必要が出てきたことで、改めてPdMという役割を 正しく理解しなければと考えるようになりました。 そこで、まずは基礎から学ぶために、PdM関連の書籍を3冊、徹底的に読み込むことから始めました ※ プロダクトマネジメントのすべて / プロダクトマネージャーになりたい人のための本 / プロダクトマネージャーのしごと 第2版 正直に言うと、本を読んだ感想としては、  「 マインドセット や考え方は、これまで自分がやってきたことと大きく変わらないな 」 と感じました。対して「 PdMの役割がいまいちよくわからない 」とも思いました。 PdMでの転職・採用経験がある方ならわかるかもしれませんが、PdMという職種は、 求職者によってイメージが大きく異なり ます。前職では、エンジニア、デザイナー、営業、CS、バックオフィス、物流、ほぼ全ての採用、面接を1年ほど担当していましたが、それと比較しても業務内容のイメージのばらつきが大きいです。 さきほど紹介した書籍は紛れもなく良書で、書いてある内容も素晴らしいです。 でも、正直に言って 「本に書かれているような役割を、そのまま実践できているPdM」はいない と思います。これまで100人以上のPdMとカジュアル面談をしてきた中で、はっきりとそう感じたからです。 ちなみに採用初期のころは、カジュアル面談をせずに、いきなり面接をしていました。 「業務内容のイメージのばらつき」のせいで、かなり苦労しました。 私が面接で質問すると、相手は「なぜそんなことを聞くんだろう?」という反応。 逆に相手からの質問に対して、私も「なんでそんな質問を?」と戸惑うことも多くありました。 今思えば、求職者の方にとっても失礼なことをしてしまっていたと思います。 そもそもプロダクトマネージャーって何者? ある人は「 ゴジラ 」みたいに圧倒的な力でプロジェクトを引っ張る存在だと言い、 ある人は「ルフィ」みたいに仲間を巻き込んで進んでいく冒険家だと言う。 「 リプリー (エイリアン)」のように未知と戦う人もいれば、「 ドラえもん 」のように未来の道具で課題を解決する存在だと語る人もいる。炭治郎、アトム、ゴン、 ケンシロウ 、イーサン・ハント… 出てくるのは漫画・アニメ・映画・実写・人間・ロボット、何でもあり。 ──そう、 みんな「PdM像」がバラバラ だったんです。 だからまずやったのは、 「我々は何者か?」を明らかにすること これを明確にしなければ、役割も、期待も、採用も、評価も全部ズレてしまう。 じゃあ、どうやったのか? 「PdM」が含まれるイベントに参加しまくり、カジュアル面談でひたすら語り、聞きました 。 「PdMとは何か」「 ラク スにおけるPdMとは何か」 「我々の物語はどう始まり、どう進んでいくのか」 まるで映画や漫画の「企画会議」のように、 ラク スのPdMという "キャ ラク ター"を 言語化 していきました。それを「カジュアル面談資料」という漫画に、約40スライド程度で内容をまとめました。そして、毎回約15〜20分間、稲垣がカジュアル面談を通じて全力で伝えています。 多くの応募者からは、「聞きたいことが全部聞けました」とお褒めの言葉を頂いています。 ちなみに、 ラク スに入社してほしいという訴求は一切行っていません。 「費用対効果が悪くない?」という疑問もありますが、このカジュアル面談には以下のようなメリットがあり、むしろ費用対効果はよいです。 自分の言葉で話すことで、自分自身のPdMとしての解像度が上がる こちらから全情報を提供するため、相手の質問を通じて新しいPdM像が明確になる 選考ではないものの、質問の質などで相手のレベルがある程度わかる 違うと感じても「 ラク スのPdMは面白そう」と他のPdMに口コミしてくれる しかし、これまで行っても採用にはあまり効果が出ませんでした。。1名が入社し、内定は出したものの承諾には至らず)。その理由は明確でした。 ラク ス?楽楽精算って聞いたことあるけど、どんな課題があるの? ラク スは、国内 SaaS 業界でベスト3に入り、最近では ARRが400億円を突破 しました。また、「楽楽精算」は導入社数で1位を誇っています。 しかし 、PdMや製品デザイン・開発を担当する人々の認知度とは、これらの実績が直接的に関連していない ことに気づきました。実際、これは弊社の技術広報の調査でも明らかになっています。 「漫画」ができたら、次は? やるべきは ラク スの開発・デザイン・PdM組織の認知 どんなに素晴らしい映画や漫画を作っても、認知されなければ意味がないのと同じです。 そこで、2023年の2月から積極的にイベントに参加・登壇し出しました。 結果として、   2024年度には    イベント登壇・対談が14回、参加が約40回(EMなどのイベントも含む) となりました。(対談・登壇は弊社技術広報の多大なる支援の成果でもあります) この活動のおかげで     4名のPdMを採用、リーダークラスのPdMや ドメイン エキスパート候補など 採用が好転し、入社した方々は現在、活躍していただいています。 また、私たち ラク スに PdMがいること や、 開発組織が「顧客志向」を強みとしていること が、これまでよりもは伝わったのではないかと思います。 イベント登壇・対談・参加の新たな価値 当初の目的は「採用」でしたが、最近ではそれ以外の価値も得られるようになっています。 イベントで多くのPdMと深く話すことで、実際にその業務を行わなくても、そのプロダクトのPdMをしているような感覚になることがあります。多くのPdMは 言語化 が得意で、非常にわかりやすく話してくれるため、そうした会話が貴重な経験となります。 村上春樹  「本を読むということは、他人の人生を 追体験 することだ。」 イベントで話したり、聞いたりすることで、他のPdMの経験を 追体験 できるのです。 成長には経験が不可欠 です。 読書で知識は増えます。知恵はその知識を実際の体験にすることで生まれます 。 だからこそ、今は採用だけでなく、知恵を得るためにもイベント登壇・対談・参加しています。 2025年度は イベント登壇・対談・参加は前年度の2倍(登壇・対談 20回 /参加 80回) を行動目標としています。 また、内容もPdMやEMだけでなく、UXや組織に関連するテーマでも登壇していこうと考えています。 (2025年度からプロダクト部となり、デザイナーと同組織になりました) tech-blog.rakus.co.jp ですので、もしそういった依頼等あれば気軽にご連絡ください。 まとめ ここまで読んで気付いた人もいると思いますが、ここまでの話のプロセスやアクションはそのまま 「 プロダクトマネジメント 」 なんです。 プロダクトの解像度をあげるためインタビューやリサーチで多面的に捉える プロダクトの価値や強み、未来も描き、誰もがわかるように言語・資料化する プロダクトを伝える・届けるためにターゲットを絞って伝えていく これらをひたすら実施し続けて、最高の状態であり続ける PdMって面白くないですか? PdMになる一番の簡単な方法は 『自分はPdMだと名乗ること』 だと思っています。 ラク スの製品管理課にご応募頂ければPdMになれますので是非ご応募ください。 career-recruit.rakus.co.jp と、ここまでが前置きです(笑)ここからがイベントレポートです 「PM、PdM向けイベントを0からみんなで作る会」の参加レポート このイベントはPdMのイベント参加者で創ろうという会です。 ROSCA 技術広報の @ROSCA_kota さんが発起人で、PdMのイベントでも良くお見かけする @uekiyuta1991 さん、 @gimupop さんがコ アメンバー で参加されていました。 会場は ジーニー さんの新宿のオフィスで、開催されました。 30名程度があつまり、イベント企画を考えるというイベントです。4-6人でチームをつくり、全6チームで約50分で企画を考えて発表するというイベントです。 自分のチームは主催の @ROSCA_kota さんと ジー ニーのPdMの方3名の5人チームでした。50分かなり濃度の濃い話をできて満足でした。 そして、本チームで考えた 「PMのMBTIを作る」 でした。 今回各チームが考えたものを投票をする形式でしたが、僅差ですが、 一位 となりました。(自分はツクルの大変なのわかったので、別チームに票を入れました) このア イデア ですが、これが本ブログの前置きの話につながります。 漫画、映画という抽象度が話より、『 ゴジラ 』『ワンピース』『 鬼滅の刃 』って決まってた方が深い話ができますよね? 私はイベントで対談する機会が多いのですが、そのたびに「当社のPdMの業務範囲はここです」といった前提の説明を必ず行うようにしています。 なぜなら、ロードマップや評価の話をする場合でも、前提となる業務領域が共有されていないと、「それが自分にとって参考になるのかどうか」が判断しづらいからです。 実際にPdMイベントに多く参加してきた中で、自分自身が感じた大きなペイン(課題)がこの「前提のズレ」でした。 要は、 これから『 ゴジラ 』の話をします! にしたい。 (ちなみに2025年5月現在、38本の ゴジラ 映画があるみたいです) PdMってかなり後発の役割だと思ったので、ChatGPT先生に聞いたら、こんな回答でした 以前にもリサーチしていて、なるほど思ったんですが、基本は   マーケティング とモノづくり の観点から誕生しています。 自分の解釈では以下の 「各職種領域」の中の「各業務」の一部を担っている のがPdMだと感じています。 「各職種領域」の中の「各業務」の一部が違うのがPdMをわかりづらくしている根底だと思っています。例えば以下のイメージが沢山あるのではないかと思います。 各職種領域の中の各業務を10個程度定義してみて、各PdMが自分がやっている業務に〇をつけてみる。すると大きく10~15個くらいのタイプにわけられるのではないか?と思っています。 さらにそのタイプは「製品フェーズ」「組織規模」「会社文化」「製品特性」で一定の共通点もある、あるいは全くない(どっちでも興味深い)のでは?と思っています。 そして、これがわかっていると同じタイプのPdMと話せばこれまで以上に前提情報が少なくても盛り上がることができるのではないかなと思っています。 最後に どうでしょうか?皆さん、作ってみたくなりませんか? 正直、これまで自分がやったアクションでは無理なんです。具体的なPdMの実務まではイベント等で聞くのは難しく、仮にできても膨大な時間がかかります。 ChatGPT先生によれば、 国内IT企業のPdMは1~3万人と推定 されるようです。 仮に2万人だとすると 信頼水準: 95% → 約370人 80% → 約160人 70% → 約25人 信頼水準70%くらいなら30人のイベントで実現できてしまいます。 是非、共感された方は「いいね」や本ブログをXで「 #PdM_MBTI 」 ハッシュタグ 付きで拡散してもらえればと思います。
こんにちは、 稲垣 です。 2025年度からはイベント参加や登壇だけでなく RAKUS Developers Blog | ラクス エンジニアブログ や Rakus Designers Rakus Designers にも積極的に投稿して行こうかなと思っています。 Xでもプロダクトマネージャーのこと、デザイナーのこと、マネジメント全般なことをポストしていますのでよろしくお願いします。 前回は「フィッシュボウル」参加について書きましたが、今回はその翌週に参加した「速読会」について書きます。 この会ですが、今PdMの中では話題になっています。参加した方々の声がブログにノートであがっています ● ログラスさんの速読会に参加してきた話 ● 4月30日にログラスさんの速読会に参加してきましたレポート ● 3時間で6冊読む!人生を100倍速にする「速読会」に参加しました ● 社内速読会を開催してみました 自分が参加したのは、4/30(水)の会です(上記、noteの tkt さんと同じ会です。) 参加のきっかけはX等でこの話題を見て、別のイベントでお会いした @tomosooon さんに声をかけてからです。 (ちなみに本イベントはconnpass等での募集はないです。) いざ速読会へその前に 速読会スタート ポイント 感想 今回速読会で読んだ他の本 最後に いざ速読会へその前に 準備としては、未読・ 積読 していた 紙の本を3冊 の用意が必要です。 自分は以下の3冊を用意しました。 ● カスタマーサクセス――サブスクリプション時代に求められる「顧客の成功」10の原則 362ページ ● ユーザーストーリーマッピング 327ページ ● 世界は感情で動く : 行動経済学からみる脳のトラップ 357ページ ページ数を書いてますが、本の内容によりますが300ページ超えると結構しんどいです 速読会スタート ざっくりは以下の流れです。 読みたい本の相手とペアになります(人数は偶数がいい) 自分の本を15分で読み切ります(とにかく読み切るのがポイントです) 読み切ったらペアの相手に2分半で本の内容を説明します 今度は相手の本を15分で読み切ります(説明を受けているので、少し読みやすいです) 読み切ったらペアの相手に2分半で本の内容を説明します 上記を3回繰り返しますので以下の通り約3時間です。  約3時間 =1セット 40分×3冊(セットごとで休憩各15分)   └ 自分の本:15分+(2.5分×2) + 相手の本:15分+(2.5分×2) さらに最後に以下の時間で読んだ本を思い出しながらまとめます(携帯でもPCでも何でもOK)   12分 =6冊×2分 今回は全体で30名程度実施しました ポイント ・相手への説明しないといけない、このゴールがあるため、頭をフル回転します ・目次から読む、まえがき、あとがきにまとめが書いてある等もありますが、このあたりは工夫次第 ・本は300ページ以内くらいがおススメです ・とにかく読み切るを意識するとよさそう ・同じ職種でやると本被るので、そこは注意 感想 ・ @tomosooon さんが仕事終わりに集まってひたすら、黙々と3時間ただ本を読む変態的な会ですと 話していましたが、まさにその通りでしたが学びは大きいです ・皆さんは、自社に持ち帰っていろんな工夫をしてやっているそうで、それは確かにと思いました 例えばこんな感じ  └ 今回はPdMだったが、他の職種と混合やってみる     ⇒職種の相互理解に効果がありそう  └ 1冊の本だけを複数の職種で速読会をやってみる     ⇒いろんな観点でまとめがでるため、本をより立体的に捉えられる ・さすがにしっかりは読み切れないので、「正読」するかの判断するために非常に良い ・自分では手に取らない本の発見にもなってりその点でも良い 今回速読会で読んだ他の本 ● マッチング理論とマーケットデザイン 233P └ダントツで難しかったが、自分では手に取らないので学びが大きかった ● サイゼリヤ おいしいから売れるのではない 売れているのがおいしい料理だ 212ページ └良かったので、自分で購入して正読する予定 ● チームトポロジー 価値あるソフトウェアをすばやく届ける適応型組織設計 354ページ └出たの時に買って読んで、その後再読してなかったので、再読予定 最後に @tomosooon さん及び会場を提供してくださった ログラス さんに感謝です また、一緒に参加したタイミーさんやログラスの皆さん、他の皆様も同じ学びができてよかったです 6月は抽選制でオープンに集めてやるようですので、まだ体験してない方は是非! loglass-tech.connpass.com
こんにちは、 稲垣 です。 2025年度からはイベント参加や登壇だけでなく RAKUS Developers Blog | ラクス エンジニアブログ や Rakus Designers note にも積極的に投稿して行こうかなと思っています。 X でもプロダクトマネージャーのこと、デザイナーのこと、マネジメント全般なことをポストしていますのでよろしくお願いします。 今回の内容は4/23(水)に「ゲスト」で参加したこちらについてです。 ※ PMフィッシュボウル 〜組織デザインってむずくない?編〜 非常に学びと良い経験ができたイベントだったので、書いておきます。 また、今後「フィッシュボウル形式」でのイベントや取り組みを検討している方へも参考になれば幸いです。 自分は「フィッシュボウル形式」は初で、「 OST 形式」は前職で経験がありました。ただ、今振り返れば OST 形式だったんだという程度でした。 人数はともかく、360度人に囲まれて話すという経験も初体験です。 余談ですが、 ログラスさん の新オフィスも初(以前の三田オフィスへ 対談イベント でお邪魔したことがあった)でしたが 、イベントに最適な綺麗なオフィスでした( 泉岳寺駅 からもすぐ!) ■「OST」と「フィッシュボウル」とは? ■『PMフィッシュボウル 〜組織デザインってむずくない?編〜』 ●前日談 ●当日 ■「 OST 」と「フィッシュボウル」とは? まず、そもそも「 OST 」や「フィッシュボウル」って何?って人もいるので、O3さんに教えてもらいます。 ざっくり、こんな感じです。 まとめると ・「多並列で自由に動く OST 」 と 「1テーマを集中討議するフィッシュボウル」  ⇒広げたいときは OST 、深めたいときは フィッシュボウル なんとなくわかりましたか?詳しくは色々調べて見てください ———————————————————————————————— ■『PMフィッシュボウル 〜組織デザインってむずくない?編〜』 では、実際の中身に入っていきます……と言いたいところですが、具体的な内容はオフレコな話題が満載だったため、濃すぎて書ききれません。 そこで今回は、雰囲気やゲスト・参加者としての感想、そして今回の進め方を中心にお伝えします。 ●前日談 折角なので、前日談からです。 まず始まりですが、開催日の1.5ヶ月前くらいにXのDMで Joeさん から、「公開 OST 登壇」の依頼を貰いました。 主催者と自分以外のゲストを教えて頂き、1分考えて快諾しました。その後、2週間後に30分程度オンラインミーティングして、当日に至ってます。 時期的に TVer さんも ラク スもPdM周りの組織に大きな変化があり、その話の情報交換をして、これ以上ここで話すと、これだけでイベントができそうなので、それを骨子にするで決まりました。 Joeさん と yokomichi さん 、は「 発散と発散で収束させず結論は出さない 」という話くらいでした ●当日 ラク スはこの時期、通年の評価時期でして、自分は評価ミーティング後、 ダッシュ で滑り込みで到着しました 入った直後に、 Joeさん にこの話をしたら、フィッシュボウルで後半では見事にその話になりました。そのくらいシナリオなしの感じでした。 当日は ラク スからは6名ほど参加(何の因果か元 ラク スの方も参加していた) 事前に参加者にはslackへの参加が案内され、そこでは質問やら何やら五月雨のような投稿が、、、 1時間足らずで1,000を超える投稿が、最初は見てたが諦めました 基本は司会の Joeさん とフォローの yokomichi さん に身を預けるスタイル 様々な会社のPdMが自社の話や自分の体験を話す(同時に複数のイベントのLTを聞いた感覚) 話は  ミドルの上位からシニアや組織マネージャークラスのPdMの話  かつ、 toB 、 toC 、規模や製品フェーズも違うわ、単一プロダクトからマルチプロダクト 様々 聞いているだけで、体力と知力の消耗が激しかったです。 そこから間髪入れずに懇親会へ。 参加者のレベルも高く、フィッシュボウルの話からそのまま懇親会に入ったため、会場のあちこちでミニフィッシュボウルが開催されているような状態でした。 何かよくわからない達成感のなかで、皆さん会場を後にされたと思います。 話せる範囲で内容を共有すると、大きくはこの2つのテーマでした PdMを取り巻く組織デザイン。PdMは開発側・事業側・その中間のどこに置くべきか? PdMの評価はみんなどうしているのか? こちらは当日の模様 ※ Xより ●感想 疲れるが、かなり心地よい疲れ。 発散、発散、また発散…なのに、不思議とモヤモヤが残らない。 準備はいらないが、「話す覚悟」は必要。 ファシリテーター の重要性と、そのキャスティングもかなり大事。 社内でやってみるのも面白そう。 ● ラク スから参加したPdMメンバーの感想 あんなにぶっちゃけた社内の事情は聴けない。とんでもない情報量で聞いて理解するのが精いっぱいだったが、組織のことをあんなに真剣に考えているもの、視座、視野の広さを知れたのは良かった。今後のマルチプロダクト構想に対して準備しておくことがわかった気がした プロダクトマネージャーの組織/立ち位置として、色々聞いてなんやかんか今のPdM-PMM体制自体は結構いいのではと思った。作る(開発)/届ける(事業部)で役割分担しやすいため。PdM-PMMが何らか共通のKPIを追う構図になっていれば、PMという共通の役割で動けるので更に良いかも(+それぞれの責任範囲のプロセスや成果を評価)一方でレポートライン(事業責任者)が事業部側にいる都合一定の不均衡は起きる可能性はある(良し悪しではなく)  第2回「EM/PMフィッシュボウル」編、6月2日(月)にやるそうです 6/2月、みなさん空けておいて下さい! 前回の #PMフィッシュボウル の好評を受け、次回はパウリさん( @pauli_agile )と一緒に「EM/PMフィッシュボウル」を開催します!! EMとPM、違う立場と視点から見た「組織デザイン(価値創造)」について、白熱の議論をぶち かまし ます! 近日connpassは公開! pic.twitter.com/KZ8RzcdSi4 — Joe Hirose | ログラス ProductHR (@JoE_HiRose) 2025年4月29日 是非、皆さん体験してみてください。(自分も参加予定です)
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スでは社内独自指標での開発生産性指標の計測を行ってきましたが、今年度からFindy Team+を開発本部全体に導入しFour Keysをベースとした計測 1 に切り替えてきました。 今回はFindy Team+導入に関する記事を書こうと思います。 導入と経緯 課題 経緯 ファインディ社との関わり 開発生産性カンファレンス 指標のハックについてはひとまず考えない Findy Team+ 検討、仮決定 内製でやるのは結構大変そう 試用期間 2チームでスモールスタート 全チーム本格導入 今後やっていきたいこと 導入と経緯 課題 ラク スではもともと 工数 見積もりをベースにした独自の生産性指標を利用していました。 ただしこの独自指標には以下のような問題が生まれていました。 見積もりを過大にすることで数値をハックできる 見積もり精度を高める取り組みは重視されていなかった 個人目標に利用されていた 特に1つ目については、見積もりを過大にすることで見かけの成果物の量が多くなり、見かけ上の指標値を良くすることができました。 指標値を良くするために過大見積もりを行う、というほど悪意のある行動はなかったかと思いますが、見積もりを小さくするための取り組み――技術的負債の低減、見積もり精度を上げて作業バッファを低減する、など――に対する取り組みが、数値上の成果に繋がらないため行いにくくなってしまっていました 2 。 経緯 そんな中、 DORA によって発表されていたState of DevOps Reportに出てくるFour Keys 3 が書籍『 LeanとDevOpsの科学 』の影響もあり注目されてきました。 LeanとDevOpsの科学[Accelerate] テクノロジーの戦略的活用が組織変革を加速する impress top gearシリーズ 作者: Nicole Forsgren Ph.D. , Jez Humble , Gene Kim , 武舎広幸 , 武舎るみ インプレス Amazon Four Keysを知った当初は、 ラク スのサービスがBtoBであることもあり、1日に何度も本番環境にデプロイするなどの状況がイメージできない 4 事もあって導入に関しては懐疑的でしたが、その後 SPACEフレームワーク の考え方を知ることで、「Four KeysもSPACEの(特にPerformance)を軸にした指標群の一例であり、成果物の産出量、レビュー数、割り込みの発生頻度(≒不具合発生頻度)など 複数の観点で計測することが重要で、これらがそれぞれカウンター指標として機能する 」という考えに至りました。 queue.acm.org これにより、自社で計測する指標としてFour Keysをベースとしながら、自社に合わせた指標にカスタマイズして採用していくことにしました。 オリジナルのFour Keysとの大きな違いは「本番環境へのデプロイ」を軸にするのではなく「開発者からの手離れ」を軸に置き換えています。 デプロイの頻度 本来は「変更を本番環境にデプロイした回数」 ラク スでは「コードレビューを終えて、メインブランチにマージされた回数」 変更のリードタイム 本来は「最初の変更コミットから本番環境にデプロイされるまでの所要時間」 ラク スでは「最初の変更コミットからメインブランチにマージされるまでの時間」 変更失敗率 本来は「デプロイに対して、本番環境で不具合が発生する割合」 ラク スでは「プルリク エス トに対して、不具合修正プルリク エス トの割合」 サービス復元時間 本来は「本番不具合発生から正常な状態に戻るまでの時間」 ラク スでは「不具合修正プルリク エス トがマージされるまでの時間」の予定 5 特に「デプロイの頻度」という項目ではフィーチャーフラグなどを駆使して、本番デプロイ回数の計測のままにしようかとも検討しましたが、フィーチャーフラグでオフにしているのであれば結局はユーザーへの価値提供はできていないため、ここにこだわる理由は見つけられませんでした 6 。 指標までは検討できたので、実際の計測基盤をどうするかという段階に話が進みます。 ファインディ社との関わり 開発生産性カンファレンス 多少時間は前後しますが、新たに開発生産性を計測していくにあたって2023年に開催されたファインディ社が主催する「開発生産性カンファレンス」に参加しました。 このカンファレンスで行われたニコール・フォースグレン博士の発表にて、当時はまだ今ほど知られていなかったSPACE フレームワーク について理解を深めることができました。今ならネットで検索したり、ChatGPTに聞けば教えてくれるはず。 指標のハックについてはひとまず考えない また社内で導入することを考えると指標のハックについても話題に上がることを考えて、カンファレンスの懇親会でお話させていただいた各社に実際の運用を聞いて回りました(話に付き合っていただいた皆様ありがとうございます)。 各社の運用を聞いていると 「ある程度ハックされることは想定しているが、あまり気にしないようにしている」 「極端なハックは逆に仕事がしにくくなると思うのである程度で抑制されると考えている」 といった回答が多かったです。 確かにマージ件数を増やすためにプルリク エス トを細分化すると言っても、1行ずつ分割してたらレビュアーに怒られると思いますし 7 、開発業務のオーバーヘッドは逆に増えて面倒になると思います。 Four KeysやSPACE フレームワーク の考え方の則って取り組む場合には、複数の指標を用いてそれらがある程度カウンター指標となることが考えられるので、ひとまずは余計なことは考えずに導入を進めることにしました。 Findy Team+ 検討、仮決定 というわけで自社でどんな計測を行いたいのかがある程度固まってきたので、計測ツールについての検討を始めました。いくつかの開発生産性計測サービスと内製によるシンプルな計測と比較していく中で Findy Team+ を提供するファインディ社にも問い合わせを行いました。 何社か話を聞いていく中で、コスト面で折り合いがつかなかったり、開発体制の厚さが ラク スの基準に合わなかったりという形で残ったのが、Findy社の Findy Team+ でした。 内製でやるのは結構大変そう 内製についても検討しましたが、 GitHub からデータを収集し、「マージ回数」「1日あたりマージ頻度」「PRあたりの平均リード時間」を算出するくらいまでは作成したものの、社内で運用保守していくにあたって 片手間でやるには大変で、専任メンバーを割り当てるのも悩ましい 8 ということがわかり、専任メンバーを当てるコストを考えたら クラウド 型の計測サービスの利用を優先的に考えたほうが良さそうだという判断を行いました。 組織がもっと大きくなり、専任のチームを設置できるくらいの規模であればできそうですが、 ラク スではまだ少し厳しそうです。 試用期間 Findy Team+の利用を仮決定したものの、実際に導入して(想定した運用の手間で)運用可能なのかという判断が必要になります。 Findy社に問い合わせて試用をお願いして、Team+を実際に触る機会をもらいました。 場合によっては内製化というパターンも完全には消えていないので、生産性計測に何が必要なのかという学習も兼ねて各機能を触っていきました。 ちょっと機能が豊富すぎて「オーバースペックかなぁ……」という感想は抱きましたが、一方で 主要指標で問題の芽を見つけたときに原因分析していくことを考えるとある程度細かく数値を取れていないと難しそう(=ある程度の機能が必要) だな、という感想も持ちました。 また、試用期間中にも新機能のリリースが頻繁に行われており、なにか機能要望があったときの反映にも期待できそうだという感想を覚えました。 2チームでスモールスタート その後、比較的新しい2つのチームに対して、試験的にFindy Team+を導入しました。 これらのチームは開発生産性に興味を持っていたということに加えて、比較的モダンな 開発プロセス を採用しており、細かな開発ルールについても柔軟に改善できる段階にありました。指標をもとに議論を始めやすいフェーズと考え、導入効果が見えやすいのではという仮説のもと選出しました。 当初の見立てではチームに計測の概念が浸透し、実際のアクションや改善につながるまでには少なくとも半年はかかるだろうと予想していました。 しかし、導入から2ヶ月目あたりで早くもいくつかの数字に変化が現れ始め、3ヶ月目には改善効果として有意な差が見られるという予想以上のスピードでチーム内の行動が改善していることが観察されました。 具体的には プルリク エス トのサイズが小さくなり、プルリク エス トあたりのリードタイムが短縮 。プルリク エス トごとのレビュー難易度が低減されたため、 偏りのあったレビュアーが分散され、レビュー待ちの ボトルネック が解消される 、といった傾向が見られました。 全チーム本格導入 導入済みチームで数字に変化が現れ始めたことを受けて、社内へのさらなる展開を進めるためにフローの整備を始めました。 単にツールを導入するだけではなく、「なぜこのツールを使うのか」「どんな成果が期待できるのか」を社内にしっかり伝え、各チームの導入モチベーションを高めることが重要だと考えています。 そこで、まずは 導入済みチームで実際にどのような効果が出ているかを紹介する勉強会を企画 しました。 この勉強会では、Findy社にも全面的にご協力いただき、開発生産性の考え方や使い始めたときのコツ、導入済みチームで出ている成果を第 三者 視点から紹介してもらう機会を設けることができています。 プルリク エス ト作成数やプルリク エス トのレビュー時間などの具体的な改善事例を共有しつつ 数字をもとにした改善ポイントの見つけ方 改善サイクルの回し方 についても紹介してもらいました。 実際に成果が出ているチームの様子や、Findy社からのリアルな説明を受けたことで、「自分たちのチームでも試してみたい」「改善に役立てられそうだ」という前向きな反応を引き出すことができました。 この取り組みをきっかけに、興味を持ってくれるチームの導入モチベーションを刺激することができたと思います。 興味をもってくれているチームから順次導入する ことで社内文化として定着させ、スムーズに浸透させていければと考えています。 今後やっていきたいこと 現在、導入済みのチームでは指標上の数値が徐々に改善傾向を見せており、一定の成果は見え始めています。 ただし、数値が改善しているからといって必ずしも現場の体感としても「良くなった」と感じられているとは限りません。 今後はメンバーへの ヒアリ ングを実施し、主観的な働きやすさやチームの健康状態にも目を向けていく予定です。 特に SPACE フレームワーク における「S(Satisfaction and well-being)」の観点を意識し、数値だけでは拾いきれない現場の声を把握しながら指標と実態のズレがあれば、それを補正していきたい と考えています。 まだFindy Team+の導入が進んでいないチームに対しても、適切なタイミングでフォローを続けていく必要があります。 単なるツールの押し付けにならないよう、各チームの状況や課題感に寄り添いながら、必要性を一緒に整理していく支援を進めていきたいと思っています。 queue.acm.org Four Keysを元にしていますが、厳密にはFour Keysではありません。おおまかにはデプロイを基準にしている部分を、レビュー後のマージに置き換えた考え方で定義しています。当初は本来の定義を変えることに疑いがあったのですが、SPACE フレームワーク の考え方に則って考えれば問題ないだろうと判断しています。 ↩ 実際は各種改善は行われていましたが、用いていた生産性指標の上では成果が現れないため、評価者が個別に取り組みを評価する手間が発生していた。 ↩ Four Keysはソフトウェアデリバリーのパフォーマンスを表す4つの指標ですが、2022年版以降には追加の指標として運用パフォーマンスを表す「信頼性」が提唱されています。 ↩ サービス利用されている顧客の中には自社内で利用マニュアルを整備していることも多々あり、事前通知が必要だったり、頻繁な新機能リリースやデザイン変更はクレームの元になると考えています。 ↩ サービス復元時間の計測はまだ本格的には行えていません。 ↩ こまめにデプロイすることでデプロイを日常業務として効率化を図る、という観点は捨てがたいところもありましたが、これを実現するのは生産性計測の取り組みではなくデプロイの仕組みやデプロイフローの見直しを行うべきで、それが終わるまで開発生産性が上がらないというのも計測の意味がなくなるので現状はこのようにしています。もちろん毎日でもデプロイできる仕組みができたら計測の基準を見直すかもしれません。 ↩ 私も説教すると思います。マージ件数をカウントする意味とか、プルリク エス トを細分化する意味とか、レビュー効率の話とか、逆に長引くくらいに。 ↩ 社内に開発生産性のプロを抱えたいというわけではないので……。 ↩
はじめに こんにちは、エンジニア3年目のTKDSです! 最近 MCP が盛り上がってます。 流れに乗ってGoでやる方法を調べて試してみました! まず簡単に現在時刻を返す MCP サーバーを作ったあと、割と実用的に使えそうなファイルを連結して返す MCP サーバーを作っていきます。 今回書いたコードの リポジトリ です。 https://github.com/tkeshun/mcp はじめに MCPとは? 今回使用するライブラリ 現在時刻を返すMCPサーバー 1. プロジェクトの準備 2.コード作成 3. パッケージのダウンロードとビルド 4. 試す 特定のディレクトリ以下のファイルを返すMCPサーバー まとめ MCP とは? MCPのドキュメント によると MCP is an open protocol that standardizes how applications provide context to LLMs. 要するに、LLMが外部リソースとアクセスする約束事を決めてくれたみたいです。 MCP についての詳細はは他に詳しい記事がたくさんあると思うのでそちらに譲ります。 では早速つくっていきましょう! 今回使用するライブラリ 今回使用するライブラリは mcp -goです。 github mcp server で使われてたので採用しました。 go.mod https://github.com/mark3labs/mcp-go では、実装に移っていきます。 現在時刻を返す MCP サーバー 今回はサーバー経由で計算して結果を返すのでtoolsを使います。 toolsには、利用可能なツール一覧を表示する tools/list と実際に叩かれるtools/callが必要なようです。 Discovery: Clients can list available tools through the tools/list endpoint Invocation: Tools are called using the tools/call endpoint, where servers perform the requested operation and return results mcp -goでは mcp .NewTool関数を使えば作れそうです。 では実際に作ってみましょう。 Goでの準備方法書いてますが知ってる人は飛ばしてOKです。 1. プロジェクトの準備 mkdir mcp-time-server go mod init mcp-time-server 2.コード作成 以下のような感じです。 サンプルを参考に書きました。 AddToolsの定義見た感じ、処理の関数はserver.ToolHandlerFunc型を満たしていればよさそうです。 まず、NewMCPServerで MCP サーバーを作ります。 次に、 mcp .NewToolで登録するtoolの素を作ります。 そして、s.AddToolでtoolと処理を紐付けて登録します。 最後に標準出力を受け付けるようにサーバー起動します。 package main import ( "context" "fmt" "time" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { s := server.NewMCPServer( "時刻答える君" , "0.0.1" ) currentTimeTool := mcp.NewTool( "current_time" , mcp.WithDescription( "現在時刻を返します" ), ) s.AddTool(currentTimeTool, func (ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error ) { jst := time.FixedZone( "Asia/Tokyo" , 9 * 60 * 60 ) now := time.Now().In(jst) message := fmt.Sprintf( "現在の時刻: %s" , now.Format( "2006-01-02 15:04:05" )) return mcp.NewToolResultText(message), nil }) if err := server.ServeStdio(s); err != nil { fmt.Printf( "サーバーエラー: %v \n " , err) } } 3. パッケージのダウンロードとビルド MCP サーバーを起動したときのPathの関係とかがよくわからないので、今回はビルドして MCP クライアントを起動する場所におきました! go mod tidy go build -o current-time-server 4. 試す inspector を使います。 README.mdに載ってるconfig. json をもとに適当に書き換えました。 一回起動してみると、 everything が選択肢にあったのでおそらくそこがサーバー名です。なので、そこ以下の値を書き換えていきます。 項目は Github Copilot Chatの設定と変わらないので、特に迷うことはないと思います。 commandに起動するバイナリ名、argに引数、envに 環境変数 を書きます。項目名にちょっとDockerfileっぽさを感じます。 今回はcommandだけで大丈夫です。 { " mcpServers ": { " current-time ": { " command ": " current-time-server ", " args ": [] , " env ": { } } } } ファイルの配置はこんな感じです。 . - current-time-server(Goのバイナリ) | - config.json では起動します。 npx @modelcontextprotocol/inspector --config ./config.json --server current-time で起動できます。 ↓出力 $ npx @modelcontextprotocol/inspector --config ./config.json --server current-time Starting MCP inspector... ⚙️ Proxy server listening on port 6277 🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀 起動するとこんな画面になります。 Connectを押して、List Toolsボタンを押すと、以下のような画面になります。 tools/listエンドポイントを叩いて、ツール一覧を取得してるようです。 表示されるメッセージは、 currentTimeTool := mcp.NewTool("current_time", mcp.WithDescription("現在時刻を返します"), ) に書いたやつなので、 mcp -goでは、 mcp .NewToolの宣言時に書いたものがlistで返される値になるようです。 current_timeを押すと、以下のようにRun Toolボタンが表示されます。 これを押すと mcp サーバーが実行されます。 無事現在時刻が表示されました!👏 以上が MCP サーバーの簡単な作り方でした。 特定の ディレクト リ以下のファイルを返す MCP サーバー AI Agent搭載エディターはファイル探してコンテキストに取り込んでくれたりしますよね? ただ、探す時間が長かったり、検討違いしてたりするケースがままあります。 そこで、最初からほしいコードを返してくれる MCP サーバーがあればいいのでは?と思い、作ろうとしました。 ソースコード は以下に載せておきます。 指定した ディレクト リのファイルを検索し、返してくれる処理を実装してます。 設定ファイルを外出しして、Goプログラムをいじらずにエンドポイントを作れるようにしました! package main import ( "context" "encoding/json" "fmt" "log/slog" "os" "path/filepath" "strings" "github.com/bmatcuk/doublestar/v4" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) type QueryConfig struct { Name string `json:"name"` // クエリ名 Description string `json:"description"` // MCP説明 Dir string `json:"dir"` // 環境変数ROOT_ENVからの相対パス PathPattern string `json:"path_pattern"` // パスパターン(glob) } func loadConfig(filename string ) ([]QueryConfig, error ) { file, err := os.ReadFile(filename) if err != nil { return nil , err } var cfg []QueryConfig err = json.Unmarshal(file, &cfg) return cfg, err } func concatFilesWithGlob(root, pattern string ) ( string , error ) { var builder strings.Builder matches, err := doublestar.Glob(os.DirFS(root), pattern) if err != nil { return "" , err } for _, match := range matches { fullPath := filepath.Join(root, match) data, err := os.ReadFile(fullPath) if err != nil { continue } builder.WriteString(fmt.Sprintf( "==== %s ==== \n " , match)) builder.Write(data) builder.WriteString( " \n\n " ) } return builder.String(), nil } func main() { // 環境変数取得 rootDir := os.Getenv( "ROOT_DIR" ) if rootDir == "" { panic ( "環境変数 ROOT_DIR が未設定です" ) } configPath := os.Getenv( "CONFIG_PATH" ) if configPath == "" { slog.Error( "CONFIG_PATHが未設定" ) } // MCPサーバー初期化 s := server.NewMCPServer( "Dynamic MCP Server" , "1.0.0" ) // 設定ファイル読み込み configs, err := loadConfig(configPath) if err != nil { slog.Error(fmt.Errorf( "設定ファイル読み込み失敗: %w" , err).Error()) } // 各ツールを登録 for _, cfg := range configs { tool := mcp.NewTool(cfg.Name, mcp.WithDescription(cfg.Description), ) localCfg := cfg // クロージャで固定 // rootDir + cfg.Dir に解決(再帰探索のベース) fullSearchRoot := filepath.Join(rootDir, localCfg.Dir) s.AddTool(tool, func (ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error ) { result, err := concatFilesWithGlob(fullSearchRoot, localCfg.PathPattern) if err != nil { return mcp.NewToolResultError(fmt.Sprintf( "ファイル探索エラー: %v" , err)), nil } return mcp.NewToolResultText(result), nil }) } // 起動 if err := server.ServeStdio(s); err != nil { fmt.Printf( "サーバーエラー: %v \n " , err) } } config. json は次のようになってます。 ROOT_DIRに対象にしたいプロジェクトのルート ディレクト リのパスを書きます。 GONFIG_PATHにはクエリや走査対象の ディレクト リを書きます ↓config. json { "mcpServers": { "current-time": { "command": "./current-time-server", "args": [], "env": { } }, "file-finder": { "command": "./mcp-file-finder", "args": [], "env": { "ROOT_DIR": "./test", "CONFIG_PATH": "./finder-config.json" } } } } GONFIG_PATHのほうにはエンドポイント名、LLMへの説明、走査対象 ディレクト リ、マッチさせるファイルパターンを書きます。 ↓finder-config. json [ { "name": "docs_query", "description": ".vscode以下の情報を取得します。vscodeなどの設定がほしい場合に使用してください", "dir": "./github-mcp-server/.vscode", "path_pattern": "**" }, { "name": "e2e_code_query", "description": "ソースコードファイルを読み込みます。E2Eテストのコードです。", "dir": "./github-mcp-server/e2e", "path_pattern": "**/*.go" } ] 実際に叩いてみると以下のようにファイル内容を取得できます。 まとめ 今回は MCP サーバーを2種類作ってみました! mcp -goは非常に簡単に MCP サーバーを作れ、Goバイナリにできるため、配布・使用が非常に簡単です。 2つ目のプログラムについては実用性も非常に高いと思います! 記事を見た皆さんもぜひ試してみてください!
こんにちは、プロダクト部副部長の 稲垣 です。 2025年4月から、プロダクトデザインの組織とプロダクトマネージャーの組織が、同じ「プロダクト部」という部門に統合されました。 マルチプロダクトでサービスの開発・運用を行う企業にとって、「製品づくりの組織デザイン」をどう構築するかは、各社が試行錯誤を重ねているテーマだと思います。本記事が、少しでもその参考になれば幸いです。 この記事では、以下の4点について紹介します: ラク スにはどんな製品があり、どのような組織体制なのか デザイナーとプロダクトマネージャーは、これまでどのように連携してきたのか 今回、なぜ「プロダクト部」が立ち上がったのか 「プロダクト部」はどのような役割を担うのか ラク スはどんな製品を提供しているのか 2025年4月現在、 ラク スでは10個の製品を提供しています( ラク スライト クラウド 提供の2製品を含む)。最も古い製品は2001年にリリースされ、最も新しい製品は2024年10月に提供を開始しました。製品ごとに成り立ちの時期には大きな差があります。そのうち半数の製品はARR(年間経常収益)で25億円を超えており、最大の製品では100億円を超える規模となっています。 どんな組織体制になっているのか ご覧のとおり、組織は大きく「開発部門」と「事業部門」に分かれており、その中に各商材ごとの組織が存在しています。 開発部門は主に拠点別(東京と大阪)に分かれており、それとは別に「横断開発部門」も設けられています。 PdM(プロダクトマネージャー)やQA(品質保証)は、商材ごとの開発組織内に数名配置されているケースもあれば、PdM・QAとして組織化されているケースもあります。プロダクトデザインについては横断開発部門内に集約されており、すべての商材を対象とした一つの組織として機能しています。 デザイナーとプロダクトマネージャーはこれまでどう連携をしていたのか プロダクトの4階層をベースに役割分担を定義すると、以下のようになります。 ここまできれいに分担されているケースは稀ですが、書籍や他社のプロダクトチームの役割分担を一般化すると、このような形になると考えています。 実際には、企業の組織体制や製品フェーズによって、一人が複数の役割を担うことも多く、以下のようなケースもよく見られます。 コア領域において、事業責任者がPMM(プロダクト マーケティング マネージャー)やリーダーPdM(プロダクトマネージャー)を兼任 ディスカバリー において、UXデザイナーが不在のためPdMがその役割を担う デリバリー(開発)において、PdMがPjM(プロジェクトマネージャー)を兼任 デリバリー(GTM:市場投入)において、PMMが不在でPdMがその役割を担う ラク スにおいても、役割分担は製品によって大きく異なるのが現状です。 PdM組織は2021年8月に新設され、「楽楽精算」を皮切りに本格的なPdMの組織化が始まり、約3年半が経過しました。 ※PdM組織(製品管理課)の新設については こちら をご覧ください(マルチプロダクトのPdMのリアルについては こちら ) PdM組織が立ち上がったことにより、 プロダクトデザイナー の役割も以下のように変化しました。 ——————————————————————————— ■PdM新設前 ・ ディスカバリー では、開発の上流やPMMと単独で連携 ・デリバリーでは、開発と直接連携 ■PdM新設後 ・ ディスカバリー では、デザイナーとPdMがPMMやCS/営業と連携しながら、お客様インタビューも実施 ・デリバリーでは、開発と連携(必要に応じてPdMがサポート) ——————————————————————————— PdM新設後、 プロダクトデザイナー はよりデザイン業務に集中したり、 ディスカバリー に積極的に参加したりできるようになりました。 その結果、理想とする役割分担の形に、少しずつ近づきつつあります。 今回、何故プロダクト部が立ち上がったのか 前置きが長くなりましたが、ここからが本題です。 今回、なぜこのタイミングで「プロダクト部」が新設されたのかについてご説明します。 まず前提としてお伝えしたいのは、たとえ組織(部)が別であっても、さまざまな取り組みを推進することは可能だということです。 実際に「楽楽精算」では、横断組織と拠点別の組織が別部門で運営されていましたが、それでも プロダクトデザイナー の役割分担の見直し・進化を実現できました。 ただし、これと同じ取り組みを同じ組織内で行っていたとすれば、より早く、スムーズに実現できた可能性があるとも考えています。 この考え方が、これから述べる「発足経緯」の前提になります。 この3つに集約されます。 1.複数サービス利用推進 楽楽シリーズではこれまで、バックオフィスのDX化に向けて「ベスト・オブ・ブリード型製品開発戦略」を採用してきました。 そして今後も、この戦略を継続していく方針です。 楽楽シリーズは、バックオフィス業務を効率化・DX化する製品を提供する中で、複数の製品をご利用いただくお客様が増えてきました。 シリーズには、従業員全体が利用するような「楽楽精算」や「楽楽勤怠」のような製品もあれば、特定の部門で使われる「楽楽販売」のような製品もあります。 これらの製品は、サービス開始時期が異なるだけでなく、「ベスト・オブ・ブリード型製品開発戦略」を採用したことにより、同じ楽楽シリーズであってもUXが各製品に最適化されすぎており、シリーズ全体としての一貫性に課題が生じるケースもあります。 また、 ラク スは2023年10月に以下のような発表を行いました。 「 ラクスが展開するバックオフィス向けクラウドサービス2023年10月よりブランド統一し、コミュニケーションを刷新 」 こうした背景を踏まえ、楽楽シリーズをご契約いただいているお客様や、これからご検討いただくお客様が、複数サービスをよりスムーズにご利用いただけるように、ブランド統一と、お客様のメンタルモデルを揃えるようなデザインへの移行が必要になってきました。 そのためには、デザイナーとPdMがこれまで以上に強く連携し、各製品間の連携もこれまで以上に意識して取り組んでいく必要があります。 これが、プロダクト部発足の経緯のひとつです。 2.UXの重要性 先に説明したとおり、 ラク スが提供している製品は、最も古いもので2001年、最も新しいもので2024年10月と、製品の成り立ちには大きな開きがあります。 また、各製品が属する市場の成熟度にも大きな差があります。 これは一般論ですが、 市場が成熟してくると、多くの製品の機能は コモディティ化 (機能の同質化)していきます。 そして、 コモディティ化 が進むと、価格競争が激化する傾向にあります。 ここでは価格競争そのものについては触れませんが、「製品価値」という観点で考えると、機能が同質化している状況下では「UX(ユーザー体験)」が差別化要因となり得ると感じています。 新しい機能を開発することももちろん重要ですが、UXを継続的に改善していくには、それを推進するためのリソース確保が不可欠です。 その際、デザイナーとPdMが同じ組織にいることで、お客様のUXに関わる課題を、同じ解像度で把握しやすくなります。 その結果、より高い価値を、より早く提供することが可能になります。 このような考えも、プロダクト部発足の背景の一つとなっています。 3.AI x SaaS の推進 2023年3月にGPT-4を搭載したChatGPTが登場して以降、業務生産性の向上だけでなく、生成AIの製品活用も一気に加速しました。 それと同時に「AIエージェント」という概念も徐々に注目されはじめ、特に2024年以降は、「 Microsoft Copilot」「 Google Gemini」「Notion AI」など、業務支援を目的としたエージェントが本格的に実装され始めました。 さらに2024年3月に「Devin」が登場したことにより、AIエージェントへの関心は一層高まりました。 最近では、AIエージェントやツール同士がモデルとやり取りするための MCP (Model Context Protocol) が話題となり、Anthropic社が オープンソース として公開しています。 さらにそれを補完する形で、 Agent2Agent(A2A) プロトコル が2025年4月9日に Google 主導(50社以上のテク ノロ ジー パートナーと共に)で発表されました。 これにより、異なるAIエージェント間の相互運用性が実現され、複雑なタスクを協調して処理できる未来が現実味を帯びています。 つまり、今まさに以下のような未来が近づいています: MCP : 一人の人間がさまざまなツールを使いこなし、何かを成し遂げる世界 A2A: 複数の“ツールを使いこなすAIエージェント”同士が協力し合い、人間を支援しながら何かを成し遂げる世界 前置きが長くなりましたが、このような世界において、 SaaS も「人間が使うツール」から「AIエージェントが利用するツール」へと役割が広がっていきます。それに伴い、これまでの「人間に最適化されたUX」だけでなく、「AIにフレンドリーなデザイン」も必要になってきます。 つまり、デザインの難易度がこれまで以上に高くなるということです。 当社の現在の製品管理課(PdM)は、エンジニア出身者が多く、テク ノロ ジー としてのAI理解に強みがあります。そのPdMとデザイナーが連携を取ることで、デザイナー自身のAIに対する理解や解像度も高まり、 人間とAIの双方に最適なデザインの提供 が可能になると考えています。 以上が、プロダクト部発足の経緯となります。 「プロダクト部」はどのような役割を担うのか こちらが、プロダクト部の ミッション 、 ビジョン 、そして 活動領域 です。 活動領域については、「 プロダクトマネジメント ・トライアングル」を用いて示しています。 先ほどご紹介した「一般的プロダクトチームの役割分担」において、 リーダーデザイナー/PdM 、 UXデザイナー/PdM は、 同じ活動領域 に属しているべきだと考えています。 Visonの()記載にしている「UX志向」については以下のように推奨する行動と推奨しない行動を明文化をしています。 明文化することで、製品づくりにおける前提となる考え方をすり合わせることができ、その上で、互いの専門性をぶつけ合える関係性が築かれることを期待しています。 どんな組織体制になっているのか プロダクト部の組織体制は、以下のようになっています。 プロダクトデザイン課は、担当する商材に応じて3つの課に分かれており、 ラク スが展開する全10商材を担当しています。 一方、PdM(プロダクトマネージャー)は1つの課で4つの商材を担当しています。 「一般的プロダクトチームの役割分担」については、 ラク スにおいても製品の状況によって異なっており、製品管理課としてのPdMが関わっていない商材も多く存在します。 プロダクト部はどんなことをするのか 具体的な内容は製品戦略に関わるため、ここでは詳細をお伝えできませんが、これが立ち上げ初期に取り組んでいる内容になります。 すでにプロダクトデザイン組織については、別の記事で以下のような取り組みを進行中である旨を発信しております。 『ラクスのプロダクトデザイン組織紹介 ― 顧客価値を高める新たな挑戦』 『プロダクトデザインの継続的UX改善への道のり 〜お客様の業務課題を解決するUI/UXを提供しつづけるために〜』 これらの取り組みに加え、今後はさらに「重点取り組み事項」を追加し、推進していく予定です。 ラク スとして、これまで以上に「デザイン」と「テク ノロ ジー 」を融合させ、最高のUXを製品に提供し続けてまいりますので、ぜひご期待ください。
はじめに 皆さん!初めまして! 楽楽請求新卒エンジニアの kaihatsuda です。 本記事では、Kotlin のテスト フレームワーク Kotest に使われている Kotlin の特徴的な記法や技術を紐解いていきます! (本記事は Kotest の 使い方解説 ではなく、その背後にある Kotlin の 技術 を理解することに焦点を当てていますので、ご了承ください。) 私たちが開発する 楽楽請求 では、サーバーサイドの実装に Kotlin を採用しています。テストコードの記述も開発サイクルに欠かせない重要な工程の一部として位置づけられており、品質を担保するための必須要素になっています。特に、テストコードの 簡潔さ や 可読性の高さ は、効率的な開発において非常に重要です。 楽楽請求では、 単体テスト を記述するために Kotest を採用しています。私自身、学生時代に 単体テスト を書く経験がほとんどなく不安でしたが、配属後に初めて触れた Kotest で以下のようなテストコードに触れたとき、その直感的な書き方に感動しました。 初めて触れた Kotest のコードのイメージ class MySpec : FunSpec({ context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } context( "四則演算の異常系" ) { test( "0で除算すると算術エラーが投げられる" ) { shouldThrow< ArithmeticException > { val result = 1 / 0 } } } }) このコードを見たとき、私はこう思いました。 「なんだこの shouldBe とか context とかいう書き方... でも、めちゃくちゃ可読性が高いし直感的に書けてすごい!」 一見すると、まるで普通の日本語の 箇条書き のように見えませんか? context("四則演算の正常系") の中に、 test("正しく加算できる") がある (2 + 2) shouldBe 4 は「2+2は4であるべき」という意味のテストである 正常系と異常系を文脈で分けて記述している Kotlin を知らなくても、何をテストしているのか直感的に理解できそうです。 でも疑問も浮かびます 「 shouldBe はまるで 演算子 みたいにスペース区切りで呼べるのは何故?」 「なんで波括弧 {} の中に context と test が並んでるの? なぜ呼び出せるの?」 「 shouldThrow<ArithmeticException> はどうやって型をチェックしているの?」 こうした Kotest の "魔法のような記法" を支えているのは、Kotlin の infix 、 拡張関数 、 レシーバ付き ラムダ式 、 inline & reified といった特徴的な機能です。(その他にも様々な技術が使われていますが、今回はここまでに留めます) はじめに 本記事のゴール 本記事で読み解くコード例 それぞれの記法の解説 1. infix 2. 拡張関数 3. レシーバー付きラムダ式 4. inline & reified 4.1 inline 4.2 reified まとめ 本記事のゴール 本記事では、 Kotest の「魔法のような記法」に着目し、その裏側で活用されている Kotlin の特 有機 能を解説します。具体的には、 infix によるスペース区切りのメソッド呼び出し 拡張関数 による既存クラスへの振る舞い追加 レシーバ付き ラムダ式 を使ったスコープ風の記述 inline & reified を活用した ジェネリクス の型情報保持 といった機能が、Kotest の柔軟で読みやすい記法を支えている仕掛けです。 そこで本記事では、Kotest を参考に作成した 簡易ライブラリ を例に取り、これらの機能が実際にどのように使われているかをコード例を示しながらひとつひとつを紐解きます。 本記事の最終的なゴールは、以下の2点です。 Kotlin の各機能(infix, 拡張関数, レシーバ付き ラムダ式 , inline & reified)の役割と使い方を理解する それぞれの機能が「なぜ必要か」「どんな恩恵があるか」を知り、自分のコードでも応用できるようにする。 Kotest の DSL が「不思議」に見える仕組みを納得する Kotest の ソースコード に実際にどう活かされているかをイメージできるようになり、より深いレベルで Kotest を使いこなせるようになる。 これにより 「Kotest の使い方」ではなく、「Kotest が成立する背景の Kotlin 技術」 を学びます。もしご自身のプロジェクトでテスト DSL (Domain-Specific Language) を拡張したり、別の場面で同様の記法を取り入れたくなったときに、本記事の内容がヒントになれば幸いです。 本記事で読み解くコード例 本記事では、解説のためKotestライクな簡易版のテストライブラリを用意しました。シンプルですが、使用例の通り Kotest っぽいテストの記述を実現しています。今後本記事では、以下のライブラリを 簡易ライブラリ と呼びます。 // レシーバ付きラムダ式で DSL を定義 fun miniSpec(block: MiniSpec.() -> Unit ): MiniSpec { return MiniSpec().apply { block() } } class MiniSpec { fun context(description: String , block: MiniSpec.() -> Unit ) { println( "Context: $description " ) block() } fun test(description: String , block: () -> Unit ) { println( "Test: $description " ) block() } } // infix & 拡張関数によるアサーション (型安全化) infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } // inline & reified を用いた例外検証 inline fun < reified E : Throwable > shouldThrow(block: () -> Any ?): E { val thrown = try { block() null } catch (e: Throwable ) { e } return when { thrown == null -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but none was thrown." ) thrown is E -> thrown else -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but ${ thrown :: class .simpleName } was thrown." ) } } // 使用例 fun main() { miniSpec { context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } context( "四則演算の異常系" ) { test( "0で除算すると算術エラーが投げられる" ) { shouldThrow< ArithmeticException > { val result = 1 / 0 } } } } } それぞれの記法の解説 1. infix 簡易ライブラリの main 関数には以下のような記述があります。 test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } 「 (2 + 2) shouldBe 4 」と書かれている部分は、Kotest ユーザーにはおなじみの書き方ですが、初めて見ると違和感があります。まるで shouldBe が 演算子 であるかのように見えますよね。 これは、infix 関数 と呼ばれる Kotlin の機能を使うことで「 . と () を省略」しているだけです。 infixの概要 Kotlin には、特定の条件を満たした拡張関数やメンバー関数に infix キーワードを付けると、 obj.methodName(arg) という呼び方を obj methodName arg のように 演算子 風 に書ける仕組みがあります。 公式ドキュメントでも「 infix notation (omitting the dot and the parentheses) 」と紹介されており、次の3つの要件を満たせば使用できます。 メンバー関数または拡張関数であること パラメータが1つだけであること 可変長引数やデフォルト引数を持たないこと 簡易ライブラリでは、 infix は以下のように利用されていました。 infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } 簡易ライブラリの通り、不思議に感じていた shouldBe は単なる関数であることが分かります(厳密には後述する拡張関数が利用されています)。そのため、 (2 + 2).shouldBe(4) として実行することも可能です。スペースを使った関数呼び出しが出来る infix によって、英語のように直感的に読めるテストコードが記述出来ていた、というわけですね。 2. 拡張関数 先ほど紹介した shouldBe は infix 関数であると同時に、 拡張関数 として実装されています。拡張関数を利用することで、既存のクラスに対して新しい振る舞いを追加できます。 拡張関数を定義するには、レシーバ型(拡張したい型)を関数名の前に付け加えます。以下は、 MutableList<Int> に要素を入れ替えるための swap 関数を追加する例です。 fun MutableList < Int >.swap(index1: Int , index2: Int ) { val tmp = this [index1] // 'this' corresponds to the list this [index1] = this [index2] this [index2] = tmp } ここで、拡張関数内の this は、関数が呼び出された対象の インスタンス を指します。上記の swap 関数は、 MutableList<Int> に対して次のように呼び出すことができます。 val list = mutableListOf( 1 , 2 , 3 ) list.swap( 0 , 2 ) println(list) // 出力: [3, 2, 1] 以上が拡張関数の簡単な紹介です。但し「クラス内部に実際に新たに関数を追加しているのではなく、あくまで振る舞いを拡張するのみ」という点に注意して下さい[ 参考 ]。 Extensions do not actually modify the classes they extend. By defining an extension, you are not inserting new members into a class, only making new functions callable with the dot-notation on variables of this type. では、改めて簡易ライブラリにおける拡張関数の利用例を見てみます。 infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } shouldBe 関数は、 ジェネリクス 型 T を拡張する形で定義しています。そのため、Kotlin のすべての型に対して呼び出し可能です。さらに型制約 U : T とすることで、 expected 引数が T と互換性のある型であることを保証しています。これにより、型安全性を保ちながら直感的な アサーション を実現しています。 3. レシーバー付き ラムダ式 次は「 miniSpec { ... } や context { ... } の波括弧がどうなっているのか?」という部分です。 以下のような階層的なテスト構造が Kotlin 特有の機能を活用して実現されています。 miniSpec { context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } .... } 見た目としては「波括弧 {} を多重に使った単純な 入れ子 構造」のように見えますが、実際には Kotlin 特有の仕組みである レシーバ付き ラムダ式 を活用しています。 ラムダ式 を引数に取る関数で「 (レシーバ型).() -> R の形」を受け取った場合、その ラムダ式 の中ではレシーバを this (省略可)で扱うことができます。(先ほど説明した拡張関数においては、拡張したい既存クラスがレシーバー型に相当します) 言葉だけでは分かりにくいので、通常の ラムダ式 と、レシーバ付き ラムダ式 を具体例から比較してみます。イメージのしやすさのため、スコープ関数の also と apply を比較します。 通常の ラムダ式 (例: also) public inline fun <T> T.also(block: (T) -> Unit ): T { contract { callsInPlace(block, InvocationKind .EXACTLY_ONCE) } block( this ) return this } // `also`: 通常のラムダ式を使用 val user2 = User( "Alice" , 25 ).also { it.name = "Bob" // レシーバではなく、`it` で明示的に参照 it.age = 30 } レシーバ付き ラムダ式 (例: apply) public inline fun <T> T.apply(block: T.() -> Unit ): T { contract { callsInPlace(block, InvocationKind .EXACTLY_ONCE) } block() return this } val user1 = User( "Alice" , 25 ).apply { name = "Bob" // レシーバとしての `this` が省略可能 age = 30 } also と apply を比較すると、次のような違いが分かります。 also : 通常の ラムダ式 を使用し、 it で対象オブジェクトを明示的に参照。 apply : レシーバ付き ラムダ式 を使用するため、 this が暗黙的に参照され、プロパティを簡潔に操作可能。 このように、レシーバ付き ラムダ式 によって、渡す側の ラムダ式 で this を暗黙的に使う(省略する) ことが出来ます。 簡易ライブラリにおけるレシーバ付き ラムダ式 の使い方を見てみます。 fun miniSpec(block: MiniSpec.() -> Unit ): MiniSpec { return MiniSpec().apply { block() } } class MiniSpec { fun context(description: String , block: MiniSpec.() -> Unit ) { println( "Context: $description " ) block() } fun test(description: String , block: () -> Unit ) { println( "Test: $description " ) block() } } レシーバ付き ラムダ式 の観点から解説すると以下のようになります。 miniSpec(block: MiniSpec.() -> Unit) : MiniSpec をレシーバとする ラムダ式 ( MiniSpec.() -> Unit )を引数として受け取り、そのスコープ内でテストを定義可能にする。 apply を使って ラムダ式 を実行し、 MiniSpec のメソッド( context() , test() )を直接呼び出せるようにする。 context(description: String, block: MiniSpec.() -> Unit) : context() で、 ラムダ式 を MiniSpec.() -> Unit を受け取ることで、さらにネストされた test("...") を同じレシーバ (= MiniSpec) で呼び出せる このような仕掛けのおかげで、「 context の中でさらに test 、その中で アサーション 」など、自然な階層構造を作れる DSL 的な書き方になっています。 4. inline & reified 最後に紹介するのは、例外検証を簡潔に行うために用いた inline と reified です。 Kotest の shouldThrow<T>() と同じように、簡易ライブラリでは次のように記述できます。 shouldThrow< ArithmeticException > { val result = 1 / 0 } この 型を指定して例外を検証する 部分が inline & reified で実現されています。 4.1 inline Kotlinでは、 コンパイラ が状況に応じて匿名クラスに変換するケースがあります。 ラムダ式 はその一例ですが、これにより、関数呼び出しに関わるオーバーヘッドが発生します。 一方、 inline キーワードを使うと、 コンパイラ が関数の呼び出しを呼び出し元のコードにインライン展開(埋め込み)するよう最適化します。つまり、わざわざ匿名クラスを作成して実行するのではなく、元のコードに直接 ラムダ式 の処理を書き込むイメージです( 参考 )。 公式ドキュメント( Inline functions )の冒頭でも「インライン関数は呼び出しのオーバーヘッドを削減してくれる」ことが紹介されています。 これにより: ラムダ式 呼び出しに伴うオブジェクト生成のオーバーヘッドが消える 後述する reified が使用可能になる というメリットが得られます。 4.2 reified Kotlin では、 Java と同じく 型消去(type erasure) の概念があります。 ジェネリック 型パラメータ( T や E など)は コンパイル 時にチェックされるだけで、実行時にその型情報が失われます。 例えば以下のようなコードで、実行時には「 List<Int> と List<String> の違いが分からない」などの状況が起こります。 fun checkList(list: List < Any >) { // 実行時点では list の型パラメータが消えているので // どんな要素型か判別できない... } しかし インライン関数の型パラメータ に reified を付与すると、 コンパイラ が「型情報を削除せずに持ち回る」ようにコードを生成します。その結果、実行時に thrown is E -> thrown のように型情報を参照できるようになるわけです。(ここを深く追いかけると記事が膨大になりそうなので、別の機会に回します。ご了承ください) 先ほどの簡易ライブラリ内の shouldThrow 関数をもう一度見てみましょう。 inline fun < reified E : Throwable > shouldThrow(block: () -> Any ?): E { val thrown = try { block() null } catch (e: Throwable ) { e } return when { thrown == null -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but none was thrown." ) thrown is E -> thrown else -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but ${ thrown :: class .simpleName } was thrown." ) } } inline によって、 ラムダ式 をインライン展開 する(=> オーバーヘッド削減 + reified 利用が可能) reified E によって、実行時に「 thrown is E -> thrown 」で「期待していた例外クラスか」を判定できる。 つまり shouldThrow<ArithmeticException> のように書くだけで、「もし投げられた例外が ArithmeticException と違ったら?」とか「そもそも例外が投げられなかったら?」というケースを簡潔に検証できるわけです。 まとめ Kotest で出てくる "不思議な記法" は、実は Kotlin 標準の機能を巧みに組み合わせた結果でした。 本記事では簡易版テストライブラリを自作してみることで、以下の機能を一通り確認しました。 infix 「 object methodName arg 」のように . と () を省略できる技術 拡張関数 「既存クラスに関数を生やせる」機能で、Kotest での アサーション ( shouldBe ) などが直感的に書ける レシーバ付き ラムダ式 ラムダ式 の中で this を特定のオブジェクトに紐づけ、 DSL のような階層的構文を実現できる inline & reified ラムダ式 の呼び出しオーバーヘッドを抑えつつ、型情報を実行時にも参照できるので、 shouldThrow のように「型を明示して例外チェック」を簡潔に実装可能 こうした言語機能を知ると、Kotest の公式 リポジトリ を眺める際も「あ、ここは拡張関数で実現してるのか」と発見が得られるかもしれません。(mockkの returns や every など) 実際に Kotest のコードを追ってみると、もっと洗練された書き方や工夫された実装が散りばめられていますので、興味を持たれた方はぜひ覗いてみてください。 最後までお読みいただきありがとうございました。
目次 はじめに Prometheusとは ハンズオン環境を構築しよう Prometheusを触ってみよう Prometheusによる監視の全体像をつかもう まとめ はじめに このブログの目的 (と、ごあいさつ) こんにちは。SREの gumamon です! 最近、 Kubernetes を使う現場がどんどん増えてきました。 Kubernetes は自律的にいろいろ動いてくれる分、「今なにが起きているのか」を把握するのが意外と難しいです。 特に、構成が動的に変わる Kubernetes では、 サービスディスカバリ機能 のある監視ツールが欠かせません。 そんな中で、 Kubernetes のメトリクス監視といえば、今や Prometheusが デファクトスタンダード です。 このブログでは、Prometheusがどのように Kubernetes の情報を集めているのかを、ハンズオン形式で体験しながら理解していきます。 「とりあえず動かして理解したい!」という方は、ぜひ一緒に試してみましょう! 対象読者と前提知識 本記事は、 Prometheusの初学者 を対象としています。 「これからPrometheusを触ってみたい」「仕組みを理解しながら学びたい」といった方に向けて、基本的な概念や構成をハンズオン形式で解説していきます。 ハンズオンの内容を円滑に進めるために、以下の知識・環境を前提としています: Kubernetes の基本的な コンポーネント (Pod、Service、Nodeなど)についての理解 本記事では詳細な Kubernetes 解説は行いませんが、基礎的な用語を把握していることが望ましいです。 Kubernetes自体を構成するコンポーネント についても概要を把握できていると理解がスムーズです。 Dockerの実行環境が手元にあること(必須) ハンズオンでは kind (kubernetes in docker) を用いて仮想的な kubernetes 環境を構築します。 実行環境は Linux が推奨ですが、 Mac やWSL( Windows Subsystem for Linux )でも問題ありません。 Prometheusとは Prometheusの概要 Prometheusは、CNCF(Cloud Native Computing Foundation)によってホストされている、 オープンソース のモニタリングツールです。 もともとは SoundCloud 社によって開発され、現在では クラウド ネイティブな監視基盤として広く採用されています。 主な用途は、 メトリクス(数値データ)ベースの監視 です。対象となるシステムからメトリクスを定期的に収集し、それを時系列データとして保存・可視化・アラートの発火などに活用します。 特徴的なのは、Prometheus自身が監視対象に プル型でアクセス し、HTTPエンドポイントからメトリクスを取得する点です。この仕組みにより、監視対象を柔軟に検出・更新できるため、構成が頻繁に変化する Kubernetes との相性が非常に良くなっています。 また、Prometheusは専用のクエリ言語「PromQL」を備えており、柔軟な集計・フィルタ・可視化が可能です。Grafanaなどの可視化ツールと組み合わせることで、強力な監視 ダッシュ ボードを構築することができます。 参考: Prometheus/OVERVIEW/What is Prometheus? Prometheusの特徴と強み Prometheusには、 Kubernetes をはじめとする クラウド ネイティブな環境に適したさまざまな特徴があります。以下に、代表的な機能や強みを紹介します。 プル型によるメトリクス収集 監視対象に対してPrometheusが定期的にアクセスし、HTTPエンドポイントからメトリクスを取得します。これにより、構成変更に強く、柔軟な監視が可能になります。 サービスディスカバリとの統合 Kubernetes やConsulなどと連携し、監視対象を自動で検出・更新することができます。 Kubernetes のように動的に変化する環境と特に相性が良いです。 シンプルなメトリクス形式(テキストベース) メトリクスは人間が読めるテキスト形式で提供されるため、開発者や運用者が直接確認・ デバッグ しやすいという利点があります。 強力なクエリ言語(PromQL) メトリクスの集計やフィルタ、演算を柔軟に記述できる専用のクエリ言語が用意されており、複雑な条件での監視や可視化も対応可能です。 豊富なExporterエコシステム OSや ミドルウェア 、 クラウド サービスなどを対象とした多数のExporterが公式・非公式に提供されており、必要な監視対象をすぐにカバーできます。 また、独自アプリケーション用のExporterも容易に作成できるため、カスタムメトリクスの取り込みも柔軟に対応できます。 Grafanaとの親和性 PrometheusのデータソースはGrafanaで広くサポートされており、グラフや ダッシュ ボードを簡単に構築できます。 Alertmanagerによるアラート機能 条件を満たすメトリクスに対してアラートを発火し、メールやSlackなどに通知することができます。アラートのグルーピングや抑制も可能です。 軽量かつ単一バイナリで動作 インストールや構成が比較的簡単で、導入のハードルが低い点も魅力です。 NOTE ハンズオンでは以下のExporterを使用します kube-state-metrics : Kubernetes リソース(Pod、Deployment、Nodeなど)の状態情報をメトリクスとして提供するExporterです。 クラスタ ー全体の構成や状態を把握するために利用されます。 node-exporter : 各ノードのCPU使用率、メモリ、ディスク、ネットワークなど、OSレベルのハードウェア/システムメトリクスを収集するExporterです。 ハンズオン環境を構築しよう ハンズオン環境の説明 本ハンズオンでは、以下の GitHub リポジトリ を使用します: gumamon/test-prometheus:v0.1.1 以下は、最終的に構築される環境の構成図です: prometheus-handson-diagram 構成図の補足 Kubernetes クラスタ は kind によりDocker上に構築されています Ingress NGINX Controller により、ローカルからのアクセスが可能です サンプルアプリケーション(go-metrics-sample)がメトリクスを提供します Prometheusは3つの コンポーネント で構成されています: prometheus-server : メトリクスの収集・保存・提供を行う本体 node-exporter : 各ノードのハードウェア/OSメトリクスを提供 kube-state-metrics : Kubernetes リソースの状態をメトリクス化 各ExporterはPrometheusにより自動的に発見・監視されます 構築手順 1) ハンズオン環境の GitHub リポジトリ をローカルにcloneしてください: git clone https://github.com/gumamon/test-prometheus.git 2) v0.1.1にcheckoutしてください git checkout v0.1.1 3) リポジトリ 内の README/Getting Started に従って構築してください: NOTE この リポジトリ は、 Kubernetes (kind)上にPrometheusとExporter、サンプルアプリケーションをデプロイし、監視の基本を体験できる構成になっています。 構築には docker, kind, helm, helmfile を使用します。 Prometheusを触ってみよう 無事にハンズオン環境は構築できましたか?早速Prometheusを触っていきましょう! PrometheusのWeb UIを開いてみる Prometheusをデプロイすると、Web UI を通じて現在の状態や収集したメトリクスを確認することができます。 ブラウザで以下のURLにアクセスしてください: http://prometheus-server.example.com NOTE この ドメイン は、ハンズオン環境に構成された Ingress によってローカルにルーティングされています。名前解決がうまくいかない場合は、 /etc/hosts にエントリを追加してください。 PrometheusのWeb UIでは、次のような情報を確認できます: ステータス情報(Targets、Configuration など) 現在のメトリクス一覧 PromQLクエリの実行結果 アラート状態(Alertmanagerと連携している場合。※今回は連携していません。) 収集したメトリクスを確認してみる Web UIが開けたら、まずは [ Query > 右上の「︙( 三点リーダ ー)」メニュー > Explore metrics ] を開いて、現在のメトリクス一覧を確認してみましょう。 prometheus-explore-metrics 以下のようにMetricのリストが出力されているはずです。 これが現在Prometheusが収集しているメトリクスの一覧になります。 prometheus-metric-list 次に、PromQLを使って実際にメトリクスをグラフ化してみましょう。 ここでは例として、 各ノードのCPU使用率 を確認してみます。 1) PrometheusのWeb UI上部にある [ Graph ] タブを開きます 2) クエリ入力欄に以下のPromQLを入力します: rate(node_cpu_seconds_total{mode="user",node="gumamon-worker"}[5m]) 3) [ Execute ] ボタンをクリックすると、下部に数値が表示されます 4) 表示形式を [ Graph ] に切り替えることで、CPU使用率の推移をグラフで確認できます prometheus-query-graph NOTE 上記のクエリは、5分間隔でCPU使用時間の変化量を示すもので、対象を以下条件で絞り込んでいます。 mode="user" によってユーザーCPU時間を対象にしています。 node="gumamon-worker" によってWorker Nodeを対象にしています。 グラフが4本あるのは cpu={0,1,2,3} があるためです。 Dockerを通じてローカル環境のCPUコア数が透過的に見えています。 このように、Prometheusではクエリを使って詳細なメトリクス分析が可能です。 必要に応じて条件を絞ったり、集計関数を組み合わせたりして、柔軟な監視が行えます。 Prometheusによる監視の全体像をつかもう Exporterが収集する情報の流れ Prometheusの概要で述べたとおり、Prometheusは監視対象に プル型でアクセス し、HTTPエンドポイントからメトリクスを取得します。 早速どのようなHTTPエンドポイントから情報を収集しているかを確認してみましょう。 収集先のエンドポイントを確認する手順 1) PrometheusのWeb UIにアクセスします: http://prometheus-server.example.com 2) 上部メニューから [ Status > Target health ] をクリックします 3) Targets画面では、現在Prometheusが監視している各エンドポイントの一覧が表示されます。 ここには以下のような情報が含まれます: Exporterのラベル名(job名) 対象のエンドポイント( IPアドレス :ポート) 現在のステータス(UP / DOWN) 最終スクレイプ時刻やレスポンスタイム prometheus-targets NOTE 本来、 Endpoint のリンクをクリックすると、実際にそのExporterが提供している 生のメトリクス(テキスト形式) をブラウザで確認することができるのですが、ハンズオン環境では閲覧することができません。 Endpoint はprometheus-serverが Kubernetes の中から観測した IPアドレス / ドメイン であり、 Kubernetes の外にあるブラウザからはアクセスができない為です。 監視対象がどう登録されているか Prometheusが監視対象をどのように把握しているか、その仕組みを理解するためには、設定ファイルの内容を確認するのが近道です。 Prometheusでは、監視対象(Target)の登録を scrape_configs というセクションで定義します。 ハンズオン環境では、以下のコマンドで prometheus-server のConfigMapを確認することができます: kubectl -n prometheus describe cm prometheus-server | less ConfigMap内には、Prometheusの設定( prometheus.yml )が格納されており、その中に scrape_configs の定義があります。 以下はその一部抜粋です: scrape_configs : - job_name : prometheus static_configs : - targets : - localhost:9090 - job_name : kubernetes-apiservers bearer_token_file : /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs : - role : endpoints relabel_configs : - action : keep regex : default;kubernetes;https source_labels : - __meta_kubernetes_namespace - __meta_kubernetes_service_name - __meta_kubernetes_endpoint_port_name scheme : https tls_config : ca_file : /var/run/secrets/kubernetes.io/serviceaccount/ca.crt サービスディスカバリの仕組み ここで注目すべきは、 kubernetes_sd_configs という設定です。 これは Kubernetes クラスタ 内のリソース情報を自動的に取得して、監視対象(Target)を動的に登録するための仕組み です。これが、いわゆる サービスディスカバリ(Service Discovery) に該当します。 role: endpoints により、 Kubernetes 内の Service + Endpoints を対象とします relabel_configs を使って、指定条件(NamespaceやService名、Port名など)に一致したものだけをフィルタし、監視対象に登録します たとえば上記の設定では、以下の条件を満たすエンドポイントだけが登録されます: Namespaceが default Service名が kubernetes Port名が https このようにして、Prometheusは 静的なIP指定ではなく、 Kubernetes リソースに基づいて柔軟に監視対象を管理する ことができます。 構成の変化にも自動で追従できるため、 Kubernetes のような動的環境では非常に効果的なアプローチです。 参照: prometheus/configuration/#kubernetes_sd_config Kubernetes との連携ロジックの俯瞰 これまでの項目で、Prometheusが scrape_configs を通じて Kubernetes クラスタ 内の監視対象を動的に検出し、Exporterからメトリクスを収集していることを確認してきました。 ここでは、Prometheusが Kubernetes とどのように連携しているのかを、図を用いて俯瞰してみましょう。 prometheus-flow 図中の番号に沿って、Prometheusの処理フローは以下のように進みます: 1) Service Discovery の開始 Prometheusは、ConfigMapに記述された kubernetes_sd_configs に基づき、 Kubernetes API Server に対して対象リソース(この場合は Endpoints)の情報を問い合わせます。 2) 監視対象エンドポイントのフィルタリング API から取得した情報に対し、 relabel_configs の条件を適用し、 実際に監視する対象 (IP/ポート)を選別します。 3) Exporterへのメトリクス収集リク エス ト 対象が決定すると、Prometheusは定期的にそのエンドポイントに対してHTTPでアクセスし、Exporterが提供する メトリクスをPull型で収集 します。 4) 収集したメトリクスの保存と可視化 取得したメトリクスはPrometheusサーバー内で時系列データとして保存され、Web UIやGrafanaを通じて可視化・分析されます。 prometheus-query-sample このように、Prometheusは Kubernetes の API を活用することで、 クラスタ の構成変化に自動的に追従し、動的な監視を実現しています。 設定は一度行えば済み、Podの追加や再起動にも自動で対応できる点が大きな利点です。 PodのEndpointをディスカバリする仕組みについての補足 先ほど例に挙げた go-metrics-sample の監視は以下の job:kubernetes-pods を利用しています。 prometheus-target- kubernetes - pods kubernetes - pods の scrape_configs は以下の条件に一致したPodのIP/PortをEndpointに追加します。 PodのAnnotationに以下があること prometheus.io/scrape: "true" #Scrape対象に含める (trueの場合対象) prometheus.io/port: <ANY> #Scrape対象のPort prometheus.io/path: <ANY> #Scrape対象のPath このため、以下のようにManifestに記載することで、Podをデプロイする担当者が任意にPrometheusのEndpointを公開することができます。 # Source: go-metrics-sample/templates/deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : go-metrics-sample labels : app : go-metrics-sample spec : replicas : 2 selector : matchLabels : app : go-metrics-sample template : metadata : labels : app : go-metrics-sample annotations : prometheus.io/scrape : "true" prometheus.io/port : "8000" prometheus.io/path : /metrics まとめ 今回は、Prometheusを使って Kubernetes 上のメトリクスを収集・可視化する仕組みを、ハンズオン形式で確認してきました。 Kubernetes の構成は日々変化するため、「どこで何が動いているのか」を把握するには、サービスディスカバリのような仕組みが欠かせません。 Prometheusはその点でとても相性が良く、Exporterやクエリ言語(PromQL)などを組み合わせることで、柔軟な監視を実現できます。 この記事が、Prometheusの基本的な動作や Kubernetes との連携のしくみを知るきっかけになっていれば嬉しいです。 NOTE メトリクスの可視化については、Prometheus単体ではなくGrafana等を使う構成が一般的かと思います。 Prometheusは 冗長化 を前提とした設計が為されていないという問題もあります。 参考: prometheus/overview/#when-does-it-not-fit Prometheusと各種サービスの連携については以下のブログにまとめています。 OSSでオブザーバビリティを実現する (Grafana Stack x OpenTelemetry on Kubernetes) もし興味がありましたら、合わせてご一読いただければと思います。 以上、最後までお読み頂きありがとうございました!
はじめまして、楽楽販売新卒エンジニアのomegumiです。 少し前に、社内で「脳に収まるコードの書き方」の輪読会が開催されました。 (輪読会とは、複数の人で同じ本を読み、その内容について意見を交わす読書会です) 初学者視点でもたくさんの学びがあったので、 コーディング経験に関係なく大事そうだと感じた学びと個人的に業務で取り入れたことについて 書いていきたいと思います。この記事を通して、私同様に「 プログラマ って何から勉強していけばいいんだ」「技術書とか設計って怖い…」と思っている方を少しでも後押しできれば幸いです。 読んだ書籍「脳に収まるコードの書き方」 今回、社内の輪読会で読んだ書籍はこちらです。 www.oreilly.co.jp 読んだ書籍「脳に収まるコードの書き方」 プログラマに求められる仕事と経験 「脳に収まるコードの書き方」から気づけた、プログラマとして目指すべき姿 まず"ソフトウェアエンジニアリング"って何? ではソフトウェアエンジニアリングの”技”とは何か? 小まとめ(結局何をやったらいい?) 「脳に収まるコード」とは? 関心事をどうまとめるか Before(関心事がまとまっていない例) After(関心事を分離し、整理した例) 小まとめ(脳に収まるコードを書くには?) 最後に プログラマ に求められる仕事と経験 本書を読んだことで得られた学びに、「 プログラマ の本当の仕事とは何か」「何をどう勉強していけばいいのか」があります。 結論から言うと「どういう状況で、どんな手法を選択すべきか判断する/できるようになること」だと私は思ったのですが、なぜ本書からそういう考えに至ったかを説明します。 「脳に収まるコードの書き方」から気づけた、 プログラマ として目指すべき姿 私は ラク スに入社するまで、チーム開発やビジネス目的の開発経験が希薄でした。そのため「 プログラマ として求められる振る舞いはどんな内容か、ビジネス組織における プログラマ の職責とは何か」を入社後によく考えるようになりました。 この書籍を読み始めるまでは、以下のような漠然とした目標で止まっていたと感じます。 可読性の高いコードを書く 変更しやすいコード設計を考える バグや想定外の挙動がない機能を作る 技術面の ボトルネック や問題解決を行う もちろん、良いコードを知り、ソフトウェアに求められる機能を早く実装できるようになることは重要だと思います。むしろ、当初の私はその手法や理想系を知るために勉強していました。 が、途中からこのような課題に直面し始めました。 ====================== 学んだ内容を業務で活かそうと考えていても、実装後や先輩に指摘をもらうまで「本にあった手法がハマるケース」だったことに気づけない ====================== この原因こそが、次で説明する「ソフトウェアエンジニアリングの”技”」が身に付いていないことであり、本書で「 プログラマ の職責」とされていた組織的な開発の鍵でした。 まず"ソフトウェアエンジニアリング"って何? みなさん、コードを書くときにソフトウェアエンジニアリングを意識したことはあるでしょうか?私は学んだことすら(さわりしか)ありません。書籍に出てきた説明をまとめると、次のように言えそうです。 ソフトウェア開発を純粋なアートから方法論へと転換しよう *1 という試み 最終的には、「仮説検証に基づいた 決定論 的なプロセス」を目指す理論 難しそうに聞こえますが、著者は 「 決定論 的なプロセス(=この状況なら絶対にこの手法!と断言できる方法)」は、現実の開発現場には存在しない と述べていました。つまり、ソフトウェアエンジニアリングだけを極めても、実際の プログラマ の仕事として十分とは言えない、ということです。 では プログラマ として何が必要なのか、本書からの学びを私なりにまとめたものが以下になります。 決定論 的なプロセスを見つける(完璧なソフトウェアエンジニアリングを体現する)以上に、 さまざまな技法を知り、ビジネスや状況に応じた適切な選択ができるようになる ことが重要。 だからこそ、 「ソフトウェアエンジニアリングの”技”」の部分で悩み、力を発揮することが プログラマ の職責。 ではソフトウェアエンジニアリングの”技”とは何か? 結論から言うと、ソフトウェアエンジニアリングの”技”とは「状況を見て判断、経験で判断」することです。個人の経験や判断頼りは組織的な開発に良くないという話だったのに、一体どういうことなのか。 キーとなる著者の考えを以下に抜粋します。 エンジニアリングが完全に 決定論 的プロセスなら、人間は必要なくなります。コンピューターと産業ロボットがあれば十分でしょう。 (出典:脳に収まるコードの書き方p252 [原書:Code That Fits in Your Head]) 適用できる山ほどの方法論があります。それでも、自分の脳を使う責任は無くなりません。スキルと適切なプロセス、経験則、技術を組み合わせることが仕事です。 (出典:脳に収まるコードの書き方p253 [原書:Code That Fits in Your Head]) 上記も踏まえて、自分なりの解釈を含めてまとめるとこんな感じです。 現状、状況ごとに「固定の理想/ルール」となる手法はない でも適用できる手法(方法論)は、山ほどある だからこそ、 「どういう論理に基づいて、その手法を選択するのか?」という思考プロセスを学ぶ ことが重要で、 プログラマ として得るべき経験とは「状況判断と手法選択のための 判断経験 」 プログラマ の職責とは、「どういう技術課題に、何の手法が最適かを判断する」こと(それにより、プロダクトの品質を担保する) つまり、「自身の脳を使い判断する」、「その判断を責任もってコード及びプロダクトに反映させる」ことが、 プログラマ の職責であるということです。 ここから考えると、課題だった「学んだ内容を実際のコードに活かせない状態」は、手法は学べている(=知識はある)が、それを採択するに至るために必要な思考プロセスが備わっていない(=経験が伴っていない)状態と捉えることができます。一度、「こういう時に使うのか!」という経験をしたり、「どういった理由・状況で採択される手法なのか?」という視点・思考プロセスを学ぶ必要があるということです。 小まとめ(結局何をやったらいい?) 方法論はどんどん勉強していい (例: カプセル化 ,関心ごとの分離手法,〇〇駆動開発など。) 定量 的視点での評価手法を学ぶ (例:テストによる0/1,チェックリスト使用,複雑度計測など。書籍に詳しい紹介がありますので、ぜひ読んでみてほしいです) 成果物をビジネスや目的に沿ったものにする開発手法も学ぶ (例:自動化,Gitの使い方など。こちらも書籍に詳しい紹介があります。ぜひ読んでみてください) 書籍では「組織として開発」のスタンスで話が進んだため、「 ステークホルダー の納得を得やすくする」ことの重要性・方法についても各所で説かれていました。コードや技術課題に対する判断力だけでなく、 ステークホルダー やビジネスサイドを踏まえた「職業 プログラマ としての判断力」も身に着けていくということです。以下は、特に「 プログラマ の職責」とされそうな部分を、自分なりにまとめたものになります。 ビジネスサイドの状況も鑑みて「技術的に譲れない・実装すべき部分はどこか」を判断する 技術的に譲れない=技術者視点でのMUSTについて、説明・納得・合意のための道筋を考える クリティカルな操作/機能を最速で動くようにする ⇒成果物の形を最速で業務目的に近づける(動く大枠から作る) ビジネスとコード双方のMUSTを担保できるラインの見極め 「脳に収まるコード」とは? 書籍「脳に収まるコードの書き方」では、組織的な開発の鍵として「人間の脳に優しい、読みやすいコード」すなわち「脳に収まるコード」を書く手法が主題となっていました。そこで、ここからは「脳に収まるコード」とはどうすれば書けるのかについて、自分のような初学者でも実践できた内容をまとめます。 関心事をどうまとめるか 読んですぐに実践できた内容として、コードを書く場所を決める際の判断手法があります。 コーディングにおける関心事とは、関心を払わなければならない変数/オブジェクトの詳細や、コードが目的とする処理タスクや機能が当てはまります。つまり、「脳の関心を惹くことと」と言い換えられます。人間の脳に優しいコードを書くためには、役割ごとに整理する手法を知ることが重要ということです。 私が書籍から学び取った、重要な判断基準は以下2つです。 どんなものも、与える役割は「専用の機能1つ」だけ 人間の脳は7つまでしか覚えられない ※著者によると、人間が一度に覚えておける容量の限界が「7」までであり、それ以上に処理や情報が増えた場合は、詳細すぎるとして切り分けを考えた方が良いとしていました。 まとめると、コードの一塊についてそれぞれシンプルな日本語で説明できる状態を保ち、一塊に含まれる詳細は7つまでに抑えることが重要ということです。これは、メソッドやクラス、 API 、プルリク エス トの文章などの様々な関心事に適用できるため、業務で実践できたことをデフォルメ化してお伝えしたいと思います。 例えば以下のように、チョコレート生成についてのクラスがあった場合、どのように修正すべきなのか考えてみました。 Before(関心事がまとまっていない例) class ChocolateFactory { public function makeChocolate($cacaoBeans) { // 準備:カカオ豆の種類ごとに担当パティシエを決定 // 加工:カカオ豆を焙煎、チョコに成型する処理 // 確認:できたチョコの品質検査を行う処理 return $chocolates; } } // チョコレートの作成・出力 $maker = new ChocolateFactory(); $chocolates = $maker- > makeChocolate(); このコードには、以下のような問題があります。 1つのことを理解したい場合でも、全てを読むことになる これは、1つのメソッド内で複数の工程について、関心事の詳細が全部入っているためです。例えばチョコ作りで通る工程をざっくり理解したい場合でも、「ここは準備の処理で、ここは確認の処理っぽい…」と順に全てを読むことになり、非常に大変です。ここから、すでに複数の役割を持たせてしまっていることがわかります。 各処理の再利用・変更がしにくい 例えば、焙煎処理だけコーヒー生成でも使いたい場合や、特殊な成型処理を追加したい場合です。 makeChocolate() 内ですべてくっついているため、新しい処理もここに書くしか無くなり、どんどん本筋の「チョコ作り」とは関係ない処理が入り込むことになります。コードの役割がどんどん増えていく原因になるということです。 何をしているのか理解しにくい 現在は単純なコメントのみのため、少ない事柄を扱っているように見えます。しかし実際、DBからカカオ豆を取得したりもみ殻と分けたりといったチョコ生成処理を書いていくと、理解すべき変数・分岐条件・使用する関数などの数はかなり多くなることが予想できます。これは、書籍の「7つまで」に反しています。 そこで、今回書籍から学べた手法を使って、関心事の観点で リファクタリング してみたコード例が以下です。 After(関心事を分離し、整理した例) class PrepareMakeChocolate { // 準備:カカオ豆の種類ごとに、担当パティシエへ振り分けるメソッド } class Patissier { // 加工:カカオ豆を焙煎、チョコに成型するメソッド } class ChocolateFactory { // チョコ作りの工程(準備・加工)書いたメソッド public function makeChocolate(): Chocolates{} // 確認:在庫に入ったチョコレートの品質を参照するメソッド public function checkChocolateQuality($id): boolean {} } // チョコを作る $chocolateFactory = new ChocolateFactory(); $chocolates = $chocolateFactory- > makeChocolate(); // 品質次第でチョコレートを提供 $chocolateFactory- > checkChocolateQuality($chocolates['id']); この変更は、以下のように可読性・再利用性の向上に貢献できます。 各クラスが1つの役割に集中している(準備,加工,確認) 各工程の影響範囲が独立している ChocolateFactory では工場としての役割「チョコを作って提供する」を考えて「チョコ作りの工程」「作ったチョコの確認」の2つをメソッドとして用意しました。管理や品質検証の工程は、厳密には「チョコを作る」工程ではないので、「作る」から分離しました。 もし焙煎や成型の処理に特殊な工程を組み込むパターンが発生したら、インタフェース化を検討しても良さそうです。上記コード例を書くにあたり、役割以外の判断基準として書籍から得た他の手法も実践しています。以下は、書籍でも紹介されていたかなり有名なコード設計の理論について、自分なりの解釈でまとめたものです。 関心事を分けるための要点2つ: コマンドとクエリを分離する システムの挙動を以下2つに大別するという考え方です。 コマンド:他のオブジェクトの状態を変更する副作用がある クエリ:状態を照会する(ので、返り値がある) 上記の例で言うと、 checkChocolateQuality メソッドは作ったチョコの状態を確認して、結果を取得するだけの「クエリ」です。 一方、 PrepareMakeChocolate 内の担当パティシエへ振り分けを行うメソッドは、他のオブジェクトに担当パティシエの情報を当てがう副作用があるため「コマンド」と考えられそうです。この2つを分ける意識は、各クラス・メソッドの役割をより明確化することに役立ちます。 やりとりする型と状態を保証すべき範囲を明確にし、 保証されたオブジェクト同士で付き合う 各クラスやメソッドに出入りするデータ・オブジェクトについて、「どのオブジェクトが、どんな状態を、どこまで保証するべきか」を決めておく必要があります。例えば PrepareMakeChocolate クラスを経由してエラーが出なかった(= PrepareMakeChocolate 内の検証に引っかからなかった)場合は、「担当パティシエが確定している状態」が保証されるべきです。 このように、関心事によってコードを書く場所の場所を判断する手法は、実際の業務でもメソッド抽出や変数の配置場所を決める時に役立ちました。 小まとめ(脳に収まるコードを書くには?) 関心事という観点で、クラスやメソッドを分ける 詳細すぎる情報は、 カプセル化 で「安全に」切り分ける コードの一部を切り出すときには、関心事単位でまとめるだけでなく以下3点を保証することが重要です。 外部から送られた「データの状態」が内部ルールと沿うか検証する 外部に返す「データの有無・状態」を明確にする 外部と内部を分けるため、公開範囲の制限を行う 覚えておきたい考え方: カプセル化 で実装の詳細を隠蔽する (出典:脳に収まるコードの書き方_p128 [原書:Code That Fits in Your Head]) 最後に 今回の輪読会参加で自分にとって最もプラスだったのは、下の2つです。 今後の業務/勉強から、どんな学びを得ていくべきかが具体化されたこと コード設計を取り巻く、様々な手法まで興味が広がったこと 「ソフトウェアエンジニアリングとか テスト駆動開発 って、よく聞くけど専門的すぎて自分にはまだ難しそう…」「設計や上流の知識が全然ない!そもそも何から勉強していけば良いの?」と感じていた私にとって、コーディング面のテクニックだけでなく「何をどう勉強すべきか」を考え直すきっかけとなった当書籍による輪読会への参加は、非常に大きな意義があったと感じています。 書籍に「初学者向けではない」とあるだけに、輪読会への参加前はためらいもありました(実際難しかったです)が、なんとか読み切った時には達成感もひとしおでした。今後、難しい書籍への挑戦や勉強会に参加する 心理的 ハードルも下がったと感じます。 ちなみに、私は本書がきっかけで「 テスト駆動開発 」に興味を持つに至りました。理由としては以下です。 切り分けたクラス/メソッドの検証に深いつながりがある プログラマ が担うべき品質担保へ直結する 書籍で、開発の進め方における”あるべき”とされていた「小さく書き進め、常に動く状態を保つ」に大きな効力を発揮する プログラマ の職責である「判断」での引き出しを増やしていくためにも、輪読会で下げてもらったハードルを飛び越え「 テスト駆動開発 Kent Beck   (著), 和田 卓人  (翻訳)」を読むなどしていきたいと思います。 また今回は盛り込めませんでしたが、「脳に収まるコードの書き方」には他にも多くのコーディングテクニックや開発改善の手法も書かれています。もちろんベテランの方にもおすすめの書籍だと思われますが、私のような初学者の方でも大量の学びがあると思いますので、ぜひ本書に挑戦してみていただきたいです。 本記事が、少しでもなんらかの興味・学びのきっかけとして届けば幸いです。 *1 : 出典:脳に収まるコードの書き方p37 [原書:Code That Fits in Your Head]
私たちは創業当初から「顧客志向」を徹底して重視し、2017年からは開発組織として「顧客をカスタマーサクセスに導く、圧倒的に使いやすい SaaS を創り提供する」というミッションを掲げてきました。 その結果、多くのお客様にプロダクトが支持され、国内 SaaS 市場でARR No.1を達成できました。 ラク スは、 特定の業務領域に特化して顧客志向でプロダクトを徹底的に磨きこみ、圧倒的に優れた顧客課題解決を目指す「ベスト・オブ・ブリード型製品開発戦略(以下ベスト・オブ・ブリード戦略)」 でプロダクト開発を行い、お客様の声を最優先にする方針をとってきました。 「ベスト・オブ・ブリード」は元々IT製品調達の用語で、業務領域ごとに最適な製品を組み合わせてシステムを構築することを指します。 ベスト・オブ・ブリード戦略はこの概念を用いて再定義したものです。 ラク スがなぜベスト・オブ・ブリード戦略を選び、どのようにお客様の声を製品へ反映しているのか。開発本部長の公手に、その背景や経緯、成功要因を聞きました。 原点は「お客様に喜ばれるものを作りたい」思い 重視したのは「お客様のリアルな姿」 マルチプロダクトを高成長させる戦略 絞り込んだターゲットから圧倒的支持を獲得 戦略成功には組織の「熱伝導性」を高めることが重要 「誰かの役に立っている」実感こそ開発の醍醐味 おわりに 原点は「お客様に喜ばれるものを作りたい」思い ラク スがベスト・オブ・ブリード戦略をとるようになった背景を教えてください。 正直、創業当初から「これがベスト・オブ・ブリード戦略だ」と意識していたわけではないですね。 「メールディーラー」を開発していたころは2000年代前半です。まだベスト・オブ・ブリードという言葉はなかったんじゃないですかね。 「お客様に喜ばれるものを作りたい」 という気持ちは強かったですね。 ただ、そういう気持ちが強いのとお客様に価値あるものが提供できるというのはまた別の話です。 ただ、「お客様に喜ばれるものを作りたい」という想いはめちゃくちゃ重要だと思います。最初の頃はお客様の話は聞いていましたが、解像度が高くありませんでした。なので、結構想像でこういうものが役立つだろうという感じで開発していたと思います。 私が「メールディーラー」の開発に参画したときは「メールディーラー」は十数社に導入されているぐらいのかなり小さなサービスでした。その後、私の後に参画された当時の役員のHIさんが PMF (Product Market Fit)を目指し奮闘していました。ECショップのメール問い合わせという領域に焦点を合わせることで、すごい勢いで売れるようになりました。 当初はお客様に役立つものを想像で開発していたとのことですが、売れ始めてから課題は出てきませんでしたか? まだまだ製品完成度も低かったことから、お客様からの要望が山のように来ました。 自分たちが想像していなかったような要望がきたり、自分たちの開発したものがお客様のイメージやHIさんのイメージと微妙にズレがあることもありました。 そこでHIさんともっとお客様の声をリアルに直接聞いて、現場を見て顧客解像度を上げていこう、ということになったわけです。開発だけでなく、営業もCSもお客様から聞いた声をどんどんデータベースに蓄積していきました。「こんな機能があると助かる」「ここが不便だから直してほしい」と具体的な要望がどんどん出てきましたね。 お客様の声を徹底的に聞き、マーケットインでお客様の声を実現していく うちに、自然と機能が豊富になり、お客様のかゆいところに手が届くシステムになり、より多くのお客様からの支持を得られるようになりました。 お客様の声をリアルに聞いたことで、ベスト・オブ・ブリード戦略に至る成功体験が得られたんですね。 はい、この成功体験がその後の ラク スのプロダクト開発の基本姿勢となり、結果としてベスト・オブ・ブリード戦略をとっていたということになるかと思っています。 あ、我々ってベスト・オブ・ブリード戦略なんだって気が付いたのは2011年とか2012年ぐらいだったと思いますね(笑)。 「ベスト・オブ・ブリード」という言葉は2004年ぐらいからあるみたいですけど。 お客様の声を徹底的に聞く方針について、具体的にはどんなことをしていたのでしょうか? 仕様一つとっても、 開発・営業・CS・ マーケティング が膝を突き合わせながら議論 していました。 先ほども述べましたが、営業やCSが商談やサポートで拾った要望、つまり「お客様からこういう声が出てる」「こんなところに不満を感じている」という情報を集めて、当社のWebデータベースでもある「楽楽販売」に情報集約しています。 それを開発と営業、CS、 マーケティング のメンバーで「どれを優先的に解決するか」「何が一番お客様にとって価値があるのか」を話し合う「製品力会議」という場を作っていましたね。 楽楽シリーズを開発する頃には 「顧客志向」のカルチャーが根付いていたため、自然と「お客様の声を最優先に意思決定する」ということができるようになっていた と思います。 重視したのは「お客様のリアルな姿」 お客様にとって価値あるものを知るため、エンジニアはどんな取り組みをしていましたか? エンジニアやUIデザイナーは、お客様先を訪問して実際の利用シーンを見たり、電話でインタビューしたりというのはやっていましたね。 業務の詳しい内容を完全に理解できなくても、直接会ってやり取りすると 「こんな人が使ってるんだ」というイメージ がつかめます。それが開発者にとっては大事なんです。 お客様が使うイメージをつかむことで、どんな変化があるのでしょうか。 開発者自身が少しでも「お客様のリアルな姿」を知っていると、いざ仕様を考えたり改善案を作ったりする時に発想が変わるんです。 自分が書いているコードが誰かの課題解決に繋がっていると思えると、モチベーションも上がりますし、本当に使う人の役に立ちたいという視点でよりよい実装ア イデア が出せるようになります。 そういった一つ一つの小さな差分が積み重なって最終的なプロダクトの使い勝手に大きな差として現れてきます。 結局、 SaaS というビジネスも人と人のつながりで成り立っているので、コードや技術の先にある、お客様という人を開発者が意識することが重要です。 マルチプロダクトを高成長させる戦略 ベスト・オブ・ブリード戦略を意識し始めた経緯を教えてください。 ベスト・オブ・ブリード戦略を自然ととっていたというのが僕の感覚ですね。 記憶はあやふやですが、 ラク スのプロダクト戦略の強みをベスト・オブ・ブリードという言葉で認識したのは先ほど述べた通り2011年とか2012年頃だと思います。 それ からし ばらくたって、各サービスをよりスピード感をもって成長させるために、サービス別組織として営業、マーケ、CSなどビジネス側の部門を 事業部制 としてまとめましたね。そのほうが自分たちのターゲットのみを意識して素早い意思決定ができます。 この頃には 全社員がベスト・オブ・ブリード戦略が当社の強みと明確に認識していた と思いますね。 ラク スは各プロダクトの技術選定にも裁量がありますが、これもベスト・オブ・ブリード戦略の考え方が活かされているのでしょうか? 技術選定自体はベスト・オブ・ブリード戦略と直接の関係はなく、開発スピードや長期的な保守性などのいくつもの要素を勘案して決めています。 各開発サービスのエンジニアが決定します。特にこれを使わないといけないという縛りはなく、一番のポイントは「そのときにベストと思った技術を選ぶ」ということです。 ただ、社内のノウハウから、データベースは PostgreSQL 、言語は Java 、 PHP がほぼ選択されます。 例えば、初期のプロダクトでは短期リリース実現のためにやや古めではあるけど再利用性などのメリットを生かすために既存の技術スタックを流用するということもやってました。 今は組織が拡大し、リソースにも余裕があるためそのような縛りなく新しい技術スタックや アーキテクチャ を選択しています。 いずれにしても 「お客様に必要な価値を、できるだけ早く届けるにはどうするか?」が根本。 技術選定はその手段にすぎない、という考え方です。 絞り込んだターゲットから圧倒的支持を獲得 ベスト・オブ・ブリード戦略によって、複数プロダクトの PMF を達成できた要因は何だと思いますか? それぞれのプロダクトで、しっかりとターゲットを絞ったこと も大きな要因です。 広く汎用的にすべてのお客様のニーズをカバーしようとしないで、「まずはこの層に刺さる機能を徹底的に磨く」ですかね。 たとえば「メールディーラー」なら先ほどとお伝えした通り、ターゲットをECショップに定め「まずはこの層に圧倒的に支持される」ことを狙っていました。 なので在庫管理システムとの連携なんかも実装しましたね。 そういった積み重ねで結果的に「これがあるとめちゃくちゃ便利」「ほかに代替があまりない」という域に達することができる。 当たり前と言えば当たり前なのですがベスト・オブ・ブリード戦略の真髄だと思っています。 ベスト・オブ・ブリード戦略では、複数プロダクト間の連携についてどのように考えているのでしょうか? 複数プロダクトを開発していると、どうしてもクロスセルのために共通機能やデータ連携という話が出てきます。 また効率性の観点から共通モジュールの開発、共通データ基盤という技術側の発想も出てきます。 ただ、当社はいったんはそれらの優先度を落とし、 ターゲット顧客が求める機能を最優先に実現してきた というのは成功要因の一つだと思いますね。 ここ最近の コンパウンド スタートアップとは真逆のやり方ですね。 データ連携や共通機能を実装してしまうとそれらに引きずられてしまい、足かせとなり、ビジネスとしての速度低下や使い勝手の低下を招いてしまいます。 戦略成功には組織の「熱伝導性」を高めることが重要 一般的に組織規模が拡大すると分業が進み、開発組織にお客様の声は届きづらくなります。 ラク スの開発組織としては、どのような対応が重要と考えていますか? 組織規模が大きくなると開発とお客様の距離は遠くなりますので、 「相手を知ろうとする」 マインドセット を持ち続けてもらうことが重要。 ここで言う相手とは、お客様と、お客様と接点を持つ社内の他部署の両方です。 お客様を知るには、可能なら 直接会いに行く・現場を見に行く のが一番です。 リアルで訪問すると会社の雰囲気や業務の流れがより具体的にわかります。 それに、何社か回ると「この業界のお客様はこういう課題を抱えているんだな」といった共通点や傾向も見えてきます。 開発はCSほど深く顧客業務を理解できるわけではないですが、それでも実際に一度触れるかどうかで、見える景色がまったく違ってくるんです。 社内の他部署との連携についてはどうでしょうか。 各部署同士がお互いの役割や温度感を知ることが大事です。 開発であれば「CS・営業がどんなやり取りをしているのか」を知るだけでも、認識が変わります。 たとえば、営業の日報を見たり、CSの対応履歴を確認したりするだけで、「こんなふうに使われているんだな」「ここに不満を持たれているんだな」と具体的なイメージがつかめます。 創業時に自然と行っていたことを、今は仕組化して意識的に行っていく必要がありますね。 はい、昔は開発、営業、CSの距離が近かったので、お客様の声やビジネスのリアルな状況(熱量)がダイレクトに伝わっていました。 でも、組織が大きくなると「お客様の声が届くまでの経路」が長くなり、熱量が薄まってしまう。 開発側・ビジネス側相互が自分から情報を取りに行き、組織の「熱伝導性」を高める必要があります。 単にビジネス責任者が熱量を持って伝えてくれるのを待つだけではなく、開発側からも「何が求められているのかを知りたい」と積極的に動くことが大事です。 そうしないと、規模が大きくなるにつれてお客様のリアルな声が遠のいてしまうんですよね。 つまり情報が自動的に伝わってくるのを待つのではなく、能動的にアクセスすることが必要になってくる、と。 そうですね。結局、どんなに規模が大きくなっても、 「顧客志向で製品を作る」ためには、開発もお客様を知る努力をし続けなければならない んです。 そのためには、お客様と直接話す機会をつくること、そして社内の他部署との情報共有を深めることが欠かせません。 昔は規模が小さかったから自然とやれていたことも、いまは能動的に取り組まないと情報が届かない。だからこそ、「どうやったら熱量を伝え合えるか」を組織として工夫する必要がありますね。 昔のような距離感は戻せなくても、 マインドセット や仕組みを作れば、お客様とのつながりを維持できるんですね。これからもその姿勢を重視していきたいですね。 「誰かの役に立っている」実感こそ開発の醍醐味 最後に、エンジニアが ラク スで顧客志向で開発することの一番の醍醐味は何でしょうか? やっぱり 「誰かの役に立っている」という実感を得られる ことですね。 技術を追う楽しさもありますが、最終的には「自分が作ったものが誰かの課題を解決している」とわかるのが一番の喜びだと思います。 当社は長年にわたってお客様の声をしっかり聞く取り組みを続け、それがベスト・オブ・ブリード戦略を支えてきました。 結果として、業界トッ プレベ ルシェアのプロダクトも複数あります。つまり、 自分たちが開発したものが「本当に世の中で使われて、誰かの役に立ち、評価されている」という手応えを得やすい んです。 あと、10年以上提供し続けているサービスも多数ありますが、古臭い技術スタックのサービスだけではありません。 既存の アーキテクチャ を刷新したりする機会もありますし、新規プロダクトの立ち上げもあります。 常に新しいことに挑戦する機会がある かと思います。 お客様の課題を意識して開発していくので、ダイレクトにお客様へ貢献できる。そういう面でも「人の役に立つものを開発したい」という想いの強いエンジニアにとってはかなりやりがいが大きいかと思います。 おわりに 今回は ラク スが大事にしてきたベスト・オブ・ブリード戦略の背景から経緯、成功要因までをお伺いしました。マルチプロダクトを PMF に導いてきた戦略の根底には、 ラク スの「顧客志向」が一貫しているのですね! 顧客志向で、お客様の声を起点にプロダクトを磨き続ける姿勢は変わりません。 組織規模が大きくなると一筋縄ではいかないところも出てきますが、これからも自分たちなりに工夫を重ねながらベスト・オブ・ブリード戦略を追求し、より多くのお客様に「 ラク スのプロダクトを使ってよかった」と言ってもらえるように頑張っていきたいですね。
こんにちは、デザインマネージャーの清水です。 私たち「プロダクトデザイン課」は、お客様の業務課題を解決するため、全プロダクトのUI/UXデザインを担っています。 あらゆるプロダクトにとって、最良のUXを目指すことは必然だと思います。 私たちもまた、お客様にとってより使いやすいプロダクトを提供するため、継続的にUX改善に取り組んでいます。 先日のブログ「 ラク スのプロダクトデザイン組織紹介― 顧客価値を高める新たな挑戦」では、 継続的なUX改善の一環である「UI刷新」プロジェクト についてご紹介しました。 ここでの「刷新」とは、既存UX(お客様の使い勝手に大きく影響する配置や導線)を急激に変更するという意味ではなく、UXの維持向上を重視しつつ、よりわかりやすいUIデザインにするイメージにとらえていただければ幸いです。 tech-blog.rakus.co.jp 今回は「UI刷新」の目的や背景、どのような価値を目指しているのかを詳しくお話ししたいと思います。 UI刷新プロジェクトとは 進め方と目指す姿 UI刷新プロジェクトとは ラク スのプロダクトが対象とする中でも、特に経費精算や勤怠管理といったバックオフィス業務は複雑で、多くの法令が関係します。そこで、ユーザに対応いただく操作も煩雑になりやすい側面があります。 ユーザに業務をスムーズに進めていただくためには、機能の充実はもちろん、直感的で迷わない操作性も欠かせません。そのため、より良いUXを提供する一環で「UI刷新」プロジェクトを立ち上げました。 今回のUI刷新は、 ラク スのバックオフィス向けプロダクトである楽楽シリーズ(楽楽精算、楽楽明細、楽楽販売、楽楽勤怠、楽楽電子保存、楽楽請求)が対象となります。 UI刷新の目的 今回の刷新には、大きく2つの目的があります。 1. UI統一による一貫した体験の提供 現在、各プロダクトのUIはそれぞれ独自の設計がされており、操作性もすべて異なっています。これは ラク スが採用している 「ベスト・オブ・ブリード製品開発戦略」 のためです。 「ベスト・オブ・ブリード」は、 業務領域ごとに最適化された“最良”の製品を、必要に応じて組み合わせて導入するというIT製品導入の用語 です。 ラク スはこれにならい、 業務領域に特化したUIや機能を作りこみ 、お客様の課題をいち早く解決できるようにしてきました。 その結果、どの領域においても最適なUI・機能を提供できるようになり、 ラク スの強みの一つとなっています。 しかし、最近は SaaS を複数組み合わせて利用する企業が増えてきました。楽楽シリーズを複数導入している場合、プロダクトごとに異なるUIに慣れる負担が生じます。そこで今回の刷新では、 プロダクトをまたいでも「同じシリーズだ」とわかるようにUIを統一し、より一貫した体験をお届けする ことを目指しています。 2. UIの最新化による使いやすさの向上 UI刷新の方向性を決める際、もう一つ大切にしたのが 「UIの最新化」 でした。 これまで ラク スのプロダクトは、お客様の業務課題を確実に解決するために、機能を優先して充実させる戦略をとってきました。おかげで多くのお客様のニーズに応え、選んでいただいています。 一方で、機能優先で開発を進めてきた結果、古いデザインが残っているプロダクトもあり、新たなUIへ進化させるタイミングを模索してきました。プロダクトを横断した「UI統一」は、その大きなチャンスだと考えています。 新しいUI コンポーネント は、視認性や可読性、 アクセシビリティ にも配慮し、どのユーザーにとっても直感的な操作ができるよう設計しています。 実際の導入にあたっては、 お客様の業務に合わせて最適化してきた既存のUXと調和を図るプロセス をとっており、後述いたします。 進め方と目指す姿 UI刷新とはいえ、既存のお客様が慣れ親しんだ操作感を一気に変えるわけにはいきません。 業務 ドメイン に特化した コンポーネント 配置を維持しつつ、画面のレイアウトやデザインをより洗練させる のが今回の方針です。 UI刷新前 UI刷新後 将来的には、プロダクトを横断して複数の楽楽シリーズを併用しても違和感を感じない、一貫した操作体験を提供し、視認性の向上や アクセシビリティ への配慮など、あらゆるユーザーにとって使いやすいUIデザインの提供が目標ですが、まずはその第一歩としてユーザーのプロダクト遷移時の違和感の解消を目指しています。 そのために、プロダクト統一の共通UI コンポーネント とデザイン ガイドライン を整備し、各プロダクトを担当するデザイナーで意識統一を図りました。楽楽シリーズは、それぞれ異なるカラースキームや コンポーネント を持っていますが、 共通UI コンポーネント とデザイン ガイドライン でデザイン基盤を整えることで、ユーザーに統一感のあるプロダクト体験を提供できる ようになります。 各プロダクトには、利用ユーザーに合わせた特有の機能や表現があるため、全てのデザインを共通UI コンポーネント で作成することは現実的ではありませんが、プロダクトを併用するユーザーに将来に渡って一貫した操作体験を提供していくため、何を統一し、何を独立させて最適化するかといった視点にこだわってデザイン ガイドライン を作り、更新し続けています。 デザインシステムの取り組み 共通UI コンポーネント やデザイン ガイドライン を発展させて、フロントエンド開発メンバーと共通の理念を持ってプロダクトの システム開発 を進めていくために、デザインシステムの導入にも取り組んでいます。 ラク スでは複数のプロダクトが独自のUIデザインを持っているため、今回のUI刷新後も各プロダクトで多くの独自 コンポーネント を使ったデザイン表現がされています。 その中で、デザイン制作目線でも システム開発 目線においても”共通のデザイン表現”とは何なのかを関係者が認識して、各プロダクトのデザイン、開発をできていることが重要だと考えています。 もっとも、これだけでUX改善まで実現できるわけではありません。 今回の刷新は、まずレガシーな画面をアップデートし、プロダクト間の共 通化 を図るもので、いわばスタートラインに立ったに過ぎません。 今後のUX改善のためには、デザイン組織としてより顧客を理解し、エンジニアやビジネスサイドとの連携を強めていくことも欠かせません。 私たちは、 このUI刷新をきっかけに、より大きなUX改善へと踏み込んでいきたい と願っています。 お客様にとって本当に使いやすいプロダクトとは何かを追求し、継続的にアップデートを重ねることで、 ラク スのプロダクトが「業務を効率化するだけでなく、使うのが楽しみになる」存在へと育っていくことを目指しています。 デザインチーム一丸となって取り組んでまいりますので、どうぞご期待ください。
ラク スが開発環境を進化させ続ける理由 こんにちは。エンジニア リングマ ネージャーの iketomo(いけとも) と申します。 多くの開発組織にとって、開発力を高め、エンジニアが活躍できる環境を整えることは重要な課題です。 開発環境は単なる「働く場所」ではなく、エンジニアが最大限の力を発揮し開発生産性を高めることが、顧客に価値を届けるための基盤だからです。 ソフトウェア開発の世界は日々進化し、新しい技術が次々と生まれています。また、市場の変化も加速しています。 この変化に対応するためには、開発力と組織の魅力を高め続ける必要があり、そのためには開発環境整備が不可欠と考えています。 ラク スも例外ではなく、開発生産性を高め競争力を向上するための開発環境整備に取り組んでいます。 今回の記事では、 ラク スの開発環境整備の基本方針、整備対象の選定・プロセスと、具体的な事例をご紹介します。 私たちの取り組みにかける姿勢をお伝えできれば幸いです。 ラクスが開発環境を進化させ続ける理由 開発環境整備の対象と基本方針 開発環境整備の選定・検証プロセス 開発生産性向上のための取り組み GitHub Copilotの導入 43インチモニタの導入 Devinの検証 まとめ 開発環境整備の対象と基本方針 整備の検討対象は、開発生産性向上につながるあらゆる環境・ツールです。 ただし単に最新のツールを何でも導入するわけではありません。 私たちは検討にあたって以下の基本方針を掲げており、 最終的には 「より高品質なソフトウェアを効率的に開発し、顧客に最大の価値を提供する」 ことを目指しています。 費用対効果を重視した選定 整備対象を選ぶとき、最も大切にするのは 費用対効果 です。 開発環境への投資は単なるコストではなく、開発生産性を高めるための戦略的な判断の一環です。 適切なツールや設備に投資することで、エンジニアの作業効率が向上し、開発のスピードと品質が向上します。 しかし、すべてのツールや技術が無制限に導入できるわけではありません。 費用対効果を考慮する際には、次のポイントから多面的に評価しています。 直接的な効果: 開発速度の向上、バグの削減、レビューやテスト、デプロイの効率化など、 開発プロセス での直接的なコスト改善が見込めるかどうか。 間接的な効果: 業務上のコミュニケーションしやすさ、ストレスの軽減、学習コストの削減など、 開発プロセス 以外でもポジティブな影響を与えるかどうか。 価値の持続性: 一時的な流行に乗るのではなく、将来的にも価値を発揮するかどうか。短期間で廃れてしまうものではなく、継続的に活用できるか。 特に、開発生産性を最大化するためには、投資することによってエンジニアにとってストレスの少ない環境を実現することが重要です。 開発者が作業に集中できる環境を整えることが、長期的に見て開発組織の競争力向上につながると考えています。 開発環境整備の選定・検証プロセス 現場の裁量で発案し、小さく試す ツールや新しい技術を導入する際には、単なる流行や市場の動向だけに頼るのではなく、実際の開発業務での有効性を確認することが不可欠です。 そのため私たちは、 開発チームが裁量を持ち、現場ニーズから提案してもらう検証プロセス を確立しています。 私たちの選定プロセスは次のステップで進められます。 開発チームからの提案: 開発環境を改善するための新しいツールや技術に関するア イデア を、各エンジニアが裁量を持って提案できる仕組みを整えています。 パイロ ットチームによる小規模検証: 提案されたツールは、まず少人数の パイロ ットチームによって試験的に導入され、実際の業務にどのような影響を与えそうか確認します。 パイロ ットチームは各開発チーム内で組成することもあれば、開発組織横断になることもあります。 評価とフィードバック : パイロ ットチームによる定性・ 定量 の結果をもとに、効果や課題を整理し、正式導入に向けた判断を行います。 本格導入と最適化 : 費用対効果が見込める場合、開発組織全体に展開し、必要に応じてカスタマイズや最適化を実施します。 このようなプロセスを取ることで、リスクを最小限に抑えつつ、最適な開発環境を実現することができます。 継続的な検証 開発環境は一度整えれば終わりではなく、技術の進化や組織の成長に応じて常に最適化し続ける必要があります。 そのため、私たちは 定期的に開発環境の見直しを行い、必要に応じてアップデート を実施しています。 継続的な改善を行うために、次の取り組みを実施しています。 定期的なレビュー: 半年に一度、社内の技術検証チームが現在の環境に対してフィードバックを行い、改善点を洗い出します。 社内ナレッジの共有: 新しく導入したツールやベストプ ラク ティスについて、社内勉強会やドキュメント、社内チャットを通じて情報を共有し、全員が活用できる状態を整えます。 技術トレンドのキャッチアップ: 最新の技術動向を常に把握し、必要に応じて新しい技術を試験導入することで、開発環境の陳腐化を防ぎます。 これらの取り組みを継続することで、エンジニアが常に最適な環境で開発に取り組めるようにし、結果的に開発効率と品質の向上につなげています。 開発生産性向上のための取り組み ここからは、具体的な取り組みについて紹介します。 GitHub Copilotの導入 目的 コーディング時間短縮、業務品質向上、学習や調査時間の短縮を通じた開発生産性向上を目的に、2023年6月から導入しています。 ソースコード の自動補完や提案機能のほか、コードレビュー、コード解説、テストデータ作成など、多くの局面での利用を想定しました。 経緯 まずテッ クリード 主導のプロジェクトチームで試験導入を行い、どのようなメリット・デメリットがあるのかを検証しました。コードサジェストにより削減した作業分を集計すると、同じ業務の所要時間は12%削減、一人当たり10時間/月の作業効率化につながることが明らかになりました。費用に見合う効果が確認できたため、最終的に全社展開を決定しました。 結果 90%以上のエンジニアが GitHub Copilotによって生産性が向上した との回答が得られました。時間換算で、1時間/日程度の作業が削減されたようです。 実際に使ったエンジニアからも「単純なコードの記述がとても楽になった」「サジェストが怖いくらい正確」という声が上がっています。 現在は GitHub Copilot Enterpriseも導入し、開発組織内でさらなる活用を図っています。活用事例やナレッジ共有を行い、組織全体の活用レベルをさらに高めていきたいと考えています。 43インチモニタの導入 目的 開発業務の可視性を向上させ、スクロールや画面切り替えの負担を軽減することで、作業効率を改善することが目的でした。 特に、大規模なコードベースを扱う際や、 ペアプログラミング の際に、広い視野を確保することで生産性の向上を狙いました。 経緯 従来のモニタ環境では、コードが縦に長くなり、スクロールや切り替えの回数が増えることで作業のリズムが崩れるケースが多発していました。 そこでテッ クリード 発案のもと、まず一部のチームで43インチモニタを試験導入し、可読性や作業効率への影響を検証しました。年間48時間以上の削減効果があることがわかりました。 結果 ソースコード の視認性が向上し、スクロールやウインドウ切り替えの回数が減少。結果として、 当初の想定通りコーディング・コードレビューや デバッグ 作業の時間短縮 につながりました。 特に関連する ソースコード を横に複数並べて同時に見ることで、プログラムの処理フローの把握をかなり効率的に実施することができるようになりました。 また ペアプログラミング やコードについて議論する際に、複数のエンジニアが同時に1つのモニタの前に集まることが多くなり、コミュニケーション活性化にもつながりました。 それ以外に当初想定なかった副次的効果で、縦に並んだタスク一覧を数多く表示できるので探しやすくなったり、メールや説明等の文章作成時に全体把握ができるので作成効率が上がるなど、 ソースコード 以外でも43インチモニタが活躍するシーンがあり、使用している全員が作業効率が良くなったと回答しました。現在では 開発組織の半数のエンジニアが利用 しています。 唯一のデメリットは画面が大きいので座っている人が見えなく、在席しているのかどうかを確認するのに席の横まで行かないといけないのですが、そこはご愛嬌で。 Devinの検証 目的 開発生産性向上の目的で、 AIエージェントの一つであるDevinの検証 も開始しています。 特に、各開発チーム内で適用可能性のある ユースケース や、十分な費用対効果が期待できるかを検証しています。 経緯 近年、チャット型やCopilot等のサジェスト型をはじめとした、 開発プロセス での生成AI活用が進んでいます。 しかし、AIエージェントは従来の生成AIとは違い、目的を与えることで自律的に複数の情報ソースやサービスから情報取得して操作を行うことができます。 タスクの計画立案から実行、成果物の提出まで一連のプロセスを自律的に遂行することで、飛躍的に生産性を向上させる可能性 があります。 私たちの社内においては、社内未導入の技術を調査・検証する「技術推進チーム」がAIエージェントのトレンドについて情報収集を進めてきました。 最新事例のフォローアップや社内での ユースケース 発掘に向けて、積極的に先行検証を開始しています。 現状 現在はバックエンド開発、AI機能開発、モバイル、SREなど、多岐にわたる部門・チームで検討を開始しているところです。 以下のような幅広い利用シーンが想定され、 費用対効果も良好な結果が得られそうな感触 です。Devinを検証中のエンジニアの声を一部紹介します。 PoCで チュートリアル を試すとき、必要な知識と実装が自動的に提案され、すぐ着手できた Devinの出力からベストプ ラク ティスやよりよい実装に気づける テストコード実装や リファクタリング に使ってみたい API 作成からCI/CD、PRの作成まで一括指示ができ、アウトソースしている感じがした。再現性の高い作業は委託しやすそう 日中、夜間関係なく稼働できるので効率的 人のアウトプットのダブルチェックに使える オンボーディング資料のアップデートなど、開発の付随作業の削減にも使えそう ユースケース の検証はまだ拡大継続中ですが、コスト削減だけでなく、PoCのようにDevinから新しい発見を得ることも期待できそうです。 まとめ 開発環境整備は、単なる効率化ではありません。エンジニアが力を発揮し、最高のプロダクトを生み出すための基盤です。 ラク スは 「費用対効果」を重視し、現場ニーズと継続的な改善を大切にしたプロセス で、確実に環境を整えてきました。DevinやCopilot、4Kモニタの導入はその一例です。 近年、生成AIをはじめとした技術革新により、開発環境はかつてないスピードで進化しています。 わずか半年後、1年後には、いま私たちが想像する以上の生産性を手にしている可能性すらあります。それはつまり、「未来の開発力」は、私たちの想像を常に超えてくるということです。 だからこそ、私たちは「現時点での最適な環境」に甘んじることはありません。 変化を受け身で待つのではなく、自ら変化を先取りし、常に“次の最適”をつくり出すことが必要 だと考えています。 私たちは、エンジニア一人ひとりがより自由に、より創造的に力を発揮できる環境を追求し続けます。 そして、テク ノロ ジー の進化を味方にしながら、より速く・より大きく・よりクリエイティブな価値を生み出せる開発組織へと進化していきます。
はじめに 楽楽販売開発課サポート対応チームのshimizu_sと申します。 前回、同チームにて以下の記事を掲載しました。 tech-blog.rakus.co.jp 今回は同じく、サポート対応チームの業務の内容について、どのようなことを実施しているかさらに詳しくご紹介します。 工夫した取り組みについて サポート対応の業務内容については前回の記事を参考にしてください。 さて、前回の記事で記載されたゴールは「お客様の疑問・もやもやを無くす」ですが、 ゴールにただ向かうだけではなく、お客さまにより満足していただけるようにサポート対応チームは以下の部分にも注力しています。 迅速に サポート対応を行う取り組み 正確に サポート対応を行う取り組み 明確に サポート対応を行う取り組み 今回は、三つの観点でサポート対応チームがどの様に工夫をしているかをご紹介したいと思います。 はじめに 工夫した取り組みについて 迅速にサポート対応を行う取り組み サポート依頼・対応のデータ蓄積 よくある質問ページの作成 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 明確にサポート対応を行う取り組み 回答作成レビュー 回答作成時チェックシート 終わりに 迅速にサポート対応を行う取り組み 疑問を解消するのはもちろんですが、お客さまにとって、返事が遅いと不安になります。 最初は小さな不安の種ですが、時間が経過していくと最終的に 製品としての信頼を損なうことに繋がります。 そうした中で、お客様の疑問をいち早く解決できるように以下の取り組みを実施しています。 サポート依頼・対応のデータ蓄積 楽楽販売では、過去に受けたお客さまからの依頼に対して、社内で使用している楽楽販売の専用データベースに以下のような情報をもって蓄えています。 質問内容 回答した内容 その回答を行った理由 現在、このデータベースには600~700件/年のデータが登録されています。 サポート対応の蓄積 これにより、同様・類似の質問が来た際、過去依頼を検索・参照することで、素早い解決ができるように取り組んでいます。 よくある質問ページの作成 カスタマーサポートチームがサポート対応チームに確認しなくても対応ができるように、 以下の観点を考慮した「よくある質問ページ」というものを作成して、社内ネットワーク内でカスタマーサポートチームへ共有しています。 質問の頻度が多い 確認する箇所が明確である よくある質問ページ こちらのページについては、 カスタマーサポートチーム側からの意見を募集しており、内容を鑑みて反映をしていく運用となります。 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 楽楽販売は柔軟なカスタマイズ性が強みの製品です。 豊富な機能を備えているため、お客様から多岐にわたる質問がやってきます。 そのため、サポート対応チーム内でも『どのように調査をしていけばよいか?』と悩むことがあります。 そうしたなかで、様々な質問に対して正確に調査し、適切に回答ができるように、 オープンソース ソフトウェアであるナレッジ共有ツール *1 を使用して、以下のようなナレッジを共有しています。 サポート依頼の調査観点 ログ調査方法 実際に機能を確認できる端末の使用方法 どのような質問が来ても、正しく内容を把握し、適切な回答を提供できるように努めております。 明確にサポート対応を行う取り組み サポート対応チームの取り組みの一つとして、"回答をいかに分かりやすく伝えるか" もあります。 なぜなら、エンジニアであるサポート対応チームと顧客対応を中心とするカスタマーサポートチームでお互いの専門としている分野が異なります。 さらに、文字ベースでのやり取りを基本としているため、曖昧な表現や一般的ではない用語を利用すると正しい内容が伝わらず、やり取りが増えるだけでなく、 お客様に誤った内容が伝わってしまう可能性がある からです。 そうしたことを防ぐため、サポート対応チームに配属後、わかりやすい回答ができるように以下の取り組みが実施されます。 回答作成レビュー 回答作成時チェックシート 詳細について説明します。 回答作成レビュー カスタマーサポートチームへ回答を伝える前に、 サポート依頼の担当者が作成した回答を一度ベテランの方に見てもらいます。 文章としておかしくないか 表現に揺らぎがないか 誤解を与えるような強い言葉(例えば『絶対』)を使っていないか 一般的ではないIT用語を使っていないか 色々な観点でチェックが入ります。 作成した回答のレビュー依頼 ベテランのチェック あらかじめ設定した基準をクリアし簡潔な回答ができるようになれば、ベテランのレビューがなくなります。 余談ですが、ここで筆者の文章力がグッと鍛えられた気がします。 回答作成時チェックシート また、サポート対応チーム内で、上記レビューであった指摘点について普遍的な内容の場合は、回答作成時のチェックシートに追加されます。 回答作成チェックシート つまり、ここに記載されているものに当てはまらないようにすることで、わかりやすく誤解を与えにくい回答になります。 (副次的にベテランのチェック時間も減るといった効果もあります) こうした文章表現のナレッジ化も取り組んでいます。 終わりに サポート対応チームでは、さまざまな工夫を行っております。 冒頭でもお伝えしたように、 「お客様の疑問・もやもやを無くす」がゴールではありますが、お客さまに、「より満足していただける」ように心がけています。 こうした取り組みは一朝一夕で成し遂げられるものではなく、すぐに成果が見えるわけではありません。 しかし、日々ユーザーが増えていく楽楽販売の商品の強みの一つとして「サポートの品質が高い」となるように、 サポート対応チームは、お客さまと実際にやり取りしていただいているカスタマーサポートチームと力を合わせて、 高品質なサポートを提供できるよう色々な工夫を行い努めていきたいと思います。 *1 : GROWI
はじめに:経費精算業務の現状と課題 当社は、経費精算業務を効率化するプロダクト「楽楽精算」を提供しています。 現在、多くの企業が紙や Excel で経費精算業務を行っており、申請から承認までに膨大な時間を要しています。 特に手作業による申請チェックや書類不備の差し戻しが、 経理 担当者の業務負担を増大させる要因となっています。 このような状況では、 経理 担当者が業績管理や予算策定といったコア業務に集中することが難しくなります。 こうした問題を解決するため、 「楽楽精算」ではプロダクトでのAI活用を一層推進する こととしました。 プロダクト開発に関わるエンジニアの皆さんにとっても、AIを活用したプロダクトの提供価値向上は大きな関心事ではないでしょうか。 一方で、AI導入の際には インパク トの見積もりや、不確実性の扱い方について悩む場面もあると思います。 そこで本記事では 経費精算業務の効率化に向けた、AIを活用した製品戦略と開発ロードマップ をご紹介します。 ロードマップ策定の目的やプロセス、不確実性への対処、期待されるアウトカムにも触れていますので、皆様のご参考になれば幸いです。 はじめに:経費精算業務の現状と課題 なぜAIが必要なのか 変化の激しいAI技術の不確実性をどう扱うか AI開発ロードマップの策定目的 どう実現していくのか AI開発ロードマップの概要 どんなアウトカムを目指すのか プロダクトでのAI活用を成功させるために なぜAIが必要なのか 少子高齢化 による労働力不足が進んでおり、企業はより少人数で高い付加価値を生み出すことが急務 です。 経理 業務をはじめバックオフィス業務も例外ではなく、AIを活用したさらなる効率化が求められます。 またAI活用により、既存プロセスのシステム化を超えた、経費精算のDX(業務の抜本改革)が可能になります。 業務の最適化を通じて、ガバナンス向上と 経理 担当者がコア業務に専念できる状態が期待できます。 変化の激しいAI技術の不確実性をどう扱うか AIは変化が激しく、AI活用によりプロダクトの提供価値がどれほど向上するかは不確実性がついてまわります。 計画を立てても、実際に運用してみると想定外の課題が発生することも少なくありません。 そのため、PoC(概念検証)を行い、最小実用プロダクト(MVP)を経て実運用に至る段階的なアプローチが重要になります。 最初から大きな範囲に適用するのではなく、 最も重要な機能からスモールスタートで進める ことが成功の鍵となると考えています。 AI開発ロードマップの策定目的 ラク スが「楽楽精算」のAI開発ロードマップを策定する主な目的は、 経理 担当者がより戦略的、創造的なコア業務に専念できる状態を創出する道筋を描く ことです。 冒頭で述べた通り、多くの 経理 担当者は経費精算などのノンコア業務に多くの時間を費やしており、企業の売上や利益に直接貢献する業務に注力できていないという課題があります。 AI技術を活用して経費精算業務を効率化し、 経理 担当者の負担を軽減することで、彼らが本来注力すべきコア業務に時間とリソースを割けるようにすることを目指しています。 どう実現していくのか ラク スは、AI開発ロードマップに沿って、以下のステップでAI機能を実装し、上記の目的を実現していきます。 出典: 当社プレスリリース   ステップ1:申請者・承認者向け機能のAI活用 最初のステップとして、 使用頻度が高く、利用ユーザー数が多い「申請者・承認者向け」の機能 からAIの導入を開始します。 申請者が経費申請を行う際の入力ミスを削減し、それに伴う差し戻しを減らすことで、承認者や 経理 担当者のチェック作業を軽減します。 今後リリース予定の「勘定科目の入力補助機能」は、このステップにおける重要な取り組みの一つで、現在PoCを進めているものです。 この機能は、AI- OCR によって領収書や請求書のデータを読み込み、過去の申請履歴や添付された領収書データを基に、AIが適切な勘定科目の入力を補助します。 これにより、申請者がどの勘定科目に分類すべきか迷うことが減り、経費申請時のミスや差し戻しを大幅に削減することが期待されています。 ステップ2: 経理 担当者向け機能のAI活用 次のステップとして、 より専門性が求められる「 経理 担当者向け」の機能 にAIを活用していきます。 具体的には、企業のガバナンス向上のための経費使用規定違反や利用目的が不明瞭な経費申請を検知するAI機能や、 経理 担当者の業務負荷を大きく削減するためのAIによる通常経費申請の自動承認・否認機能などの開発を目指します。 AI開発ロードマップの概要 ラク スは、2025年3月期から2029年3月期までのAI開発ロードマップを策定し、段階的にAI活用を進めることで、 経理 業務全体の生産性向上を目指しています。 出典: 当社プレスリリース     フェーズ1(2025年3月期~2026年3月期): 経理 担当者のチェック作業軽減 経費申請の入力補助機能 申請前の不備をチェックする機能 経費申請内容のチェック機能 承認経路を適切化する機能 フェーズ2(2027年3月期~2029年3月期): 経理 担当者がコア業務に専念できる状態の創出 経費使用規定違反や利用目的が不明瞭な経費申請を検知する機能 AIによる通常経費申請の自動承認・否認機能の開発 開発ロードマップの詳細はプレスリリースにも掲載しておりますので、併せてご覧ください。 https://www.rakus.co.jp/news/2024/1212.html どんなアウトカムを目指すのか ラク スは、これらのAI機能を実装することで、 1社あたりの経費精算作業時間を大幅に削減する ことを目指しています。 現在の 楽楽精算にAI機能を導入することにより、60%以上の作業時間短縮が可能になる と期待されています。 さらに、このロードマップは経費精算領域だけでなく、 他の楽楽シリーズのサービスにもAI活用を視野に入れ、機能アップデートを継続する ことも視野に入れています。 ラク スは、顧客基盤の強みを生かし、バックオフィスサービスへのAI活用を早期に展開することで、顧客企業全体の業務効率化、生産性向上に貢献しようとしています。 そして、 ラク スは バックオフィス業務支援領域で最も頼りにされる存在 を目指しています。 AI技術を駆使して、顧客の要望や法改正などの外部環境の変化に柔軟に対応しながら、常に進化し続けることで、バックオフィス業務のDXを包括的に支援し、業界をリードする存在となることを目指しています。 プロダクトでのAI活用を成功させるために AIを活用してプロダクトを成長させるためには、ユーザー視点でその有効性を評価し、不確実性を考慮しながら現実的な計画を立てることが重要です。 そのために、AIの導入目的を明確にし、単なる技術導入ではなく、 課題解決を中心に据える ことが求められます。 また、PoCからMVP、最適化といった段階を踏みながら、 柔軟に計画を修正していく ことも不可欠です。 そうすることで、AIの力を最大限に活かし、プロダクトの価値を高めることができると考えています。 本記事が、AI活用によるプロダクト成長のヒントとなれば幸いです。
はじめに こんにちは。楽楽精算の サポートエンジニア を担当している梅田です。 サポートエンジニアの役割は、楽楽精算の仕様や技術を理解するだけでなく、問い合わせ対応時に適切な調査を行い、開発チームやインフラチームと連携しながら迅速に解決策を導くことです。そのため、テスト技法や業務知識を学び、最新の技術動向を把握することが大切です。 ラク スでは「 学習し成長し続ける 」ことを行動指針のひとつとして掲げており、 私たちサポートエンジニアもこの指針を大切にしています 。 お客様により良いサポートを提供するためには、製品知識に加えて、技術や法制度の変化を理解し、 常に最新の情報に対応していくことが不可欠 です。 そこで、実務に役立つスキルを学び、 サポートエンジニアチーム内や所属している課の単位で 勉強会を開催・実施しています。本記事では、 これらの勉強会の取り組みを通じて得られた成果 をご紹介します。 はじめに なぜ勉強会が必要なのか? 学習の必要性を実感したお問い合わせエピソード 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート 2023年度:チームの関心が高まり、継続的な学習文化へ 2024年度:業務時間内の勉強会が本格化 スキルアップとチームの成長を目指す「勉強会の目的と取り組み」 目的1. 実務に活かせるスキルの向上 工夫:実務に直結するテーマを選定 目的2. キャリアの方向性を明確にする 工夫:スキルの可視化とフィードバックの仕組み 目的3. チーム内の知識共有を促進する 工夫:持ち回り発表者制によるアクティブラーニングの実践 持ち回り発表者制のメリット・デメリットとサポート体制 メリット デメリット サポート体制 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 「学習し成長し続ける」ために     なぜ勉強会が必要なのか? サポートエンジニアの業務では、技術調査や問い合わせ対応に加え、製品仕様や法制度の理解が欠かせません。しかし、新機能の追加や法改正のたびに、日々の業務だけで知識をすべて習得するのが難しいのが現実です。 学習の必要性を実感したお問い合わせエピソード 定時実行で生成されるファイルに、実行時間直前に作成したデータが含まれていないというお問い合わせがありました。 当初はデータが消失したのではないかと懸念しましたが、調査の結果、次回の定時実行には問題なく出力されることが確認できました。これは、システムの負荷を分散させるために処理タイミングを調整している設計によるもので、仕様どおりの動作でした。 もしシステムの設計を深く理解していれば、落ち着いて対応し、迅速に説明できたと感じました。 この経験を通じ、機能の仕様だけでなく、処理の仕組みや技術的な制約を深く理解し、お客様に的確に伝える力を磨く必要があると改めて認識しました。 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート サポートエンジニアチームの学習文化は、私自身の1人勉強会(自主学習)から始まりました。当時の私は、 システムエンジニア としての経験はあったものの、テスト設計や品質保証の視点が不足しており、 お客様からの問い合わせに対し、システムのどの部分を検証すべきか、影響範囲をどのように調査すべきかを効率的に判断することが難しい状況でした。 そこで、 上司からの勧めもあり 、 業務の幅を広げるためにテスト設計とソフトウェア品質の評価、 ITサービスマネジメント という視点を 体系的に学習 することを決意しました。 JSTQB(ソフトウェアテスト技術者資格) を通じて、テスト設計の基礎やソフトウェアの品質を評価する視点を学びました。特に、「どのような観点で検証すれば、システムの品質を維持できるか」という考え方を学んだことで、問い合わせ対応の精度が向上しました。学んだ知識を活用することで、ログ分析や再現手順を整理し、調査をより明確に進めることができました。 また、 ITIL(ITサービス運用資格 ) を学ぶことで、問い合わせ対応を単発の業務として終わらせず、継続的な改善につなげるという視点も得ることができました。学習の結果、問い合わせ内容を蓄積し、対応フローの最適化やドキュメント整備を進めることで、よりスムーズな対応ができるようになりました。 2023年度:チームの関心が高まり、継続的な学習文化へ 私が取り組んでいた自主学習に興味を持つメンバーが増え、 有志のメンバーが集まり、少人数での勉強会が始まりました 。この勉強会では、 チームとして JSTQB 、 ITIL 、 日商簿記(財務会計資格) 、 OSS-DB(データベース資格) 、 LinuC(Linux資格) などの取得を目的として幅広い分野を学習しました 。 JSTQB ・ ITIL の学習を通して、 品質保証 と ITサービスマネジメント に関する知識を深め、 問い合わせ対応の精度向上 に貢献しました。 日商簿記 の学習では、 経理 業務に関する理解を深め、 運用に沿ったご案内 や 会計に関する適切なご説明 ができるようになり、 サポート品質の向上 に繋がりました。 OSS-DB ・ LinuC の学習では、 データベースやサーバー に関する知識を習得し、 開発・インフラチームとの連携 を円滑化し、 技術的な問い合わせへの対応力 を高めました。特に、 OSS-DB の勉強会では、梅田以外の メンバーが発表者 を務めることで、 「教え合う」文化 が生まれました。発表者を担当したメンバーは、 人に伝えることで知識を深め 、参加者も 積極的に質問 することで、 相互理解を深めました 。 2023年を通して、 個人の学び が チーム全体の成長 に繋がる 学習環境 が整い、 資格取得 のみならず、 業務に直結する知識 を学び、 サポートの質を高める文化 が根付きました。 2024年度:業務時間内の勉強会が本格化 2023年度までの取り組みが評価され、2024年度からは 業務時間内での勉強会開催が本格化 しました。これにより、 資格取得 を目的とした学習から、 実務 や 自分たちのスキル成長 に直結する勉強会へと発展しました。 勉強会開催の推移 この年に新たに取り組んだ勉強会のテーマには、次のようなものがあります。 なぜなぜ分析勉強会 : 問い合わせ対応時の問題を深掘りし、問題の本質に迫るなぜなぜ分析の手法を学びました。この勉強会を通じて、 分析視点が身に付き、問題の本質を捉えた対応と問い合わせの迅速な回答、業務効率化 に繋げることができました。 生成AIの活用事例紹介 :生成AIを活用した問い合わせ対応の効率化や業務改善への応用方法を紹介しました。この勉強会を通じて、 問い合わせ対応の効率化 や 業務改善 に繋がる 具体的なア イデア が得られました。( 前回のブログ記事 で活用事例として紹介 しました) 経理 実務勉強会 :決算業務の流れを理解し、経費精算システム(楽楽精算)や会計システムとの連携について学習しました。この勉強会を通じて、 経理 業務全体の流れ と、 各システムがどのように連携しているか をメンバー全員が理解できました。 こうした実践的な学びを通じて、 業務の効率化や品質向上につながる知識を身につける機会が増えました。 スキルアップ とチームの成長を目指す「勉強会の目的と取り組み」 サポートエンジニアチームでは、 メンバーの スキルアップ と業務の質向上を目的に勉強会を実施 しています。単なる知識習得にとどまらず、 実務に活かし、キャリアにつなげ、チーム全体を成長させる ことを目指し、次の 3つの目的 を掲げています。 目的1. 実務に活かせるスキルの向上 サポート業務では、 製品の検証や運用、品質保証の知識に加え、テスト設計や自動化、影響調査などのスキルが必要です 。これらを実践的に学び、日々の業務に活かせるようにすることが勉強会の第一の目的です。 工夫:実務に直結するテーマを選定 メンバーが「知識を得るだけでなく、すぐに活用できる」よう、勉強会のテーマを業務課題と結びつけています。 たとえば、 Playwright 勉強会 では、E2E(エンドツーエンド)テストの自動化手法を ハンズオン形式 で学びました。参加者はすぐに業務へ応用できました。実際に、勉強会の翌週には 自動化テストの導入を進めるメンバーが現れ 、成果につながりました。 目的2. キャリアの方向性を明確にする サポートエンジニアとしての専門性を高めるだけでなく、品質保証やシステム運用、開発との連携など、多様なキャリアの選択肢を意識できるようにすることを目的としています。そのため、 ITスキル標準 ( IT 分野で働く人がどんなスキルを持っているかを客観的に評価するための基準) を活用してスキルの棚卸しを行い、各メンバーが習得すべきスキルを明確にしています。 習得すべきスキルと達成度を可視化 工夫:スキルの可視化とフィードバックの仕組み ITスキル標準 を基に、サポートエンジニアに必要なスキルを整理し、それに沿った勉強会のロードマップを作成しました。スキル習得の進捗を確認しながら、次に学ぶべき内容を計画し、定期的な1on1で上司と話し合う機会を設けています。 参考:2024年度勉強会のロードマップ 目的3. チーム内の知識共有を促進する 勉強会を通じて、 チーム全体のスキルを向上させ、属人化を防ぐ ことも大きな目的です。知識が特定のメンバーに偏らないようにすることで、 誰もが安心して業務に取り組める環境 を整えています。 工夫:持ち回り発表者制によるアクティブラーニングの実践 サポートエンジニアチームでは、「自分が知りたいことを学び、それを共有することで理解を深める」という アクティブラーニング の考え方を取り入れ、メンバーが交代で発表を担当する 持ち回り発表者制 を導入しています。 勉強会のロードマップは、運営者(梅田)が作成し、各メンバーが必要なスキルを明確にできるよう調整しています。発表者は、このロードマップに沿って準備を進め、知識を深めながらアウトプットの機会を得ることで、実務に直結するスキルを身につけています。 また、「分からなければ聞く」「確認しやすい」というサポートエンジニアチームの文化を活かし、発表後のディスカッションを活発に行うことで、相互理解を深めています。 持ち回り発表者制のメリット・デメリットとサポート体制 持ち回り発表者制は、チーム全員が主体的に学ぶ環境をつくるために導入しました。 しかし、発表者にとっては学びの機会となる一方で、準備の負担や進行の難しさといった課題もあります。そのため、発表者制のメリットとデメリットを整理し、より円滑に進めるためのサポート体制を整えています。 メリット 参加者が受け身にならず、主体的に学ぶ機会が増える 発表者を担当することで、理解を深めるだけでなく、伝える力も身につく 特定の人に負担が偏らず、チーム全体で スキルアップ できる デメリット 初めて発表者を担当するメンバーにとっては、準備に時間がかかる 人によって説明のレベルや進行のスムーズさに差が出る サポート体制 こうした課題を解決するために、 発表者向けのサポート体制 を整えています。初めて発表者を担当するメンバーには、過去の資料を共有し、必要に応じて経験者がフォローすることで、負担を軽減しています。 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 サポートエンジニアチームでは、業務に直結する資格の取得を推奨しており、特に JSTQB ・ITILv4・ 日商簿記 3級 は「3種の神器」として定着しています。これらに加えて、運用管理・技術インフラ・品質保証の知識を深めるため、さまざまな資格取得にも取り組んでいます。 資格取得は スキルの可視化 や キャリア形成の指標 となるだけでなく、実務での適用を意識した学習につながっています。 以下に、取得を推奨している資格を 「品質保証」「運用・管理」「技術インフラ」「業務知識」 のカテゴリに分けて整理しました。 サポートエンジアチーム推奨資格一覧 カテゴリ 資格名 推奨度 説明 品質保証 JSTQB ◎ ソフトウェアテスト に関する国際的な資格認定制度で、品質保証に必要な知識を体系的に学ぶことができる。 品質保証 JCSQE ○ ソフトウェア品質保証に関する知識を体系的に学べる資格。 開発プロセス や品質管理の基礎を理解し、実務での品質向上に役立てることができる。 運用・管理 ITIL ◎ ITサービスマネジメント に関するベストプ ラク ティスを体系化した フレームワーク 。ITサービスの品質向上や効率化を体系的に学ぶことができる。 技術インフラ LinuC ○ Linux のシステム管理や運用に関する知識を証明する資格。サーバー管理やインフラ構築の基礎を体系的に学ぶことができる。 技術インフラ OSS-DB ○ オープンソース のデータベースに関する資格認定制度。特に PostgreSQL の管理や運用に必要な知識を体系的に学ぶことができる。 技術インフラ AWS(SAA) ○ クラウド サービス「 AWS 」に関する技術知識や運用スキルを証明する資格。インフラ設計やセキュリティ対策の理解を深め、 クラウド 環境の適切な活用に役立てることができる。 業務知識 日商簿記 ◎ 企業の運営や会計に関する基礎知識を体系的に学ぶことができる。 サポートエンジニアチーム全体のスキル向上を目的に、資格取得を積極的に推進した結果、 資格取得者は年々増加 しており、以下のような推移を示しています。 資格取得者数の推移 「学習し成長し続ける」ために サポートエンジニアチームの勉強会は、2024年度から本格的に始まり、まだ発展の途中です。現状、発表者が業務多忙や急遽の休みなどで勉強会がスキップになることがあります。 勉強会を停滞させずに継続するには、発表者の負担を分散し、進行を柔軟に調整できる仕組みを整えることが重要です。 今後も、勉強会の着実な継続を続けて、自分たちの成長につなげるために、勉強会運営の工夫を続けていきます。 また、これまでチーム内での学習が中心でしたが、他部署でも専門的な勉強会( リファクタリング 最新技法など)が開催されていることが分かりました。今後は、 社内の知見を活かし、他部署の発表者を招いたり、資料提供を受けたりすることで、学習の幅を広げていきます 。 私自身も、勉強会の質を向上させるために、 LT会 やテックブログへの参加を継続し、資料作成やプレゼンテーションのスキルを磨いていきます。さらに、チーム内で関心の高いコンテンツ(最近であればコンテナ技術やNotebookLMを活用した業務改善等)についても学び、実務への応用を進める予定です。 「 学習し成長し続ける 」 という姿勢を大切にし、新しい知識を積極的に吸収しながら他業界の優れた事例も取り入れ、個人とチームの成長につなげていきます。勉強会を実践的な学びの場へと進化させ、さらに成長を加速させるため、今後も改善を続けていきます。 const ctx = document.getElementById('studyChart').getContext('2d'); const studyChart = new Chart(ctx, { type: 'bar', data: { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格取得向け', data: [1, 3, 0], // 各年の資格取得向け勉強会数 backgroundColor: 'rgba(54, 162, 235, 0.6)' }, { label: '業務知識向け', data: [0, 0, 7], // 各年の業務知識向け勉強会数 backgroundColor: 'rgba(255, 206, 86, 0.6)' }, { label: '技術向け', data: [0, 0, 5], // 各年の技術向け勉強会数 backgroundColor: 'rgba(75, 192, 192, 0.6)' }, { label: '最新技術&ツール', data: [0, 0, 3], // 各年の最新技術系勉強会数 backgroundColor: 'rgba(255, 99, 132, 0.6)' } ] }, options: { responsive: true, scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } } } }); const ctx2 = document.getElementById('radarChart').getContext('2d'); const data = { labels: ['実務/テスト', '実務/プロダクトサポート', '知識/商材理解', '知識/IT共通', '知識/専門知識'], datasets: [{ label: '達成度', data: [10, 59, 67, 50, 82], borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', pointBackgroundColor: 'rgba(54, 162, 235, 1)', pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: 'rgba(54, 162, 235, 1)' }] }; const options = { responsive: true, maintainAspectRatio: false, aspectRatio: 1, // チャートの縦横比を調整(小さく) layout: { padding: 0 // 余白を最小限に }, plugins: { legend: { display: false // 凡例を非表示 } }, scales: { r: { suggestedMin: 0, suggestedMax: 100, ticks: { stepSize: 25, callback: function(value) { return value + '%'; } }, pointLabels: { font: { size: 10 // 軸ラベルのフォントサイズを小さく } }, grid: { lineWidth: 0.5 // グリッドの線を細くして余白を減らす } } } }; new Chart(ctx2, { type: 'radar', data: data, options: options }); const ctx3 = document.getElementById('qualificationChart2').getContext('2d'); const data2 = { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格保有者数(累計)', data: [3, 17, 23], // 各年度の資格保有者数(累計) backgroundColor: 'rgba(0, 115, 207, 0.2)' // 青色 } ] }; const qualificationChart2 = new Chart(ctx3, { type: 'bar', data: data2, options: { responsive: true, plugins: { legend: { display: false } // 凡例を非表示 }, scales: { y: { beginAtZero: true, title: { display: true, text: '資格保有者数' }, ticks: { precision: 0 } // 小数点を表示しない } } } });
こんにちは!株式会社 ラク スで技術広報を担当している川東と申します! 2025年2月27日に開催された 『EMConf JP』 は、エンジニア リングマ ネジメントを実践するEMにとって刺激的なイベントでした! 公式サイト( fortee.jp/emconf-2025 )にもある通り、今回のテーマは 「増幅」 と 「触媒」 。すなわち、EMたちが持つ熱意や知見をより大きく広げ、組織全体に好影響を与えるセッションが盛りだくさんでした。熱気あふれる会場には多くのEMやエンジニアが集まり、リアルな知見や体験を共有する貴重な場となりました。 弊社からはPdMチームのEMである稲垣が登壇しております! 本レポートでは、ホールAとホールBの両方で行われたセッションを、筆者の独断と偏見で学びを感じたポイントに絞って振り返っていきたいと思います! (実際にイベントに参加して技術広報2名で全セッション視聴してきました!) セッションレビュー キーノート エンジニア リングマ ネージャという働き方と知識体系のロードマップ(広木大地/レクター) EMとしての役割は「エンジニアの管理職」から進化し、【4つのP】―人材育成、技術戦略、プロジェクト管理、プロダクト連携―が求められるようになったという視点が印象的でした。また、フェ イルファ ストの精神や 心理的 安全性の確保など、現場で実践すべきポイントを明確に示してくれました。 hirokidaichi.github.io n=1の経験が紡ぐエンジニア リングマ ネジメントの可能性(岩瀬義昌/ NTTコム 、fukabori.fm) 個人的に一番印象に残ったセッションです。実践に基づくリアルな体験談と、EMとしての具体的なノウハウ(「ポエムドリブンマネジメント」など)が盛り込まれており、すぐにでも現場で活かしたい内容ばかりでした。 speakerdeck.com 【ホールA】 Potential EM制度を始めた理由、そして2年後にやめた理由(青木啓剛/estie) 期間限定でEMとしての素養を見極める制度の試み。その中で、EMに求める最重要要件や、常に組織を変革していく必要性が浮き彫りにされました。 speakerdeck.com エンジニアリング価値を黒字化する、バリューベース戦略を用いた技術戦略策定の道のり( Kazuki Maeda/atama plus) 経営戦略と技術戦略の橋渡しとして、バリューベース戦略を活用する方法が紹介され、経営層と対等な目線で議論を進めるための具体的なアプローチが印象に残りました。 speakerdeck.com 1行のコードから社会課題の解決へ:EMの探究、事業・技術・組織を紡ぐ実践知(熊谷遼平/レディーフォー) 事業、技術、組織の3軸からEMの役割を探究している点が印象的でした。特に、学習文化形成のために業務時間の3割を本業以外に充てる施策は、メンバーの成長と組織の活性化に寄与すると感じました。 speakerdeck.com Two Blades, One Journey: Engineering While Managing(ohbarye/スマートバンク) EMとICの両立が可能であるという実体験からの提案。EMとしての柔軟な働き方や、必要に応じた役割転換のヒントが、参加者にとって大いに参考になるだろうと思わせるセッションでした。 speakerdeck.com 「共創型エンジニア リングマ ネジメント」の挑戦と実践(うっしー/kubell) 組織拡大に伴う局所最適やサイロ化の問題に対し、EMグループ全体で課題解決に取り組む姿勢は、共創の精神そのものでした。全員が責任を共有するという考え方は、今後の組織運営においても非常に魅力的です。参考にしたいです! speakerdeck.com サバイバルモード下でのエンジニア リングマ ネジメント(こにふぁー/Kyash) 売上減少や人材流出といった厳しい現場での実践例は、危機時における判断の難しさを改めて認識させられる内容でした。​会場は立ち見が出るほどに盛況でした! speakerdeck.com 【ホールB】 プロダクト部門のマネージャー全員でマネジメントポリシーを宣言した記録(piro takahara/スタンバイ) 部門全体で共通のポリシーを作ることで、情報共有と成功体験のすり合わせが図られ、組織拡大の中での一体感を醸成する重要な取り組みとして参考になりました。 speakerdeck.com 急成長する企業で作った、エンジニアが輝ける制度(池ノ上倫士/SHIFT) EMがいない状況下で導入されたスキルツリーや評価制度が、給与上昇率向上や 離職率 の低下につながった具体例が紹介され、エンジニアが輝ける土台作りの重要性を再認識させられました。 speakerdeck.com 楽しいぞEM拡張パズル!課題と共に役割を広げちゃおう!(笹健太/クリエーションライン) 「EMって何をすればいいのか?」という疑問を、PMトライアングルの視点から整理。完璧なスー パーマン ではなく、得意分野を生かしながら成長する姿勢は、多くのEMにも共感できるセッションだったと思います。 speakerdeck.com 大規模 アジャイル から学ぶ、エンジニア リングマ ネジメントの本質(Kittaka shun/TOKIUM) 複雑化する組織で、 アジャイル 手法を応用した意思決定と役割分担の工夫が紹介され、EMとしての自信とポジティブな取り組み方を後押しする内容でした。 speakerdeck.com わたしがEMとして入社した「最初の100日」の過ごし方(daiksy/ はてな ) パラシュート人事としてのスタートに伴う期待値調整や、既存の文化へのリスペクトの重要性を具体的に語っており、新任EMにとっては必聴のアド バイス となりました。 speakerdeck.com プレイングマネージャー は本当に悪なのか? - 令和時代の プレイングマネージャー を戦略的にハックする(すずけん/カミナシ) 技術力とマネジメントを同時に発揮する プレイングマネージャー のメリットと課題を、実践的な視点で議論。意思決定と実行の迅速さ、評価への納得感が強調されました。 speakerdeck.com EMの仕事がLVアップした3つの視点 ~これまでに出会ったEMから学んだこと~(shinden (新田 智啓)/Datachain) 「WHY(価値を知る)」「WHAT(理想と課題設定)」「HOW(体系的な学び)」という3つの視点で、EMとしてのレベルアップを図る方法がシンプルかつ説得力をもって伝えられました。 speakerdeck.com 多様なマネジメント経験から導き出した、事業成長を支えるEMの4つの コンピテンシー (稲垣剛之/ ラク ス) 弊社(株式会社 ラク ス)からはPdMチームを統括するEMの稲垣が登壇しました。今回、大変多くのCfPの中から弊社EMのCfPを採択していただけたのはとても光栄でした! セッションの内容としては、論理的かつ具体的に、EMが事業成長にどう寄与できるかを弊社の評価軸である コンピテンシー という形で整理しました。実践的なヒントをお届けできていれば幸いです。 © 2025 EMConf JP 2025 実行委員会 speakerdeck.com 「Govtechという巨大な山に挑む」エンジニアが活躍する、世界最強の行政DXチームを目指す(井原正博/廣瀬幸帆/GovTech東京) 新しい公共 価値の創出に向け、内製化と組織文化の再構築を目指す壮大なビジョンが示され、行政領域でのチャレンジとして非常に刺激的な内容でした。 まとめ EMConf JPはオフライン限定のイベントでしたが、会場の熱量にすさまじいものを感じました! 各セッションを通じて感じたのは、確かにEMとしての業務は容易ではないものの、失敗を恐れずチャレンジすることで得られる学びや、組織全体に与えられる インパク トの大きさです。 今後もこのようなイベントが開催されることで、各々の知見や経験を共有しながら、より多くのEMが組織の成長に貢献していく未来を期待せずにはいられません。 EMに興味のある皆さん!ぜひ、この熱い波に乗って、EMとしてのキャリアをさらに進化させてみてはいかがでしょうか?
はじめに エンジニア2年目のTKDSです! 今回は GitHub のデ バイス フローを利用したユーザー認証の方法についてご紹介します。 デ バイス フローはブラウザに直接アクセスできない CLI ツールでも GitHub 側に認証を依頼できる機能です。 今回はデ バイス フローによる認証を経て発行されるアクセス トーク ンを使って、認証・認可を要求したユーザー名を取得し、ユーザー確認に利用するところまでやっていきます。 はじめに GitHub Appsによるデバイスフロー 実際にやってみる GitHub Appsの準備 アプリの準備 実行 まとめ 参考文献 GitHub Appsによるデ バイス フロー 認証リク エス ト時に返ってくるURL・ユーザーコードを使用し、ブラウザで認証を行うとアプリケーション側でアクセス トーク ンの発行を行えます。 アクセス トーク ンのリク エス トには初回の認証リク エス ト時に返ってくるデ バイス コードが必要なため、アクセス トーク ンのリク エス トを行うエンドポイントを単純に叩いてアクセス トーク ンを横取りされる危険性は低いと考えられます。 この方法を利用するメリットとしては、 - ユーザーはURLを開いて、ユーザーコードを入力するだけでいい - ユーザーの認証機能を直接提供せずに、 GitHub に認証を依頼できる - ユーザーに認証させたあと取得できるユーザー トーク ンを使いユーザー情報を取得できるので、認証依頼時点でユーザーの情報を知ってる必要がなく、エンドポイントから返ってきたユーザー トーク ンを使ってはじめてユーザーの特定ができる - GitHub Appsで権限を絞れば、ほとんどの情報取得をシャットアウトできる→ユーザー トーク ンはもちろんもらさない前提で安全性も高い 等があります。 ユーザー側の利便性、アプリ自体はURLとユーザーコードを提供するだけの利便性がいいと思って今回採用してみました。 では次に実装例を示していきます。 実際にやってみる 今回やりたかったのは、ユーザー認証と認証したユーザーのアカウント名の取得です。 まず、今回の処理の流れを以下に示します。 ユーザーがまずアプリを通して、デ バイス フローを GitHub に依頼します。 そして、返ってきた値のうち、URLとuser_codeをユーザーに提示します。 ユーザーが認証するまでの間、アプリ側は GitHub の トーク ン払い出しのエンドポイントをたたきつづけます。 ユーザーは表示されたURLにアクセスし、ログイン後、アクセス トーク ンの入力・権限の認可を行います。 ユーザーによる認可が完了した以降で、アクセス トーク ン払い出しURLにアクセスすると、ユーザーコードを使って認証・認可をしたユーザーに紐付いたアクセス トーク ンを取得できます。 この時点でアプリはユーザーが誰かわからないので、アクセス トーク ンを使ってユーザーの情報を取得します。   最終的にレスポンスのloginキーに含まれてるユーザー名を確認することで、どのユーザーがアクセス トーク ンを払い出したかわかります。 では、実際に準備していきます。 GitHub Appsの準備 今回のデ バイス フロー認証には GitHub Appが必要なため用意します。 New GitHub Appで GitHub Appsの作成をしてください。 設定画面に遷移するので、 Enable Device Flow にチェックをいれてください。 次に、 GitHub Appによって発行された トーク ンになんの操作を許すか設定します。 今回は個人のメールアドレスだけですが、ここで設定を変えれば、所属してるオーガナイゼーションの取得なども可能になります。 今回は一番弱い権限かつ取得しても問題なさそうな情報にしたいので、Email adressesの項目をRead-Onlyで設定します。 これで GitHub Appの設定は完了です。 client_id を控えておいてください、あとで使います。 アプリの準備 CLI アプリを準備していきます。 CLI アプリ側でやりたいことは、デ バイス フローのリク エス ト、ユーザーへのURLとuser_codeの提示、アクセス トーク ン払い出しのポーリング、取得したアクセス トーク ンによるユーザー情報の取得です。 Goによるコードを書きました。 サードパーティ のライブラリ使ってないため、そのまま動くと思います。 今回は先にgo mod initしてしまっていたので、go.modがある状態前提です。 package main import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "strings" "time" ) const ( // clientID = "" // GitHub AppのClient ID deviceAuthURL = "https://github.com/login/device/code" accessTokenURL = "https://github.com/login/oauth/access_token" githubUserURL = "https://api.github.com/user" pollingInterval = 5 * time.Second // intervalより長くしない、とりあえず5秒 ) // デバイスコード type deviceCodeResponse struct { DeviceCode string `json:"device_code"` UserCode string `json:"user_code"` VerificationURI string `json:"verification_uri"` ExpiresIn int `json:"expires_in"` Interval int `json:"interval"` } // アクセストークン type accessTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` Scope string `json:"scope"` Error string `json:"error,omitempty"` } var clientID string func main() { clientID = os.Getenv( "CLIENT_ID" ) // 1. デバイスコードを取得 deviceCodeRes, err := requestDeviceFlow() if err != nil { log.Fatal(err) } fmt.Printf( "verification_uri: %s \n " , deviceCodeRes.VerificationURI) fmt.Printf( "user_code: %s \n " , deviceCodeRes.UserCode) // 2. アクセストークンを取得(ポーリング) timeout := 3 * time.Minute ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() var accessTokenResponse accessTokenResponse loop: for { select { case <-ctx.Done(): default : accessTokenRes, err := requestAccessToken(deviceCodeRes.DeviceCode) if err != nil { log.Fatal(err) } if accessTokenRes.AccessToken != "" { accessTokenResponse = *accessTokenRes break loop } switch accessTokenRes.Error { case "authorization_pending" : fmt.Println( "ユーザー認証待ち" ) case "slow_down" : fmt.Println( "ポーリング感覚短すぎ" ) case "access_denied" : log.Fatal( "認証失敗" ) case "expired_token" : log.Fatal( "デバイスコードの有効期限が切れた" ) default : log.Fatal(accessTokenResponse.Error) } // time.Sleep(pollingInterval) } // time.Sleep(time.Duration(deviceCodeRes.Interval) * time.Second) time.Sleep(pollingInterval) } fmt.Println(accessTokenResponse) userInfo, err := getGitHubUserInfo(accessTokenResponse.AccessToken) if err != nil { log.Fatal(err) } fmt.Println(userInfo) } func requestDeviceFlow() (*deviceCodeResponse, error ) { client := http.Client{} sendData := url.Values{} sendData.Set( "client_id" , clientID) sendData.Set( "scope" , "repo,user" ) req, err := http.NewRequest( "POST" , deviceAuthURL, strings.NewReader(sendData.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) dCodeResp, err := client.Do(req) if err != nil { log.Fatal(err) } defer dCodeResp.Body.Close() var deviceCodeRes deviceCodeResponse err = json.NewDecoder(dCodeResp.Body).Decode(&deviceCodeRes) if err != nil { return nil , err } return &deviceCodeRes, nil } func requestAccessToken(deviceCode string ) (*accessTokenResponse, error ) { client := &http.Client{} data := url.Values{} data.Set( "client_id" , clientID) data.Set( "device_code" , deviceCode) data.Set( "grant_type" , "urn:ietf:params:oauth:grant-type:device_code" ) req, err := http.NewRequest( "POST" , accessTokenURL, strings.NewReader(data.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) tokenResp, err := client.Do(req) if err != nil { return nil , err } defer tokenResp.Body.Close() var tokenRes accessTokenResponse err = json.NewDecoder(tokenResp.Body).Decode(&tokenRes) if err != nil { return nil , err } return &tokenRes, nil } func getGitHubUserInfo(accessToken string ) ( string , error ) { req, err := http.NewRequest( "GET" , githubUserURL, nil ) if err != nil { return "" , err } req.Header.Set( "Authorization" , "Bearer " +accessToken) req.Header.Set( "Accept" , "application/vnd.github.v3+json" ) client := &http.Client{} resp, err := client.Do(req) if err != nil { return "" , err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "" , err } return string (body), nil } 実行 漏れたら困る値もあるのでぼかしつつ実際の流れを載せていきます。 1 . アプリ起動 環境変数 からclient_idを取得するようにしてあるので、 コマンドライン で指定して起動します。 client_idには、先ほど GitHub Appを作ったときに控えておいたclient_idを指定してください。 CLIENT_ID="<控えておいたclient_id>" go run main.go verification_uri: https://github.com/login/device user_code: xxxx-xxxx 2 . ブラウザでユーザーコードを入力 表示されたURLにアクセスし、user_codeを入力してください。 ユーザーコードの認証が成功すると、権限委譲の画面に移行するので、緑のボタンを押してください。 成功すると以下画面が表示されます。 これでユーザー側操作は完了です。 3 . アクセス トーク ンの取得とユーザー情報の取得 アプリ側は2の間ずっと トーク ン払い出しのエンドポイントをポーリングしています。 これには事前に払い出したclient_idと認証リク エス ト時にuser_codeと一緒に返ってくるdevice_codeを使用します。 取得できたアクセス トーク ンをもとにユーザー情報を取得します。 ここで取得したユーザー情報をもとに、アクセスしていいユーザーなのか判別などします。 ※ここの画面のスクショはアクセス トーク ンやユーザー情報が出てしまっているので割愛します。 以上で、デ バイス フローによるユーザー認証ができました。 まとめ 今回はデ バイス フローを利用して、 ユーザー認証を行いました。 今回用意したアプリではclient_idをユーザー側が入力しましたが、サーバー側で行うようにすることでより堅牢にユーザー認証できるのではないかと考えられます。 GitHub 側に認証を任せて簡単にユーザー認証ができるのは非常に便利です。 ここまで読んでいただきありがとうございました! 参考文献 https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-login-with-github-button-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#using-the-device-flow-to-generate-a-user-access-token https://www.authlete.com/ja/developers/device_flow/ https://tex2e.github.io/rfc-translater/html/rfc8628.html?utm_source=chatgpt.com