Findy/ファインディのブログ - TECH PLAY

TECH PLAY

Findy/ファインディ

Findy/ファインディ の技術ブログ

全223件

こんにちは! Findy Team+ 開発チームでEMをしている ham です。 Findy Team+のバックエンドはRuby on Railsで開発しており、Rubyの静的解析ツールとして広く知られているRubocopをTeam+の開発でも積極的に活用しています。 ファインディでは「爆速開発」を掲げており、開発速度に直結するCI(継続的インテグレーション)の実行時間を非常に重視しています。具体的には、CIの実行時間は遅くとも10分以内、理想としては5分以内に完了することを目標にしています。 CIの実行時間短縮に向けた取り組みについては、以前公開した次の記事で詳しくご紹介していますので、ぜひご覧ください。 tech.findy.co.jp 今回、バックエンドのCIの実行時間を見直したところ、Rubocopの実行時間が5分以上かかっていることに気づきました。これは改善の余地があると感じ、実行時間短縮に取り組むことを決めました。 また、CIではRubocopの他にRSpecでテストを実行しているのですが、RSpecの実行時間の方がRubocopよりも圧倒的に長いためそちらの改善ばかりに気を取られて、Rubocopの実行時間は見過ごされていました。 なお、この時もテストは7分ほどかかっておりRubocopより長かったですが、こちらは並列実行と実行環境のスペックアップを駆使することで3分程度まで短縮できました。 そこで、今回はRubocopの実行時間短縮に取り組むことを決意し、その結果、5分以上かかっていた実行時間を1分未満に短縮できました! なぜRubocopの実行時間が長かったのか? CIにおけるRubocopの実行は、基本的に全てのRubyファイルに対して無条件に実行されていました。 そのため、プロジェクトの規模が拡大するにつれて、解析対象となるファイル数も増加し、比例して実行時間も伸びていったのが主な原因です。 改善内容 実行時間短縮のために、次の3つの変更を加えました。 無条件で全ファイル解析していたが、編集したファイルだけ対象とする Gitの差分情報を用いて、変更があったファイルのみをRubocopの解析対象とするようにしました。これにより、CIの実行ごとに解析するファイル数を大幅に削減できました。 ファインディではGitHub Actionsを使ってCIを実行しているため変更したファイルを取得できる tj-actions/changed-files を活用して対象ファイルを取得しました。 設定ファイルやGemがアップデートされたときは全て解析 前述の変更により通常時は変更ファイルのみを対象としますが、Rubocopの設定ファイル(.rubocop.ymlなど)やRubocop本体や関連Gemのバージョンがアップデートされた場合は全てのファイルを解析するようにしました。 これは、設定変更やバージョンアップによって、これまで問題なかった箇所が新たに指摘される可能性があるため、CIの信頼性を保つ上で重要な対応です。 ファイル指定だと除外設定が無視されたので無視されないように変更 Rubocopには、コマンド引数に対象ファイルを明示的に指定すると、.rubocop.ymlで指定されている除外設定(Exclude)が無視されるという仕様があり、本来は解析対象外のファイルまで解析されてしまうという問題が発生しました。 これを解決するため、 --force-exclusion オプションを利用できることがわかりました。こちらのオプションを指定することで対象ファイルを明示的に指定した場合でも除外設定が反映されるようになります。 bundle exec rubocop -f github --force-exclusion ${{ all_changed_files }} 結果 これらの改善を適用した結果、5分以上かかっていたRubocopの実行時間は1分未満にまで劇的に短縮されました! これにより、CI全体の実行時間が短縮され、開発者はより素早くフィードバックを得られるようになりました。Rubocopの指摘への対応も迅速に行えるようになり、コードの品質維持にも貢献しています。 まとめ CIにおける静的解析の実行時間は、テスト実行時間に比べると見過ごされがちですが、積み重なると開発体験を損なう要因となります。 今回のRubocopの実行時間短縮で行った次の対応は、実装難易度も低く簡単にできるものでしたが、大きな効果をもたらしてくれました。 無条件で全ファイル解析していたが、編集したファイルだけ対象とする 設定ファイルやGemがアップデートされたときは全て解析 ファイル指定だと除外設定が無視されたので無視されないように変更 もしあなたのプロジェクトでもCIのRubocop実行時間が長いと感じているのであれば、本ブログで紹介した内容が少しでも参考になれば幸いです。変更ファイルのみを対象とする工夫は、他の静的解析ツールにも応用できる可能性がありますので、ぜひ検討してみてください。 ファインディでは一緒に働くメンバーも絶賛募集中です。興味がある方はぜひこちらから ↓ herp.careers
こんにちは、ファインディでソフトウェアエンジニアをしている nipe0324 です。 先日、AWSさんの Coding Agent at Loft #2 〜 AI コーディング活用事例 Night - 効果的な組織導入と実践〜 (第2回目) で登壇させていただき、登壇内容を記事として書き起こしました。 この記事では、Devinと協働して 「個人のアウトプットを1.5倍にした実践3ステップ」 をご紹介します。 Devinと協働して個人のアウトプットは1.5倍に増加 2025年5月末時点では、Copilot Coding Agent、Codex、Jules などのリリースが続々とされており、自律型のコーディングエージェントが盛り上がってきています。 Devinに特化せず他のコーディングエージェントでも使える内容になっていますので、 自律型のコーディングエージェントの利活用に興味ある方 はご一読くださいませ。 ちなみに、生成AI界隈は動きが早いので、数ヶ月後はどうなるかわかりません。また、現時点での業務で試した結果の1事例として参考にしてください。 はじめに Devinと協働してアウトプットを1.5倍にした3ステップ ステップ1. Devinに教える 1-1. 小さなタスクから始める 1-2. Devinに開発のルールを教えていく ステップ2. Devinに慣れる 2-1. Devinのドキュメントを読む 2-2. 試行錯誤して勘所を掴む 2-3. Knowledgeを洗練させる 2-4. AI Friendlyな環境を整備する ステップ3. Devinと協働する 3-1. Devinに定型作業を任せる 3-2. Devinと一緒にタスクを進める 3-3. Devinにサブタスクを任せる おまけ: Devinの活用Tips Tips1. GitHub/AWSアカウント管理のワークフローを自動化 Tips2. 口調を変えるKnowledgeで親しみやすくする Tips3. PMが仕様把握のためにDevin Searchを利用 Tips4. AI駆動な仮実装 Devinに対する所感や協働して変わってきたこと まとめ はじめに Microsoftさんの最近の研究では、組織がAIトランスフォーメーションをしていくためには、次の3つのフェーズがあるようです。 Phase 1 Human with assistant :AIがアシスタントする Phase 2 Human-agent teams :エージェントがチームにジョインして、同僚として一緒に働く Phase 3 Human-led, agent-operated :人間がディレクションをして、エージェントが業務を遂行する。必要があれば人がチェックする AI Transformationの3段階(引用元: https://www.microsoft.com/en-us/worklab/work-trend-index/2025-the-year-the-frontier-firm-is-born ) その中で、今回の話はこの 「Phase2 エージェントと一緒に働く」 をトライしている話になります。 Devinと協働してアウトプットを1.5倍にした3ステップ ステップとしては次の3ステップで進めていきました。期間はDevinの導入から大体3ヶ月ぐらいかけて実施していきました。 ステップ1. Devinに「教える」 ステップ2. Devinに「慣れる」 ステップ3. Devinと「協働する」 個人的なポイントとしては、ステップ3までいくと、 コーディングエージェントの良さを感じ、日々のアウトプットをスケールしていけるという実感 をもてました。 ステップ1. Devinに教える 最初のステップは「Devinに教える」です。 Devinに小さなタスクを依頼しながら基本的な開発ルールを教えていくことで、 Devinが軽微な修正をそつなくこなせる ようになります。 1-1. 小さなタスクから始める まずは、文言変更や軽微なタスクを依頼してDevinの動きを知ります。 実際にDevinが開発している画面をみながら、Devinの開発環境・作業手順・やりとり方法などを押さえることで今後のDevinとのやりとりの土台をつくれます。 DevinとやりとりするWeb画面 アンチパターンとして、エージェントへの期待値が高すぎることで、最初から「急ぎのタスク」や「大きめなタスク」を依頼し精度がでなく幻滅してしまうことがあります。 人と同じで小さなタスクから開発に慣れてもらうことが大切です。Devinはチームに入った新人だと思い、焦らずまずは文言変更やドキュメント修正など小さなタスクから依頼していくことをオススメします。 1-2. Devinに開発のルールを教えていく 開発のルールは、会社やプロジェクトごとに違います。そのため、Devinに小さなタスクをお願いしながら開発ルールを教えていきます。 Devinには Knowledge という機能があります。Devinとの会話からルールをほぼ自動的に学び、Knowledgeとして保存することで、次回以降のタスク精度が高まります。 以下は、実際にDevinが学んだ開発ルールのKnowledgeの一部です。 ブランチ作成、PR作成、コミット作成、テスト実行など基本的な開発ルールをKnowledgeとしてDevinに学んでもらいました。 実際に学んだ開発ルールのKnowledge(抜粋) ステップ2. Devinに慣れる 次のステップとして、「Devinに慣れる」です。 Devinのドキュメントを読みながら、試行錯誤して依頼の勘所を掴みます。また、Knowledgeを洗練させ、AI Friendlyな環境を整えることで、 Devinがよくあるタスクで90点以上のタスク精度を実現できる ようになります。 2-1. Devinのドキュメントを読む Devin の Documentationの Essential Guidelines は必読です。Devinにうまく依頼するための重要なプラクティスが記載されています。 例えば、次のような内容が記載されています。 Devinは「ジュニアエンジニア」として扱うのが最適です。明確で十分な指示があれば、インターンや新人エンジニアが対応可能なタスクを任せることができます。 ベストプラクティス: 小さなタスクを並行して実行 :朝の始まりに複数の小さなタスクをDevinに割り当て、昼食時にレビューを行うと効率的です。 Slackでの迅速な対応 :Devinは30分程度で完了するタスクに適しており、長期間放置されがちなバックログの解消に役立ちます。 明確な成功基準のあるタスクに集中 :CIのパス確認や自動デプロイのテストなど、結果が明確に確認できるタスクが理想的です。 小さく始める :Devinの性能は長時間のセッション(≒10 ACU以上)で低下する可能性があるため、小規模なタスクから始めることを推奨します。 引用元: https://docs.devin.ai/essential-guidelines/when-to-use-devin の英語を翻訳 2-2. 試行錯誤して勘所を掴む Devinに様々なタスクを依頼しながら、依頼内容、依頼方法、タスク粒度の勘所を掴んでいきます。また、Devinのドキュメントを読んで学んだことを試しながら、より良い協働方法を模索していきます。 例えば、変数で指示をすることで汎用的なプロンプトを作成する、どの程度までの抽象的な指示ならタスクを遂行してもらえるか確認するなど試してみました。 そして、試行錯誤した結果、Linter対応や横展開系の対応、FeatureFlagの追加・削除、管理画面の軽微な改修、簡易なSentryのアラート対応などの定型作業は、ほぼ90点以上の精度でプルリクエストを作成できました。 Tipsとして、Devinの作成したプルリクエストの精度がいまいちなときは、Devinの設定画面で「Devinは承認を待たずに進める」のチェックをオフにするがおすすめです。複雑なタスクの場合、Devinの作業計画を承認してから作業を進めることができるので、作業計画段階で早めに方向性のすり合わせができ、「プルリクエストが出来上がった後のこれじゃない感」を大幅に減らせます。 「Devinは承認を待たずに進める」のチェックをオフにする 2-3. Knowledgeを洗練させる Devinとの試行錯誤を通しながら、DevinのKnowledgeのバリエーションを増やしていきます。 例えば、テーブルを扱う変更やビジネスロジックの作成、APIの実装、ドキュメンテーションの追加など、さまざまなケースのKnowledgeが溜まっていきます。 これにより、より多くのタスクを精度よく任せられるようになります。 しかし、KnowledgeがあってもDevinがその内容通りに動かないこともあります。 そのような場合は、うまくいかなかったやりとりをもとに「今回のやりとりを次回以降で活かすためにKnowledgeを更新して」と伝え根気強くKnowledgeを更新します。 それでも駄目なら、初期の依頼プロンプトに内容を盛り込んだり、現状のLLMの限界として諦めています。 2-4. AI Friendlyな環境を整備する 単純にDevinに頑張ってもらうだけでなく、AIが開発をしやすいコードベースや開発環境を整備することも大事です。 個人的に、AI Friendlyな環境として以下3つは特に重要だと考えています。 自動テスト、Linter、CIは必須 :Devin自身でフィードバックループが回せなくなるので作業の精度が下がります。また、レビュアーのレビュー時のガードレールにもなるので必須です。 コードの統一や暗黙のルールを明文化 :既存の実装を踏襲してもらうとDevinのタスク精度が高くなります。コードベース上で同じ目的のためにいろんな実装方式があるとDevinが迷ってしまうので、コードの統一感を高めています。また、暗黙知も明文化してKnowledgeとして教えます。 場合によっては開発ルールを変更する :Devinに何度伝えてもうまく対応できないことがあったので、ルールを変更しました。具体的には、コミットメッセージの文字数のルールを守れなかったため、開発ルールとしての制限を緩めることで対応しました。 ステップ3. Devinと協働する ここまででDevinへの依頼に慣れて、ある程度の難易度のタスクならほぼ満足できるようになってきます。 最後のステップでは、Devinに定型作業を任せたり、一緒にタスクを進めたりすることで、 個人のアウトプットを増加 させていきます。 3-1. Devinに定型作業を任せる 「これは9割以上の精度でDevinに任せられる」というタスクはDevinに任せていきます。定型作業のようなものは、ほぼ完璧にプルリクエストを仕上げてくれます。 最近では、個人のAIエージェントとの協働を通じてマインドも変化してきました。ある程度定型化されたタスクは「Devinに任せれば良さそう」いう考えになってきています。 例えば、軽微な修正やFeatureFlagの追加・削除、モデル作成、マイグレーションファイル作成、管理画面の簡易な対応などはDevinに任せて、その間に自分はDevinができない作業を進めることが多くなりました。 3-2. Devinと一緒にタスクを進める Devinは、CursorやClineなどローカルマシンで実行するエージェントに比べて相対的に作業時間は長くなります。しかし、タスクを分担して連携して開発できれば、大きな問題にはならないと考えています。 私がよく実施する「Devinと一緒にタスクを進める方法」は主に2つです。 よくあるDevinと一緒にタスクを進める方法 パターン①:タスクを分解し、Devinがメインで開発を担当し、難しい部分やレビューを自分が行う パターン②:横展開系のタスクで、最初の実装イメージは自分が作成し、その後の横展開はDevinに並列で実装を任せ、レビューを自分が行う 3-3. Devinにサブタスクを任せる たまに、メインでないサブタスクを裏でDevinに丸っと依頼して作ってもらうことをします。 Devinの公開APIを利用した自前ワークフローツール(※Roo Codeの Boomerang Tasks の簡易版のようなもの)を作成し、一連の依存関係のあるタスクリストをワークフローツールで実行しています。 これにより、1〜2時間ほどで一通りの対応(5〜8プルリクエスト)をDevinに実装してもらうことが可能になりました。 タスクの定義例。依存関係にそってタスクを実行してくれる 課題としては、個々のタスク精度にはまだ伸びしろがあります。また、メインタスクとサブタスクを頻繁に切り替える必要もでてくるため、かなり疲れることもあります。 改善案として、レビューの一部をエージェント化したり、ワークフローのオーケストレーターやタスク出しをエージェント化したりすることで、より丸ごと任せられるか試してみたいと考えています。 おまけ: Devinの活用Tips 最後におまけとして、Devinの活用Tipsをいくつかご紹介します。 Tips1. GitHub/AWSアカウント管理のワークフローを自動化 SREチームでは、SlackワークフローとDevinを組み合わせてGitHubとAWSのアカウント管理をほぼ自動化しています。 SlackワークフローでDevinに依頼 流れとしては次のとおりです。 前提として、Terraformでアカウント管理をしている。 Slackのワークフローで依頼をすると、@Devinメンションでワークフロー内容が記載される。 Devinが反応してアカウント管理(追加・更新・削除)のプルリクエストを作る。 SREがプルリクエストをレビューをする。修正がある場合プルリクエスト上でコメントをするとDevinが修正してくれる。 SREがプルリクエストをマージすると、Terraformによりアカウントが追加・更新・削除される。 SREの方に効果を聞いたところ、月末月初などユーザー申請の対応が五月雨にあり地味に時間がかかっていた工数が 感覚値で1日程度削減 できたようです。 Devinには、Slackからタスクを依頼をできる機能があるので、Slackのワークフローとの接続がとても簡単にできるところはいいところだと思っています。 Tips2. 口調を変えるKnowledgeで親しみやすくする DevinのKnowledgeで口調を指定することで、エンジニアがDevinに親しみを感じやすくしています。 Devinはデフォルトだと割とかしこまった感じの口調なのですが、Knowledgeを指定することで砕けた感じの会話ができるようになり、チームとDevinの親近感が醸成されました。(たぶん...) Devinの口調を変えるKnowledgeとDevinとのやりとり 実用的な観点ではDevinが Knowledge をどのぐらい理解して作業をするかを日々検証できるというメリットもあると思っています。 Tips3. PMが仕様把握のためにDevin Searchを利用 Devinには、コーディングだけでなくDevin Searchという自然言語で質問をして回答を得られる機能があります。現在はβ板なので無料で利用できます。 Devin Searchで仕様を確認 実際にPMからメール送信の条件に対する質問をされ、Devin Searchで検索したら100点の回答が得られました。 そのため、PMにDevinのアカウントを発行して、詳細の仕様が気になる場合はDevinに聞いてもらうという取り組みをしました。 Tips4. AI駆動な仮実装 まだ試行錯誤段階ですが、Devinをフル活用して、仮実装を感覚値で2~3日想定が1日で完了しました。また、仮実装を通して設計やコードの解像度が高まり、「実現性の検証」や「改善点の発見」を短期間で実現できました。 仮実装の流れは次のように進めました。 Devin Wikiで新しいリポジトリの主要機能や設計を把握 Devin Searchで影響範囲を設計とコードレベルで把握 Devin に大きめなタスクを渡して仮実装 Cursorを使いながら仮実装の細部を修正して動くようにする ある程度頭の中に設計イメージがあり、そのイメージをDevinに伝えて設計をしてもらい、仮実装を進めていくことで素早く検証を進めることができました。(※もちろん仮実装のコードは捨てて、本実装時に細部も含めてちゃんと作り込みます) Devinに対する所感や協働して変わってきたこと Devinとうまく協働することで、いままではチームのエンジニアが5~6名ぐらいだったのが、3名前後でも同程度のアウトプットをだせるようになっていきそうな感覚を受けています。 しかし、足元少しずつ顕在化しはじめているのですが、より少人数のチームでプルリクエストの数が増えているのでチームのレビュー負荷が高まっています。 具体的な解決策のアイデアはまだないですが、レビューのあり方をよりAI駆動によせていく必要があると思っています。 また、Devinと協働する中で個人の動きも少しずつ変わっています。 ミーティングが多くなってしまった日でも、Devinに実装をお願いすることで想定の進捗どおりに開発できた 一部のプロダクトマネジメント業務やデータ分析など業務の幅が広がった 生成AIの進歩は早いですが、新しい技術に一喜一憂しながら、楽しんでいこうと思っています。 まとめ 今回の記事では、 「Devinと3ヶ月協働して個人のアウトプットを1.5倍にした3ステップ」 についてご紹介しました。 Devinと協働する3ステップは次のとおりです。 ステップ1. Devinに教える :小さなタスクからはじめ、開発ルールを教える。 ステップ2. Devinに慣れる :Devinのドキュメントを読みながら試行錯誤をする。また、Knowledge拡充やAI Friendlyな環境を整備する。 ステップ3. Devinと協働する :Devinに定型作業を任せる。Devinと一緒にタスクを進める。Devinにサブタスクを全て任せる。 今後は次のようなことにトライしていきたいと考えています。 個人のアウトプット向上から、チームや組織のアウトプット増加につなげていきたい Devinに任せられる範囲を増やし、AI駆動開発をより推進したい また、セキュリティなどには気をつけつつも、DevinにとらわれずCopilot Coding Agent, Claude Code + GitHub Actionsなどいろいろと試しながら、より開発組織のスピードと品質を向上させていきたいと考えています。 ファインディでは生成AIやAIエージェントを試して開発生産性を高めることで、ユーザーにより価値を届けることを推進しています。 こうした新しい技術の利活用に興味がある方は、ぜひカジュアル面談にお越しくださいませ。 herp.careers
はじめに 皆さんこんにちは。ファインディに転職してまもなく1年を迎える、CTO室/SREチームの安達( @adachin0817 )です。今回は複数のWordPressサイトをShifterへ移行しましたので、その取り組みについてご紹介します。 Shifterとは ja.getshifter.io Shifterは、WordPressサイトを静的に変換・ホスティングできるマネージドサービスです。動的に動作するWordPressをShifterがビルドし、生成されたHTML/CSS/JavaScriptの静的ファイルをCDN経由で配信する構成となっています。主なメリットは次のとおりです。 セキュリティ強化:PHP実行環境を持たないため、WordPress本体やプラグインの脆弱性リスクが激減。 運用負荷の軽減:インフラやWordPressのバージョンアップ管理が不要。 表示速度の高速化:静的ファイルをCDN 経由で高速配信できるため、ユーザー体験の向上が期待。 自動バックアップ・簡単ロールバック:管理画面から任意のタイミングで復元可能。 Shifterに移行する背景 旧構成図 元々はAmazon Lightsail上の単一サーバーで複数のサイトを運用しており、画像ファイルやデータベースもすべて同一のインスタンス上で管理されていました。コーポレートサイト以外はPHPやプラグインのバージョン管理が行われておらず、明確な運用者もいなかったため、誰がメンテナンスしているのか分からない状況が続いていました。 また、コーポレートサイトに関しては情報システムチームが毎週のようにセキュリティアップデートを対応する必要があり、テーマのデプロイも自動化されておらず手動で行っていたため、運用負担が大きいという課題を抱えていました。 こうした背景に加え、会社全体でも定期的なバージョンアップ対応やセキュリティ強化に十分なリソースを確保することが難しく、改善が急務となっていました。そのような中、2024年に全社的な信頼性向上を目的として横断SREチームが発足しました。SREチームでは属人化していたWordPress環境を集約・統制することで、信頼性の向上を目指す取り組みが始まりました。 その解決策としてマネージドサービスへの移行を検討し、上記のメリットとコストが比較的安価のため、最終的にShifterを採用しました。次は、ローカル開発環境についてご紹介していきます。 開発環境の構築 ローカル開発環境では、コーポレートサイト以外の開発環境が整っていなかったため、Dockerを使って1から構築し直すことにしました。リポジトリ構成としては、WordPress本体をwordpress-coreリポジトリ(仮)で一元管理し、各サービスのテーマは個別のリポジトリで管理する方式を採用しました。これは、すべてを単一リポジトリに集約すると容量の肥大化や管理の複雑さを招くためです。 ※以下仮としています compose.yml ❯❯ cat docker-compose.yml volumes: php-fpm-sock: services: wp-nginx: container_name: wp-nginx build: docker/dev/nginx/ image: wp-nginx ports: - '80:80' - '443:443' volumes: - ./wordpress-core:/var/www/wp:delegated - ./site-corporate:/var/www/wp/corporate/wp-content/themes/site-corporate:delegated - ./site-enterprise01:/var/www/wp/enterprise01/wp-content/themes/site-enterprise01:delegated - ./site-enterprise02:/var/www/wp/enterprise02/enterprise/wp-content/themes/site-enterprise02:delegated - ./site-blog:/var/www/wp/blog/wp-content/themes/site-blog:delegated - php-fpm-sock:/var/run/php-fpm tty: true depends_on: - wp-app wp-app: container_name: wp-app build: docker/dev/app image: wp-app volumes: - ./wordpress-core:/var/www/wp:delegated - ./site-corporate:/var/www/wp/corporate/wp-content/themes/site-corporate:delegated - ./site-enterprise01:/var/www/wp/enterprise01/wp-content/themes/site-enterprise01:delegated - ./site-enterprise02:/var/www/wp/enterprise02/enterprise/wp-content/themes/site-enterprise02:delegated - ./site-blog:/var/www/wp/blog/wp-content/themes/site-blog:delegated - php-fpm-sock:/var/run/php-fpm tty: true depends_on: - wp-db wp-db: container_name: wp-db build: docker/dev/mysql image: wp-db command: --default-authentication-plugin=mysql_native_password ports: - '33066:3306' volumes: - ./mysql/db_data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql ShifterがPHP 8.1に対応していることから、Appコンテナ・Nginxコンテナ・MySQLコンテナも同じバージョンに揃えて構成しました。WordPressのバージョンや依存パッケージに差異があると、静的化後に想定外の動作になる恐れがあるためです。Shifterでは開発用のDocker環境も用意されていますが、できる限り本番と同等の構成になるよう意識して環境を整備しました。 さらに、Justfileを導入することで、hosts登録やmkcertによる自己署名SSL証明書発行、S3からのDBダンプ取得などのセットアップを自動化しました。複雑だった開発環境の構築手順が、今では just all コマンドを実行すれば素早く構築できるようになりました。ただ、運用していく中で、開発環境自体のバージョンアップもしなければなりませんが、今後GitHub Actionsで自動化していきたいと思っています。 Justfile 開発用ドメイン設定 リポジトリのclone mkcertの実行 wp-config.phpのコピー Docker Composeの起動から停止 DBのリストア setup-hosts: for hosts in "127.0.0.1 dev.site.co.jp" "127.0.0.1 dev.site.co.com" "127.0.0.1 dev-site2.com" "127.0.0.1 dev-site3.com"; do \ if ! grep -q "$hosts" /etc/hosts; then \ sudo bash -c "echo '$hosts' >> /etc/hosts"; \ fi; \ done setup-mkcert: cd ~/www/wordpress-core && \ brew install mkcert && \ mkcert -install && \ mkdir -p docker/dev/nginx/cert && \ cd docker/dev/nginx/cert && \ mkcert "*.hoge.co.jp" clone-repos: @echo "Cloning repositories..." repos="site-corporate site-blog site-enterprise01 site-enterprise02"; \ for repo in $repos; do \ dir="$HOME/www/$repo"; \ url="git@github.com:hoge/$repo.git"; \ if [ ! -d "$dir" ]; then \ git clone --depth 1 "$url" "$dir"; \ echo "Cloned $repo repository."; \ else \ echo "$repo repository already exists. Pulling latest changes."; \ cd "$dir" && git pull origin main; \ fi; \ done copy-wp-config: for dir in "corporate" "site-blog/blog" "site-enterprise01/enterprise01" "site-enterprise02/enterprise02"; do \ cd ~/www/wordpress-core/$dir && cp wp-config-dev.php wp-config.php; \ done start-docker: docker-compose up -d stop-docker: docker-compose stop down-docker: docker-compose down restore-db profile: rm -rf ./db_dumps aws --profile={{profile}} s3 cp s3://hoge/dev/MySQL/ ./db_dumps --recursive --exclude "*" --include "*.sql" for file in ./db_dumps/*.sql; do \ db_name=$(basename "$file" .sql); \ echo "Copying $file to wp-db container"; \ docker cp "$file" wp-db:/tmp/$(basename "$file"); \ echo "Dropping and creating $db_name"; \ docker exec wp-db mysql -u root -e "DROP DATABASE IF EXISTS \`$db_name\`; CREATE DATABASE \`$db_name\`;"; \ echo "Restoring $db_name from /tmp/$(basename "$file")"; \ docker exec wp-db sh -c "mysql -u root $db_name < /tmp/$(basename "$file")"; \ echo "Cleaning up /tmp/$(basename "$file") from wp-db container"; \ docker exec wp-db rm "/tmp/$(basename "$file")"; \ done rm -rf ./db_dumps all profile: just setup-hosts just clone-repos just copy-wp-config just start-docker just restore-db {{profile}} Shifter移行とトラブルシューティング Shifterへの移行にあたっては、公式ドキュメントにも記載されているとおり、All-in-One WP Migrationプラグインを使ったエクスポート・インポート方式が推奨されています。この方法を使えば、WordPressサイトを簡単に丸ごと移行できるのが大きなメリットです。実際に試してみたところ、基本的な移行作業はスムーズに完了しましたが、いくつか想定外の不具合も発生しました。以下にその内容を共有します。 独自テーマのページネーションが正しく動作しない コーポレートサイトのページネーションが正しく機能しないという事象が発生しました。Shifterでは、静的サイトを生成する際にWordPress REST API を通じて対象のURL一覧(JSON形式)を取得し、それをもとに静的化を行います。しかしながら、WordPressのアーカイブページのページネーションはデフォルト設定のままだと検出されず、静的化の対象から漏れてしまうことがあります。この問題に対しては、Shifterが提供する ShifterURLS::AppendURLtoAll を使うことで、手動で静的化対象のURLを追加が可能です。次のようにfunctions.phpに追加し、ページネーションURLを明示的に含めることで対応しました。 function.php function my_append_urls( $urls ) { $limit = get_option('posts_per_page'); $args = array( 'post_type' = > 'post', 'post_status' = > 'publish', 'posts_per_page' = > -1, 'fields' = > 'ids', 'no_found_rows' = > true, ); $the_query = new WP_Query($args); if ($the_query- > have_posts()) { $last_page = ceil(count($the_query- > posts) / $limit); for ($i = 2; $i <= $last_page; $i++) { $urls[] = home_url('/news/page/' . $i . '/' ); } } return $urls; } add_action( 'init' , function(){ add_filter( 'ShifterURLS::AppendURLtoAll' , 'my_append_urls' ); } ); 一部画像が表示されない All-in-One WP Migrationを使った移行ではスムーズに進みましたが、日本語ファイル名の画像が表示されないという問題が発生しました。Shifterへの移行後、原因を調査したところ、画像ファイル名が日本語の場合に限って読み込めないというバグを確認できました。数枚ほどだったので、対象のファイルを手動でリネームし、再アップロードをする必要がありました。 サブディレクトリでシェアボタンがShifterの仮ドメインになってしまう 既にCloudFrontにカスタムドメインが設定されている場合、Shifter 側のCDNに同じカスタムドメインを適用できません。このようなケースでは、Shifterで生成された静的コンテンツのみを対象に、CloudFront経由でカスタムドメイン配下のサブディレクトリに公開する必要があります。 /blog のようにサブディレクトリでの公開が必要な場合、 shifter-cli を使って --no-shifter-cdn オプションを指定することで、ShifterのCDN配信を無効化し、自前のCloudFront経由でのホスティングが可能になります。 CloudFrontのオリジンドメインにShifterが提供するCloudFront URLを指定し、キャッシュポリシーを無効化に設定することで、Shifterで生成された静的サイトをそのまま公開できます。最後にShifterでデプロイを実行することで、シェアボタンやOGPのリンク先にShifterの仮ドメインが含まれることはなくなりました。 ドメイン紐づけ $ npm install -g @shifter/cli $ shifter domain:attach --username hoge --password hoge --site-id hoge --domain hoge.com --no-shifter-cdn terraform/cloudfront.tf data "aws_cloudfront_cache_policy" "caching_disabled" { name = "Managed-CachingDisabled" } resource "aws_cloudfront_distribution" "hoge" { provider = aws.us-east-1 origin { domain_name = "hoge.cloudfront.net" origin_id = "blog" origin_path = "" custom_origin_config { http_port = 80 https_port = 443 origin_protocol_policy = "https-only" origin_ssl_protocols = [ "TLSv1.2" ] } ordered_cache_behavior { allowed_methods = [ "DELETE" , "GET" , "HEAD" , "OPTIONS" , "PATCH" , "POST" , "PUT" ] cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id cached_methods = [ "GET" , "HEAD" ] compress = true path_pattern = "blog/*" target_origin_id = "blog" viewer_protocol_policy = "https-only" } ordered_cache_behavior { allowed_methods = [ "DELETE" , "GET" , "HEAD" , "OPTIONS" , "PATCH" , "POST" , "PUT" ] cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id cached_methods = [ "GET" , "HEAD" ] compress = true path_pattern = "blog/" target_origin_id = "blog" viewer_protocol_policy = "https-only" } } これらトラブルシューティングを解決したことにより、次はテーマのデプロイ方法について説明したいと思います。 Shifter Github Plugin/Theme Installedでのテーマデプロイ https://github.com/getshifter/shifter-github ShifterではGitHub経由でテーマをデプロイするための公式プラグイン「Shifter GitHub Plugin/Theme Installed」が提供されています。このプラグインを利用することで、次の手順でテーマの更新が可能になります。 テーマをgit tagでバージョン付け git pushでタグを反映 テーマ一式を .zip に圧縮し、GitHubの Releases にアップロード Shifter側で該当リリースの.zip を取得し、テーマを最新化 もともとは複数のテーマをモノレポで管理しようとしていましたが、Shifterの仕様上、1テーマ、1プロジェクト(単一リポジトリ)が前提となります。さらに、GitHub Actionsを活用してリリースの自動化も実装しており、手動作業を減らして運用効率を向上させました。 github/workflows/release.yaml name : Tag and Release on : push : branches : [ main ] jobs : tag-and-release : name : Tag and Release runs-on : ubuntu-latest permissions : contents : write defaults : run : working-directory : "./" steps : - uses : actions/checkout@v4 - name : Fetch tags run : git fetch --tags - name : Get latest tag id : get_latest_tag run : | TAG=$(git tag --sort=-v:refname | head -n 1 || echo "0.0.0" ) echo "Latest tag: $TAG" echo "latest_tag=$TAG" >> $GITHUB_ENV - name : Calculate next tag id : calculate_next_tag run : | IFS='.' read -r MAJOR MINOR PATCH <<< "${{ env.latest_tag }}" NEXT_TAG="$MAJOR.$MINOR.$((PATCH+1))" echo "Next tag: $NEXT_TAG" echo "next_tag=$NEXT_TAG" >> $GITHUB_ENV - name : Create new tag run : | git tag ${{ env.next_tag }} git push origin ${{ env.next_tag }} - name : create archive env : PACKAGE_NAME : "hoge-theme" FILES_TO_ARCHIVE : "*" run : | sed -i -e "s/{release version}/${{ env.next_tag }}/g" ./style.css zip -r ${PACKAGE_NAME}.zip ${FILES_TO_ARCHIVE} - name : Upload to GitHub Release env : PACKAGE_NAME : "hoge-theme" GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} GOPATH : /home/runner/go run : | go install github.com/tcnksm/ghr@latest ${GOPATH}/bin/ghr \ -b "Release ${{ env.next_tag }}" \ -replace \ "${{ env.next_tag }}" \ "${PACKAGE_NAME}.zip" style.css Version: { release version } 現在の運用体制について Shifterのアカウント管理は情報システムチームが担当し、テーマの修正はエンジニアやSREチームが対応しています。また、記事の公開やコンテンツの更新は広報チームやマーケティングチームが行っており、各チームの役割が明確に分担されるようになりました。 移行してみて Shifter導入によって、SREチームだけでなく非エンジニアのメンバーでも運用できる環境が整いました。Shifterのダッシュボードはシンプルで直感的に操作でき、バックアップの復元も簡単です。また、静的ファイル化によりPHPの脆弱性リスクが排除され、HTMLとCSSのみで構成されることでセキュリティ面でも安心できるようになりました。さらに、CDNが標準で組み込まれているため、パフォーマンスの向上も実感しています。一方で気になったところは記事をリリースするまで、毎回Shifter側で静的化デプロイをしなければならないので、反映までに時間がかかるといったところです。 さらに、1つのShifterアカウントで複数のサイトを一元管理できる点も大きなメリットです。以前はサイトごとにユーザーアカウントを発行していたため管理が煩雑でしたが、現在は運用効率が大幅に向上し、ユーザー棚卸しも簡単になりました。また、デジタルキューブ様の導入事例でも紹介していただきましたので、ぜひご覧ください。 ja.getshifter.io まとめ 長らく技術的負債となっていたWordPress環境でしたが、SREチームを中心にShifterへの移行を進めたことで、信頼性の向上と一元管理を実現できました。自分自身、初めてShifterを導入するにあたり、静的化に伴う挙動の違いなど、試行錯誤する場面も多く苦労しました。しかし、今では導入して本当に良かったと感じています。今後は新たなメディアを立ち上げる際もShifterを利用し、SREチームを通じた管理フローを社内で徹底していきたいと思っています。 最後まで読んでいただきありがとうございました。SREチームは絶賛募集しているので、カジュアル面談ぜひお待ちしております!🙏 herp.careers 最後に aws.amazon.com AWS Summit Japan 2025で登壇することになりました!Security LakeとAIの活用についてお話します!6/26(木) 12:40~行いますので、ご都合合う方はぜひご参加をお待ちしております!
こんにちは。Findy Tech Blog編集長の高橋(@Taka_bow)です。 本記事は「 エンジニア達の人生を変えた一冊 」シリーズの第4弾となります。エンジニアとしてのキャリアや技術的な視点に大きな影響を与えた一冊とは?それぞれの思い入れのある本から、技術への向き合い方や成長の軌跡が垣間見えるかもしれません。 今回は佐藤さん、中村さん、甲斐さんの3名のエンジニアに、人生を変えた一冊を紹介していただきます。 まずはファインディのテクノロジーを統括するCTO佐藤さんからです!幅広い知見を持つ佐藤さんが、エンジニアとしての原点となった一冊とは?大学時代に出会った運命の本が、その後のキャリアをどう形作ったのでしょうか。 ■ 佐藤将高さん / CTO ■ ファインディ株式会社のCTOを務めています。トップバッターを務めさせていただきます! コンピュータの構成と設計 コンピュータの構成と設計 MIPS Edition 第6版 上 作者: David Patterson , John Hennessy 日経BP Amazon コンピュータの構成と設計 MIPS Editoin 第6版 下 作者: David Patterson , John Hennessy 日経BP Amazon コンピュータの構成と設計 作者: デイビッド・A・パターソン、ジョン・L・ヘネシー 出版社: 日経BP Amazon: 第6版 上 、 第6版 下 私が紹介する「コンピュータの構成と設計」は、コンピュータアーキテクチャの基礎から応用までを体系的に解説した書籍です。通称「パタ&ヘネ」として知られており、著者の両名はチューリング賞も受賞している著名な研究者です。 この本を読んだきっかけ 私が大学時代、恥ずかしながら「コンピュータについて何もわからない」状態で授業を受けていました。その後、大学院受験をするタイミングで試験内容にコンピュータアーキテクチャのセクションがあり、この本を何度も読み返し勉強を進めてきました。命令セットやプロセッサの種類を詳しく知ることが自分にとってどれほど重要なのか理解できていませんでしたが、この本との出会いが私のコンピュータに対する理解を大きく変えることになりました。私にとっては革命的な本です。 本の内容 この本は世界中の大学でコンピュータアーキテクチャの教科書として使われている名著で、もしかしたら大学で勉強したことがある方がいるかもしれませんね。 コンピュータアーキテクチャの内容をわかりやすく解説しており、コンピュータの歴史を踏まえながら命令やプロセッサなどの概念を理解しやすく説明しています。1996年の初版以来、私が読んだ時点の第5版ではMIPSアーキテクチャが中心でしたが、現在の6版では最新のアーキテクチャやトレンドも反映されています。 この本から影響を受けた点/学んだ点 この本を読んだことで、コンピュータの動作が頭の中でだんだんイメージできるようになりました。特に、Webサービスを提供する上では、クライアントと記憶媒体間のデータのやり取りを考慮しながらプログラムを組む必要があることを理解できました。サービスをより大規模に展開しようとすると、ハードウェアについての知識がますます重要になってくることを実感しました。また、I/Oやメモリに関する知識が深まったことで、プロダクトの課題発見に今でも役立てています。 特に印象に残った部分 世の中にある技術書は難解なものが多いと感じていましたが、この本は特に読みやすく、当時学生だった私にとってアーキテクチャの基礎を掴むのにとても役立ちました。 表面的なプログラミングだけでなく、その下層で何が起きているかを理解することで、より良いシステム設計ができるようになりました。例えば、CPU内部の論理設計から、メモリ階層・I/O・並列計算機までのコンピュータの動作原理を広く理解することは、エンジニアとしてのキャリアを積む中で常に私の基盤となっています。 全体的に図が多く、内部の動作をイメージしやすい良書です。 このような方におすすめ ソフトウェア以外にも、コンピュータサイエンス全般を幅広く学びたい方にお勧めの一冊です。特に、Webサービスやアプリケーション開発に携わるエンジニアの方々には、パフォーマンスチューニングやシステム設計の基礎知識として役立つでしょう。また、コンピュータサイエンスを学び始めたばかりの学生の方にも、その読みやすさから入門書として最適です。 また、理解が進んだ方には「上パタ&ヘネ」と言われる「Computer Architecture: A Quantitative Approach」という本もありますので、物足りない方はこちらもおすすめです。 佐藤さんの原点は「パタ&ヘネ」にあったんですね。大学時代に出会ったこの一冊が、現在CTOとして活躍する佐藤さんのコンピュータに対する深い理解の礎となったことがよく伝わってきます。技術書との出会いが、その後のキャリアを大きく左右することを実感させられます。 次は、Findy Team+のフロントエンド開発を担当する中村さんです!中村さんが選んだ一冊は、実際の開発現場で直面した課題から見つけ出した救世主のような本。Webセキュリティの世界への扉を開いた体験とは? ■ 中村大輝さん / フロントエンドエンジニア ■ こんにちは、 Findy Team+ の開発をしている 中村 です。 体系的に学ぶ 安全なWebアプリケーションの作り方 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践 作者: 徳丸 浩 SBクリエイティブ Amazon 体系的に学ぶ 安全なWebアプリケーションの作り方 作者: 徳丸 浩 出版社: SBクリエイティブ 私が紹介する「体系的に学ぶ 安全なWebアプリケーションの作り方」は、Webアプリケーションのセキュリティを体系的に解説した書籍です。通称「徳丸本」として知られています。 この本を読んだきっかけ エンジニアとして働き始めた1〜2年目の頃、当時私が担当していたWebサービスで脆弱性診断を受ける機会がありました。その結果、XSSやCSRFといった複数の脆弱性が指摘され、修正を求められる事態となりました。 1つ具体的な例を挙げると、そのWebサービスにはユーザーがレビューのようなものを投稿できる機能がありました。しかし、その入力欄に悪意あるスクリプトが埋め込まれ、他のユーザーがそのレビューを閲覧すると、JavaScriptが実行されるXSS(クロスサイトスクリプティング)が発生しました。この脆弱性により、ユーザーのクッキー情報が盗まれたり、不正な操作が実行されたりする可能性があり、非常に危険でした。これらの修正事項を解決するため、Webセキュリティに関する知識が必要となり、何をどう対策すれば良いのか悩んでいたときに出会ったのが「体系的に学ぶ 安全なWebアプリケーションの作り方」でした。 本の内容 この本は、Webアプリケーションの脆弱性を体系的に解説し、その対策方法を具体的に示しています。特に、XSSやCSRF、SQLインジェクションなど、代表的な脆弱性ごとにその仕組みと対策をわかりやすく解説しており、Webセキュリティに関する理解を深めることができます。また2011年の初版から、HTML5の普及に伴う改定などいくつかの改訂が重ねられた第2版まで現在出版されています。 この本から影響を受けた点/学んだ点 この本を読むことで、脆弱性とは単なる「エラー」ではなく、悪意ある攻撃者によって悪用される可能性があるとても重大なリスクであることを理解しました。また、脆弱性の種類ごとにどのような攻撃が行われるかを学び、実際のコードにどう対策を実装すればよいかを具体的に理解できました。 特に印象に残った部分 特に印象に残ったのは、XSS(クロスサイトスクリプティング)の項目です。当時私が担当したWebサービスでもXSSが報告され、その危険性を初めて実感しました。本書の例をもとに、エスケープ処理やCSP(コンテンツセキュリティポリシー)を適用することで安全性を高める方法を学び、実際の修正にも役立ちました。 このような方におすすめ Webアプリケーション開発に関わるすべてのエンジニアにお勧めです。特に、開発経験が浅く、Webセキュリティに自信がない方には非常に助けになる一冊です。 中村さんの「徳丸本」との出会いは、まさに現場のピンチを救った実践的な体験でしたね。エンジニアとして直面した実際の脆弱性問題が、セキュリティへの深い理解につながった瞬間が印象的です。技術書は時に、現場の課題を解決する強力な味方になるのですね。 最後は、2025年3月に入社したばかりの甲斐さんです!同じくFindy Team+のフロントエンド開発を担当する甲斐さんが選んだのは、コードの「変更しやすさ」という永遠のテーマに挑んだ一冊。大規模開発の現場で見つけた設計の知恵とは? ■ 甲斐和基さん / フロントエンドエンジニア ■ こんにちは、 甲斐 です! 2025年3月にファインディへ入社、フロントエンドエンジニアとして Findy Team+ の開発をしています。 現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の実践技法 現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法 作者: 増田 亨 技術評論社 Amazon 現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の実践技法 作者: 増田 亨 出版社: 技術評論社 この本を読んだきっかけ エンジニアとして働き始めて2年目の頃、初めて大規模なコードベースのアプリケーションを担当することになりました。コードベースが巨大かつ複雑で影響範囲を調査するだけでも数時間かかるような状況に課題感を感じ、変更しやすいコード・設計に興味を持ったことがきっかけでこの本を手に取りました。 本の内容 システム設計について、ドメイン駆動設計とオブジェクト指向プログラミングを中心に、変更に強いコードを書くための設計・手法について具体的なコードを例に挙げて解説しています。ベースの話はオブジェクト指向に沿ったものですが、変更に強い構造を作るための手法や考え方は他のプログラミングパラダイムでも活用できるものになっています。 この本から影響を受けた点/学んだこと この本を読んだことで、高凝集・疎結合であることで変更しやすい構造になるということを知識としてだけではなく、具体的に実践するための手法・考え方をベースに設計を考えるようになりました。この考え方は直接的にではないですが、フロントエンド開発に主軸を移してもからもコンポーネント設計や共通ロジックの切り出しなどを検討する際に活かされています。 特に印象に残った部分 全体を通して「変更を楽にするため」の手法について触れていたことが印象に残っています。特にドメインオブジェクト(ドメインに紐づくデータとロジックを扱うことに特化したオブジェクト)を使用した関心ごとの分離について言及しており、コードの整理だけでなく業務ドメインに対する理解を深めることが良い設計をする上でも重要であることを学びました。 例として取り上げるサンプルコードの題材が発注システムなど実践的なものだったので、よくあるサンプルコードのような「定義はわかるけど実際にどう扱うのか?」がイメージしづらいようなことはなく読みやすかったです。 このような方におすすめ プログラミングを始めて、コードの書き方はわかったけど設計ってどうすればいいのか?現場のコードベースが変更しづらいけどなぜだろう?など設計・オブジェクト指向について具体的なユースケースを見つつ学びたい方におすすめです。 おわりに 今回ご紹介した3名のエンジニアたちが人生を変えた一冊は、それぞれの専門分野や関心領域を反映した多様な選択でした。コンピューターの基礎原理からWebセキュリティ、そしてシステム設計まで、エンジニアとしての成長過程で出会った本が、その後のキャリアや技術的視点に大きな影響を与えていることがわかります。 技術書との出会いは、単なる知識の獲得だけでなく、問題解決の糸口や新たな視点をもたらしてくれるものです。皆さんも、自分の技術的な課題や興味に合わせた一冊を見つけ、エンジニアとしての視野を広げてみてはいかがでしょうか。 ファインディでは、さまざまなバックグラウンドを持つエンジニアが活躍しています。技術にこだわり、より良いものを追求する仲間とともに働いてみませんか? 現在、ファインディでは新しいメンバーを募集中です。 興味のある方は、ぜひこちらからチェックしてみてください!
こんにちは。 ファインディ株式会社 で Tech Lead をやらせてもらってる戸田です。 現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。 GitHub Copilotやチャットベースの開発支援ツールなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。 そのような状況の中で、このたび弊社からリモートMCPサーバーを公開することとなりました。 そこで今回は、リモートMCPサーバーを公開する上で実際に考慮したポイントを紹介します。 それでは見ていきましょう! 公開したリモートMCPサーバーの概要 インフラ構成 内部実装 認証情報 Streamable HTTP Transport まとめ 公開したリモートMCPサーバーの概要 MCPに関しては、前回の記事で詳しく説明していますので、公式ドキュメントと合わせてそちらをご覧ください。 tech.findy.co.jp modelcontextprotocol.io 今回公開したリモートMCPサーバーを使うことで、GitHub CopilotやDevinの利用状況を取得して可視化することが可能になります。詳細はリリースブログをご覧ください。 https://findy.co.jp/2843/ findy.co.jp なお、今回公開したリモートMCPサーバーのご利用は、2025/05/19現在、ファインディ株式会社のクライアント企業様限定となっております。 ご利用希望の方は、申込みフォームからお申し込みください。 docs.google.com インフラ構成 今回、弊社が公開したリモートMCPサーバーのインフラ構成は次のようになっています。 開発当初はAWS Lambdaを使うことを検討していたのですが、次の理由から採用を見送り、現状のシンプルな構成に落ち着きました。 実装内容をLambdaに合わせる必要がある 後述するSSE Transportを使う場合、クライアント、サーバー間のコネクションを維持する必要があり、その要件がLambdaと合わない 今回はdeploy時にDocker imageをECRにPushして、ECSを使ってサーバーを起動します。ログ出力はCloudWatchを使い、VPC経由でクライアント側からアクセス出来るようになっており、比較的よく見かけるシンプルな構成になっているかと思います。 今回のリモートMCPサーバーは、ファインディのSREチームが取り組んでいるセキュリティを担保したインフラ環境構築の一環として、HCP TerraformのPrivate Registryを活用した初の本番環境構築事例となりました。 このPrivate Registryに登録したモジュールは、Trivyによるセキュリティスキャンでエラーが出ない状態を担保しています。 一方で、全社的に必須とされている数多くの設定の中には、リモートMCPサーバーの特性上、一部適用が難しいものもありました。 特に、一部のWAFルールがリモートMCPサーバーの通信をブロックしてしまうケースが存在しました。 そのため、インフラ構築を進めながらPrivate Registryに登録したモジュールのカスタマイズも同時に行い、リモートMCPサーバーの要件を満たすインフラ環境を実現しました。 この取り組みにより、ファインディではより迅速かつセキュアにサービス提供ができる基盤が整ったと言えます。 内部実装 リモートMCPサーバーの内部実装の方法は、後述する認証情報の取得とTransportの利用が異なるだけで、基本的にローカルで実行するものと同じです。 具体的なコードを書くと、次のようになります。 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js" ; import express, { Request , Response } from "express" ; import { z } from "zod" ; const getMcpServer = ( name : string ) => { const mcpServer = new McpServer( { name : 'sample-mcp-server' , version : '1.0.0' , } , { capabilities : { logging : {} } } ); mcpServer.tool( "addition" , "足し算をする" , { a : z.number(), b : z.number() } , async ( { a , b } ) => ( { content : [ { type : "text" , text : `Hello ${ name } ` } , { type : "text" , text : String (a + b) } ] } ) ); return mcpServer; } ; async function main () { try { const app = express(); const transports: Record < string , SSEServerTransport > = {} ; app. get ( "/sse" , async ( req : Request , res : Response ) => { const transport = new SSEServerTransport( "/message" , res); const sessionId = transport.sessionId; transports[sessionId] = transport; const name = req. get ( "X-Hoge-Name" ); const { server } = getMcpServer( name ); await server.connect(transport); server.onclose = async () => { await server. close (); process .exit( 0 ); } ; transport.onclose = () => { delete transports[sessionId]; } ; } ); app.post( "/message" , async ( req : Request , res : Response ) => { const sessionId = req.query.sessionId as string | undefined ; if (!sessionId) { res. status ( 400 ). send ( 'Missing sessionId parameter' ); return ; } const transport = transports[sessionId]; if (!transport) { res. status ( 404 ). send ( 'Session not found' ); return ; } await transport.handlePostMessage(req, res, req. body ); } ); const PORT = process .env.PORT || 3001 ; app.listen(PORT, () => { console .log( `Server is running on port ${ PORT } ` ); } ); process .on( 'SIGINT' , async () => { for ( const sessionId in transports) { try { await transports[sessionId]. close (); delete transports[sessionId]; } catch (error) { console .error( `Error closing transport for session ${ sessionId } :` , error); } } process .exit( 0 ); } ); } catch (error) { console .error( 'Error starting server:' , error); process .exit( 1 ); } } main(). catch ( error => { console .error( 'Unhandled error:' , error); process .exit( 1 ); } ); SSE TransportでステートフルなMCPサーバーを実装する場合のサンプルコードとなります。 MCPサーバー自体の実装はローカル環境で実行するものと変わりませんが、MCPサーバーをExpressなどのサーバーに接続して起動している点が異なります。 つまるところ、サーバーに接続する箇所以外は基本的に同じ実装で良いので、サーバーの起動部分とMCPサーバーの実装部分を分けて実装しておくと、ローカルで実行するケースとリモートで実行するケースの両方に対応できるようになります。 また、SSE Transportを使って通信を行う場合、MCPクライアントとのセッション管理を行う必要があります。MCPサーバーと接続、切断した時や、サーバーそのものを停止した際にセッションに対する各種操作を実行する必要があります。 認証情報 MCPサーバーからAPIを実行する際にアクセストークンが必要なケースがあります。 ローカル環境でMCPサーバーを実行する場合は環境変数を使ってアクセストークン等を渡すのが一般的です。 しかし、リモートMCPサーバーの場合は環境変数に個人のアクセストークンを設定することは出来ません。リモートMCPサーバーの場合、動作する場所がローカル環境ではなく共通のサーバーになるからです。 こういったケースの場合、環境変数ではなくheaders経由でアクセストークンを渡すことが出来ます。 { " servers ": { " sample-mcp ": { " url ": " https://example.com/sse ", " type ": " sse ", " headers ": { " X-Hoge-Name ": " Fuga ", " X-Access-Token ": " <YOUR_ACCESS_TOKEN> " } } } } このようにMCPクライアント側の設定ファイルを記述することで、MCPサーバーと接続したタイミングにheaders経由でリモートMCPサーバーが必要な情報を受け取ることが出来るようになります。 Streamable HTTP Transport これまでのリモートMCPサーバーとの通信方式はSSE Transportを使うのが一般的でした。 しかし、PROTOCOL VERSION 2025-03-26からSSE Transportはdeprecatedになり、代わりにStreamable HTTP Transportを使うことが推奨されるようになりました。 modelcontextprotocol.io Streamable HTTP Transportに置き換わることで、次のようなメリットがあります。 ステートレスなMCPサーバーを実装できる プレーンなHTTPサーバーとして実装できる MCPクライアントとサーバーのコネクションを維持し続ける必要がなくなる 2025/05/19現在ですと、MCPクライアント側のStreamable HTTP Transportへの対応が出来ていないケースがあるため、しばらくはSSE Transportで通信できるようにする必要がありそうです。 弊社としましても、公開直後はSSE Transportでの提供を行い、折を見てStreamable HTTP Transportに移行していく予定です。 MCPサーバー側のTransport関連の実装は、公式SDKにサンプルコードがあるので、そちらを参考にしてみると良いでしょう。 github.com まとめ いかがでしたでしょうか?今回の記事がリモートMCPサーバーを公開する上での参考になれば幸いです。 今回公開したリモートMCPサーバーはα版という位置付けであり、今後も機能追加や改善を行っていく予定です。 2025/05/19現在、ファインディ株式会社のクライアント企業様限定の公開とはなっていますが、ご利用希望の方は是非、申込みフォームからお申し込みください。 docs.google.com 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
こんにちは。 ファインディ株式会社 で Tech Lead をやらせてもらってる戸田です。 現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。 GitHub Copilotやチャットベースの開発支援ツールなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。 そのような状況の中で、MCPというプロトコルが話題となっていることは読者の皆さんもご存知かと思います。 そこで今回は、弊社の開発組織でのMCPサーバーの導入と実装、そして実績について紹介します。 それでは見ていきましょう! MCPとは 導入 実装 実績 動的にプロンプトのテキストを作成して返す Devinと連携する Figmaデータのlintを行う セキュリティ面の考慮 まとめ MCPとは MCP(Model Context Protocol)は、アプリケーションが大規模言語モデル(LLM)に情報やツールへのアクセス方法を提供する、新しいオープンプロトコルです。 USB-Cが様々なデバイスを標準的な方法で接続するように、MCPはAIモデルを多様なデータソースやツールへつなぐための、標準化された方法を提供します。 これがこれまでのやり方、特に従来のAPIとどう違うのかというと、AI連携の開発を大幅に簡素化し、より動的な機能を提供する点にあります。従来は、各サービスに対して個別のコードや設定が必要で、「それぞれのドアに個別の鍵が必要」な状況に似ていました。しかし、MCPは単一の標準化されたコネクタとして機能するため、一度の統合で複数のツールやサービスにアクセスできる可能性があります。 詳しくは次の公式ドキュメントをご覧ください。 modelcontextprotocol.io このMCPの規格に則って作成されたサーバーをMCPサーバーと呼びます。 導入 弊社でも例に漏れず、MCPサーバーの利用を開始しました。 Model Context Protocol servers で紹介されている公式MCPサーバーを開発リポジトリに追加するだけで、すぐに利用開始できます。 github.com GitHubのMCPサーバーを使って、Pull requestやIssueの情報を取得して、そのままLLMに渡すことができます。 github.com SentryのMCPサーバーを使って、不具合の詳細を取得して、そのままLLMに渡すことが可能です。その情報をそのままCopilotやAgentなどに解析してもらい、原因を特定した後に自動的に実装コードを修正してもらうことが出来ます。 github.com AWSのDocumentのMCPサーバーを使うことで、CopilotなどからAWSについての質問を投げて、ドキュメントの内容を検索して返してもらうことができます。その内容はそのままLLMに渡すことも可能です。 github.com 利用方法はとても簡単です。各種AIエージェントの設定ファイルに追記して、MCPサーバーを起動するだけです。 例えばVSCodeでGitHubのMCPサーバーを利用する場合、次のような記述を追加します。 { " servers ": { " github ": { " command ": " docker ", " args ": [ " run ", " -i ", " --rm ", " -e ", " GITHUB_PERSONAL_ACCESS_TOKEN ", " ghcr.io/github/github-mcp-server " ] , " env ": { " GITHUB_PERSONAL_ACCESS_TOKEN ": " <YOUR_ACCESS_TOKEN> " } } } } このように、既に多くのプロダクトやサービスでMCPサーバーは公開されており、開発者やプロダクト開発の生産性を向上させるための強力なツールとなっています。 実装 各社から提供されているMCPサーバーを活用していると、組み合わせ次第で色々なことが出来ることに気づきます。 そこで弊社の開発組織で活用できるようなMCPサーバーを自分たちで作ることにしました。 公式から開発用のSDKが提供されているのですが、今回は弊社が普段から使っているTypeScriptのSDKを使って実装しています。 github.com 公式のSDKを使うことで、MCPサーバー内部の実装に集中することが出来るので、利用しない手はありません。 次のコードは、与えられた2つの数値に対して計算を行い、その結果を返すMCPサーバーの実装例です。 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ; import { z } from "zod" ; const mcpServer = new McpServer( { name : "Sample MCP Server" , version : "0.0.1" } ); mcpServer.tool( "addition" , "足し算をする" , { a : z.number(), b : z.number() } , async ( { a , b } ) => ( { content : [{ type : "text" , text : String (a + b) }] } ) ); mcpServer.tool( "multiplication" , "掛け算をする" , { a : z.number(), b : z.number() } , async ( { a , b } ) => ( { content : [{ type : "text" , text : String (a * b) }] } ) ); try { const transport = new StdioServerTransport(); await mcpServer.connect(transport); } catch (error) { console .error( 'Error starting server:' , error); process .exit( 1 ); } mcpServer.tool でtoolを追加します。 1つ目の引数でtoolの名前、2つ目の引数でtoolの説明、3つ目の引数でtoolのパラメータ、4つ目の引数でtoolの実装を定義しています。 また、用途や内容によってはMCPサーバーそのものを分けたいケースが出てきます。ですが、そのたびにリポジトリを切ってしまうと、同じような処理のコードを分散管理することになってしまいます。 そのため弊社では、以前から活用していたNxを利用して、MCPサーバーの実装をモノレポで管理するようにしました。 tech.findy.co.jp モノレポにすることで、複数のMCPサーバーから利用できる共通関数などを作成できます。外部APIを実行するClientなどが良い例です。このように弊社ではMCPサーバーの社内エコシステムを実現しています。 更にNxのgenerator機能を使うことで、モノレポとMCPサーバーの初期設定はコマンドを数回実行するだけで完了するようにしています。 Nxのgenerator機能については以前の記事で紹介しておりますので、併せてご覧ください。 tech.findy.co.jp このような整備を行うことで、社内のエンジニアがMCPサーバーを作るハードルを下げることができました。 実績 社内エコシステムの整備を行ったことにより、数名のエンジニアで、3日間で10個のMCPサーバーと30個のtoolを実装して、社内のエンジニアに配布することを実現できました。 今回はその中の一部を紹介します。 動的にプロンプトのテキストを作成して返す パラメータを受け取り、その内容に応じて動的にプロンプトのテキストを生成して返すMCPサーバーを実装しました。 開発チーム内で共通で利用したいプロンプトがあり、内容を動的に切り替えたい場合などに便利です。 例えば、他のMCPサーバーからデータを取得して、そのデータをこのMCPサーバーに渡すことで、外部データのテキストを利用してプロンプトを生成するといったことが実現できます。 このように、プロンプトのテキストを返し、それをそのままLLMで実行するような使い方がMCPサーバーでは可能です。 Devinと連携する 完全自律型AIエンジニアのDevinは、α版ではありますがAPIを公開しています。 docs.devin.ai そのAPIをMCPサーバーから実行することにより、Devinに関する情報を取得したり、セッションを作成、停止できるようにしました。 これによりエンジニア自身の作業中に、SlackやDevinの画面を直接確認せずともDevinの管理ができるようになりました。 このように、外部APIを実行して情報を取得したりデータ登録、削除を行うことがMCPサーバーでは可能です。 Figmaデータのlintを行う デザインプラットフォームのFigmaはAPIを公開しています。 www.figma.com そのAPIをMCPサーバーから実行して、対象のFigmaのデータを取得して、データの中身をチェックして、デザインルールに則っているかどうかlintのようなチェックを行うMCPサーバーを実装しました。 例えば、指定したフォント以外を使っている場合や、利用できるpx数ではない場合など、デザインルールに則っていない場合は警告を返すようなことが可能です。 このように、外部APIからデータを取得して、内容を解析したり加工して返すようなことがMCPサーバーには可能です。 セキュリティ面の考慮 外部APIを実行する場合、アクセストークンが必要になるケースが多いと思います。 このようなケースの場合、MCPサーバーを実行する際に環境変数を使ってアクセストークンを渡すことができます。 利用するアクセストークンの権限を絞り、環境変数でアクセストークンを隠蔽して実行することで、安全にMCPサーバーを管理、実装することが可能になります。 また、外部の公式以外のMCPサーバー、いわゆる野良MCPサーバーを利用する場合は注意が必要です。 悪意のあるMCPサーバーに対してアクセストークンなどを渡してしまうと、意図しない情報漏洩やデータの改ざんなどのリスクがあります。 そのため、外部の野良MCPサーバーを利用する場合は、内部実装を確認して実際に実行されている処理を確認することをオススメします。 まとめ いかがでしたでしょうか? MCPサーバーを実装するハードルとコストは非常に低く、それに対するリターンは非常に大きいため、コストパフォーマンスが非常に高いです。 是非一度、公式ドキュメントを参考にして簡単なMCPサーバーを実装してみてください。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
このブログの内容をポッドキャストでも配信中! ソフトウェア開発現代史年表 Ver2.07 このブログの内容をポッドキャストでも配信中! はじめに DevOps誕生以前(〜2000年代前半) 2009年:DevOpsのはじまり Flickrの伝説的講演「10+ Deploys Per Day」 パトリック・ドボアと「DevOps」という言葉の誕生 2010年代前半:ジェズ・ハンブルと継続的インテグレーション(CI)から継続的デリバリー(CD)への発展 継続的インテグレーション(CI)とは何か 継続的デリバリー(CD)への発展 2010年代中盤:ジーン・キムとDevOpsの理論体系化 2010年代後半〜現在:DevOpsの普及と進化 Googleによる「SRE(Site Reliability Engineering)」の提唱 「You build it, you run it(作ったものは自分で運用する)」文化の定着 DORA(DevOps Research and Assessment)の設立と影響 クラウドネイティブ技術とDevOpsの融合 DevOpsの文化的側面の重視 DevOpsの本質と今後の展望 日本におけるDevOps:まだ「一部の人たちのモノ」状態 DevOpsとAI導入の共通障壁 【告知】ソフトウェア開発の伝説的パイオニアたちが来日! はじめに こんにちは。ソフトウェアプロセス改善コーチ兼アジャイルコーチ兼エンジニア兼Findy Tech Blog編集長の高橋( @Taka-bow )です。 30年以上前、エンジニアとしての第一歩を踏み出したばかりの私に、ある先輩が教えてくれた言葉があります。 「動いているコードには触れるな」 この言葉は、当時の日本のIT現場における「安定性最優先」の価値観を象徴しています。一度リリースされたシステムは「完成品」として扱われ、必要最低限の変更以外は避けるべきという考え方が主流でした。変更は常にリスクをはらんでおり、「動いているものを変えれば壊れる可能性がある」という恐れが、技術的な進化よりも優先されていたのです。 しかし、DevOpsの登場によって、この価値観は大きく変わりました。「変更を恐れる」文化から「変更を受け入れる」文化へ。「安定性のために変更を避ける」から「頻繁な小さな変更によって安定性を高める」へと変化しました。 つまり「動いているコードには触れるな」から 「常に改善し続けるコード」 へと進化したのです。 この価値観の転換は、単なる技術的な進化ではなく、ソフトウェア開発に対する根本的な考え方の変革です。 世界的には当たり前となりつつあるこの変革ですが、日本の状況はどうでしょうか。興味深いことに、日本では一部のWeb系企業やスタートアップを除き、DevOpsの波はまだ十分に広がっていません。 なぜでしょう? この疑問を探る前に、まずDevOpsがどのように生まれ、発展してきたのかを理解する必要があります。ソフトウェア開発現代史の視点から、この変革をもたらした先駆者たちの物語を紹介していきたいと思います。 下図の年表に示すように、DevOpsの歴史は2009年頃から始まります。 DevOps誕生以前(〜2000年代前半) 2000年代前半までのソフトウェア開発では、 開発と運用は完全に分断され、しばしば対立関係にありました 。開発チームの目標は「新機能を素早くリリース」、運用チームの目標は「システムの安定性維持」であり、この2つの目標はしばしば衝突していました。 開発チームは「とにかく早くリリースしたい」 → 変更を加えたがる 運用チームは「安定稼働が最優先」 → 変更を嫌う 結果として、開発は「リリース遅延」に苛まれ、運用は「障害対応」に追われる 変更のリスクを最小限にするために、厳格なリリースプロセスが敷かれ、開発者は運用チームから「勝手にデプロイするな」と釘を刺されていました。その結果、リリースサイクルは長期化し、開発の俊敏性が犠牲になっていったのです。 特に、大規模なリリースでは 手作業によるデプロイ が行われることが多く、設定ミスや環境差異が原因で障害が頻発していました。こうした問題を解決し、 開発と運用のギャップを埋める新たなアプローチ が強く求められていました。 2009年:DevOpsのはじまり Flickrの伝説的講演「10+ Deploys Per Day」 こうした状況に一石を投じたのが、2009年のO'Reilly Velocity 09 Conferenceで行われた、Flickrのエンジニアによる伝説的な講演です。当時Flickrのエンジニアだった ジョン・オールスパウ(John Allspaw)とポール・ハモンド(Paul Hammond) が「 10+ Deploys Per Day: Dev and Ops Cooperation at Flickr 」のタイトルで発表し、業界に衝撃を与えました。 この講演で彼らは、当時としては驚異的な「 1日に10回以上のデプロイ 」を実現している事実を明らかにしました。多くの企業が月に1回や四半期に1回のリリースサイクルに苦しんでいた時代に、これは革命的な実践だったのです。 彼らが強調したのは次のポイントです。 開発と運用の協力関係 - 両チームが共通の目標(ユーザー価値の提供)に向かって協力する 小さな変更の頻繁なデプロイ - 大きな変更を一度にリリースするのではなく、小さな変更を頻繁にリリースする 自動化の徹底 - テスト、デプロイ、モニタリングなど、あらゆる工程を自動化する 「失敗は避けられない」前提 - 失敗を防ぐのではなく、素早く検知して回復する能力を高める この講演により「高頻度デプロイは可能」との認識が広まり、多くの企業がFlickrの実践に触発されて自社のデプロイプロセスを見直すきっかけとなりました。この講演は、YouTubeで「10+ Deploys Per Day: Dev and Ops Cooperation at Flickr」として今でも視聴可能であり、DevOpsを学ぶ人々にとって必見の資料となっています。 このとき日本では:アジャイル導入の緩やかな歩み 2001年に発表された「アジャイルソフトウェア開発宣言(アジャイルマニフェスト)」は、「プロセスやツールよりも個人と対話を」「包括的なドキュメントよりも動くソフトウェアを」といった価値観を掲げ、後のDevOps誕生の重要な思想的背景となりました。しかし、2000年代前半のアジャイル導入期において、日本企業の多くはこの本質である「変化への適応」や「顧客との協働」といった考え方を十分に受け入れることができませんでした。日本の階層的な組織構造や詳細な計画を重視する文化が、アジャイルの柔軟性と相容れなかったのです。DevOpsはアジャイルの延長線上に生まれた考え方であり、この最初のアジャイル導入のチャンスを逃したことが、後のDevOps導入の遅れにも直接的な影響を与えることになりました。 パトリック・ドボアと「DevOps」という言葉の誕生 Flickrの講演と同じ2009年、「 DevOps 」という言葉が誕生しました。この言葉の背後には、ベルギーのITコンサルタント パトリック・ドボア(Patrick Debois) の興味深い物語があります。 *1 実は、ドボアの旅は2007年に始まっていました。ベルギー政府のデータセンター移行プロジェクトに携わっていた彼は、開発者とシステム管理者の間の対立に悩まされ、解決策を模索していました。 転機となったのは2008年8月、トロントで開催されたAgileカンファレンスでした。ソフトウェア開発者の アンドリュー・シェーファー(Andrew Shafer) が「Agile Infrastructure(アジャイルインフラストラクチャ)」というセッションを開催しましたが、参加者はたった一人、パトリック・ドボアだけでした。二人はこの出会いをきっかけに、開発と運用の間の壁を取り払うアイデアについて議論を始めました。 そして2009年、先述のFlickrの講演「10+ Deploys Per Day」に触発されたドボアは、エンジニアのナスラット・ナサー(Nasrat Nassar)からの「ベルギーで独自のVelocityイベントを開催してはどうか」というツイートをきっかけに行動を起こします。彼は「 DevOpsDays 」というイベントを開催することを決意しました。 この名前は、「development(開発)」と「operations(運用)」の最初の3文字を取り、「days(日)」という単語を追加したものでした。2009年10月30日に開催されたこのカンファレンスには、開発者、システム管理者、ツール開発者など多様な参加者が集まりました。 カンファレンス終了後、議論はTwitterに移り、より覚えやすいハッシュタグとして、ドボアは名前を「 #DevOps 」に短縮しました。これが「 DevOps(Development + Operations) 」という言葉の誕生であり、それ以来、このムーブメントはDevOpsとして知られるようになりました。 興味深いことに、スペルについては今でも意見の相違があります。主流の使用法は「DevOps」ですが、創始者のドボアを含む少数派は「Devops」を主張し、一部の人々は大文字を完全に排除して「devops」とすることを主張しています。 ドボアのX(当時はTwitter)アカウント(@patrickdebois)は初期のDevOpsムーブメントの中心的な情報源となり、#devopsというハッシュタグを通じて、世界中の実践者がアイデアを共有する場となっていきました。 このとき日本では:クラウド導入の機会損失 2006年から2010年にかけてのクラウド黎明期において、AWSをはじめとするクラウドサービスの登場は、単なるインフラの選択肢ではなく、DevOpsの実践を加速させる技術的基盤でした。「Infrastructure as Code」や「自動化」といったDevOpsの核心的な実践を可能にする環境が整いつつあったのです。しかし、日本企業の多くは「セキュリティ懸念」「既存システムへの投資保護」「クラウドの信頼性への疑問」などを理由に慎重な姿勢を取り続けました。この時期に積極的なクラウド活用を進めていれば、DevOpsへの移行も自然な流れとして実現できたはずです。この「待ちの姿勢」が、後のDevOps導入の遅れにも繋がることになりました。 なお、パトリック・ドボアは昨年2024年1月に行われたDevOpsDays Tokyo 2024の基調講演のため来日しています。 私は現地でお話を聞きましたが、このときのドボアは AIにとても興奮 しており、特にRAGについてのお話でした。 当時の私は、ドボア氏が何に興奮していたのか、何も分かっていませんでした……でも、今なら分かります。 改めてビデオを見返して、気になった発言をピックアップしました。 私がとても気に入っている概念のひとつに、こういう考え方があります。 エージェントの性能が向上すれば、私たちは直接「もの」を作るのをやめ、 「ものを作るもの」を作るようになります。 つまり、自動化の次の段階に進むのです。 多くの人が私にこう言いました。 「DevOpsでは乗り遅れた。でも今度のAIの波では遅れたくない。」 成熟したDevOps文化を持つ人々こそ、GenAIのアーリーアダプターになりつつあるのです。 私たちエンジニアは、常にやりすぎる傾向があります。 でも、それでいいのです。 今もっとも難しい新しいプログラミング言語? それはもしかすると「英語」か「日本語」かもしれませんね。 もっと、たくさん痺れる事を仰っているので、ぜひ本編YouTubeをごらんください。 2010年代前半:ジェズ・ハンブルと継続的インテグレーション(CI)から継続的デリバリー(CD)への発展 さて、話を戻しましょう。ここで登場するのが、DevOpsの歴史において重要な役割を果たした人物、 ジェズ・ハンブル(Jez Humble) です。彼はソフトウェアデリバリーの自動化と効率化に関する先駆的な研究と実践で知られ、継続的デリバリー(CD)の概念を体系化した第一人者として世界中のソフトウェア開発に多大な影響を与えました。 DevOpsの言葉が広まり始めた2010年代前半、まず理解しておくべき重要な概念が「 継続的インテグレーション(Continuous Integration, CI) 」です。CIは実はDevOpsより前から存在していた概念で、2000年代初頭にアジャイル開発の文脈で広まりました。 特に マーティン・ファウラー(Martin Fowler) と ケント・ベック(Kent Beck) がその重要性を提唱しています。 継続的インテグレーション(CI)とは何か 継続的インテグレーションの核心は、 「開発者が頻繁にコードを統合し、自動テストによって問題を早期に発見する」 という実践です。具体的には次のような特徴があります。 頻繁なコミット - 開発者は少なくとも1日1回、コードをメインブランチに統合する 自動ビルド - コードがコミットされるたびに自動的にビルドが実行される 自動テスト - ビルド後に自動テストが実行され、問題があれば即座にフィードバックが得られる 問題の早期発見 - 統合の問題を早期に発見することで、修正コストを低減する CIの実践により、「統合地獄(Integration Hell)」と呼ばれる、長期間分岐したコードを統合する際の困難を避けることができます。CIを実現するツールとしては、 Jenkins (旧Hudson)、 Travis CI 、 CircleCI などが広く使われるようになりました。 なお、CI(継続的インテグレーション)と密接に関係する実践として、TDD(Test-Driven Development:テスト駆動開発)があります。TDDは「テストを先に書き、そのテストをパスする最小限の実装を行う」という開発手法で、Kent Beckがエクストリーム・プログラミング(XP)の中で体系化し、広く普及させました。 テスト駆動開発 作者: Kent Beck オーム社 Amazon TDDによって作成される自動テスト群は、CIパイプラインにおける重要な構成要素となり、コードの品質を継続的に保証する基盤となります。TDDが主にコードレベルの品質を担保するのに対し、CIはチーム全体での統合品質を確保する役割を果たします。両者はいずれも「早期フィードバック」と「問題の早期発見」を重視しており、DevOps実践の基盤として不可欠な考え方です。 継続的デリバリー(CD)への発展 CIが「コードの統合と検証の自動化」に焦点を当てているのに対し、 継続的デリバリー(CD) はその先の「デプロイメントまでの自動化」にまで範囲を広げた概念です。この概念を体系化したのが、ジェズ・ハンブルとデビッド・ファーリー(David Farley)による「 Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation 」(2010年、日本語訳は2012年)の書籍でした。 継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化 作者: Jez Humble , David Farley KADOKAWA Amazon この書籍では、「 継続的デリバリー(Continuous Delivery, CD) 」の概念が体系的に説明され、その核心は「 ソフトウェアをいつでも本番環境にリリース可能な状態にする 」ことにありました。 ジェズ・ハンブルとデビッド・ファーリーは、この書籍で継続的デリバリーを実現するために次のような実践を提唱しています。 デプロイメントパイプライン - コードの変更が自動的にビルド、テスト、デプロイされる一連のプロセス 自動化されたテスト - 単体テスト、統合テスト、受け入れテストなど、あらゆるレベルのテストを自動化 本番環境と一致したステージング環境 - 「開発環境では動くのに本番では動かない」問題を解消 ブルーグリーンデプロイメント - ダウンタイムなしでのデプロイを可能にする手法 これらの実践は現代のCI/CDパイプラインの基盤となり、ジェズ・ハンブルとデビッド・ファーリーの貢献によって「継続的インテグレーション(CI)」から「継続的デリバリー(CD)」へと、自動化の範囲が大きく拡大していきました。 このとき日本では:継続的デリバリー(CD)とゲートキーパー文化の相克 2010年代前半には、継続的デリバリー(CD)の波が世界中に広がりました。海外では多くの企業がデプロイパイプラインを構築し、頻繁なリリースを実現していました。一方、日本企業の多くは「手動リリース」や「長いリリースサイクル」、「厳格な承認プロセス」を維持し続ける傾向がありました。これは日本特有の品質管理への強いこだわりや、変更に対する慎重な姿勢が影響していると考えられます。「リリース=大イベント」「変更=リスク」という認識が根強く、DevOpsの核心である「小さな変更を頻繁に行う」文化への移行が進みにくい土壌があったのです。特に、多くの組織では品質保証部門が「ゲートキーパー」として機能し、リリース前の最終承認権を持つ構造が確立されていました。このゲートキーパー型の品質保証プロセスは、継続的デリバリーが目指す「自動化されたテストによる品質担保」という考え方と根本的に相容れず、プロセス重視の文化から脱却することが難しかったのです。 2010年代中盤:ジーン・キムとDevOpsの理論体系化 2010年代中盤、DevOpsムーブメントの中心人物として台頭したのが ジーン・キム(Gene Kim) です。彼は優れた技術者であると同時に、複雑な概念を分かりやすく伝える稀有な才能を持ち合わせていました。それまで現場レベルの実践知として広がっていたDevOpsの考え方を、組織全体で取り組むべき体系的な方法論として確立した功績は計り知れません。彼の著作は技術書の枠を超え、多くの組織でDevOps変革の指南書となっています。 2013年、ジーン・キムは、ケビン・ベア(Kevin Behr)、ジョージ・スパッフォード(George Spafford)と共に「 The Phoenix Project: A Novel about IT, DevOps, and Helping Your Business Win (邦題:The DevOps 逆転だ!)」を出版しました。 The DevOps 逆転だ! 作者: ジーン・キム , ケビン・ベア , ジョージ・スパッフォード 日経BP Amazon この小説形式の書籍は、架空の自動車部品メーカー「Parts Unlimited」のIT部門が、DevOpsの原則を採用することで危機を乗り越える物語を描いています。物語形式でありながら、DevOpsの本質的な考え方を分かりやすく伝え、多くの読者に影響を与えました。 続いて2016年には、 ジェズ・ハンブル 、パトリック・ドボア、ジョン・ウィリス(John Willis)と共に「 The DevOps Handbook: How to Create World-Class Agility, Reliability, and Security in Technology Organizations 」を出版し、DevOpsの実践をより体系的にまとめあげました。 The DevOps ハンドブック 理論・原則・実践のすべて 作者: ジーン・キム , ジェズ・ハンブル , パトリック・ボア , ジョン・ウィリス 日経BP Amazon この書籍では、 DevOpsの三つの原則(Three Ways) が提唱されています: フロー(Flow) - 開発からデプロイまでの流れを最適化する フィードバック(Feedback) - 問題を早期に発見し、素早く修正する 継続的学習と実験(Continual Learning and Experimentation) - 失敗から学び、継続的に改善する ジーン・キムの貢献により、DevOpsは単なる技術的プラクティスではなく、 組織文化の変革 として広く認識されるようになりました。 このとき日本では:DevOpsの理論と技術基盤の受容の遅れ ジーン・キムらがDevOpsの理論を体系化していた2013年以降、世界ではマイクロサービスアーキテクチャが注目され、Dockerの登場によりコンテナ技術が普及し始めました。これらの技術は、DevOpsの三つの原則(フロー、フィードバック、継続的学習)を実践するための技術的基盤でしたが、日本企業の多くはモノリシックなアーキテクチャと従来のデプロイモデルを維持し続けました。マイクロサービスへの移行には技術的な挑戦だけでなく、組織構造の変革(「2ピザチーム」など小規模で自律的なチーム編成)も必要でしたが、日本の階層的な組織文化はこうした変革に適応しにくい面がありました。「The Phoenix Project」や「The DevOps Handbook」といった書籍は日本語にも翻訳されましたが、その思想を実践に移す組織はWeb系企業の一部に限られていました。 2010年代後半〜現在:DevOpsの普及と進化 2010年代後半になると、DevOpsの考え方は業界全体に広がり、様々な形で進化していきました。 Googleによる「SRE(Site Reliability Engineering)」の提唱 2016年、Googleは「 Site Reliability Engineering: How Google Runs Production Systems 」の書籍を出版し、「 SRE(Site Reliability Engineering) 」の概念を広めました。 SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム オライリージャパン Amazon SREはDevOpsの思想を具現化し、システム運用と開発の連携を深めるための実践的なアプローチです。具体的には、ソフトウェアエンジニアリングの考え方をインフラや運用に適用し、システムの信頼性を向上させることを目的としています。 「You build it, you run it(作ったものは自分で運用する)」文化の定着 Amazonのエンジニアリング文化から生まれた「 You build it, you run it (作ったものは自分で運用する)」の考え方もこの時期に広く普及しました。これは開発者が自ら作ったシステムの運用にも責任を持つ考え方で、開発と運用の境界をさらに曖昧にするものです。この文化の下では、開発者はコードを書くだけでなく、デプロイ、モニタリング、障害対応など、システムのライフサイクル全体に関わることが求められるようになりました。 DORA(DevOps Research and Assessment)の設立と影響 Dr. Nicole Forsgren (開発生産性Conference 2024にて) 2016年、 ニコール・フォースグレン(Nicole Forsgren) 、 ジェズ・ハンブル(Jez Humble) らによって「 DORA(DevOps Research and Assessment) 」が設立されました。DORAはDevOpsの実践と組織のパフォーマンスの関係を科学的に調査・分析する組織として、毎年「 State of DevOps Report 」を発行し、世界中の何千もの組織からデータを収集・分析することで、DevOpsの実践がビジネス成果にどのように影響するかを明らかにしてきました。 特に、 「デプロイ頻度」「変更のリードタイム」「変更失敗率」「サービス復旧時間」 の4つの主要指標(Four Key Metrics)を確立し、これらがビジネスパフォーマンスと強い相関関係にあることを示しています。 2018年には、ニコール・フォースグレン、ジェズ・ハンブル、そしてジーン・キムによって書籍「 Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations (邦題:LeanとDevOpsの科学)」が出版され、DORAの研究成果が体系的にまとめられました。この書籍によってDevOpsの実践は「科学」として確立される重要な一歩を踏み出しました。 LeanとDevOpsの科学[Accelerate] テクノロジーの戦略的活用が組織変革を加速する (impress top gear) 作者: Nicole Forsgren Ph.D. , Jez Humble , Gene Kim インプレス Amazon DORAの10年にわたる研究と進化については、2025年4月に開催されたDevOpsDays Tokyo 2025にて私がお話した資料で詳しく解説していますので、そちらもぜひご参照ください。 DORAの研究は、DevOpsの実践が単なるトレンドではなく、科学的に裏付けられた効果的なアプローチであることを証明する上で、非常に重要な役割を果たしています。 クラウドネイティブ技術とDevOpsの融合 AWS、Google Cloudなどのクラウドサービスの活用が拡大し、「 クラウドネイティブ 」な開発が注目され始めました。これを支える技術として、コンテナ技術(Docker)やコンテナオーケストレーション(Kubernetes)が普及し、インフラとアプリケーションの管理がより柔軟かつ自動化されるようになりました。これらの技術はDevOpsの実践をさらに加速させる役割を果たしています。特に「 Infrastructure as Code(IaC) 」の考え方が広まり、インフラストラクチャの構成もコードとして管理されるようになりました。これによりインフラの変更も開発プロセスの一部として扱われるようになり、開発と運用の統合がさらに進みました。現代ではTerraform、AWS CloudFormation、Ansible、Puppet、ChefなどのIaCツールが広く使われるようになっています。 DevOpsの文化的側面の重視 技術的な進化と並行して、DevOpsの 文化的側面 も重視されるようになりました。成功したDevOps組織では次のような文化的特徴が見られます。 心理的安全性 - 失敗を非難せず、学びの機会として捉える文化 実験と学習の奨励 - 小さな実験を繰り返し、継続的に改善する姿勢 部門間の協力 - 開発、運用、セキュリティなど、従来は分断されていた部門間の壁を取り払う 共有責任 - 「これは私の担当ではない」考え方ではなく、全員がプロダクトの成功に責任を持つ これらの文化的側面がなければ、いくら優れたツールを導入しても、DevOpsの真の価値を実現できないという認識が広まっています。 DevOpsの本質と今後の展望 2009年に始まったDevOpsの旅は、今や業界標準となる考え方を生み出しました。現在、多くの開発チームが CI/CDを導入し、自動化されたデプロイパイプラインを運用 しています。開発者がコードをコミットすると、すぐにテストが走り、数分後には本番環境へデプロイされるようになりました。リリースのたびに夜を徹して作業したり、「障害対応のために今すぐオペレーションチームを呼べ」と叫んだりする文化は、徐々に過去のものになりつつあります。 しかし、DevOpsの本質は単なる「自動化」ではありません。 開発と運用の対立を超え、継続的に改善し続ける組織文化を作る ことこそがDevOpsの本質なのです。パトリック・ドボア、ジェズ・ハンブル、ジーン・キムをはじめとする先駆者たちが伝えたかったのは、ツールや技術の導入ではなく、根本的な組織文化の変革でした。 これからの開発者は、ツールやプロセスだけでなく、 組織全体の在り方に目を向ける必要があります。 そして、彼らが築いたこの文化をさらに進化させていくことが求められているのです。 日本におけるDevOps:まだ「一部の人たちのモノ」状態 DevOpsは世界的に広がりを見せていますが、日本においては普及のペースが異なる面もあります。 例えば、AgileやScrumのイベントと比べると DevOpsDays Tokyo の参加規模や盛り上がりが海外の同様のイベントと比較して小さいことからも、日本特有の課題があることがうかがえます。 また、2018年10月には「ソフト高速開発のDevOps推進協議会が2年余で解散」というニュースも報じられました。 日本でDevOpsの導入が遅れている理由を構造的・文化的・歴史的背景から分析すると、次のような要因が浮かび上がってきます。 要因 説明 「開発」と「運用」の切り分けの曖昧さ 日本の多くの企業では、「システム部門」が開発・運用・保守のすべてを抱える体制が一般的でした。このため、DevOpsが解決しようとした「開発と運用の断絶」問題が顕在化していなかったのです。しかし、これは必ずしも良い状況ではありませんでした。役割は分かれていなくても、責任やナレッジの分担があいまいで属人的になりやすいという弊害がありました。つまり、「DevとOpsが分かれていない」状態が「連携している」状態を意味するわけではなかったのです。 受託開発構造と多重下請けによる分業文化 日本では、ユーザー企業 → SIer(元請)→ サブ → 孫 という多重下請け構造が見受けられます。この構造だと、開発はA社、運用はB社、保守はC社という分断が起きやすく、DevOpsの前提である「共通の目標と責任」が成立しにくい環境にあります。DevとOpsが物理的にも契約上も分断されているため、DevOps導入が困難な状況が生まれているのです。 製造業モデルの異なる解釈:工程主義・完璧主義 日本のITは製造業の品質管理思想の影響を強く受けていますが、その解釈に課題がありました。本来、トヨタ生産方式は「継続的改善(カイゼン)」や「小さな改善の積み重ね」を重視し、これはDevOpsの思想と共通しています(実際、DevOpsの「リーン」の考え方はトヨタ生産方式が起源です)。しかし、多くの日本のIT現場では、製造業の手法を「工程の厳格な管理」「完璧な品質の追求」という側面だけを取り入れ、「リリース=出荷」「不具合=欠陥品」という感覚が強くなりました。このため、頻繁なリリース(Continuous Delivery)や早期フィードバックといったDevOpsの実践が「リスク」だと認識される傾向があるのです。皮肉なことに、トヨタ生産方式の本質を正しく理解していれば、DevOpsへの親和性はむしろ高かったはずなのです。 インフラのアウトソーシング文化 データセンターやネットワーク運用を外部委託(SIer、DC事業者など)するのが主流で、自社にインフラ運用の知見が蓄積しにくい環境があります。クラウドネイティブな文化が根付く前は、Opsの自動化やSRE的観点がなじみにくかったのです。これにより、DevOpsの「Infrastructure as Code」「SRE」文化が根付きにくい土壌が形成されていました。 ITが"事業の中核"ではないという認識 海外(特に米国)では、2000年代以降「ITは競争力の源泉」と位置づけられていましたが、日本ではITが依然として「業務支援」「コストセンター」と見なされ、IT部門が戦略の中心になりにくい状況がありました。このため、DevOpsが本来持つ「ビジネス成果を最大化するための仕組み」としての意義が伝わりにくかったのです。 しかし、近年日本企業においてもDevOpsへの関心が高まっています。その背景には次のような変化があります。 クラウドネイティブの普及 :AWS、Google Cloudなどのクラウドサービスの活用が拡大し、インフラのコード化や自動化の基盤が整いつつある 内製回帰の動き :特にメガベンチャーやSaaS企業を中心に、システム開発の内製化が進み、DevOpsの実践がしやすい環境が生まれている 開発生産性の重視 :Developer Productivity向上への注目が高まり、CI/CDパイプラインの構築など、開発効率を高める取り組みが増えている AI革命の波 :ChatGPTをはじめとする生成AIの登場により、ソフトウェア開発および運用の在り方が根本から変化しつつある。コードの生成、テストの自動化、運用時のログ分析や障害対応といった領域でAIが実用段階に入り、従来の手作業中心のプロセスは再構築を迫られている。AIは単なる補助ツールではなく、開発サイクル全体に組み込まれる存在となりつつあり、その文脈においてDevOpsも再定義され始めている。 この変化は一様ではありません。新興企業やIT専業企業ではDevOpsの実践が進む一方、大企業・公共系・受託構造の現場では依然として課題が残っています。 DevOpsとAI導入の共通障壁 冒頭のパトリック・ドボアは、1年前のDevOpsDays Tokyo 2024に於いて 「成熟したDevOps文化を持つ人々こそ、GenAIのアーリーアダプターになりつつある」と仰っており、事実そうなりました。 つまり、AI革命の波が押し寄せる中、DevOpsを受け入れられなかった組織は、同じ理由でAIの波にも乗り遅れる危険性があります。 日本でDevOpsを根付かせるためには、単なる「技術導入」ではなく「組織文化改革」として捉える必要があります。「DevOpsを導入する」=「ツールを導入する」ではなく、「職能や役割を超えてチームが協働する文化を育む」ことであり、この文化的転換が日本では特に重要です。 そのため、日本の組織構造を考慮した上で、DevOpsの本質を理解し、自分たちの組織に合った形で取り入れていくためには、次のようなアプローチが有効と思われます。 変化を恐れない文化の醸成 「失敗は学びの機会」という認識を組織全体で共有する 小さな変更から始め、成功体験を積み重ねることで自信をつける クロスファンクショナルチームの形成 部門の壁を越えた協働を促進し、共通の目標に向かって取り組む環境をつくる 開発・運用・ビジネス部門が一体となって価値を生み出す体制を構築する 長期的視点での投資判断 短期的ROIだけでなく、競争力維持の観点から技術投資を判断する 継続的な学習と実験の文化を育み、組織全体の適応力を高める DevOpsの歴史は、技術の進化だけでなく、人々の協力関係や組織文化の変革の歴史でもあります。その本質を理解し、自分たちの組織に合った形で取り入れていくことが、現代のソフトウェア開発者に求められています。そして、日本の文化や組織構造に合ったDevOpsの形を模索していくことも、私たち日本のエンジニアの重要な課題なのです。 ◆ AI時代の適応 ◆ AI時代は、変化に適応できる組織とそうでない組織の差がさらに拡大します。DevOpsの本質である「変化を恐れず、継続的に改善し続ける文化」は、この時代において競争力の源泉となります。 AIの導入に伴い、一部では大規模言語モデルのコストやセキュリティの観点から自社データセンターでのAI基盤構築が選択されています。しかし、これは単なるオンプレミスへの回帰ではなく、クラウドで培われたDevOps手法(IaC、コンテナ技術)を活用した新たな形態です。インフラの場所に関わらず、「自動化」「協働」「継続的改善」というDevOpsの価値はAI時代においてより重要になっています。 過去にDevOpsの波に乗り遅れた組織も、AI時代という転換点を変革の契機として活かすことができるのではないでしょうか。 【告知】ソフトウェア開発の伝説的パイオニアたちが来日! 2025年7月3日(木)・4日(金)9:30〜19:00、弊社主催「 開発生産性Conference2025 」にて、ソフトウェア開発の歴史を塗り替えた二人の巨匠が来日します。 アジャイルとDevOpsの世界的権威が同時来日する歴史的瞬間に、ぜひ立ち会ってください。 ケント・ベック(Kent Beck) — アジャイル開発の父、XPの創始者、TDDの提唱者。 ジーン・キム(Gene Kim) — DevOps革命の立役者、『The Phoenix Project』著者。 本ブログで紹介した「ソフトウェア開発の開拓者たち」が、目の前で語る貴重な機会です。彼らの思想と実践が世界のソフトウェア開発をどう変えたのか、そして日本のエンジニアが今後どう進むべきか—直接その声を聞ける、まさに一生に一度のチャンスです。 DevOpsの本質を理解し、AI時代を勝ち抜くための知恵を、開発現場の革命家たちから直接学びませんか? 参加登録はこちら : 開発生産性Conference2025公式サイト ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 *1 : Edwards, D. (2014, May 16). The incredible true story of how DevOps got its name. New Relic. https://newrelic.com/blog/nerd-life/devops-name
こんにちは! Findy Team+ 開発チームでEMをしている ham です。 今年もRubyKaigi 2025に参加してきました。 私はコロナ後に三重県で開催されたときから4年連続で参加しているのですが、今年も興味深いセッションがたくさんあり、Rubyが着実に進化していることを感じることができました! 本記事では、その中の1つである「 The Ruby One-Binary Tool, Enhanced with Kompo 」で紹介された「 Kompo 」について、実際に試してみた結果と所感を紹介します。 Kompoとは 'Hello, world!'を返すスクリプト kompo-vfs Kompo Rails 最後に Kompoとは Kompoとは、READMEで「A tool to pack Ruby and Ruby scripts in one binary.」と紹介されている通り、Rubyスクリプトをバイナリにして配布できるツールです。 Rubyのスクリプトをバイナリにすることで、配布が容易になったり、実行環境にRubyのインストールが不要になるため配布先での実行が容易になります。 Kompoは2024年のRubyKaigiでも「 It's about time to pack Ruby and Ruby scripts in one binary 」のセッションで紹介されており、興味を持っていました。 2024年の時点ではRailsなどの大きなGemは実行できていないとのことだったのですが、2025年の発表ではRailsが動いており進化を感じました! 'Hello, world!'を返すスクリプト 今回のセッションでRailsが動作するようになったと発表されていたので、Webサーバーが動作するバイナリを作ってみることにしました。 とはいえ、最初から大きなものを作ろうとすると詰まる可能性が高いです。何事もスモールスタートが良いですね。 入門といえば 'Hello, world!'ということで、まずは'Hello, world!'を返却するRubyスクリプトでトライしました。 なお、ここからの内容は執筆時点(2025年4月)の情報なので最新版では変更されている可能性があります。 kompo-vfs Kompoは内部で仮想ファイルシステムを使っているとのことです。 当初は既存のライブラリで実現できないか検討したとのことですが、Kompoのやりたいことにマッチするものがなかったとのことで「 kompo-vfs 」を自作したそうです。 リポジトリを見ていただければわかりますが、こちらはRustで書かれていました。 Kompoを使うにはまずkompo-vfsをbuildしておく必要があるとのことです。 READMEに沿って作業します。 READMEには次のように記載されています。(※手順はKompoのREADMEに記載されています) ## prerequisites Install [kompo-vfs](https://github.com/ahogappa/kompo-vfs). #### Homebrew $ brew tap ahogappa/kompo-vfs https://github.com/ahogappa/kompo-vfs.git $ brew install ahogappa/kompo-vfs/kompo-vfs ### Building To build komp-vfs, you need to have cargo installation. $ git clone https://github.com/ahogappa/kompo-vfs.git $ cd kompo-vfs $ cargo build --release Set environment variables. $ KOMPO_CLI=/path/to/kompo-vfs/target/release/kompo-cli $ LIB_KOMPO_DIR=/path/to/kompo-vfs/target/release MacBookを使っているのでbrewの手順を試しましたがうまくいかなかったので、リポジトリをcloneする方法で実施しました。 mainブランチで試してみましたが、buildがエラーになりました。 % cargo build --release ... error: could not compile `kompo_storage` ( lib ) due to 2 previous errors; 1 warning emitted こちら色々解析したところ、MacBookには対応してなさそうだとわかりました。 そこでDockerを立ち上げてその中でbuildすることにしました。 Kompoの実行もDockerで実施した方が良さそうだったので、Rubyイメージから作成しました。 # Dockerfile FROM ruby:3.4.3 # Install dependencies RUN apt-get update && apt-get install -y \ git \ build-essential \ libssl-dev \ zlib1g-dev \ libyaml-dev \ libgmp-dev \ libreadline-dev \ pkg-config \ autoconf \ bison \ curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install latest Rust using rustup RUN curl --proto ' =https ' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ENV PATH= "/root/.cargo/bin:${PATH}" # Set working directory WORKDIR /app # Copy project files COPY . /app/ # Install bundler and dependencies RUN gem install bundler && bundle install CMD [ " tail ", " -f ", " /dev/null " ] buildしてbashでコンテナに入り、改めてbuildしたら成功しました🙌 % docker build -t hello-world . % docker run -it --rm -v .:/app hello-world bash root:/app# cd kompo-vfs/ root:/app/kompo-vfs# cargo build --release ... Finished `release` profile [ optimized ] target ( s ) in 5 .40s Kompo 次に'Hello, world!'を返すRubyスクリプトを作成します。 # hello.rb p ' Hello, world! ' Kompoはgemが公開されていないのでリポジトリをcloneしてローカルで生成します。 色々試行錯誤したのちに気づいたのですが、登壇スライドの26ページによるとkompoは feature/use_rtld_next の方が最新と思われるのでそちらを利用します。 Dockerコンテナ内で gem build を実施して、無事 kompo-0.2.0.gem が生成できました。 root:/app/kompo# gem build kompo.gemspec WARNING: licenses is empty, but is recommended. Use an license identifier from https://spdx.org/licenses or 'Nonstandard' for a nonstandard license, or set it to nil if you don't want to specify a license. WARNING: open-ended dependency on mini_portile2 (>= 0) is not recommended use a bounded requirement, such as "~> x.y" WARNING: open-ended dependency on async (>= 0) is not recommended use a bounded requirement, such as "~> x.y" WARNING: You have specified the uri: https://github.com/ahogappa0613/kompo for all of the following keys: homepage_uri changelog_uri source_code_uri Only the first one will be shown on rubygems.org WARNING: See https://guides.rubygems.org/specification-reference/ for help Successfully built RubyGem Name: kompo Version: 0.2.0 File: kompo-0.2.0.gem 次に、Kompoをインストールします。 root:/app# gem install kompo/kompo-0.2.0.gem ... Successfully installed kompo-0.2.0 10 gems installed 時は来た!あとは梱包(Kompo)するだけです!実行には数分かかるので待ちます。 root:/app# kompo --help Usage: kompo [options] -e, --entrypoint=VAL File path to use for entry point. (default: './main.rb') -g, --use-group=VAL Group name to use with 'bundle install'. (default: 'default') --[no-]gemfile Use gem in Gemfile. (default: automatically true if Gemfile is present) --local-kompo-fs-dir=VAL --verbose Verbose mode. --dest-dir=VAL Output directry path. (default: current dir) --bundle-cache=VAL Specify the directory created by 'bundle install --standalone'. --ruby-version=VAL Specify Ruby version. (default: current Ruby version) --rebuild --repack root:/app# kompo -e hello.rb --local-kompo-fs-dir=kompo-vfs ... /usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.WD+0x0): multiple definition of `WD'; /tmp/ccNwyWlB.o:(.rodata+0x0): first defined here /usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.PATHS_SIZE+0x0): multiple definition of `PATHS_SIZE'; /tmp/ccNwyWlB.o:(.rodata+0x28): first defined here /usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.PATHS+0x0): multiple definition of `PATHS'; /tmp/ccNwyWlB.o:(.rodata+0x30): first defined here /usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.FILES_SIZE+0x0): multiple definition of `FILES_SIZE'; /tmp/ccNwyWlB.o:(.rodata+0x192c8): first defined here /usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.FILES+0x0): multiple definition of `FILES'; /tmp/ccNwyWlB.o:(.rodata+0x192d0): first defined here collect2: error: ld returned 1 exit status /usr/local/bundle/gems/kompo-0.2.0/lib/kompo.rb:193:in 'Kernel#system': Command failed with exit 1: gcc (RuntimeError) エラーが発生しました。 kompo-vfsで、 WD や PATHS などの定義が重複しているようです。 Rustなんもわからんので雰囲気ですが、 kompo-vfs/kompo_fs/dummy_fs.c の中で WD や PATHS が定義されているので怒られた定義をコメントアウトして再buildしてやり直してみました。 // kompo-vfs/kompo_fs/dummy_fs.c // const char FILES[] = {}; // const int FILES_SIZE = 0; // const char PATHS[] = {}; // const int PATHS_SIZE = 0; // const char WD[] = {47,119,111,114,107,115,112,97,99,101,115,47,114,117,98,121,95,112,97,99,107,97,103,101,114,47, 0}; const char START_FILE_PATH[] = { 46 , 47 , 109 , 97 , 105 , 110 , 46 , 114 , 98 , 0 }; 再度Kompoを実行。今回は正常終了し、バイナリ( app )が生成されました🎉 root:/app# kompo -e hello.rb --local-kompo-fs-dir = kompo-vfs ... info: Finish kompo! root:/app# ls -l app -rwxr-xr-x 1 root root 76437456 Apr 24 02:15 app root:/app# ./app " Hello, world! " 最後に実行します。せっかくなのでRubyが入っていない環境で実行します。 Dockerfileはこちらを使いました。 FROM ubuntu:latest # Set working directory WORKDIR /app # Copy project files COPY . /app/ CMD [ " tail ", " -f ", " /dev/null " ] コンテナを立ち上げて、バイナリを実行します。 Rubyが入っていない環境で実行できました🎉 % docker build -t ubuntu-app . % docker run -it --rm -v .:/app ubuntu-app bash root@62df4e7aa257:/app# ./app " Hello, world! " Rails 簡単なRubyスクリプトでは実行できることがわかったので、次はRailsに挑戦です! ただ、結論は「色々試行錯誤したものの起動できず」でした... Kompoするところでエラーになったり、Kompoはできたが起動できなかったり、これ以上の解析は難しいので今回は諦めました🙏 最後に 今回は簡単なRubyスクリプトしかできませんでしたが、ワンバイナリで配布してすぐに実行できることはとても便利だと感じました。今後の更なる進化に期待です!! 5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。 LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! https://pixiv.connpass.com/event/352852/ pixiv.connpass.com また、ファインディではこれからも新しいものを取り入れつつ、Rubyを積極的に活用してRubyとともに成長していければと考えております。 一緒に働くメンバーも絶賛募集中なので、興味がある方はぜひこちらから ↓ herp.careers
こんにちは。ファインディでソフトウェアエンジニアをしている nipe0324 です。 先日、愛媛県松山市で開催されていたRubyKaigi 2025 に参加してきました。 様々なセッションに参加し、他社のエンジニアと話す中で多くの刺激をうけました。特に印象深かったのは、Sansanさん、TwoGateさん、Timeeさんなどの企業がRubyの型を導入して運用していたことです。 本記事では、N番煎じですが、 Findy転職のRailsプロジェクトにSorbetを入れて型を試してみました。 Rubyの型に興味を持っているけどなかなか試せていないという方の参考になれば嬉しいです。 Sorbetとは? Sorbetのメリット・デメリット Sorbetを使ったRubyのコード例 型導入のモチベーション 課題:Interactorの入出力が不明確 検証した結果 検証の実施内容 Sorbetのセットアップ ActiveRecordのモデルに型を追加 GraphQLに型を追加 Interactorに型を追加 型を試してみた所感 最後に Sorbetとは? Sorbet( https://github.com/sorbet/sorbet ) はRubyのコードに型注釈をつけて、型エラーを検出できるツールです。 StripeやShopifyなどの企業で利用されています。 Sorbetを利用している企業例 Sorbetのメリット・デメリット Sorbetの主なメリットとデメリットは次のとおりです。 メリット 型エラーを事前に検出できる コードの可読性と保守性の向上 IDEのコード補完がより有効に使える リファクタリングが安全に行える ドキュメントとしての役割 デメリット 導入コストがかかる(コードの修正、型定義の作成) Rubyの動的な特性が一部制限される チーム全体に学習コストが発生する すべてのgemやライブラリが型に対応しているわけではない Sorbetを使ったRubyのコード例 Sorbetを使うと、型をインラインで定義できます。 このコード例では、 sig を使って、 greet メソッドが String 型の引数を受け取ることを宣言しています。そのため、数値(Integer)を渡すと型エラーが検出されます。 # typed: true class User extend T :: Sig sig {params( name : String ).void} def greet (name) " Hello #{ name }" end end User .new.greet( " Tom " ) # OK - 文字列を渡している User .new.greet( 3 ) # 型エラー - 数値を渡している 補足:Rubyの型チェッカー 現時点でRubyの主要な型チェッカーとして「Sorbet」と「Steep」があります。また、型定義の書き方として「RBI(Ruby Interface)」と「RBS」があります。 現時点の組み合わせとして、「SorbetとRBI」、「SteepとRBS」というように型チェッカーと型定義を組み合わせ使います。 型導入のモチベーション Findy転職のRailsアプリケーションでは、「GraphQL API」、「Interactor」、「Model」といったレイヤーで実装をしています。 Findy転職のRailsアプリケーションのレイヤー抜粋 Interactor層では、 collectiveidea/interactor という gemを使ってビジネスロジックを実現しています。 1つの操作(ユーザー登録やデータ検索など)を独立した「インタラクション」として実装でき、責務を分割しやすい特徴があります。 課題:Interactorの入出力が不明確 Interactor gemの内部では OpenStruct を使ってデータの受け渡しをしています。つまり、どんな値でも自由に設定できる柔軟性がある反面、入出力として何があるのか不明確になりやすいです。 Interactorのサンプルコードで問題点を確認してみます。 # イメージ実装 # Interactorの実装 class CreateJobDescriptionInteractor include Interactor def call job_description = JobDescription .new(create_params) if job_description.save # contextには何でも入れられるため # Interactorの返り値を知るには実装を読む必要がある context.job_description = job_description else context.fail!( error_messages : job_description.errors.full_messages) end end private def create_params # contextにどんな値が入るかは呼び出し元を見る必要がある context.params.slice( :name , :description , :job_type ) end end # 呼び出し元 # Interactorの中身を読まないと何が返されるか分からない result = CreateJobDescriptionInteractor .call( params : { title : ' 求人票 ' , description : ' 求人票の内容 ' , job_type : ' バックエンドエンジニア ' }) if result.success? result.job_description # job_descriptionが返るのは実装を読まないと分からない else result.error_messages # error_messagesが返るのも実装を読まないと分からない end この問題に対して、型を導入することで入出力を明確にし、コードの可読性と保守性を向上させることを目指しました。 検証した結果 Sorbetの 公式ドキュメント を見ながら、GraphQL、Interactor、Modelに対して型を定義してみました。 結果としては、次のとおりです。 ActiveRecordのモデル と GraphQLのAPI は tapioca を使うことで型定義(RBIファイル)をある程度自動生成できるため導入が簡単 Interactor は、gemの特性上、型との相性が悪く、PORO(Plain Old Ruby Object)などの設計の変更の実施が必要そう 検証の実施内容 Sorbetのセットアップ SorbetのGetting Started ( https://sorbet.org/docs/adopting ) を見ながらセットアップをしました。 まず、Gemfileに必要なgemを追加して bundle install を実行します。 # Gemfile gem ' sorbet ' , group : :development gem ' sorbet-runtime ' gem ' tapioca ' , require : false , group : [ :development , :test ] 次に、Tapiocaを使ってSorbetを初期化します。このコマンドで sorbet ディレクトリが作成され、プロジェクト内のGemに対して自動的に型定義(RBIファイル)が生成されます。 $ bundle exec tapioca init Sorbetによる型チェックを実行します。初回は多くの型エラーが出るので、修正していきます。 $ bundle exec srb tc 型エラーを修正して、型チェックが成功したら初期セットアップは完了です 👏 $ bundle exec srb tc No errors! Great job. ActiveRecordのモデルに型を追加 tapioca dsl コマンドを使うことで、ActiveRecordモデルやGraphQL-RubyのDSL(Domain Specific Language、特定領域向け言語)から自動的に型定義ファイル(RBIファイル)が作成できます。 $ bundle exec tapioca dsl Loading DSL extension classes... Done Loading Rails application... create sorbet/rbi/dsl/skill.rbi create sorbet/rbi/dsl/user.rbi create sorbet/rbi/dsl/job_description.rbi create sorbet/rbi/dsl/xxxx.rbi ... 例えば、JobDescriptionモデルがある場合、次のようなRBIファイルが自動的に作成されます。これによりモデルのプロパティに型情報が付与されます。 class JobDescription # ... sig { returns(:: String ) } def title ; end sig { params( value : :: String ).returns(:: String ) } def title= (value); end sig { returns( T :: Boolean ) } def title? ; end sig { returns( T .nilable(:: String )) } def title_before_last_save ; end では、実際に型チェックが機能するか検証してみます。 # typed: true をファイルの上部に追加し、型チェックの対象にします。さらに、型エラーの動作確認のためわざと不正な値を設定してみます。 # frozen_string_literal: true # typed: true class JobDescription < ApplicationRecord def type_check_error_method self .title = 1 # String型のプロパティにInteger型を設定(エラーになるはず) end end Sorbetを実行すると、予想通り型エラーが検出されました。👏 $ bundle exec srb tc app/models/job_description.rb:6: Assigning a value to value that does not match expected type String https://srb.help/7002 6 | self.title = 1 ^ Got Integer(1) originating from: app/models/job_description.rb:6: 6 | self.title = 1 ^ Errors: 1 GraphQLに型を追加 graphql-ruby を利用しているプロジェクトでは、GraphQLの型定義も tapioca dsl コマンドで自動生成されます。 # 自動生成されるRBIファイルのイメージ class CreateJobDescriptionMutation sig { params( title : :: String , company_id : :: Integer ).returns( T .untyped) } def resolve ( title :, company_id :); end end ActiveRecordモデルと同様に、型定義に違反した実装をすると型エラーが発生します。これによりGraphQLのリゾルバーやミューテーションにも型安全性を導入できます。 ただし、自動生成されたRBIファイルでは戻り値が T.untyped (型のない状態)になることがあるため、具体的な型を指定していく必要があると感じました。 Interactorに型を追加 Interactorでは、型定義をうまく行うことができませんでした。 Interactor gem で定義されている context が、 OpenStruct を使っておりデータが柔軟に設定できるがゆえに型定義がうまくできませんでした。 # frozen_string_literal: true # typed: true class CreateFooInteractor extend T :: Sig include Interactor # contextのアクセスのための型定義 # 上手く定義できず、`T.untyped`(型のない状態)になっている sig { returns( T .untyped) } attr_reader :context def call foo = Foo .new(create_params) if foo.save context.foo = foo else context.fail!( error_messages : foo.errors.full_messages) end end private def create_params context.params.slice( :title , :company_id ) end end 改善案としては、PORO (Plain Old Ruby Object) による実装に変えて、入出力の型を明確にすることで、型定義を記載するという方法が考えられます。 また、 https://github.com/maxveldink/sorbet-result にあるような Rustの Result 型に似た実装を導入するのも効果的です。Result型は「成功」か「失敗」のどちらかの状態を表現するもので、型安全な方法で結果を扱えるようになります。 型でガチガチにするとRubyの良さが失われてしまう懸念もあるため、段階的に導入しつつバランスを見ていく必要があります。 型を試してみた所感 今回既存のRailsプロジェクトでSorbetによる型を試しに導入しました。 感想としては、検討事項は他にもありますが、前向きに型導入を進めていこうと思いました。 ActiveRecordやGraphQLにほぼ自動的に型定義を追加できたり、段階的に型チェックを有効化できるので小さく始めやすい ローカル開発やCIで型チェック、GraphQLやテーブルスキーマ変更時の型更新のフローを整備する必要がある SorbetとSteepのどちらが良いかは好みによるので検討は必要がある など 最後に ファインディでは、一緒にRubyやRailsの開発をしてくれる仲間を募集しています。 興味のある方は、ぜひこちらからチェックしてみてください! herp.careers また、2025/5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、3社合同でRubyKaigi 2025の振り返りを行います。 オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!! pixiv.connpass.com
はじめに こんにちは!ファインディでFindy Team+を開発している中嶋( @nakayama__bird )です! RubyKaigi 2025に参加してきました! 今回のRubyKaigiが初参加で楽しみ半分緊張半分だったのですが最高な3日間でした! Rubyを使う人、Rubyを作る人、そしてRubyで遊ぶ人などたくさんの出会いがあり、日頃の業務でRuby on Railsに触れるのとはまた違った視点でRubyを考えるきっかけとなりました。 複数のセッションに参加した中で特に関心を持ったruby.wasm、そしてruby.wasmを使ったスライド作成ツールgibier2を使ってみての感想をまとめていきたいと思います。 ruby.wasmとは? ruby.wasmとは、WebAssemblyという技術を使用してRubyのコードをブラウザ上で実行できる仕組みのことです。 Rubyをブラウザ上で実行できることで、環境構築をせずにRubyのコードを試せる環境の提供やフロントエンド実装への可能性を広げるといったメリットがあります。 github.com わかりやすい例だとruby.wasmのREADMEに掲載されている Try Ruby が挙げられます。ブラウザ上でRubyのコードが動くため、RubyをPCにインストールせずともどんな挙動になるのかを簡単に確認できます。 私自身、約1年半ほど前にコードを初めて書いた駆け出しエンジニアなのですが、ブラウザ上で動きが確認できるのは学習スタートのハードルが下がるという点でとても良いなと思いました。 また、ruby.wasmを活用した事例として、 Writing Ruby Scripts with TypeProf のセッションで、TypeProfをブラウザ上で試すことができる TypeProf.wasm が紹介されていました。 気軽に試してみて、もしエラーなどあれば報告してくださいというようなお話をしており、ruby.wasmが新しい技術を手軽に試せる環境として機能している点も大きな魅力だなと感じました。 TypeProf を ruby.wasm で完全ブラウザ上で動くようにしてみた。触ったらすぐにいろいろ粗が見つかるんで、気づいたらコントリビュートおねがいします https://t.co/4cF3Tm9QSm — Yusuke Endoh (@mametter) 2024年12月26日 x.com gibier2について gibier2は、ruby.wasmを使ったスライド作成ツールです。 dRuby on Browser Again! でトークセッションをした youchan さんが開発したもので、マークダウンでスライドを作成できます。 #RubyKaigi2025 Day1 の "dRuby on Browser Again!" のスライドを公開しました。ruby.wasmを読みこむのに少々時間がかかります。 https://t.co/oUKgrNdipp — よう (@youchan) 2025年4月18日 x.com github.com gibier2の前身として gibier があるのですが、こちらはRubyをJavaScriptにコンパイルする Opal を使用しているためスライド作成ツールという点では共通しているものの中身は別物です。 これまで私自身、スライドを作成する際にCanvaやGoogle スライドを使いGUI上で操作して作成していました。しかしながら箇条書きで内容整理をしながら登壇資料が作成できて便利そうということで、マークダウンを使ったスライド作成ツールが気になっていたため早速使ってみました! セットアップ 開発環境:ruby 3.3.6(RubyのWebAssemblyサポートは3.2.0以降 1 ) READMEを参考に実行しました。具体的なコマンドは、次の通りです。 リポジトリをcloneして bundle install します。 $ git clone https://github.com/youchan/gibier2 $ cd gibier2 $ bundle install その後、wasmディレクトリでのセットアップを行います。 README通りに bundle exec rbwasm build -o dist/ruby.wasm をしたところ失敗したため、先に cd wasm && bundle install を実行しました。 $ cd wasm && bundle install その後、wasmディレクトリで次のコマンドを実行します。 $ bundle exec rbwasm build -o ../dist/ruby.wasm $ bundle exec rbwasm pack ../dist/ruby.wasm --dir ./src::/src -o ../dist/app.wasm 初回のbuildには時間がかかるのしばらく待ちます。 ルートディレクトリに戻りRackアプリケーションサーバーを立ち上げます。 $ cd .. $ bundle exec rackup http://localhost:9161 を開くとサンプルのスライドが表示されます。 実際に使ってみる 基本的に slide.md にマークダウンで資料の内容を書いていきます。 <!-- slide.md --> ## RubyKaigi 2025行ってきました - ruby.wasmを知ることができた - gibier2を使ってみた - スピーカーとお話しできた スライドの背景画像は public/images 配下におきます。 それを public/css/custom.css で適宜設定します。 /* public/css/custom.css */ .page { background-image : url( "/images/new_background.png" ) ; color : #222 ; } 特にカスタマイズしないと、 # だとセンタリングされた状態、 ## だと見出しとして表示されます。 <!-- slide.md --> # #が1つの場合 <!-- slide.md --> ## #が2つの場合 マークダウンのためコードブロックやリンクの追加も可能です。 <!-- slide.md --> ## コードブロックやリンクの挿入も可能 <200b> `` `ruby def hello puts "Hello, World!" end <200b> ``` [Findy Tech Blog](https://tech.findy.co.jp/) まとめ RubyKaigiで関心を持ったruby.wasm、そしてruby.wasmを使ったスライド作成ツールgibier2を使ってみたという内容をまとめました。 発表を聞いてすぐにgibier2を試したのでぜひ youchan さんにお話を聞きたいなと思っていたところ、偶然にも弊社のDrink Upでお会いできました。セッション後、早速使ってみましたと伝えると喜んでもらえて私も嬉しい気持ちになりました。 このように、登壇者とコミュニケーションを取りやすいのもRubyKaigiの良いところです。 gibier2について、今回のRubyKaigiの登壇資料用として作ったため、汎用的なツールとしてはこれからだという話をしていました。 またGUI上でより細かなデザインをできるようにしたりPDFへ書き出せるようにしたりしたいなど、今後の展望についてもお話を聞くことができとても良い経験となりました。 またビルドの流れでREADMEに記載されている通りに実行したところうまく立ち上がらなかったため、Issueを作成してみました。 github.com OSSへの貢献したいなと考えていたので、RubyKaigiでの出会いをきっかけに実際に行動に移せてよかったです。 最後に 5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。 オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! 今回のブログで取り上げたgibier2を使って登壇資料を作成予定なので、ぜひ見にきていただけると嬉しいです! pixiv.connpass.com また、ファインディではこれからも新しいものを取り入れつつ、Rubyを積極的に活用してRubyとともに成長していければと考えております。 一緒に働くメンバーも絶賛募集中なので、興味がある方はぜひこちらから ↓ herp.careers https://www.ruby-lang.org/ja/news/2022/12/25/ruby-3-2-0-released/ ↩
こんにちは、 Findy Freelance の開発をしているエンジニアの @2bo です。 先日、愛媛県で開催されたRubyKaigi 2025に参加してきました。ファインディのブースにお立ち寄りいただいた方、Rubyクイズに答えてくださった方、Drinkupに参加していただいた方、運営やSpeakerの皆様、ありがとうございました! おかげさまでとても楽しく過ごすことができ、興味深いセッションもたくさんありました。 本記事では、その中の1つである @sinsoku_listy さんの「 Automatically generating types by running tests 」で発表されていた「 RBS::Trace 」を早速、Findy FreelanceのRailsプロジェクトで試してみた結果と所感を紹介します。 RBS::Traceとは 実行手順 1. Gemfileへの追加 2. RSpecの設定 3. テストの実行 結果の確認 Inline RBS RBSファイル 参考: テストコード 結果からわかったこと Steepの導入 1. Gemfileへの追加 2. Bundle install 3. Steepの設定 4. VSCodeのSteep拡張のインストール 5. RBSファイルのエラーをコメントアウト VSCodeでの型情報の表示結果 所感 RBS::Trace導入のメリットと可能性 活用における注意点 RBSのプロジェクトへの導入について 最後に 参考 RBS::Traceとは RBS::Trace は、コード実行時にメソッドの引数と戻り値の型情報を収集し、自動的にInline RBSとしてコメントを挿入したり、RBSファイルを作成してくれるGemです。 実行手順 Findy FreelanceのRailsプロジェクトのテストでRBS::Traceを実行してみました。 執筆時点のバージョンは 0.5.1 です。 なお、今回は大枠の動作と結果を確認することが目的のため、対象は app/models/ 配下に絞っています。 次の手順で実行しました。 1. Gemfileへの追加 gem " rbs-trace " Gemfile追記後にbundle installを実行します。 $ bundle install 2. RSpecの設定 次に、RSpec用の設定ファイルを作成します。 RBS::Trace.new の引数 paths で対象のファイルを指定しています。 RBSファイルの格納先として、 sig/trace/ を指定しています。なお、この設定は任意です。 # spec/support/rbs_trace.rb RSpec .configure do |config| # RBSの出力対象とするファイルを指定 trace = RBS :: Trace .new( paths : Dir .glob( "#{ Dir .pwd } /app/models/**/* " )) config.before( :suite ) { trace.enable } config.after( :suite ) do trace.disable trace.save_comments # RBSファイルの格納先を指定 trace.save_files( out_dir : " sig/trace/ " ) end end 3. テストの実行 テストを実行します $ bundle exec rspec spec/models/ 結果の確認 テストを実行すると、Inline RBSが対象のファイルのメソッド定義の上に追記され、RBSファイルが生成されます。 それぞれの結果を次に示します。 なお、掲載しているのは例示用のコードで、実際のFindy Freelanceのコードや処理とはまったく関係ありません。 Inline RBS app/models/user.rb に次のようなInline RBSが生成されました。 class User < ApplicationRecord has_many :projects # @rbs () -> String def full_name format_name end # @rbs () -> Project? def main_project projects.find_by( active : true ) end # @rbs (Date) -> Project::ActiveRecord_AssociationRelation def projects_after_date (date) projects.where( ' start_date >= ? ' , date) end private # @rbs () -> String def format_name "#{ last_name } #{ first_name }" end end RBSファイル sig/trace/app/models/user.rbs に次のようなRBSファイルが生成されました。 class User def full_name : () -> String def main_project : () -> nil | () -> Project def projects_after_date : ( Date ) -> Project :: ActiveRecord_AssociationRelation def format_name : () -> String end 参考: テストコード RBS::TraceによるRBSの自動生成は、テストで実行された内容に依存するため、参考としてテストコードの例を掲載します。 RSpec .describe User do describe ' #full_name ' do subject { user.full_name } let( :user ) { create( :user , first_name : ' 太郎 ' , last_name : ' 山田 ' ) } it ' returns a string combining last_name and first_name ' do expect(subject).to eq( ' 山田 太郎 ' ) end end describe ' #main_project ' do subject { user.main_project } let( :user ) { create( :user ) } context ' when there is an active project ' do let!( :active_project ) { create( :project , user : user, active : true ) } let!( :inactive_project ) { create( :project , user : user, active : false ) } it ' returns the active project ' do expect(subject).to eq(active_project) end end context ' when there is no active project ' do let!( :inactive_project ) { create( :project , user : user, active : false ) } it ' returns nil ' do expect(subject).to be_nil end end end describe ' #projects_after_date ' do subject { user.projects_after_date(target_date) } let( :user ) { create( :user ) } let( :target_date ) { Date .new( 2025 , 4 , 1 ) } let!( :before_project ) { create( :project , user : user, start_date : Date .new( 2025 , 3 , 31 )) } let!( :on_date_project ) { create( :project , user : user, start_date : Date .new( 2025 , 4 , 1 )) } let!( :after_project ) { create( :project , user : user, start_date : Date .new( 2025 , 4 , 2 )) } it ' returns only projects starting on or after the specified date ' do expect(subject).to include (on_date_project, after_project) expect(subject).not_to include (before_project) end end end 結果からわかったこと 実行した結果、次のようなことがわかりました。 直接テストしていないが、テスト中に実行されるprivateメソッドの型情報も生成されている ApplicationRecordのサブクラスを返すメソッドは、具体的なクラス名で型情報が生成されている Railsのassociationやscopeメソッドには、型情報が生成されない これらはメソッド定義ではないため当然の結果である ActiveRecord::AssociationRelationのサブクラスのインスタンスを返すメソッドは、[具体クラス名]::ActiveRecord_AssociationRelationのように型情報が記載される これはRailsの仕様によるもので、そのようなクラスを動的生成しているためである 既に記載されているInline RBSは上書きされない RBSファイルの内容はテストを実行するたびに更新される Steepの導入 せっかくRBSファイルが生成されたので、型チェッカーである Steep もセットアップして型のあるRailsプロジェクトを疑似体感してみました。 ただし、RBS::TraceだけでRailsプロジェクトの型チェックすべてパスさせることはできないため、VSCodeで型情報を確認できるようにすることを目的としています。 次の手順でセットアップしました。 1. Gemfileへの追加 # Gemfile gem ' steep ' 2. Bundle install Gemfile追記後にbundle installを実行します。 $ bundle install 3. Steepの設定 設定ファイルとなる Steepfile 作成します。 D = Steep :: Diagnostic target :app do # RBSファイルの格納先を指定 signature " sig/trace " # Steepで型チェックする対象のファイルを指定 check " app/models " # 型チェック結果を全て抑制(無音)する configure_code_diagnostics( D :: Ruby .silent) end 4. VSCodeのSteep拡張のインストール steep-vscode をVSCodeにインストールします。 5. RBSファイルのエラーをコメントアウト RBSファイル内にエラーがあると、VSCodeのSteep拡張が動作しないため、エラーしている箇所をコメントアウトしました。本来は型定義を追加、修正するなどしてエラーを解消する必要がありますが、今回はVSCodeで型情報を確認できることが目的のため、このような対応をしました。 先のRBSファイルの例で言うと、RBS::Traceの実行だけでは Project::ActiveRecord_AssociationRelation クラスの型情報が生成されないため、 RBS::UnknownTypeName エラーになります。これをコメントアウトすることでエラーを握りつぶしています。 class User def full_name : () -> String def main_project : () -> nil | () -> Project # RBS::UnknownTypeName エラーになるためコメントアウト # def projects_after_date: (Date) -> Project::ActiveRecord_AssociationRelation def format_name : () -> String end VSCodeでの型情報の表示結果 メソッドの呼び出し箇所をホバーすると、型情報が表示されます。 また、入力の補完時にも型情報が表示されます。 所感 RBS::Trace導入のメリットと可能性 RBS::Traceは、RBSが全くないRailsプロジェクトにとって、型情報導入の最初の一歩として非常に有効だと感じました。テスト実行だけで型情報が自動生成されるため、手動で記載する手間が大幅に省けます。 Inline RBSだけの生成も可能なので、ドキュメント生成ツールとしても活用できそうです。これは人間にとって読みやすいだけでなく、GitHub Copilotなどの生成AIツールにも型情報を提供できるメリットがあります。生成AIがInline RBSから型情報を読み取れば、より正確なコード提案が得られるのではないかと期待しています。 将来的には、 RBS::Inline が RBS 本体に組み込まれる計画もあるようで、Inline RBSだけで型チェックができる日も来るかもしれません。 活用における注意点 型情報の出力結果はテストの実行内容に依存します。例えばStringとnilを返すメソッドがあっても、nilを返すケースのテストがなければ、型情報はStringだけになってしまいます。つまり、RBS::Traceを活用するには、テストの品質確保が前提となります。 また、Railsプロジェクト全体にRBSやSteepを導入した場合、相応のメンテナンス工数がかかると感じました。RBS::Traceだけでは、Rails自体が生成するクラスやメソッドの型情報は提供されないため、 rbs_rails などの併用や、手動での型情報メンテナンスも必要になるでしょう。 RBSのプロジェクトへの導入について 今回の試行から、Findy FreelanceプロジェクトへのRBSやSteep導入を決定したわけではありませんが、これらのツールとエコシステムの現状を実感できました。RBS::Traceのおかげで試すハードルが下がったのは大きな収穫です。開発者の @sinsoku_listy さんには感謝しています。 最後に RubyKaigi 2025では、Rubyの型に関するセッションがいくつかありました。 すべてを聞けてはいませんが、総じてRubyの型に関するエコシステムは今後もまだまだ進化していきそうだと感じました。 正直、私は今までちゃんとキャッチアップができていなかったのですが、RubyKaigiへの参加をきっかけに興味が強くなり、理解を深めるきっかけとなりました。 今後もRBS, Steep, Sorbetなどの型に関するエコシステムの進化をキャッチアップしつつ、プロジェクトに導入するかどうかを検討していきたいと思います。 5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。 オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!! pixiv.connpass.com ファインディでは、一緒にRubyやRailsの開発をしてくれる仲間を募集しています。 興味のある方は、ぜひこちらからチェックしてみてください! herp.careers 参考 RBS::Trace GitHub Automatically generating types by running tests (発表資料) Steep GitHub "型"のあるRailsアプリケーション開発 (発表資料)
こんにちは!ファインディ株式会社でエンジニアをしている みっきー です。 先日開催されたRubyKaigi 2025に参加しました。 去年はLTの登壇があり、準備で忙しかったので、今年はたくさんのRubyistと話したり、セッションを見られることをとても楽しみにしていました!! 今回は特に印象に残った「 On-the-fly Suggestions of Rewriting Method Deprecations 」というセッションについて紹介します。 自己紹介 deprewriter-ruby deprewriter-ruby gemの紹介 動かしてみた 作者のohbaryeさんに聞いてみた 最後に 自己紹介 私はFindy Team+でバックエンドエンジニアとして働いており、普段はRubyを使った開発をしています。 また、プライベートでは「 omochi gem 」というRuby gemを開発・メンテナンスしています。 そのため、今回のRubyKaigi 2025で「On-the-fly Suggestions of Rewriting Method Deprecations」というセッションを見つけたときは、すぐに興味を持ちました。非推奨メソッドの置き換えを自動化できるツールがあれば、ユーザーの移行をスムーズにサポートできると思ったからです。 deprewriter-ruby deprewriter-ruby gemの紹介 「On-the-fly Suggestions of Rewriting Method Deprecations」セッションでは、 ohbarye さんが開発した「 deprewriter-ruby 」というgemが紹介されました。このgemは、非推奨になったメソッドの呼び出しを検出し、新しいメソッドへの置き換え方法を自動的に提案してくれるツールです。 deprewriter-rubyの特徴は次の通りです: 設定ファイルによる柔軟な定義 : YAMLファイルで非推奨メソッドとその代替メソッドのマッピングを定義できます。 インラインの提案 : コードを実行すると、非推奨メソッドが使われている箇所で、代替メソッドへの置き換え方法が提案されます。 自動修正機能 : 提案された変更を自動的に適用することも可能です。 このgemは、Rubyの標準的な警告メカニズムを拡張して、単に「このメソッドは非推奨です」と警告するだけでなく、「このメソッドは非推奨です。代わりにこのように書き換えてください」という具体的な提案をします。 speakerdeck.com 動かしてみた セッション後、早速deprewriter-rubyを試してみました。 まず、次のようにインストールします # Gemfile gem " deprewriter " , github : " ohbarye/deprewriter-ruby " 次に、ライブラリコードで非推奨メソッドとその代替メソッドを定義します # Library code require " deprewriter " class Legacy def old_method (arg) puts " Using deprecated old_method with #{ arg }" end def new_method (arg) puts " Using new_method with #{ arg }" end extend Deprewriter deprewrite :old_method , to : ' new_method({{arguments}}) ' end そして、非推奨メソッドを含むコードを実行すると # User code legacy = Legacy .new legacy.old_method( " example argument " ) 環境変数を設定して実行することで、異なるモードで動作させることができます # ログモード - 非推奨メソッドの警告と提案を表示 $ DEPREWRITER=log ruby your_script.rb # 差分モード - 変更の差分を表示 $ DEPREWRITER=diff ruby your_script.rb # 書き換えモード - 自動的にコードを書き換え(注意して使用してください) $ DEPREWRITER=dangerously_rewrite ruby your_script.rb ログモードでは次のような警告と提案が表示されます $ DEPREWRITER=log bundle exec ruby user_code.rb Calling deprecated method: W, [2025-04-21T19:49:55.132998 #2137] WARN -- : DEPREWRITER: Legacy#old_method usage at user_code.rb:9 is deprecated. You can apply the diff below to resolve the deprecation. --- ./user_code.rb 2025-04-21 19:49:55.123740000 +0900 +++ ./user_code.rb 2025-04-21 19:49:55.123740000 +0900 @@ -6,7 +6,7 @@ # Call the deprecated method puts "Calling deprecated method:" -legacy.old_method("example argument") +legacy.new_method("example argument") # The deprewriter will detect this call and show a warning # suggesting to use new_method instead Using deprecated old_method with example argument 書き換えモードでは次のような警告と変更がおこなわれます。 $ DEPREWRITER=dangerously_rewrite bundle exec ruby user_code.rb Calling deprecated method: W, [2025-04-21T20:11:49.426183 #39447] WARN -- : DEPREWRITER: Dangerously trying to rewrite. It will rewrite a file to apply the deprecation and load the file Calling deprecated method: Using new_method with example argument Using deprecated old_method with example argument $ git diff diff --git a/user_code.rb b/user_code.rb index d1a2a2b..bfcab60 100644 --- a/user_code.rb +++ b/user_code.rb @@ -6,4 +6,4 @@ legacy = Legacy.new # Call the deprecated method puts "Calling deprecated method:" -legacy.old_method("example argument") +legacy.new_method("example argument") 手元の環境で demo を作成し、試してみました。 驚くほど簡単に非推奨メソッドの検出と提案が行われ、ユーザーがコードを更新する手間を大幅に削減できることを実感しました。 作者のohbaryeさんに聞いてみた セッション後、ohbaryeさんに直接話を聞く機会がありました。セッションの内容だけでなく、ツール開発のきっかけや普段の情報収集の方法についても興味深いお話を伺うことができました。 ohbaryeさんは Hacker News を定期的にチェックして、技術トレンドや面白いプロジェクトの情報を集めているそうです。 セッション内で紹介のあったPharoというプログラミング言語もHacker Newsで見かけたそうです。 また、PharoのDeprewriterに触れたきっかけで、deprewriter-rubyを作ったそうです。 「最近は日本語版もあるので、英語が苦手な方にもとっつきやすくなりました」と教えてくれました。さらに、情報収集の効率化について次のようなアドバイスもいただきました。 著名人や有名企業(Shopify、GitHubなど)のブログなど質の高い情報ソースはfeedlyというRSSリーダーを使って集めるようにしています。すべての未読記事を消化するのは大変なので、時間があるときに気になったものだけを読むようにしていますね。 このような効率的な情報収集の方法は、私も取り入れていきたいと思いました。 「必要だから作る」という実践的なアプローチにも共感し、自分のプロジェクトでも同じような姿勢で取り組んでいきたいと感じました。 ohbaryeさんに直接感想を伝えられたことで、オープンソースコミュニティの温かさも実感できた貴重な機会でした! 最後に deprewriter-rubyは、Rubyの非推奨メソッドの置き換えを自動化するという、一見小さな問題に焦点を当てたgemですが、その影響は大きいと感じました。 非推奨メソッドの変更は避けられないものであり、ユーザーの移行をいかにスムーズにサポートするかは、ライブラリ開発者にとって重要な課題です。 day3の Ruby Committers and the World 内で、Matzも後方互換を大切にしている話をしていました。 Matz「基本的にはなにもdeprecateしたくない。提案するのは彼らですが、互換性を壊すと怒られるのは僕なので… deprecateするにはマイグレーションパスを用意して、十分猶予期間を準備してからになるので数年から十年単位の話になる」 #rubykaigi #rubykaigiA — 黒曜@Leaner Technologies (@kokuyouwind) 2025年4月18日 x.com 現在のdeprewriter-rubyには、複雑なメソッド呼び出し(メタプログラミングなど)の書き換えができない、非推奨メソッドが呼び出されるたびに書き換え処理が実行されて効率が悪い、非標準のRubyライブラリに依存しているといった制限があります。 しかし、ohbaryeさんはこれらの課題に対して、より複雑なメソッド呼び出しへの対応、最初の呼び出し後の処理をスキップして最適化する、依存関係を減らしてスタンドアロン化するといった明確な改善計画を持っています。 このような継続的な改善への取り組みを見ると、今後さらに実用的なツールへと進化していくことが楽しみです!! RubyKaigi 2025では他にも多くの興味深いセッションがありました。 Rubyコミュニティの活発な技術共有やライブラリやツール開発の文化に、改めて感謝の気持ちを抱いた3日間でした。 5/13(火)に、 「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」 として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。 オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!! ファインディでは、一緒に働く仲間を募集しています!! 興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。Findy Tech Blog編集長の高橋(@Taka_bow)です。 この記事は これが私の推しツール!シリーズ の第3弾になります。今回も、 推しツール紹介 と題して、弊社エンジニア達が日々の開発業務で愛用しているツールやOSSを紹介していきます。 トップバッターは奥田さんです! ■ 奥田さん / PdM室 / GenAIイネーブルメント ■ データサイエンティストのだーさん ( @Dakuon_Findy ) です。2025年の1月よりファインディのプロダクトマネジメント室 GenAIイネーブルメントチームにデータサイエンティストとして参画しております。このチームでは、LLMを活用した各種プロダクトの強化や、社内オペレーションの改善に取り組んでいます。 Polars (Pythonライブラリ) Polarsの概要 Polarsは、高速かつ明示的なスキーマ定義を特長とするデータフレームライブラリです。大規模データであってもローカル環境でカラムの型の一貫性を保ったまま効率的に処理できるため、信頼性とスピードを両立したデータ分析が可能になります。 Polarsを使ってる様子 Polarsの推しポイント 処理が速い!型がある!の2点です。この2点がデータ分析における推しポイントとなる理由を以下で詳述します。 1. 処理速度 前職で経験したのですが、pandasで2時間かかっていた処理が、Polarsに書き換えた途端に5分少々で完了し、驚愕しました。処理が速すぎて「処理の間に別のコードをのんびり書こう」が成立せず、自分のタイピング速度が開発のボトルネックになっているような感覚になります。ひりつきますね。 次の図は公式が公開している、Parquet形式データ読み込みを含んだ処理速度のベンチマークです。例えば1つめのクエリ (Q1) ではpandasは25秒、Polarsでは約1.5秒と、約16.7倍の速度が出ており、全体を通じてPolarsの処理速度はpandasの約11倍 (Q6) 〜81倍 (Q5) の速度が出ていることが見て取れます。 Updated PDS-H benchmark results より引用 2. カラムごとに明確な型を持てる Polarsでは、スキーマを用いてカラムごとに明示的な型を定めることができます。たとえば「数値型 ( pl.Int64 )」とスキーマで決めたカラムに文字列が混ざっていると読み込み処理の段階で型エラーになります(もちろん、Object 型を使えば混在も可能ですが、それを明示しない限りはエラーで気づけます)。これは一見「融通が利かない」と思われがちですが、表記揺れに気づけたり、特定の型を前提とした処理を組んでもバグが起こりづらいというメリットがあります。 正常なスキーマでCSVを読み込んだ例 誤ったスキーマでCSVを読み込んだ場合のエラー Polarsがここまで快適なのは、単なる実装の工夫ではなく、そもそもの「設計思想」によるところが大きいと考えられます。 公式ドキュメント でも次のように述べられています: Philosophy The goal of Polars is to provide a lightning fast DataFrame library that: Utilizes all available cores on your machine. Optimizes queries to reduce unneeded work/memory allocations. Handles datasets much larger than your available RAM. A consistent and predictable API. Adheres to a strict schema (data-types should be known before running the query). Polars is written in Rust which gives it C/C++ performance and allows it to fully control performance-critical parts in a query engine. これを見てみると、私の推しポイントはまさにこの設計思想の上に成り立っていることがわかります。 “provide a lightning fast DataFrame library” → 推しポイントの「処理が速い」につながる話です。まさに "lightning" な速度です。 “Strict schema” → 推しポイントの型の話そのものです。明示的な型で処理を行えるという安心感は本当に大きいです。 つまり、「速い・型がある」が推しポイントになるデータフレームライブラリというのはPolarsが最初から目指していたものだった、というわけです。 たとえば長期の時系列データのように、機器の変更などで表記揺れが発生しやすくデータ量も非常に大きいケースでは、Polarsの「型」と「処理速度」の強みが特に活きます。そんな場面に出会ったときにはぜひPolarsのことを思い出してもらえると嬉しいです。 Polarsの設計思想にしっかり踏み込み、自分の手で試したからこその実感が伝わる記事ですね。速度と型、安全と快適さを追い求めるPolars愛が詰まったツール紹介でした! 次は、久木田さんです! ■ 久木田さん / プロダクト開発部 / バックエンド・SRE ■ Freelance開発チームの久木田です。ファインディ入社3年目です。 バックエンド開発からインフラ構築、SRE的業務にも携わっています。 HTTPie HTTPieの概要 HTTPieとはコマンドラインで使えるHTTPクライアントツールです。 curl の代替として使用できるものです。 コマンドが直感的でわかりやすく、レスポンスのJSONは自動的に色付けされて見やすく表示されます。まだβ版ですが、Webアプリやデスクトップアプリも提供されています。 HTTPieを使ってる様子 HTTPieは、レスポンスがJSONであれば自動で整形・色付けしてくれます。 公式サイトのインタラクティブなデモページでCLIコマンドを試すことができるので、ぜひそちらでも試してもらいたいです。 HTTPie CLI: HTTP & API testing client https://httpie.io/cli/run デスクトップアプリも使っており、こちらも使いやすく愛用しています。 デスクトップアプリ版は基本的にはGUIで操作するようになっており、リクエスト履歴の管理などがしやすくAPIテストを効率的にできます。 HTTPieの推しポイント CLIで使う場合 JSON出力の見やすさ 自動整形とシンタックスハイライトでAPIレスポンスの確認がしやすい オプションが簡潔 覚えるオプションが少なく、短く済むので直感的に使える デスクトップアプリの場合 使いやすいUI リクエストの作成や管理が簡単 コード生成機能 作成したリクエストをcurlコマンドやPython (requests)、JavaScript (fetch)など、他の言語やツールのコードに変換してくれる機能が便利 CLIとデスクトップアプリと場合によって使い分けできるので、API開発やテストでJSONデータを頻繁に扱う人に、HTTPクライアントツールとしておすすめです。 AWS Peacock Management Console AWS Peacock Management Consoleの概要 Chromeの拡張機能で、AWSコンソールを使いやすくするためのツールです。 AWSアカウントIDによってAWSコンソールのヘッダーの色を変更してくれます。 また、右上のログインユーザーの情報部分にアカウントのエイリアスも表示してくれます。 AWS Peacock Management Consoleを使ってる様子 拡張機能に追加してもらったあとに、オプションで設定をします。 適用したいアカウントIDを入力し、環境ごとに設定したい色を16進数のカラーコードで指定します。 指定することで次のように色が変わります。 アカウントのエイリアスの表示は自動でしてくれます。 Staging環境 Production環境 AWS Peacock Management Consoleの推しポイント 自分がログインしている環境を間違えない。 プロダクト・環境ごとにAWSの環境が違うのですが、ヘッダーの色分けによって自分が今どのアカウントにいるかを常に把握できます。 複数のAWSアカウントを使っている開発者にはぜひ導入してもらって、事故を減らしてもらえたらと思います。 直感的な操作感と環境ごとの安全設計にこだわったHTTPieとAWS Peacock Management Console。CLIとGUIを使い分けながら、開発の快適さを追求したツール紹介でした! 最後は金丸さんです! ■ 金丸さん / プロダクト開発部 / バックエンド ■ Findy Freelance バックエンド開発の金丸です。 オペレーション改善などのバックオフィス機能開発を中心として、Findy Freelanceのバックエンド開発に携わっております。 Warp Warpの概要 Warpは次の特徴を持つターミナルツールです。 - 強力なコマンド補完 - コマンド単位で入出力を管理するブロック - ショートカット、ペイン分割などのカスタマイズ可能 生成AIと連携したAgentモードも特徴の1つです。 (私自身はまだ使いこなせていません。。。) Warpの推しポイント 推しポイントは2つあります。 サジェストが強力 過去に実施したコマンドの傾向やエラー出力をもとに、次に実施するべきコマンドをサジェストしてくれます。 例: git add -> git commit -> push branch -> open pull request の流れを自動的に補完してくれる 上記のGIFでは git push でエラーが出た際に出力された git push --set-upstream origin hogehoge をサジェストしてくれています。 ブロック機能 Warpはコマンド実行単位でブロックが作成されます。 ブロック機能により、各コマンドに対応する出力結果が明確になり、理解しやすくなるという利点があります。 また、ブロック内の出力だけを選んでコピーしたり、検索したりする操作も簡単に実施できます。 特定コマンドの実行結果をコピーする 出力結果内から該当箇所を検索する なるほど、Warpの補完とブロック機能は確かに嬉しいですね。日々のターミナル操作がぐっと快適になる感じ、良さそうです。使い込むほどに良さがわかるツールですね。 おわりに 今回ご紹介した3名も、それぞれの開発スタイルに合わせてツールを選び抜き、日々の作業を快適にする工夫を重ねていましたね。スピードや操作性、ミスの防止など、それぞれが注目したポイントからはエンジニアとしてのこだわりが垣間見えます。道具選びひとつ取っても、開発生産性と心地よさを追求する姿勢が伝わってきました! ファインディでは、さまざまなバックグラウンドを持つエンジニアが活躍しています。技術にこだわり、より良いものを追求する仲間とともに働いてみませんか? 現在、ファインディでは新しいメンバーを募集中です。 興味のある方は、ぜひこちらからチェックしてみてください!
こんにちは。ファインディでデータサイエンティストをしています、sasanoshouta( @Edyyyyon )です。 今回は、社内向けに提供している「Findy Team+の使い方について、ざっくばらんに質問できる」社内Botを作成した際の取り組みと、そこから得た学びをご紹介します。 どんなBot? Bot作成の背景 社内PdMに舞い込んでくるプロダクトに関する問い合わせに対する業務効率化 Difyを社内用に解放しており、既にいくつかのBotがSlackワークスペースに産み出されていた どのように作成したか 作成した結果どうだったか 今後について 最後に どんなBot? まず、今回作成したBotの概要を、代表的な2つのユースケースを通じてご紹介します。 1つ目の代表的なユースケースとして、Botの名前の通り、 「Team+の使い方」に関する質問 ができます。 ユースケース1では、Team+のDevOps分析画面の使い方についての質問に、Botがどのように回答するかを紹介します。 後述しますが、こちらのBotを作る上で基にした情報はサポートページの内容をコンテキストとしている為、URLを付与して返答する事のできる質問に対しては基本的にURL付きで返答を返してくれます。 ユースケース1: デプロイしたはずのプルリクがDevOps分析に表示されない原因を教えて下さい デプロイしたプルリクがDevOps分析に表示されない場合、以下のような点をご確認いただくと原因を特定しやすくなります。 1. スタッツ管理で設定している正規表現が実際のブランチ名やタグ名に合っていない 例として、「^(main|master|pri)$」のように設定している場合、ブランチ名が「pri/~」などスラッシュを含む形だとマッチせず、デプロイ対象に含まれません。 → 正規表現を見直し、「^(main|master|pri.*)$」など適切な設定に修正いただくと改善されるケースがあります。 2. デプロイ(プルリク作成/タグ付け)を行ったメンバーが対象チームに所属していない、または表示対象OFFになっている → プロダクト画面上で、チームメンバーの設定や表示設定が正しく行われているかどうかご確認ください。 3. モニタリングのラベル設定が適切でない → 特定の文字列を含んだラベルのみを集計対象にしている場合は、ラベル付けのルールを再度ご確認ください。 上記を確認いただいても解決しない場合は、以下のサポートページや社内向けドキュメントURLもあわせてご参照ください。 ■サポートページ ・正規表現を使ったデプロイ頻度の集計例など https://hogehoge ・DevOps分析の概要 https://fugafuga もしご不明点や追加のご質問があれば、遠慮なくお知らせください。 2つ目の代表的なユースケースとして、単純なTeam+の使い方だけでなく 「特定のシチュエーションを想定した時にTeam+をどのように活用すると良いのか?」 と言うようなやや抽象度が高い質問や、壁打ちのような質問にも対応することができます。 ユースケース2: プロダクトオーナー、プロダクトマネージャーの育成観点でどんな活用方法が有効ですか?また重視するといい指標があれば教えてください。 プロジェクトオーナーやプロダクトマネージャーの育成には、以下のようなTeam+活用方法が考えられます。 1. プロジェクト全体を俯瞰できる情報共有 - 各担当メンバーのタスクを整理し、進捗と課題を可視化することで、オーナーおよびマネージャーが的確に優先度を判断しながら学習・意思決定できる環境を整えます。 2. 定量的なデータにもとづく振り返り - スプリント終了時やマイルストーンごとに、タスク消化数・リードタイム・タスク完了率などのデータを確認しながら振り返りを実施することで、チームビルディングの視点を持ちつつ育成が行えます。 3. コラボレーションを通じたスキル学習 - 例えば、生産性向上やユーザーインタビューなど多面的な業務に関わる際、それぞれ詳しいメンバーからレビューやフィードバックをもらえる仕組みを作り、学習機会を増やすことで、オーナーやマネージャーへ幅広いスキルを身につけさせることができます。 重視するとよい指標の例: • タスク完了率・計画対実績の差 • リードタイム(アイデアからリリースまでの期間) • タスクごとの担当メンバーのフィードバック量・質 • プロダクト改善提案数(チームやユーザーからの新たなアイデア) • チームメンバー間のコミュニケーションログやレビューの頻度 これらを定期的にモニタリングし、学習サイクル(学習→実践→振り返り)を短く回すことで、プロジェクトオーナーやプロダクトマネージャーが実践的に成長しやすくなります。 (中略) これらをプロダクトオーナーやプロダクトマネージャーの活動に取り入れることで、顧客への価値提供や開発組織の生産性向上につながりやすくなります。 Bot作成の背景 このBotを作るに至った背景は、以下のような理由からです。 社内PdMに舞い込んでくるプロダクトに関する問い合わせに対する業務効率化 Difyを社内用に解放しており、既にいくつかのBotがSlackワークスペースに産み出されていた 社内PdMに舞い込んでくるプロダクトに関する問い合わせに対する業務効率化 Team+には、日夜プロダクトの進化を支えるPdMが6名在籍しています。Team+はユーザー様に多様な機能を提供している性質上、様々な観点での問い合わせが届きます。 平均すると、1人1日2件程度は問い合わせをいただいており、これに対応する必要があります。 Difyを社内用に解放しており、既にいくつかのBotがSlackワークスペースに産み出されていた カレー屋さんBotをはじめ、ファインディ内では幅広い職種が使うことのできる多様なBotが稼働しています。これらのBotは社内向けにホスティングされたDifyで作成されており、希望者はDify上で色々なテストをすることができるので、気軽にBotを始めとしたツール作成にチャレンジをすることができます。 こうした背景と、今年よりプロダクトマネジメント室に所属するデータサイエンティストが 生成AI活用を推進するミッションを背負うようになった 事もあり、「 勝手にBot作ってみよう。面白いものができそうだよね 」と言うチャレンジから産まれました。 (余談ですが、同じやってみた系の取り組みとして同チームメンバーのだーさん( @Dakuon_Findy )が書いてくれた記事もありますので、是非ご覧になってみてください。) tech.findy.co.jp どのように作成したか 作り方自体は至ってシンプルです。以下の情報をコンテキストにしたBotを、簡単なプロンプトを添えてDify上に作成するだけです。 Team+のサポートページコンテンツ100ページ分 社内に蓄積された問い合わせFAQ100問分 プロンプトも非常にシンプルで、実際にはたった 4行 で構成されています。 あなたはTeam+というプロダクトについて知り尽くしているプロフェッショナルです。 ユーザーからの問い合わせに対して相応しいものをコンテキスト内から回答してください。 コンテキストの情報には、プロダクトのサポートページURLまたは社内ドキュメントのURLが添付されているはずです。 コンテキストの情報を元に回答が行える場合は、参照元のURLをユーザーへ提供してください。 たったこれだけで出来てしまいました。 作成した結果どうだったか Bot作成前、PdMに届いていた問い合わせの件数が新機能に関する問い合わせを除き ほぼゼロ にすることができました。 また、このBotは社内で職種問わず広く使われているのですが、以下のような声を多数いただくことができました。 今後について このように社内でも好評を得られていることから、プロダクトへの実装を進めようと考えています。 実際にプロダクト上への実装を行う為に、コンテキストの強化・Ops構築やハルシネーション対策など、生成AIを活用した機能ならではの考慮すべき点が論点として残っていますので、これらを解消していく予定です。 最後に ファインディでは、データサイエンティストを始めとするデータアナリスト、データエンジニアなどのデータ系職種の採用を強化中です。 こちらの記事を読んでいただき、取り組みに興味を持っていただいた方は是非カジュアル面談でお話しましょう。 herp.careers また、こちらの記事は「 プロダクトマネージャーLT Night 」と言う弊社主催のイベント内でも登壇させていただいた内容になります。 その際の登壇資料も掲載しておきますので、ぜひご覧ください。 speakerdeck.com
こんにちは。 ファインディ株式会社 で Tech Lead をやらせてもらってる戸田です。 現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。 GitHub Copilotやチャットベースの開発支援ツールなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。 そのような状況の中で、「プロンプトの書き方がわからない」「プロンプトが思った動作をしてくれない」といった声をよく耳にします。 そこで今回は、生成AIに対するプロンプトの書き方のコツを紹介します。プロンプトの書き方のコツを知るだけで、生成AIの精度は驚くほど向上します。 それでは見ていきましょう! 長い文章よりも、簡潔な階層構造 記述の順序 対象と実行内容を絞る まとめ 参考文献 長い文章よりも、簡潔な階層構造 長文のプロンプトよりも、マークダウン記法などを利用して作成した階層構造のプロンプトの方が、生成AIの理解度が高い傾向にあるようです。 例えば、次のプロンプトを実行するとします。 buttonを作成して返すReact Componentを作ってください。propsにはテキストと無効フラグとクリック時のイベントを受け取ることが出来ます。テキストは必須です。無効フラグは任意で ` is ` のprefixを付けてデフォルト値は無しです。クリック時のイベントは任意です。 このプロンプトの実行結果は次のようになりました。 import React from 'react' ; type ButtonProps = { text : string ; isDisabled ?: boolean ; onClick ?: () => void ; } ; export const ButtonComponent: React.FC < ButtonProps > = ( { text , isDisabled = false , onClick } ) => { return ( < button type = "button" disabled = { isDisabled } onClick = { onClick } > { text } </ button > ); } ; プロンプトの意図が伝わっていない箇所が見られました。 isDisabled にデフォルト値が設定されてしまっています。 今度は、次のようにマークダウン記法の階層構造に書き直して実行してみます。 buttonを作成して返すReact Componentを作ってください。 - props - テキスト - 必須 - 無効フラグ - 任意 - ` is ` のprefixを付ける - デフォルト値は無し - クリック時のイベント - 任意 import React from 'react' ; type ButtonProps = { text : string ; isDisabled ?: boolean ; onClick ?: () => void ; } ; export const ButtonComponent: React.FC < ButtonProps > = ( { text , isDisabled , onClick } ) => { return ( < button type = "button" disabled = { isDisabled } onClick = { onClick } > { text } </ button > ); } ; isDisabled にデフォルト値が設定されなくなりました。この結果から、階層構造で書かれたプロンプトの生成結果の精度が上がることを理解できたかと思います。 このようにプロンプトに長文が書かれていた場合、文章を分解してマークダウン記法などの段落構造などに書き変えることをオススメします。 記述の順序 プロンプトやサンプルコードを記述する順番によって、生成AIが出力する内容が変わるケースがあります。 例えば、次のプロンプトを実行するとします。 次のコードを参考にして、UserSetting用のRepositoryクラスを作ってください ```typescript export interface UserSetting extends Entity { userId: string; notificationTarget: 'email' | 'slack'; } ``` ```typescript export interface IRepository<T> { findById(id: string): Promise<T | null>; } export interface Entity { id: string; createdAt: Date; updatedAt: Date; } ``` ```typescript export interface User extends Entity { name: string; email: string; role: 'admin' | 'user'; } export class UserRepository implements IRepository<User> { private users: User[] = []; async findById(id: string): Promise<User | null> { return this.users.find(user => user.id === id) || null; } async findAll(): Promise<User[]> { return [...this.users]; } async findByEmail(email: string): Promise<User | null> { return this.users.find(user => user.email === email) || null; } async findByRole(role: 'admin' | 'user'): Promise<User[]> { return this.users.filter(user => user.role === role); } } ``` ```typescript export interface Organization extends Entity { name: string; contractStatus: 'active' | 'inactive' | 'pending'; } export class OrganizationRepository implements IRepository<Organization> { private organizations: Organization[] = []; async findById(id: string): Promise<Organization | null> { return this.organizations.find(org => org.id === id) || null; } async isContract?(org: Organization): Promise<boolean> { return org.contractStatus === 'active'; } } ``` 実装内容が多い UserRepository を先に記述して、実装内容が少ない OrganizationRepository を後に記述しています。 このようなプロンプトの場合、先に書いた大きい方の内容が優先され、後に書いた小さい内容が無視されることがあります。 今回のケースだと、後述した OrganizationRepository の isContract? メソッドの例が無視される可能性があります。 実行結果は次のようになりました。 export class UserSettingRepository implements IRepository< UserSetting > { private userSettings : UserSetting [] = [] ; async findById ( id : string ): Promise < UserSetting | null > { return this .userSettings. find ( setting => setting. id === id) || null ; } async findAll (): Promise < UserSetting []> { return [ ... this .userSettings ] ; } async findByUserId ( userId : string ): Promise < UserSetting | null > { return this .userSettings. find ( setting => setting.userId === userId) || null ; } async findByNotificationTarget ( target : 'email' | 'slack' ): Promise < UserSetting []> { return this .userSettings. filter ( setting => setting.notificationTarget === target); } } 生成されたコードは全て this.userSettings に対するもののみで、 UserSetting のデータに対しての関数が生成されていないことがわかります。 次に、実行したプロンプトの OrganizationRepository に対する記述と UserSettingRepository に対する記述を入れ替えて実行してみます。 実行結果は次のようになりました。 export class UserSettingRepository implements IRepository< UserSetting > { private userSettings : UserSetting [] = [] ; async findById ( id : string ): Promise < UserSetting | null > { return this .userSettings. find ( setting => setting. id === id) || null ; } async findAll (): Promise < UserSetting []> { return [ ... this .userSettings ] ; } async findByUserId ( userId : string ): Promise < UserSetting | null > { return this .userSettings. find ( setting => setting.userId === userId) || null ; } async findByNotificationTarget ( target : 'email' | 'slack' ): Promise < UserSetting []> { return this .userSettings. filter ( setting => setting.notificationTarget === target); } async hasActiveNotification ( setting : UserSetting ): Promise < boolean > { return setting.notificationTarget !== null && setting.notificationTarget !== undefined ; } } UserSetting のデータを受け取ってチェックを行う hasActiveNotification が追加されていることがわかります。 この結果から、プロンプトの内容の記述順が生成結果に影響が出ることが理解できるかと思います。 対象と実行内容を絞る 生成AIからの結果の精度が低い場合、一度に多くの対象を選択しているケースがあります。 例えば、次のプロンプトを実行するとします。 次の変更を実行してください ## 変更対象 - 次の条件の全てに合致するファイル全て - ` app/lib/**/*.tsx ` に合致する - ` app/lib/**/*_spec.tsx ` に合致しない ## 変更内容 - ` window.alert ` を ` toast.error ` に変更する - ` toast ` は ` @findy/ui ` からimportする - propsで定義されているboolean型の項目の型定義を ` boolean ` から ` boolean | undefined ` に変更する 対象のファイル全てに対して複数の変更内容を実行するプロンプトになっています。 階層構造になっていますし、対象と内容を具体的に指定しており、特に問題ないように見えます。 しかし、このプロンプトを実行した場合、場合によっては実行結果の精度が落ちる可能性があります。 変更対象となっている app/lib 配下には多くのファイルが含まれている可能性があります。生成AIに対して一度の多くの対象を指定すると、対象を絞り込むことに対してリソースを使ってしまうため結果の精度が落ちてしまう傾向にあります。 このようなケースの場合、合致するフォルダの指定を app/lib/hoge/ 以下を見るようにプロンプトを変更して複数回の実行に分けると良いでしょう。 次の変更を実行してください ## 変更対象 - 次の条件の全てに合致するファイル全て - ` app/lib/hoge/**/*.tsx ` に合致する - ` app/lib/hoge/**/*_spec.tsx ` に合致しない ## 変更内容 - ` window.alert ` を ` toast.error ` に変更する - ` toast ` は ` @findy/ui ` からimportする - propsで定義されているboolean型の項目の型定義を ` boolean ` から ` boolean | undefined ` に変更する 変更対象のフォルダを指定して、一度に対象とするファイルを限定的にしました。このプロンプトを実行後、次は別のフォルダに対して同様のプロンプトを実行することで、生成AIの結果の精度が向上する可能性があります。 これで解決かと思いきや、更に改善の余地が見つかりました。変更内容が複数ある場合、プロンプトを分割して実行すると更に精度が上がります。今回のケースだと処理自体の変更と型定義の変更で異なる変更の指示が含まれています。 こういったケースの場合、プロンプトと実行を分割すると良いです。 次の変更を実行してください ## 変更対象 - 次の条件の全てに合致するファイル全て - ` app/lib/hoge/**/*.tsx ` に合致する - ` app/lib/hoge/**/*_spec.tsx ` に合致しない ## 変更内容 - ` window.alert ` を ` toast.error ` に変更する - ` toast ` は ` @findy/ui ` からimportする 次の変更を実行してください ## 変更対象 - 次の条件の全てに合致するファイル全て - ` app/lib/hoge/**/*.tsx ` に合致する - ` app/lib/hoge/**/*_spec.tsx ` に合致しない ## 変更内容 - propsで定義されているboolean型の項目の型定義を ` boolean ` から ` boolean | undefined ` に変更する 今回のケースのように複数の対象、内容の全てを一度に指定するのではなく、対象を絞って単一の変更内容を指示してプロンプトの実行そのものを分けることで、生成AIの結果の精度が向上する傾向にあります。 まとめ いかがでしたでしょうか? ちょっとした工夫や気遣いでプロンプトの精度が上がることがわかったかと思います。今後もトライアンドエラーを繰り返しながら、生成AIを活用するためのプロンプトの書き方を磨いていきましょう。 カスタムインストラクションでのプロンプトのコツも紹介しているので、是非こちらも参考にしてみてください。 tech.findy.co.jp 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers 参考文献 Best practices for using GitHub Copilot in VS Code
ファインディ株式会社 でフロントエンドのリードをしております新福( @puku0x )です。 弊社では、フロントエンド系のリポジトリの多くに Nx を採用しています。 Nxは、Nx Cloudと連携することでCIの高速化やコストパフォーマンスの改善が期待できます。 直近のアップデートにより、Nx Agents(Nx Cloudが提供するサービスの1つ)の新機能「Assignment rules」が正式リリースされました。 nx.dev これまで解決が難しかったタスク割り当ての最適化が、いよいよ解決できそうです👏 今回はAssignment rulesの解説と、実際に使った場合の効果をご紹介しようと思います。 Nx Agentsとは? DTEの革命「Assignment rules」 Nx Agentsの課題 Assignment rulesの登場 Assignment rulesの効果 まとめ Nx Agentsとは? Nxには、Distribute Task Execution(DTE)というCI高速化の機能があります。 nx.dev DTEでは、ビルドやテストといったタスクを複数のCIマシンで分散実行します。単純なタスク別の並列化と比較して、CIマシンの数を増やした際の高速化の効率が良いという特徴があります。 Nx Agentsの動作イメージ(Nx公式ドキュメントより抜粋) DTEのマネージドサービスが「Nx Agents」であり、ファインディでは昨年Nx Agentsを導入し、CIの高速化に成功しました。 tech.findy.co.jp DTEの革命「Assignment rules」 Nx Agentsの課題 Nx Agentsを用いたDTEでは、CIの負荷に応じて利用するマシンの台数やスペックを動的に切り替えられます。 次の設定は、スペックが中程度のマシン3台を最小構成とし、Nxが検知したCIの負荷(=コードの変更に影響のあるプロジェクトの割合)をもとに、高スペックなマシンを混ぜつつ最大8台でCIを動かす例です。 distribute-on : 00-changeset-s : 3 custom-linux-medium-js 01-changeset-m : 3 custom-linux-medium-js, 2 custom-linux-large-js 02-changeset-l : 4 custom-linux-medium-js, 4 custom-linux-large-js # changesetは任意の数を定義できます # マシンスペックは https://nx.dev/pricing#resource-classes 参照 この機能はCIの高速化に役立つ一方、課題もありました。 コア数の多いCPUを活用できない (Nxの --parallel オプションはCI全体に適用されてしまう) 負荷の高いタスクがスペックの低いマシンで実行されるのを防げない 後者は特にメモリ不足によるエラーを発生させやすく、CIの再実行にかかる時間やコスト・開発者体験の低下が問題となっていました。 Assignment rulesの登場 そんな時に現れたのが「Assignment rules」です。 nx.dev この機能により、メモリ不足の最大の要因であった「負荷の高いタスク」をスペックの高いマシンに割り当てるといったことが可能になりました。 また、タスク単位で parallelism を設定することで、マシン内の最大タスク並列実行数を調整でき、コア数の多いCPUを有効活用できます。 設定の例をお見せします。 assignment-rules : - projects : - app1 # アプリケーション用のタスクを想定 targets : - build* # build や build-storybook が該当 - typecheck run-on : - agent : custom-linux-medium-js parallelism : 1 # メモリが足りなくなるため並列実行させない - agent : custom-linux-large-js parallelism : 3 # 高スペックなマシンの場合は並列実行させる - targets : - build - test - typecheck run-on : - agent : custom-linux-medium-js parallelism : 2 - agent : custom-linux-large-js parallelism : 4 - targets : - cspell - lint - stylelint run-on : - agent : custom-linux-medium-js parallelism : 6 - agent : custom-linux-large-js parallelism : 8 Assignment rulesの効果 比較対象として、4コアマシン×8台の場合を設定します。 参考: Nx Agentsを導入してフロントエンドのCIを約40%高速化しました - Findy Tech Blog Assignment rulesによってCI時間やコストがどれほど変化したかを見てみましょう。 次の表は、キャッシュヒット無しで全てのタスクが実行された場合のCI時間および想定されるCIのコストを表しています。 Before After(Assignment rules有効) CI時間 10m 31s 10m 54s コスト $0.968 $0.726 CI時間はそれほど変わらず25%ほど安くなりました。 マシンスペックを抑えた場合のメモリ不足を心配する必要が無くなったのも嬉しいところです。 まとめ Assignment rulesにより、Nx Agentsを用いたCIはさらに最適化されます。 個人的に parallelism での並列数設定は待望の機能でした。 (あとは各タスクの優先度を設定できたら最高なのですがNrwlの皆さんいかがですか...!?) CIの高速化・安定化は、チームのパフォーマンスを考える上で重要になります。Nxの機能の活用により、強固な開発の仕組みを構築できるでしょう。 今後もNxに目が離せませんね👍 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディでソフトウェアエンジニアをしている栁沢( @nipe0324a )です。 ファインディでは、25年1月末から転職開発チームにDevinがジョインし、現在2ヶ月ほど一緒に働いています。 Devinのアウトプットとして、プルリクエストのマージ数は「 2ヶ月で197件 」、「 1日あたり平均5.2件 」とバリバリ開発をしてもらっています。 過去2ヶ月のDevinのマージ済みプルリク数の推移 Devinの2ヶ月の費用としても約30万($2,000 = 1,000 ACUs)ほどで、比較的簡易なタスクかつ一部サポートは必要でしたが十分なアウトプットを得ることができました。 今回の記事では、Devinに効果的にアウトプットを出してもらうために 試して上手くいったところ 上手くいかなかったところ 具体的なTips などをご紹介できればと思います。 ちなみに、 ファインディ内でのDevinの利用状況は現在検証中の段階であり、まだ「開発生産性が劇的にあがりました!」といえる状況ではありません。 Devinの1つの利用事例として楽しんでいただければ嬉しいです。 Devinはどこまでできる? 試行錯誤しつつDevinで上手くいったこと 基本的な開発ルールやプロセスに慣れてもらう 開発中に見つけた軽微なタスクを任せる 軽微なアラート対応やWant対応を任せる 小規模な開発を任せていく ここが辛いよDevin 自分でやったほうが早くない? GitHub CopilotやClineなどのほうが精度良くない? レビューに反応して余計な対応をしてしまう Devinと今後どう付き合っていくか? 最後に Devinはどこまでできる? Devinを2ヶ月試した結果として、「 ジュニアからミドルレベルのタスクを概ね任せられそう 」という感触を得ています。 難易度が易しい〜普通ぐらいのタスクにおいて、要件把握・設計から動作確認までできるポテンシャルを感じています。 現段階でのDevinに任せられるタスク難易度と業務範囲のポテンシャル しかし、Devinの開発結果に対する品質担保は人の手で行う必要があるため、業務範囲を全部任せるということはまだできません。 そのため、「生産性が大幅にあがりました!」というにはもう少し時間がかかると思っています。 生成AI界隈の動きは激しく、今後のDevinの精度向上やスピードアップのネタは尽きないので、楽しみに待ちながらDevinと引き続き働いていきたいです。 Devinの継続的なバージョンアップ(現時点では1.3) LLMの応答速度や性能向上(最近だとClaude 3.7 など) 拡散モデルなどによるコード生成の大幅な高速化 マルチエージェントの高度化 などなど 試行錯誤しつつDevinで上手くいったこと 小さなタスクから依頼して試行錯誤しながら、Devinがチームに少しずつ慣れていけるように進めていきました。 基本的な開発ルールやプロセスに慣れてもらう Devinの開発環境は、次のようになっています。 左側に「Devinと人がやりとりするチャット欄」、右側に「エディタ」、「ターミナル」、「ブラウザ」、「プランナー」があります。 Devinの開発環境 また、Devinへのタスク依頼は、「DevinのWeb画面」、「Slackメンション」、「VS Code」、「API」から実施できます。 チームにJOINしたばかりのDevinはチームの開発ルールを知らないので、まずはチームの開発ルールやプロセスに慣れてもらうことから始めました。 具体的には、システム概要、リンターや自動テストの実行方法、コミットメッセージやプルリクの書き方など覚えていく必要があります。 そのため、文言変更や軽微な改修を通して、開発ルールを教えていきました。 Devinは作業のやり取りを通してKnowledgeという形でルールを学んでいきます。 Knowledgeは特定の条件のときに参照されるプロンプトで、Knowledgeとして学んだルールを次の開発で活かしていくことができます。 次のようにKnowledgeとして「コミット作成」「PR作成」「テスト実行」といったチームのルールにそった開発ができるようになりました。(たまに無視することもありますが...) 実際に学んだDevinのKnowledgeの一部抜粋 開発中に見つけた軽微なタスクを任せる コードを読んでいるときや実装中に「不要な処理を見つけたから消したい」、「この処理をリファクタしたい」となることはないでしょうか? VSCodeのDevinプラグイン( https://github.com/CognitionAI/devin-extension )を使うことでこういった軽微な修正をスムーズに対応できます。 プラグインを使うことで、後でやろうから「 Devinに依頼 → PRをレビュー → DONE!! 」というスムーズな流れに変わります。 VSCodeプラグインでDevinに軽微なタスクを依頼 軽微なアラート対応やWant対応を任せる 「Mustじゃないけどこのタスクもやりたいよね」、「アラートが気になっているけど忙しいから手がつけられない」といった話はよくあります。 そういったときに、Devinに任せることで、効果的に対応できます。 いろいろ試した結果として、軽微なものであれば、 7~8割の精度でイシュー作成〜対応という一連の作業を任せること ができました。 Devinにタスクを任せる際の工夫として、過去のプルリクを参考にさせることで、Devinがよりスムーズにタスクを理解し、実行できるようにしています。 例えば、次のようなタスクは概ねDevinに任せることができました。 管理画面のフィルター追加・ラベル付与・簡易なバリデーション追加 Sentryで通知がきたN+1の調査と対応方針検討・対応実施 命名ルールの統一 ログ出力フォーマットの横展開 Rubocopの対応 などなど 最近では、技術系の改善イシューは私が書くよりDevinに書いてもらったほうがよいとさえ思っています。 具体例として、Devinに次のようなイシュー作成の依頼をしました。 @Devin AAA, BBB などで XXX に設定するフィールドの名前が yyy になっているのを zzz にしてください イシューテンプレを参考にして、よしなに記載してください すると、コードを調べて数分後にはGitHubイシューを作ってくれました。 「概要」、「現状の問題点」、「期待される効果」が簡潔に記載されていて素晴らしいと思いました。もちろん、ちょっと気になる部分は調査したり、手直しをすることもあります。 Devinが作成したイシュー(一部マスクしています) 小規模な開発を任せていく 試行錯誤中ですが、私が持っている小規模なタスクにおいては、 フロントエンド、バックエンド問わず基本的にDevinに開発をリードしてもらっています。 イメージとしては、watanyさんのスライド内の「コーディングにおける"自動運転"のレベル」におけるレベル3の段階です。(とてもわかりやすかったので勝手ながら引用させていただきました) 具体的には次のような流れでDevinと開発を進めています。 1️⃣ Devinがイシューから対応範囲やタスク洗い出しをして、イシューにコメントをする 2️⃣ 私がイシューのコメントを見ながらヌケモレがないか確認して、Devinが実行しやすいようにTODOリストを整理する 3️⃣ TODOリストのアイテムごとに1つずつDevinに任せる 直列で実施する必要がある場合は、作業するbaseブランチを伝えながら作業を進めてもらう 並列で実施できる場合は、アイテム単位で依頼することで、5並列ぐらいでDevinに開発を進めてもらう 4️⃣ DevinがPRを作成したら、私がコードレビューや動作確認などで品質チェックをして、他の開発者にレビューを依頼する このような流れでDevinと開発を並走しています。 現状のDevinのタスク精度は50~100点とわりとブレがあります。そのため、レビューコメントでDevinに修正をしてもらったり、作業を引き継いで残りの開発や修正をするということもよくあるため、まだまだ試行錯誤な段階です。 ここが辛いよDevin ここまででDevinと一緒に働きながら上手くいったところをご紹介しましたが、使っている中でまだまだこれからかなという部分にもふれていきます。 自分でやったほうが早くない? シニアな方からよく聞きますが、Devinに任せるよりも自分でやったほうが早いので、自分でやってしまうことはよくあります。 次のグラフのように、Devinもスピードは早くなっているのですが、現時点では簡単なタスクでも5~10分ぐらいはかかっている印象です。 devin is now ~2.1x faster – and keeps getting faster every day. every night for the last 5 months, we ran devin on the same set of SWE tasks (a private set of tasks inspired by customer use cases). we've shipped a lot since and devin got 10-40% faster every month pic.twitter.com/CGxeXbTo7g — Silas Alberti (@silasalberti) 2025年2月27日 また、精度のブレの問題もあります。100点のPRを作るときもあれば、概ねいいんだけどちょっと気になるなという70~80点ぐらいのPRもあったり、全然だめじゃんという30点ぐらいのPRもあります。 この精度のブレがあるなら、「自分でやったほうがトータル早くない?」という考えになることはとてもわかります。 GitHub CopilotやClineなどのほうが精度良くない? 弊社の多くのエンジニアは、活用度は人それぞれですがGitHub CopilotやClineなどのAIアシスタントやAIエージェントを利用しながら開発をしています。 Devinに頼むよりも、Copilot Editsで編集するほうが精度の高い結果になることもあります。また、ローカルで動作するため、その後のプロンプトのやりとりやコードの修正もしやすいです。 やりとりの手間やコストを考えると、CopilotやClineなどを使ってローカルで開発するほうが良いと考えることもとてもわかります。 レビューに反応して余計な対応をしてしまう Devinは自律型AIエンジニアということもあり、Devinが作成したPRに対してコメントをすると、コメントの対応をしてくれます。また、CI結果をチェックしてCIを通るように対応をします。 Devinがコメントに反応すると👀がつき、対応すると🎉 便利な機能ではありますが、Devinが作成したPRでは、デフォルトでレビューコメントへの反応が有効になっています。 「(aside)」をつけたコメントをするとDevinは反応しないのですが、よく間違えて「(aside)」をつけ忘れることで、レビュー時に不要な対応をされることもたまにあります。 Devinと今後どう付き合っていくか? GitHub Copilot や Clineなどと併用しつつ、引き続きDevinを試していく予定です。 生成AI界隈は動きが早いので、今後どのプレイヤーが登場しどのように進化していくのかわかりません。そのため、Devinを試しつつ、他のAIエージェントやAIアシスタントとも併用しながら、開発生産性を高めていきたいと考えています。 また、個人的には、既存の仕組みを効率化するにとどまらず、 AIエージェントと人が協業することで良い意味で開発体験をリビルドしていきたい と考えています。 その結果として、一人のエンジニアがより大きな価値をだせるようになると、いろいろ面白くなってくると思っています。 現に0->1の世界では、生成AIやSaaSサービスをフル活用した開発スタイルで、今までよりも質の高いMVPをどんどん作って検証しているという話もちらほら聞き始めています。 最後に ファインディでは生成AIやAIエージェントを前向きに試して開発生産性を高めることで、ユーザーにより価値を届けることを推進しています。 こうした新しい技術の利活用に興味がある方は、ぜひカジュアル面談にお越しくださいませ。 herp.careers
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。 GitHub Copilotやチャットベースの開発支援ツールなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。 しかし、これらのツールを導入すれば即座に開発生産性が向上する、というわけではありません。 生成AIを効果的に活用し、真の意味で開発生産性を向上させるためには、適切な準備と理解が不可欠です。 今回は生成AIと既存コードの関係性を掘り下げ、開発生産性を最大化するための具体的な準備について詳しく解説します。 単なるツールとしての導入にとどまらず、組織全体で生成AIと協調して働くための基盤づくりに焦点を当てています。 それでは見ていきましょう! 生成AI活用のための準備 既存コードの最適化 不要なコードの削除 統一されたコーディング規約 コードの設計の一意性 ドキュメンテーションの充実 docコメントやAPIドキュメント カスタムインストラクション プロンプトを記録 継続的な改善を可能にする開発文化 テストコードとCI/CDの整備 Pull requestの粒度 まとめ 参考文献 生成AI活用のための準備 生成AIの効果は既存のコードベースの品質と密接に関連していると言われています。( 1、 2) 生成AIは与えられたコンテキストを基に学習して提案を行います。既存のコードベースが高品質である場合、生成AIはより精度が高い提案をすることができるはずです。 そのため、生成AIを効果的に活用し、真の意味で開発生産性を向上させるためには、適切な準備が不可欠です。 既存コードの最適化 不要なコードの削除 不要なコードが残っていると、生成AIはそれらのコードも学習してしまい、不適切な提案をする可能性があります。 例として次のような実装コードがあるとします。 export const getNotEmptyStrList = ( data : string []): string [] => { const result = [] ; for ( let i = 0 ; i < data. length; i++) { if (data[i]. length > 0 ) { result. push (data[i].trim()); } } return result; } ; export const filterStrList = ( data : string []): string [] => { return data. filter (( item ) => item. length > 0 ). map (( item ) => item. trim ()); } ; 気付いた読者の方もいると思いますが、この例は関数名と実装コードが違うだけで、やっていることは同じです。 getNotEmptyStrList の方は冗長なコードとなっています。 このコードの状態で次のようなプロンプトを実行したとします。 このコードを参考にして、数値配列から0より大きい値だけを抽出して、その値を2倍にする関数を作成してください。 このプロンプトを複数回実行すると、生成されるコードにバラツキが出てしまいました。 export const filterAndDoubleNumbers = ( data : number []): number [] => { const result = [] ; for ( let i = 0 ; i < data. length; i++) { if (data[i] > 0 ) { result. push (data[i] * 2 ); } } return result; } ; export const filterAndDoubleNumList = ( data : number []): number [] => { return data. filter (( item ) => item > 0 ). map (( item ) => item * 2 ); } ; このように、不要なコードが残っていると生成AIからの提案の精度が低下してしまいます。 このようなケースの場合、不要なコードを削除して既存コードを最適化することで、生成AIの理解度を向上させることができます。 実装コードから不要なコードを削除して次のように変更します。 export const filterStrList = ( data : string []): string [] => { return data. filter (( item ) => item. length > 0 ). map (( item ) => item. trim ()); } ; filterとmapを使ったモダンなコードのみに変更しました。この状態で同じプロンプトを複数回実行すると、生成されるコードが一貫性を持つようになります。 export const filterAndDoubleNumList = ( data : number []): number [] => { return data. filter (( item ) => item > 0 ). map (( item ) => item * 2 ); } ; 統一されたコーディング規約 Google の Style Guides のようなスタイルガイドを採用し、統一されたコーディング規約に従ったコードベースは、生成AIの理解度を向上させます。 例えば、命名規則が一貫していれば、生成AIはその規則を学習し、同様の命名パターンを提案できます。 例として次のような実装コードがあるとします。 import { useState } from 'react' ; type SelectOption = { label : string ; value : number ; } ; export const useTestHooks = () => { const [ select_option , setSelectOption ] = useState< SelectOption >(); const [ submitValue , setSubmitValue ] = useState< number >(); const handleSelectOption = ( option : SelectOption ) => { setSelectOption(option); } ; const HandleClickSubmitButton = () => { if (!select_option) return ; setSubmitValue(select_option.value); } ; return { handleSelectOption , HandleClickSubmitButton , } as const ; } ; 各種名称にキャメルケースとスネークケースなどが混在してしまっています。 このコードの状態で次のようなプロンプトを実行するとします。 select_option.valueを3倍にしてsubmitValueにsetするハンドラー関数を追加してください プロンプトを複数回実行すると、実行されるたびに生成されるコードにバラツキが出てしまいました。 const HandleClickTripleSubmitButton = () => { if (!select_option) return ; setSubmitValue(select_option.value * 3 ); } ; const handleClickTripleSubmitButton = () => { if (!select_option) return ; setSubmitValue(select_option.value * 3 ); } ; そこで、命名規約をキャメルケースに統一するように変更します。 import { useState } from 'react' ; type SelectOption = { label : string ; value : number ; } ; export const useTestHooks = () => { const [ selectOption , setSelectOption ] = useState< SelectOption >(); const [ submitValue , setSubmitValue ] = useState< number >(); const handleSelectOption = ( option : SelectOption ) => { setSelectOption(option); } ; const handleClickSubmitButton = () => { if (!selectOption) return ; setSubmitValue(selectOption.value); } ; return { handleSelectOption , handleClickSubmitButton , } as const ; } ; この状態で同じプロンプトを複数回実行すると、生成されるコードが一貫性を持つようになります。 const handleClickTripleSubmitButton = () => { if (!selectOption) return ; setSubmitValue(selectOption.value * 3 ); } ; 単純な例ですが、既存コードに統一性がない場合、生成AIがどの情報を正とするかが曖昧になり、提案の精度が低下してしまう一例でした。 このように、コーディングスタイルの一貫性は生成AIの提案の精度や一貫性に大きく影響します。 コードの設計の一意性 アーキテクチャやデザインパターンが一貫している場合、生成AIはそのパターンを学習し、より適切な提案をすることができます。 例えば、次のようなコードがあるとします。 export interface IRepository < T > { findAll (): Promise < T []> ; findById ( id : string ): Promise < T | null > ; } export interface Entity { id : string ; createdAt : Date ; updatedAt : Date ; } export interface User extends Entity { name : string ; email : string ; role : 'admin' | 'user' ; } export class UserRepository implements IRepository< User > { private users : User [] = [] ; async findAll (): Promise < User []> { return [ ... this .users ] ; } async findById ( id : string ): Promise < User | null > { return this .users. find ( user => user. id === id) || null ; } } interfaceとclassを使ってリポジトリパターンを実装しています。このコードが存在する状態で、次のようなコードを追加します。 export interface Order extends Entity { userId : string ; products : Array < { productId : string ; quantity : number ; } > ; totalAmount : number ; status : 'pending' | 'completed' | 'cancelled' ; } そして、追加したinterface用のRepositoryクラスを生成してもらうためにプロンプトを実行します。 Order用のRepositoryクラスを生成してください すると、生成されたコードは次のようになります。 export class OrderRepository implements IRepository< Order > { private orders : Order [] = [] ; async findAll (): Promise < Order []> { return [ ... this .orders ] ; } async findById ( id : string ): Promise < Order | null > { return this .orders. find ( order => order. id === id) || null ; } } 既存コードの UserRepository を参考にして、 OrderRepository が生成されたことがわかると思います。 このようにデザインパターンや型定義などが一貫していると、生成AIはそのパターンを学習し、より精度の高いコード生成できます。 ドキュメンテーションの充実 生成AIが読み込む内容は実装コードだけでなく、READMEなどのドキュメントも含まれます。そのため、ドキュメントの充実は生成AIの学習に少なからず影響します。 生成AI時代のドキュメンテーション能力は、更に求められるようになってくるでしょう。 docコメントやAPIドキュメント 関数やクラスに記載されるdocコメントは、生成AIがコードの意図を理解する上で重要な情報源となります。 docコメントは実装コードを読み込んで生成AIが自動生成することも可能ですが、実装コードの内容によっては必ずしも十分な情報を提供できない場合があります。このようなケースの場合、まずdocコメントを生成AIに自動生成してもらい、意図や仕様を補足することで、生成AIの理解度を向上させることができます。 また、REST APIの仕様を記述したSwagger/OpenAPIドキュメントやGraphQLのスキーマ定義などは、生成AIがAPIの仕様やクエリを理解する上で重要な情報源となります。 生成AIがAPIの仕様を理解することでモックデータの自動生成などが容易になり、開発効率を向上させることができます。 カスタムインストラクション 生成AI向けのカスタムインストラクションを調整するのも良いでしょう。 カスタムインストラクションは生成AIに対して、特定のコンテキストやルールを教えるための手段です。これを調整して育てることで、生成されるコードや提案内容の精度を向上させることができます。 プロジェクトやリポジトリのコーディング規約やドメイン知識などをカスタムインストラクションに記載しておくことで、それらの内容を生成AIに読み込ませ、提案内容に反映できます。 カスタムインストラクションについては別の記事でも紹介していますので、是非参考にしてみてください。 tech.findy.co.jp tech.findy.co.jp プロンプトを記録 実際に利用したプロンプトを記録して残しておくことも重要です。コード内のコメントや各種ドキュメント、Pull requestなどに記載しておくとよいでしょう。 実際に求めている変更内容とプロンプトの内容が一致しているのかをレビューする必要があるからです。 また、そのコードがどんなプロンプトを利用して生成されたのかが残っていなかった場合、機能追加や修正時にまた一からプロンプトを作らないといけなくなってしまいます。 実装コードを変更したいならプロンプトを変更する。といったパラダイムシフトが来るのはそう遠くない未来なのかもしれません。 最近だと自分は、プロンプトのテンプレートもドキュメントに残すようにしています。状況と変更内容が近いのであれば、プロンプトをコピーして調整して実行するだけで、同じような作業を横展開することが簡単になるからです。 継続的な改善を可能にする開発文化 ここまで説明した内容を実践して維持するためには、継続的な改善や技術的負債の解消を可能にする開発文化が不可欠です。 なぜならば、ドキュメントの整備や既存コードの最適化などは一度だけで終わるものではなく、継続的な取り組みが必要だからです。 テストコードとCI/CDの整備 このような開発文化を作る上でテストコードの整備は特に重要な要素と言えます。一定以上のテストコードが存在することで、継続的な変更に対する心理的安全性を確保できます。 またCI/CDを整備し、自動化されたビルド、テスト、デプロイを行うことで、開発プロセスを効率化し、品質を維持できます。 テストコードとCI/CDの整備については別の記事でも紹介していますので、是非参考にしてみてください。 tech.findy.co.jp Pull requestの粒度 適切な粒度のPull requestを維持することも重要です。Pull requestの粒度が適切であれば、生成AIに対して「これを実現させるためにはこの修正が必要」とピンポイントで学習させることができます。 tech.findy.co.jp Pull requestの粒度はプロンプトの精度にも繋がってきます。 生成AIで実行するプロンプトは可能な限り単一責務の方が実行結果の精度が高い傾向にあります。1行のプロンプトで全ての内容を実行するのではなく、プロンプトを分解して階層構造にしたり、対話形式で実行することで、より精度の高い実行結果を実現できます。(*3) 日頃からタスク分解やPull requestの粒度を意識した開発文化にすることで、プロンプトを作成する際にも応用できるのです。 tech.findy.co.jp これらの準備ステップを着実に実行することで、生成AIをより効果的に活用するための基盤を整えることができます。 まとめ いかがでしたでしょうか? 生成AI活用のための準備ステップは、単なる事前作業ではなく、生成AIの効果を最大化するための基盤づくりです。 生成AIの活用は目的ではなく、より良いソフトウェアをより効率的に開発するための手段です。 堅固な開発基盤と文化こそが、生成AIの真の力を引き出す鍵となります。 小さな一歩から、大きな変革が始まります。技術の進化に伴い、私たち開発者の役割も進化していきます。 この変化を恐れるのではなく、積極的に受け入れ、活用していくことで、私たちはソフトウェア開発の新たな時代を切り開いていこうと考えています。その未来に向けて、今日から準備を始めましょう。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers 参考文献 Best practices for using GitHub Copilot in VS Code #Provide context to Copilot Copilot works best when it has sufficient context to know what you're doing and what you want help with. Best practices for using GitHub Copilot in VS Code #Be consistent and keep the quality bar high Copilot is going to latch on to your code to generate suggestions that follow the existing pattern, so the adage "garbage in, garbage out" applies. Best practices for using GitHub Copilot in VS Code #Be specific and keep it simple When you ask Copilot to do something, be specific in your ask and break down a large task into separate, smaller tasks.
こんにちは。Findy Tech Blog編集長の高橋(@Taka_bow)です。 この記事は 自慢の作業環境を大公開シリーズ の第6弾になります。今回も、3名のエンジニアの作業環境を紹介します! トップバッターは安達さんです! ■ 安達さん / プロダクト開発部 / SRE ■ みなさんこんにちは。SREチームの安達( @adachin0817 )です。 僕は週2出社とリモートワークでハイブリッドな働き方をしていますが、エンジニアガジェッターでもあります。それでは作業環境を紹介していきたいと思います。 デスク周り全体 マシン 個人 Mac mini(M2 Pro) Mac mini(Intel i7) MacBook Air(M1) iPad iPad mini 会社 MacBook Pro(M3 Max) 機材構成は上記のようになっています。なぜ個人でMacBook AirもあるのにMac miniも持っているの?とよく質問されますが、Appleオタクという理由だけではありません。ラップトップのバッテリーは数年で劣化してしまうため、交換時にコストがかかってしまいます。また、MacBook Proはコストも高いので、コストパフォーマンスが良いMac miniをメインとして利用しており、外で開発する場合はMacBook Airを利用しております。Intel Mac miniの利用用途としては、BootCampでWindows 11が動作するようにしています。仕事柄SREといったことから、環境依存が発生しないように個人の開発環境周りの検証を日々行っています。 デスク/ディスクシェルフ 最近、デスク周りを改善し、 COFO Desk Premium を購入しました。椅子も COFO Chair Premium でCOFOに統一をしています。昇降デスクは裏面が全てマグネットになっているため、配線をきれいに仕上げられるのと、 COFOマグネット用オリジナルアイテム も販売されています。モニター台である Fengeのデスクシェルフ を置くことで、見えない配線を隠すことができます。 ディスプレイ ディスプレイは BenQウルトラワイドモニター38インチ/EW3880R を利用しています。BenQといえば台湾を拠点とする電気製品メーカーですが、ディスプレイの鮮やかさはダントツだと思います。また、このディスプレイはtreVoloスピーカーが内蔵されており、高音質と迫力ある低音で、ゲームや映画にも最適です。 また、 モバイルディスプレイ/kksmart 16インチ(WQHD) も活用しています。このディスプレイにはSlackやChatGPTを表示させて素早く返信や検索ができる環境を構築しました。以前はワイドモニターのみで作業していましたが、コマンドタブでの頻繁な切り替えに煩わしさを感じていました。結果として、低価格な小型ディスプレイの導入により生産性向上を実感できています。 このように素晴らしい理想のデスク環境が整いましたが、次はこだわっているアイテムの紹介をしていきましょう。 キーボード/トラックボール キーボードは HHKB Studio でトラックボールは Kensington を利用しています。トラックボールはマウスと違って、手首が痛くならず、手全体で操作できるので、非常に便利です。キーボードの前に謎のスイッチングが見えていると思いますが、こちらについて解説していきたいと思います。 こだわりのアイテム紹介 自作充電マジェットスタンド iPhoneの充電を整えるにあたり、色々と考慮する必要がありました。元々は RORRY ワイヤレス充電器 を利用していましたが、スマホ、Apple Watch、イヤフォンと1つに充電ができるものの、アイテムが大きすぎるといったデメリットがありました。また、Magsafeで充電すると、取り外す際に必ず両手を使わなければならないのも不便でした。 こうした教訓から、リスペクトしている YouTuberツヨシ さんの動画を参考にして、デスクシェルフの裏に ステンレスカット版 を両面テープで固定し、 マジェットスタンド の上に THREEKEY Qi2 充電器 を磁石で貼り付けています。この自作マジェットスタンドにより、充電中のiPhoneを片手で簡単に取り外せるようになりました。 同じ仕組みを応用し、モバイルディスプレイもマジェットスタンドでデスクシェルフに取り付けているので、角度調整が非常に便利になりました。 分配器について このスイッチングは個人PCと会社PCを瞬時に切り替える 分配器 です。以前まで、会社PCを切り替える場合、毎回HDMIやUSB-Cを抜き差ししていましたが、この分配器があることで、簡単に行き来できるようになりました。 リモートワークでの工夫 リモートワークを快適にするには、昇降デスクは欠かせません。自分は午後になると集中力の低下を感じるため、スタンディングデスクを活用し、MTGやペアプロをより楽しくこなせるようにしています。(ちなみにスタンディングしていると愛犬がデスク下で眠りにつきます) また、基本的なことですが、「仕事したい」と思えるデスク環境を整えることが何より重要です。自分はガジェット好きなので、日々改善点を探しながら環境をアップデートするのも、リモートワークの楽しみのひとつになっています。 今後導入したいもの 購入しつくした、と言っても過言ではないですが、しいてあげるならiPadのアームです。それ以外は妻もリモートワークをしているため、昇降デスクを購入しようか検討しております。機会があれば個人ブログでも紹介したいと思います。 デスク環境はかなり費用がかかってしまいましたが、以上が僕の作業環境となります。もっとアイテムを紹介したいですが、長くなってしまうので、個人的に色々と聞きたい!という方にはぜひ、ファインディのイベントやXでのDM等お待ちしております!ありがとうございました! 安達さんは、Mac miniを活用し、コストパフォーマンスと環境依存を考慮した構成が印象的でした。また、自作の充電スタンドや分配器の導入による快適な環境構築が参考になりますね! では、次は土屋さん行ってみましょう! ■ 土屋さん / CTO室 / データエンジニア ■ データエンジニアの 土屋 (しゅんそく) です。私の所属するデータソリューションチームはFindyのデータ基盤の設計、開発、運用を担当しています。 所属チームにあわせて、週2-3日のペースで出社しています。土日は家で作業していることが多いため、作業環境は私の最も大きな関心事の1つです。 デスク周り全体 机は作業スペースが広く、昇降機能が付いている Flexispot E7 Pro を利用しています。天板はカーブ型にしています。手の届く範囲が増えるのでお勧めです。 デスクのモニターの高さを調節可能にしています。自分の目線とモニターの良く見る位置が同じ高さになっていてほしいので高さ調整ができるアームにしています。 モニター JAPAN NEXT WUXGA のモニターがお気に入りです。特徴は縦横比です。 コードを読むときにそれなりの高さがほしいですが、単純にサイズを大きくすると横が長くなり首を振る必要がでてきます。このモニターは24インチ、解像度1980x1200と縦長でコードが読みやすいです。 キーボード Mistel Barocco MD600 Alpha RGB BT Black(英語配列) を利用しています。キーボードは一日触るので体に負担の掛けないことを意識して選んでいます。肩を痛めない左右分離型でタッチが軽めのキーボードを探していたところ、Mistel Baroccoに行き着きました。 キートップは趣味の麻雀仕様にしています。Vimを使っているのでhjklが東南西北です。ただ、jに突起がついていないため若干使いにくいです。 マウス Kensington ケンジントン Slimblade Pro を利用しています。通常のトラックボールマウスとは異なり、指への負担を軽減する特徴を持っています。ブラウザなどの戻るボタンは右前に配置されており、この位置は非常に使いやすく感じられます。 こだわりのアイテム紹介 iPad Air iPad Air 。ほぼ GoodNotes と Kindle , ラムダノート で利用しています。 GoodNotesはメモアプリで、メモや頭の整理として紙に書く感覚でノートを書けます。 (※ 画像は業務と無関係です) エンジニアは油断すると物理本により家が埋もれます。KindleやLambda Noteを通じた電子書籍購入のおかげで床抜けの心配から開放されました。ただ、積読が増えてしまうのが難点といえます。 リモートワークでの工夫 家具、色彩 家の中で作業することが多いので、自分の趣味にあったものにこだわっています。具体的にはカフェやホテルのような見た目が好みなので、書斎は木製の素材と観葉植物をふんだんに利用しています。 作業するにあたって、光は重要です。白や青色の光を浴びていると目がすぐに疲れてしまいます。ブラウザやターミナルをダークモードにしたり、間接照明を黄色みがかったオレンジのライトにするなど工夫をしています。 冷凍庫 リモートワーカーは家に籠りがちであるため、食料庫も重要です。大量の冷凍食品、生鮮食品を保存するために冷蔵庫とは別の冷凍庫を購入しています。 今後導入したいもの 先日購入した サンワサプライ ワイヤレスカメラ CMS-V65BK のマイク機能が微妙なため、独立したマイクの購入を検討しています。 土屋さんは、縦長のモニターや分離型キーボードの活用など、身体負担を減らすことを重視した構成が特徴的ですね。さらに、家具、色彩の雰囲気作りが素敵です! では、最後になりますが秋田さんお願いします! ■ 秋田さん / プロダクト開発部 / フロントエンド ■ Findy(転職サービス)のフロントエンド開発を担当しています、秋田と申します。週1〜2日で出社しており、リモートワークが多めのため作業環境は落ち着く雰囲気を大事にしています。 デスク周り全体 デスクは FlexiSpot の 昇降式デスクにDIYで作成した天板を載せており、チェアは Ergohuman のものを利用しています。座り作業・立ち作業のどちらでも快適に使えるので助かっています。 また、ファインディではオフィスでErgohumanのチェアが利用できるので出社時とリモートで同じチェアが利用できるのは嬉しいポイントです。 マシン 会社貸与 16インチMacBook Pro 私物 12.9インチ iPad Pro ディスプレイ ディスプレイはPHILIPSの27インチ4Kディスプレイをメインに利用しつつ、MacBook Pro16インチのディスプレイをサブに利用して開発しています。また、時々iPad Proでメモを取りたい場合があるので手元に置いてあります。 キーボード/マウス HHKB Professional HYBRID Type-S にキーキャップをサードパーティのものでカスタマイズしています。メカっぽい色味が気に入ってます。 マウスについてはMacのTrackpadの操作感が好きなので、 Magic Trackpad のみで作業をしています。 こだわりのアイテム紹介 ERGOTRON モニターアーム モニターアームにはERGOTRONのアームを利用しています。 昇降デスクのおかげで立ったり座ったりすることが多いためディスプレイの位置は頻繁に調整したくなるのですが、 このアームはディスプレイやMacBookの位置を自由自在にスムーズな移動ができるためとても便利です。またシルバーな見た目も気に入っています。 belkin MagSafe 3-in-1 充電器 ガジェットの充電器は belkinのワイヤレス充電器 を利用しています。私はiPhone、Apple Watch、AirPodsなど小型ガジェットにはApple製品を多く使用しているため、この充電器1台で気軽にワイヤレス充電できる点に満足しています。Apple製品を複数お持ちの方におすすめです。 リモートワークでの工夫 作業に集中するときは音楽を流すことが多いので、iPhoneからBluetoothで TEAC のUSB DACアンプに接続し、 DALI のスピーカーで出力しています。 スピーカーは天井近くに設置しており音が自然に広がるため、部屋のどこにいても違和感なく聴くことができます。スピーカーの位置を工夫するだけで音の響き方が変わり、高音から低音までバランスよく鳴るので気に入ってます。長時間聴いていても耳が疲れにくく気分転換にもなるので、仕事の効率を上げるためにも良い環境を整えたいと考えています。 今後導入したいもの HHKBのキーボードも気に入っているのですが自作の分割キーボードに興味があり、HHKBの配列に似た Choco60 や 7sPro あたりで組み立てを検討しています。チーム内にも詳しいメンバーがいるので教えてもらいながら導入したいと考えています。 秋田さんは、DIY(!)で作成した天板を活用した昇降デスクや、オーディオ環境への強いこだわりが印象的でした。スピーカー配置と音響効果の工夫は、作業空間をより快適にするヒントとなりそうですね! おわりに 3名それぞれが、自分の働き方に合わせて最適な作業環境を整えており、エンジニアがどのように生産性を高めているかが垣間見える内容でした。デスクやデバイスの選定だけでなく、作業の効率化や快適さを追求する工夫が随所に見られ仕事に対する熱意が感じられます。 ファインディでは、さまざまなバックグラウンドを持つエンジニアが活躍しています。技術にこだわり、より良いものを追求する仲間とともに働いてみませんか? 現在、ファインディでは新しいメンバーを募集中です。 興味のある方は、ぜひこちらからチェックしてみてください!
このブログの内容をポッドキャストでも配信中! open.spotify.com こんにちは。ソフトウェアプロセス改善コーチでFindy Tech Blog編集長の高橋( @Taka_bow )です。 2024 DORA Reportについての連載も、今回で最終回です。 今回はDORA Reportの中から、 前回取り上げたAI関連以外で 個人的に気になったトピックをまとめました。 本記事ではv.2024.3をベースに解説します。なお、執筆時点で日本語版はまだリリースされていませんでした。また、 正誤表 を確認しなるべく最新の情報を参照するように努めました。 DORA Reportのライセンスは次の通りです。 “Accelerate State of DevOps 2024” by Google LLC is licensed under [CC BY-NC-SA 4.0]( https://creativecommons.org/licenses/by-nc-sa/4.0/ ) なお、DORA Report原文は Google Cloudのこちらのページ からダウンロードできるので、ぜひ一次情報に触れてみてください。 このブログの内容をポッドキャストでも配信中! Software delivery performance 本当に優れたチームとは「Eliteな改善」を実現するチーム 「手戻り率」の登場 Reliability(信頼性)は引退? Platform engineering プラットフォームエンジニアリングとパフォーマンスの関係 プラットフォームエンジニアリングがもたらす可能性 意外な落とし穴 Developer experience ユーザー中心の開発がDevExを向上させる 質の高いドキュメントがユーザー中心開発を増幅する 常に変わる優先事項の危険性 おわりに Software delivery performance DORAは、ソフトウェアデリバリープロセスのアウトカムを効果的に測定する手法として、4つの重要指標(Four Keys)と呼ばれるソフトウェアデリバリーの指標を継続的に検証してきたことは御存知の通りです。 2024 DORA Reportの結論としては、 最高パフォーマンスのチームは、Four Keys(変更リードタイム、デプロイ頻度、デプロイ失敗時の復旧までの時間、変更失敗率)すべてで優れた結果を出している一方、最低パフォーマンスのチームはこれらFour Keysすべてで低い結果となっています。 パフォーマンスの高低は業種に関係なく、あらゆる産業分野のチームが各パフォーマンスグループに存在しています。(p6) と、いつも通りです。 しかし、パフォーマンスレベル表が提示される直前、次のような注目すべき一文が添えられていました。 私たちは、前年のクラスター分析との 一貫性を保つため 、 元の4つの ソフトウェアデリバリー指標に対してクラスター分析を実施しました。(p13) 「一貫性を保つため」とはどういう事でしょうか? 実は、今回のレポートでDORAはソフトウェアデリバリーパフォーマンスの測定指標を再定義しています。 DORAはこの変更を 「Evolving(進化)」 と表現しています。 本当に優れたチームとは「Eliteな改善」を実現するチーム まず、この新しい指標の説明に入る前にFour Keysベースの、いつものクラスター分析の結果を見てみましょう。 【2024 パフォーマンス指標】 指標 Elite High Medium Low Deployment frequency オンデマンド(1日複数回) 1日1回以上週1回未満 週1回以上月1回未満 月1回以上6ヶ月に1回未満 Change lead time 1日未満 1日以上週1回未満 1週間以上1ヶ月未満 1ヶ月以上6ヶ月未満 Failed deployment recovery time 1時間未満 1日未満 1日未満 1週間以上1ヶ月未満 Change failure rate 5% 20% 10% 40% 全回答者に占める割合 19%(18-20%) 22%(21-23%) 35%(33-36%) 25%(23-26%) *89% 信頼区間 集計では、 Change failure rate (変更失敗率)に特異な傾向 が見られます。 この特徴は次のグラフを見るとより明確です。 Figure 1. Software delivery performance levels なんと、 変更失敗率のHighレベルとMediumレベルが逆転 しているのです。 私は、この決定を DORAからの強いメッセージ と捉えました。それは、次の解説に表れています。 私たちは、より高速なチームを「High パフォーマー」、より遅いが安定しているチームを「Medium パフォーマー」と呼ぶことにしました。この決定は、これらのパフォーマンスレベルを使用する際の 潜在的な落とし穴の一つを浮き彫り にしています。つまり、特定のパフォーマンスレベルに到達することよりも、改善を続けることの方がチームにとって重要であるという点です。 本当に優れたチームとは、「Eliteなパフォーマンス」を達成するチームではなく、「Eliteな改善(elite improvement)」を実現するチームなのです。 (p14) 「パフォーマンスレベルを上げましょう」「Eliteを目指しましょう」といったスローガンは、本質的な目的を見失わせる危険性をはらんでいます。真に重要なのは、品質とスピードを同時に追求するマインドです。この両立を実現するためには継続的な改善活動が不可欠だと言えます。 さて、変更失敗率ですがDORA Reportの歴史においては当初から 予測しづらい異質な指標 として認識されてきました( 第2回のブログ で触れています)。 2024年でもその影響が現れてしまったわけですが、一方で、DORAは変更失敗率に関して大きな実験をしています。それは、変更失敗率に関するアンケートの質問の追加と、新たな計測指標の仮説検証です。 私たちは、 変更失敗率という指標がチームに求められる手戻り作業量の代替指標として機能する という長年の仮説を持っています。デリバリーが失敗すると、チームはその変更を修正する必要があり、おそらく別の変更(修正コミット)を加えることになります。 この理論を検証するため、今年はアプリケーションの手戻り率に関する別の質問を追加しました。 「あなたが主に担当しているアプリケーションやサービスにおいて、過去6ヶ月間に計画されていなかったものの、アプリケーション内のユーザーに影響するバグに対処するために実施されたデプロイは約何回ありましたか?」 私たちのデータ分析により、手戻りと変更失敗率が関連しているという仮説が確認されました。(p11) 「手戻り率」の登場 そこでDORAは、 長年の仮説であった「手戻り率」を追加 することにしました。 しかし、具体的にどう影響を与えているのかの明確なデータは提示されていません(「仮説が確認された」という明言のみ)。 従来のFour Keysとの関係を整理すると次のようになります。 p11 の表を元に筆者が作成 ポイントは、ソフトウェアデリバリーに関連するメトリクスを 「スループット」と「安定性」を明確に分けた ことだと思います。(個人的には「デプロイ失敗時の復旧までの時間」は「安定性」を測る指標だと思っていましたが、今回は明確に「スループット」であると提案されました。) つまり、指標として不安定な「変更失敗率」は新たな「手戻り率」というパートナーを迎え、新ユニットを結成したのです。 従来所属していたグループ「Four Keys」(変更リードタイム、デプロイ頻度、デプロイ失敗時の復旧までの時間、変更失敗率の4つの主要メトリクス)に籍を置くものの、メンバー同士の関係性は変わった……と言ったところでしょうか。 今後の関係性も含め継続的な分析を期待したいところです。私達も実験してみる必要性を感じています。 Reliability(信頼性)は引退? なお、今回のDORA Reportではパフォーマンス指標としてのReliability(信頼性)についての言及は限定的であり、「A decade with DORA(DORAと共に過ごした10年)」という章で少し紹介されただけでした。 Reliability(信頼性)はこのままひっそりと引退するのか、それとも重要な指標として再評価されるか、今後も注目していきたいと思います。 なお、あくまでソフトウェアデリバリーパフォーマンスの指標として有意なのか?という視点の話であって、 信頼性が重要なメトリクスであることには変わりない ことを申し添えておきます。 Platform engineering 2024 DORA Reportは、基本的にソフトウェアデリバリーに焦点を当てたレポートですが、今回はプラットフォームエンジニアリングについてかなりの紙面を割いています。このレポートでは、プラットフォームの活用が、ソフトウェアのデリバリーと運用面のパフォーマンスにどのように関係しているのかを詳細に検証しています。 プラットフォームエンジニアリングとパフォーマンスの関係 まだ日本国内では聞き慣れない言葉かもしれませんので、最初にプラットフォームエンジニアリングの解説をしておきます。 ご存じの方は読み飛ばしてください。 【プラットフォームエンジニアリングとは】 プラットフォームエンジニアリングとは、近年、クラウドネイティブ技術の進化により、開発環境が複雑化し、開発者がインフラ管理に時間を取られる課題が浮上しています。プラットフォームエンジニアリングは、この問題を解決するために、開発者向けのセルフサービス型プラットフォームを構築し、生産性を向上させる手法 です。 この考え方は Team Topologies における Platform Team の役割と密接に関係しています。Team Topologies では、開発チーム(Stream-aligned Team)が機能開発に集中できるよう、Platform Team がインフラ管理を抽象化し、共通の開発基盤を提供することを推奨しています。Spotify の Backstage は、Internal Developer Platform(IDP)を構成する要素の一つであり、主に開発者向けのポータル(Dev Portal)として機能します。IDP には、インフラのセルフサービスプロビジョニングや CI/CD の統合など、より広範な機能が含まれる場合があります。 プラットフォームエンジニアリングを支える重要な技術の1つが Infrastructure as Code(IaC) であり、その実現において重要な役割を果たすのが Terraform です(他に、Pulumi や Crossplane などのツールも選択肢として存在します)。Terraform を活用することで、開発者がセルフサービスでインフラをプロビジョニングできる環境 を整えることが可能になります。例えば、Terraformモジュールを用意することで、開発者は簡単な設定だけでクラウドリソースを構築でき、インフラ管理の負担が軽減されます。 プラットフォームエンジニアリングは単なるツール導入ではなく、組織文化やプロセスの改善も含めた包括的な戦略 です。適切なツールを導入するだけではなく、開発者のニーズを把握し、継続的にフィードバックを反映させる仕組みが求められます。チームが「本来やるべき仕事」に集中できる環境を整えることで、ソフトウェアの提供速度を向上させ、ビジネスの成長にも貢献します。 Internal Developer Platform(IDP)を利用している開発者は、次のような結果が分かったそうです。いくつかのポジティブな結果と共に、欠点も見えています。 ポジティブ ネガティブ 個人の生産性が8%向上 スループットが8%低下 チームのパフォーマンスが10%向上 変更の安定性が14%低下 組織全体のソフトウェアデリバリーおよび運用のパフォーマンスが6%向上 プラットフォームエンジニアリングがもたらす可能性 プラットフォームの使用期間と生産性 *1 の関係を見ると、プラットフォームエンジニアリングの取り組み開始時に初期の成果が得られ、その後プラットフォームが年数を重ね成熟するにつれて一旦低下し、その後回復するというパターンが見られました。 このパターンは、変革の取り組みにおいてよく見られるもので、初期の成果を上げた後に困難に直面するケースを指します。しかし、長期的には生産性の向上が維持されており、ソフトウェアデリバリーと運用プロセスにおける内部開発者プラットフォームの重要性とその潜在的な価値を示しています。 意外な落とし穴 上記で書いた通り、スループットの8%低下や変更の安定性の14%低下といった欠点も確認されています。DORAとしても明確な分析はできておらず、次のような仮説を提示しています。 # 仮説 詳細 1 プロセスの複雑化 コードが本番環境に到達するまでの「引き継ぎ」段階が増え、各段階で時間が追加される 2 プラットフォーム強制利用の問題 アプリ開発の全工程でプラットフォームの使用が義務付けられる場合、さらに6%のスループット低下 3 実験の促進 プラットフォームが開発者に変更を自信を持って行う環境を提供し、より多くの変更を可能にする 4 品質保証の課題 プラットフォームが変更の品質を十分に保証できていない、またはチームがスループットを優先して自動テスト機能を活用していない また、プラットフォームエンジニアリングとバーンアウト *2 の関係性に関する調査では、変更の不安定性とプラットフォーム利用の組み合わせによってバーンアウトリスクが高まることが明らかになりました。 この興味深い関連性について、DORAは3つの仮説シナリオを提示していますが、本ブログで取り上げるのは止めます。詳しく知りたい方は原著をお読みください。 プラットフォームエンジニアリングの章は全体的に具体性が欠けており、個人的には科学的ではないと感じた章です。 今後の研究成果に期待したいと思います。 Developer experience 2024 DORA Reportでは、デベロッパーエクスペリエンス *3 (以降、DevEx)に関して多くの誌面を割いています。このセクションでは、DevExが組織の成功に直結するという重要な発見と、それを改善するための具体的な方法について掘り下げています。 AIの時代においても、最終的にソフトウェアを構築するのは人間です。DevExがいかに製品品質と組織のパフォーマンスに影響するのか、DORAレポートに基づいて解説します。 なお、私は以前DevExの科学的な研究について解説したブログを書いておりますので、合わせてお読み頂ければと思います。 ユーザー中心の開発がDevExを向上させる レポートでは、DevExを向上させるにはまず、エンドユーザーエクスペリエンス(UX)を向上させることが重要であるという点が強調されています。エンドユーザーのニーズを深く理解し、それを開発プロセスの中核に据える組織では、開発者の生産性が向上するとともに、組織全体の成長が促進されることが調査で示されています。 ユーザーのニーズを軸に開発を行うことで、開発者の仕事の満足度が高まり、結果的に組織全体のパフォーマンス向上につながる可能性があります。ユーザー中心(User-Centric)のマインドセットを実践している開発者には、次のような特徴が見られます。 より高い生産性を発揮する( 目的意識が明確になり、不要な作業が減る ) バーンアウトのリスクが低下する( 仕事の意義を感じやすく、ストレスが軽減される ) より高品質な製品を生み出す( ユーザーの課題に直結したソリューションを提供できる ) また、ユーザーエクスペリエンス(UX)を最優先する組織では、ソフトウェアデリバリーの安定性やスループットが必ずしも品質の前提条件とはならないという点が指摘されています。 その理由として、最終的な製品の品質は「迅速なデリバリー」ではなく、「ユーザーにとっての価値」によって決まる可能性があるからです。 このレポートの知見を踏まえると、ユーザー価値の創出に組織的に取り組むことの重要性が浮かび上がります。ファインディでは幸いなことに、専門のデザインチームがありエンジニアと共にユーザーエクスペリエンス(UX)に力を入れているため、この課題への取り組みはすでに進んでいると言えるでしょう。今後もさらなる進化を目指して努力を続けていきたいです。 一方で、ユーザーフィードバックを十分に取り入れない組織では、安定したデリバリー速度が品質を確保する唯一の手段になりがちであるとも述べられています。 これは本来「ユーザー価値の創出」が目的であるべきところ、「安定したデリバリー」という手段が目的化してしまう典型的な例といえるでしょう。その結果、ユーザーの実際のニーズとはズレた機能や、見た目は魅力的でも実際にはほとんど使われない機能("shiny but hardly used")が生まれるリスクが高まるという考察がされています。 最終的な目標は、私たちが作る製品をユーザーに愛してもらうことです。「デベロッパーエクスペリエンス」の章で述べているように、ユーザーに焦点を当てることで、製品の機能に存在意義が生まれます。開発者は、これらの機能がユーザーエクスペリエンスの向上に役立つことを確信して、自信を持って開発を進めることができます。(p73) これは非常に興味深い考察です。シンプルに言い換えれば「エンジニアもお客様の喜ぶ顔を見れば生産性は上がり組織も成長する」ということなので、 商売の基本 みたいな話だなと思いました。 質の高いドキュメントがユーザー中心開発を増幅する 2021年からドキュメントの調査を開始したDORAですが、2024年のレポートでは、質の高いドキュメントとユーザー中心のアプローチを組み合わせると、製品パフォーマンスが向上することが明らかになりました。 質の高いドキュメントの評価には、内容の見つけやすさや信頼性といった要素が含まれます。 DORAは、 アジャイルマニフェスト(アジャイルソフトウェア開発宣言) を引用して次のように述べています。 アジャイルマニフェストは 「包括的なドキュメントよりも動くソフトウェアを」 *4 を提唱しています。しかし、私たちは依然として、質の高いドキュメントが動くソフトウェアの重要な構成要素であることを認識しています。 「包括的なドキュメント」という表現は、ドキュメントを含む不健全な慣行を表しているのかもしれません。 問題のあるドキュメントには、官僚的な目的だけのために作成されたドキュメントや、経営陣と従業員の間の不信感を取り繕うためのドキュメントが含まれます。不健全なドキュメント文化には、ドキュメントを作成するものの、維持や整理を行わないことも含まれます。 アジャイルマニフェストが宣言された2001年頃は、「ドキュメント作成に時間を取られすぎる問題」が開発現場の切実な課題でした。 アジャイルソフトウェア開発宣言 当時の多くの開発 では詳細な仕様書作成が前提とされ、要件変更が発生するたびに仕様書の修正に追われ、実際の開発よりもドキュメント管理に多大な時間を費やしていました。大量の紙にプリントして製本、そのドキュメントを納品、といった風習もまだ残っていた時代です。 また、現代に生きる私たちは忘れがちですが、2001年頃の技術的な制約も大きく影響していました。 ツールはWordやWiki、Lotus Notes/Domino *5 が主流でしたが、当時のツールはバージョン管理機能が貧弱で、複数人での同時編集が不可能、変更履歴の追跡も困難でした。 また、大きなドキュメントは頻繁にクラッシュし、図や表の扱いも現在より遥かに制限されていました。さらに、ストレージは現在と比べて容量が小さく高価であり、ネットワーク速度も遅いという環境でした。 一方、 クラウド中心の現代 ではNotionやConfluence、GitHub Wikiなど、協働作業に適したドキュメント管理ツールが普及し、リアルタイム共同編集や変更履歴の自動管理が当たり前となっています。 DORAレポートによれば、現代におけるドキュメントの役割は「ユーザーの声やフィードバックをチーム全体に共有し、それを製品に反映させる」という、より価値創造に直結したものへと進化しています。 DORAレポートは、健全なドキュメント文化を構築するための具体的な実践方法も提示しています: 重要なユースケースの文書化 テクニカルライティングの研修 明確な責任者と更新プロセスの定義 チーム内でのドキュメント作業の分担 開発ライフサイクルの一部としてのドキュメント維持 古いドキュメントの削除 業績評価における文書作成貢献の認識 さらに、詳しく知りたい方は原著をお読みください。 常に変わる優先事項の危険性 DORAの調査で最も注目すべき発見の1つは、組織の優先事項が頻繁に変わることによる悪影響です。 エンジニアであれば直感的に「危険」と思える現象です。 この感覚は誰もが経験したことがあるでしょう。数か月かけて新しい機能の開発に取り組んできて、それがユーザーにとって必要なものであると確信し、集中し、モチベーションも高い状態です。ところが、突然、あるいは突然のように感じるほど急に、 経営陣が組織の優先事項を変更します。 すると、あなたのプロジェクトが一時停止されるのか、中止されるのか、別の形に変わるのか、それとも全く違うものになるのかが分からなくなります。 データによれば、優先事項が不安定な組織では、 生産性に小さいながらも確実な低下が見られる バーンアウトが大幅に増加する 興味深いことに、強力なリーダーシップや質の高いドキュメント、ユーザー中心のアプローチがあっても、優先事項が不安定であれば従業員はバーンアウトのリスクにさらされ続けることが明らかになりました。DORAは、これが慢性的なストレスによるものと推測しています。 一方で興味深いことに、優先事項が安定すると、逆にソフトウェアデリバリーのスピードと安定性が低下するという予想外の結果も報告されています。これについてDORAは、安定した組織では変更が少なくなるか、推奨されるよりも大きなバッチでの出荷が行われるためではないかと仮説を立てています。 おわりに さて、全4回にわたってお届けしたDORAレポートに関するブログも一旦終わりにし、2025年のDORAレポートを待ちたいと思います。 皆様におかれましてはDORAレポートを参考にしながら、ぜひ自組織についての仮説や理論を組み立てるのに役立ててください。独自の実験に取り組むことで、より実践的な知見が得られるでしょう。 今回のブログを書きながら改めて感じたのは、ソフトウェア開発の複雑さです。 クネビン(Cynefin)フレームワークで例えると「Complicated」ではなく、「Complex」の領域に位置します。 Tom@thomasbcox.com, CC BY-SA 4.0 < https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons 領域 特徴 対応方法 例 明確(Clear) 因果関係が明確であり、誰が見ても同じ答えが得られる領域。ルールやベストプラクティスが効果的。(2014年までは単純(simple)、その後明白(obvious)と呼ばれ、最近の名称に変更) 「分類し、対応する」(Sense — Categorize — Respond) マニュアル化された業務、明確な手順がある問題 煩雑(Complicated) 因果関係が理解できるが、専門知識や分析が必要。答えが複数存在する可能性もある。理論的に解決可能。 「分析し、対応する」(Sense — Analyze — Respond) エンジニアリング、法律の専門的問題、データ分析が必要な業務 複雑(Complex) 因果関係が曖昧で、試行錯誤を通じてパターンが見えてくる。予測が困難で、正しい答えが最初からわからない。 「試し、理解し、対応する」(Probe — Sense — Respond) 新製品開発、文化変革プロジェクト、組織変革 混沌(Chaotic) 因果関係がなく、予測不可能な状況。迅速な介入が必要で、秩序を取り戻すことが優先される。 「即座に行動し、理解し、対応する」(Act — Sense — Respond) 自然災害、事故発生直後の対応、緊急事態 混乱(Confusion) どの領域にも属さない、または適用領域が不確かな状況。複数の視点が入り乱れ、争いが発生することも。 状況を分解し、他の4領域に割り当てることで解決の糸口を見つける 情報が混乱していて、どの領域に該当するか判断が難しい場合 様々な要因が絡み合いComplexになりやすいソフトウェア開発ですが、パターンは存在します。 まず「試し、理解し、対応する」(Probe — Sense — Respond)というアプローチで小さな実験を繰り返し、そこから学んでいく。この積み重ねが、ソフトウェア開発では大事なのだと、改めてDORAレポートを熟読して実感しました。 そして、この複雑さ(Complexity)こそが、ソフトウェア開発の魅力であり、日々新たな発見と創造の喜びをもたらしてくれるのだと実感しています。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers *1 : DORAの生産性(Productivity)の定義:個人が自身の仕事において、どれだけ効果的かつ効率的であると感じているか、価値を生み出し、タスクを達成できているかを測るもの。 *2 : バーンアウト(Burnout)とは、長期間のストレスや過重な業務負担によって、心身ともに疲弊し、モチベーションや集中力が低下する状態を指します。特にソフトウェアエンジニアのように、高い認知負荷や締切に追われる職種では発生しやすく、「燃え尽き症候群」とも呼ばれます。主な症状には、慢性的な疲労、仕事への意欲の低下、感情の枯渇などがあり、生産性の低下や離職の要因にもなります。DORAは10年に渡り、このようなバーンアウトのリスクを考慮した調査を数多く実施しています。 *3 : 私は頑なに「開発者体験」とは訳しません。正しいニュアンスを伝えるなら「開発者が快適かつ効果的に働ける環境全般」とするべきで、でもこれだと面倒なのでそのままの言葉で良いかなと思っています。 *4 : 「包括的なドキュメントよりも使い物になるソフトウェアを」と訳したほうが良い説を支持しています。 https://x.com/t_wada/status/1848630196775346294 *5 : Lotus Notes/Dominoとは、IBMが1995年にLotus Development社を買収して獲得した企業向けグループウェア・情報共有プラットフォームです。1990年代から2000年代初頭にかけて多くの企業で導入され、メール、カレンダー、文書管理、ワークフロー機能などを統合した先進的なシステムでしたが、操作の複雑さやカスタマイズの難しさも指摘されていました。