株式会社ラクスのブログ - TECH PLAY

TECH PLAY

株式会社ラクス

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

968

こんにちは、デザインマネージャーの清水です。 私たち「プロダクトデザイン課」は、お客様の業務課題を解決するため、全プロダクトのUI/UXデザインを担っています。 あらゆるプロダクトにとって、最良のUXを目指すことは必然だと思います。 私たちもまた、お客様にとってより使いやすいプロダクトを提供するため、継続的にUX改善に取り組んでいます。 先日のブログ「 ラク スのプロダクトデザイン組織紹介― 顧客価値を高める新たな挑戦」では、 継続的なUX改善の一環である「UI刷新」プロジェクト についてご紹介しました。 ここでの「刷新」とは、既存UX(お客様の使い勝手に大きく影響する配置や導線)を急激に変更するという意味ではなく、UXの維持向上を重視しつつ、よりわかりやすいUIデザインにするイメージにとらえていただければ幸いです。 tech-blog.rakus.co.jp 今回は「UI刷新」の目的や背景、どのような価値を目指しているのかを詳しくお話ししたいと思います。 UI刷新プロジェクトとは 進め方と目指す姿 UI刷新プロジェクトとは ラク スのプロダクトが対象とする中でも、特に経費精算や勤怠管理といったバックオフィス業務は複雑で、多くの法令が関係します。そこで、ユーザに対応いただく操作も煩雑になりやすい側面があります。 ユーザに業務をスムーズに進めていただくためには、機能の充実はもちろん、直感的で迷わない操作性も欠かせません。そのため、より良いUXを提供する一環で「UI刷新」プロジェクトを立ち上げました。 今回のUI刷新は、 ラク スのバックオフィス向けプロダクトである楽楽シリーズ(楽楽精算、楽楽明細、楽楽販売、楽楽勤怠、楽楽電子保存、楽楽請求)が対象となります。 UI刷新の目的 今回の刷新には、大きく2つの目的があります。 1. UI統一による一貫した体験の提供 現在、各プロダクトのUIはそれぞれ独自の設計がされており、操作性もすべて異なっています。これは ラク スが採用している 「ベスト・オブ・ブリード製品開発戦略」 のためです。 「ベスト・オブ・ブリード」は、 業務領域ごとに最適化された“最良”の製品を、必要に応じて組み合わせて導入するというIT製品導入の用語 です。 ラク スはこれにならい、 業務領域に特化したUIや機能を作りこみ 、お客様の課題をいち早く解決できるようにしてきました。 その結果、どの領域においても最適なUI・機能を提供できるようになり、 ラク スの強みの一つとなっています。 しかし、最近は SaaS を複数組み合わせて利用する企業が増えてきました。楽楽シリーズを複数導入している場合、プロダクトごとに異なるUIに慣れる負担が生じます。そこで今回の刷新では、 プロダクトをまたいでも「同じシリーズだ」とわかるようにUIを統一し、より一貫した体験をお届けする ことを目指しています。 2. UIの最新化による使いやすさの向上 UI刷新の方向性を決める際、もう一つ大切にしたのが 「UIの最新化」 でした。 これまで ラク スのプロダクトは、お客様の業務課題を確実に解決するために、機能を優先して充実させる戦略をとってきました。おかげで多くのお客様のニーズに応え、選んでいただいています。 一方で、機能優先で開発を進めてきた結果、古いデザインが残っているプロダクトもあり、新たなUIへ進化させるタイミングを模索してきました。プロダクトを横断した「UI統一」は、その大きなチャンスだと考えています。 新しいUI コンポーネント は、視認性や可読性、 アクセシビリティ にも配慮し、どのユーザーにとっても直感的な操作ができるよう設計しています。 実際の導入にあたっては、 お客様の業務に合わせて最適化してきた既存のUXと調和を図るプロセス をとっており、後述いたします。 進め方と目指す姿 UI刷新とはいえ、既存のお客様が慣れ親しんだ操作感を一気に変えるわけにはいきません。 業務 ドメイン に特化した コンポーネント 配置を維持しつつ、画面のレイアウトやデザインをより洗練させる のが今回の方針です。 UI刷新前 UI刷新後 将来的には、プロダクトを横断して複数の楽楽シリーズを併用しても違和感を感じない、一貫した操作体験を提供し、視認性の向上や アクセシビリティ への配慮など、あらゆるユーザーにとって使いやすいUIデザインの提供が目標ですが、まずはその第一歩としてユーザーのプロダクト遷移時の違和感の解消を目指しています。 そのために、プロダクト統一の共通UI コンポーネント とデザイン ガイドライン を整備し、各プロダクトを担当するデザイナーで意識統一を図りました。楽楽シリーズは、それぞれ異なるカラースキームや コンポーネント を持っていますが、 共通UI コンポーネント とデザイン ガイドライン でデザイン基盤を整えることで、ユーザーに統一感のあるプロダクト体験を提供できる ようになります。 各プロダクトには、利用ユーザーに合わせた特有の機能や表現があるため、全てのデザインを共通UI コンポーネント で作成することは現実的ではありませんが、プロダクトを併用するユーザーに将来に渡って一貫した操作体験を提供していくため、何を統一し、何を独立させて最適化するかといった視点にこだわってデザイン ガイドライン を作り、更新し続けています。 デザインシステムの取り組み 共通UI コンポーネント やデザイン ガイドライン を発展させて、フロントエンド開発メンバーと共通の理念を持ってプロダクトの システム開発 を進めていくために、デザインシステムの導入にも取り組んでいます。 ラク スでは複数のプロダクトが独自のUIデザインを持っているため、今回のUI刷新後も各プロダクトで多くの独自 コンポーネント を使ったデザイン表現がされています。 その中で、デザイン制作目線でも システム開発 目線においても”共通のデザイン表現”とは何なのかを関係者が認識して、各プロダクトのデザイン、開発をできていることが重要だと考えています。 もっとも、これだけでUX改善まで実現できるわけではありません。 今回の刷新は、まずレガシーな画面をアップデートし、プロダクト間の共 通化 を図るもので、いわばスタートラインに立ったに過ぎません。 今後のUX改善のためには、デザイン組織としてより顧客を理解し、エンジニアやビジネスサイドとの連携を強めていくことも欠かせません。 私たちは、 このUI刷新をきっかけに、より大きなUX改善へと踏み込んでいきたい と願っています。 お客様にとって本当に使いやすいプロダクトとは何かを追求し、継続的にアップデートを重ねることで、 ラク スのプロダクトが「業務を効率化するだけでなく、使うのが楽しみになる」存在へと育っていくことを目指しています。 デザインチーム一丸となって取り組んでまいりますので、どうぞご期待ください。
ラク スが開発環境を進化させ続ける理由 こんにちは。エンジニア リングマ ネージャーの iketomo(いけとも) と申します。 多くの開発組織にとって、開発力を高め、エンジニアが活躍できる環境を整えることは重要な課題です。 開発環境は単なる「働く場所」ではなく、エンジニアが最大限の力を発揮し開発生産性を高めることが、顧客に価値を届けるための基盤だからです。 ソフトウェア開発の世界は日々進化し、新しい技術が次々と生まれています。また、市場の変化も加速しています。 この変化に対応するためには、開発力と組織の魅力を高め続ける必要があり、そのためには開発環境整備が不可欠と考えています。 ラク スも例外ではなく、開発生産性を高め競争力を向上するための開発環境整備に取り組んでいます。 今回の記事では、 ラク スの開発環境整備の基本方針、整備対象の選定・プロセスと、具体的な事例をご紹介します。 私たちの取り組みにかける姿勢をお伝えできれば幸いです。 ラクスが開発環境を進化させ続ける理由 開発環境整備の対象と基本方針 開発環境整備の選定・検証プロセス 開発生産性向上のための取り組み GitHub Copilotの導入 43インチモニタの導入 Devinの検証 まとめ 開発環境整備の対象と基本方針 整備の検討対象は、開発生産性向上につながるあらゆる環境・ツールです。 ただし単に最新のツールを何でも導入するわけではありません。 私たちは検討にあたって以下の基本方針を掲げており、 最終的には 「より高品質なソフトウェアを効率的に開発し、顧客に最大の価値を提供する」 ことを目指しています。 費用対効果を重視した選定 整備対象を選ぶとき、最も大切にするのは 費用対効果 です。 開発環境への投資は単なるコストではなく、開発生産性を高めるための戦略的な判断の一環です。 適切なツールや設備に投資することで、エンジニアの作業効率が向上し、開発のスピードと品質が向上します。 しかし、すべてのツールや技術が無制限に導入できるわけではありません。 費用対効果を考慮する際には、次のポイントから多面的に評価しています。 直接的な効果: 開発速度の向上、バグの削減、レビューやテスト、デプロイの効率化など、 開発プロセス での直接的なコスト改善が見込めるかどうか。 間接的な効果: 業務上のコミュニケーションしやすさ、ストレスの軽減、学習コストの削減など、 開発プロセス 以外でもポジティブな影響を与えるかどうか。 価値の持続性: 一時的な流行に乗るのではなく、将来的にも価値を発揮するかどうか。短期間で廃れてしまうものではなく、継続的に活用できるか。 特に、開発生産性を最大化するためには、投資することによってエンジニアにとってストレスの少ない環境を実現することが重要です。 開発者が作業に集中できる環境を整えることが、長期的に見て開発組織の競争力向上につながると考えています。 開発環境整備の選定・検証プロセス 現場の裁量で発案し、小さく試す ツールや新しい技術を導入する際には、単なる流行や市場の動向だけに頼るのではなく、実際の開発業務での有効性を確認することが不可欠です。 そのため私たちは、 開発チームが裁量を持ち、現場ニーズから提案してもらう検証プロセス を確立しています。 私たちの選定プロセスは次のステップで進められます。 開発チームからの提案: 開発環境を改善するための新しいツールや技術に関するア イデア を、各エンジニアが裁量を持って提案できる仕組みを整えています。 パイロ ットチームによる小規模検証: 提案されたツールは、まず少人数の パイロ ットチームによって試験的に導入され、実際の業務にどのような影響を与えそうか確認します。 パイロ ットチームは各開発チーム内で組成することもあれば、開発組織横断になることもあります。 評価とフィードバック : パイロ ットチームによる定性・ 定量 の結果をもとに、効果や課題を整理し、正式導入に向けた判断を行います。 本格導入と最適化 : 費用対効果が見込める場合、開発組織全体に展開し、必要に応じてカスタマイズや最適化を実施します。 このようなプロセスを取ることで、リスクを最小限に抑えつつ、最適な開発環境を実現することができます。 継続的な検証 開発環境は一度整えれば終わりではなく、技術の進化や組織の成長に応じて常に最適化し続ける必要があります。 そのため、私たちは 定期的に開発環境の見直しを行い、必要に応じてアップデート を実施しています。 継続的な改善を行うために、次の取り組みを実施しています。 定期的なレビュー: 半年に一度、社内の技術検証チームが現在の環境に対してフィードバックを行い、改善点を洗い出します。 社内ナレッジの共有: 新しく導入したツールやベストプ ラク ティスについて、社内勉強会やドキュメント、社内チャットを通じて情報を共有し、全員が活用できる状態を整えます。 技術トレンドのキャッチアップ: 最新の技術動向を常に把握し、必要に応じて新しい技術を試験導入することで、開発環境の陳腐化を防ぎます。 これらの取り組みを継続することで、エンジニアが常に最適な環境で開発に取り組めるようにし、結果的に開発効率と品質の向上につなげています。 開発生産性向上のための取り組み ここからは、具体的な取り組みについて紹介します。 GitHub Copilotの導入 目的 コーディング時間短縮、業務品質向上、学習や調査時間の短縮を通じた開発生産性向上を目的に、2023年6月から導入しています。 ソースコード の自動補完や提案機能のほか、コードレビュー、コード解説、テストデータ作成など、多くの局面での利用を想定しました。 経緯 まずテッ クリード 主導のプロジェクトチームで試験導入を行い、どのようなメリット・デメリットがあるのかを検証しました。コードサジェストにより削減した作業分を集計すると、同じ業務の所要時間は12%削減、一人当たり10時間/月の作業効率化につながることが明らかになりました。費用に見合う効果が確認できたため、最終的に全社展開を決定しました。 結果 90%以上のエンジニアが GitHub Copilotによって生産性が向上した との回答が得られました。時間換算で、1時間/日程度の作業が削減されたようです。 実際に使ったエンジニアからも「単純なコードの記述がとても楽になった」「サジェストが怖いくらい正確」という声が上がっています。 現在は GitHub Copilot Enterpriseも導入し、開発組織内でさらなる活用を図っています。活用事例やナレッジ共有を行い、組織全体の活用レベルをさらに高めていきたいと考えています。 43インチモニタの導入 目的 開発業務の可視性を向上させ、スクロールや画面切り替えの負担を軽減することで、作業効率を改善することが目的でした。 特に、大規模なコードベースを扱う際や、 ペアプログラミング の際に、広い視野を確保することで生産性の向上を狙いました。 経緯 従来のモニタ環境では、コードが縦に長くなり、スクロールや切り替えの回数が増えることで作業のリズムが崩れるケースが多発していました。 そこでテッ クリード 発案のもと、まず一部のチームで43インチモニタを試験導入し、可読性や作業効率への影響を検証しました。年間48時間以上の削減効果があることがわかりました。 結果 ソースコード の視認性が向上し、スクロールやウインドウ切り替えの回数が減少。結果として、 当初の想定通りコーディング・コードレビューや デバッグ 作業の時間短縮 につながりました。 特に関連する ソースコード を横に複数並べて同時に見ることで、プログラムの処理フローの把握をかなり効率的に実施することができるようになりました。 また ペアプログラミング やコードについて議論する際に、複数のエンジニアが同時に1つのモニタの前に集まることが多くなり、コミュニケーション活性化にもつながりました。 それ以外に当初想定なかった副次的効果で、縦に並んだタスク一覧を数多く表示できるので探しやすくなったり、メールや説明等の文章作成時に全体把握ができるので作成効率が上がるなど、 ソースコード 以外でも43インチモニタが活躍するシーンがあり、使用している全員が作業効率が良くなったと回答しました。現在では 開発組織の半数のエンジニアが利用 しています。 唯一のデメリットは画面が大きいので座っている人が見えなく、在席しているのかどうかを確認するのに席の横まで行かないといけないのですが、そこはご愛嬌で。 Devinの検証 目的 開発生産性向上の目的で、 AIエージェントの一つであるDevinの検証 も開始しています。 特に、各開発チーム内で適用可能性のある ユースケース や、十分な費用対効果が期待できるかを検証しています。 経緯 近年、チャット型やCopilot等のサジェスト型をはじめとした、 開発プロセス での生成AI活用が進んでいます。 しかし、AIエージェントは従来の生成AIとは違い、目的を与えることで自律的に複数の情報ソースやサービスから情報取得して操作を行うことができます。 タスクの計画立案から実行、成果物の提出まで一連のプロセスを自律的に遂行することで、飛躍的に生産性を向上させる可能性 があります。 私たちの社内においては、社内未導入の技術を調査・検証する「技術推進チーム」がAIエージェントのトレンドについて情報収集を進めてきました。 最新事例のフォローアップや社内での ユースケース 発掘に向けて、積極的に先行検証を開始しています。 現状 現在はバックエンド開発、AI機能開発、モバイル、SREなど、多岐にわたる部門・チームで検討を開始しているところです。 以下のような幅広い利用シーンが想定され、 費用対効果も良好な結果が得られそうな感触 です。Devinを検証中のエンジニアの声を一部紹介します。 PoCで チュートリアル を試すとき、必要な知識と実装が自動的に提案され、すぐ着手できた Devinの出力からベストプ ラク ティスやよりよい実装に気づける テストコード実装や リファクタリング に使ってみたい API 作成からCI/CD、PRの作成まで一括指示ができ、アウトソースしている感じがした。再現性の高い作業は委託しやすそう 日中、夜間関係なく稼働できるので効率的 人のアウトプットのダブルチェックに使える オンボーディング資料のアップデートなど、開発の付随作業の削減にも使えそう ユースケース の検証はまだ拡大継続中ですが、コスト削減だけでなく、PoCのようにDevinから新しい発見を得ることも期待できそうです。 まとめ 開発環境整備は、単なる効率化ではありません。エンジニアが力を発揮し、最高のプロダクトを生み出すための基盤です。 ラク スは 「費用対効果」を重視し、現場ニーズと継続的な改善を大切にしたプロセス で、確実に環境を整えてきました。DevinやCopilot、4Kモニタの導入はその一例です。 近年、生成AIをはじめとした技術革新により、開発環境はかつてないスピードで進化しています。 わずか半年後、1年後には、いま私たちが想像する以上の生産性を手にしている可能性すらあります。それはつまり、「未来の開発力」は、私たちの想像を常に超えてくるということです。 だからこそ、私たちは「現時点での最適な環境」に甘んじることはありません。 変化を受け身で待つのではなく、自ら変化を先取りし、常に“次の最適”をつくり出すことが必要 だと考えています。 私たちは、エンジニア一人ひとりがより自由に、より創造的に力を発揮できる環境を追求し続けます。 そして、テク ノロ ジー の進化を味方にしながら、より速く・より大きく・よりクリエイティブな価値を生み出せる開発組織へと進化していきます。
はじめに 楽楽販売開発課サポート対応チームのshimizu_sと申します。 前回、同チームにて以下の記事を掲載しました。 tech-blog.rakus.co.jp 今回は同じく、サポート対応チームの業務の内容について、どのようなことを実施しているかさらに詳しくご紹介します。 工夫した取り組みについて サポート対応の業務内容については前回の記事を参考にしてください。 さて、前回の記事で記載されたゴールは「お客様の疑問・もやもやを無くす」ですが、 ゴールにただ向かうだけではなく、お客さまにより満足していただけるようにサポート対応チームは以下の部分にも注力しています。 迅速に サポート対応を行う取り組み 正確に サポート対応を行う取り組み 明確に サポート対応を行う取り組み 今回は、三つの観点でサポート対応チームがどの様に工夫をしているかをご紹介したいと思います。 はじめに 工夫した取り組みについて 迅速にサポート対応を行う取り組み サポート依頼・対応のデータ蓄積 よくある質問ページの作成 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 明確にサポート対応を行う取り組み 回答作成レビュー 回答作成時チェックシート 終わりに 迅速にサポート対応を行う取り組み 疑問を解消するのはもちろんですが、お客さまにとって、返事が遅いと不安になります。 最初は小さな不安の種ですが、時間が経過していくと最終的に 製品としての信頼を損なうことに繋がります。 そうした中で、お客様の疑問をいち早く解決できるように以下の取り組みを実施しています。 サポート依頼・対応のデータ蓄積 楽楽販売では、過去に受けたお客さまからの依頼に対して、社内で使用している楽楽販売の専用データベースに以下のような情報をもって蓄えています。 質問内容 回答した内容 その回答を行った理由 現在、このデータベースには600~700件/年のデータが登録されています。 サポート対応の蓄積 これにより、同様・類似の質問が来た際、過去依頼を検索・参照することで、素早い解決ができるように取り組んでいます。 よくある質問ページの作成 カスタマーサポートチームがサポート対応チームに確認しなくても対応ができるように、 以下の観点を考慮した「よくある質問ページ」というものを作成して、社内ネットワーク内でカスタマーサポートチームへ共有しています。 質問の頻度が多い 確認する箇所が明確である よくある質問ページ こちらのページについては、 カスタマーサポートチーム側からの意見を募集しており、内容を鑑みて反映をしていく運用となります。 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 楽楽販売は柔軟なカスタマイズ性が強みの製品です。 豊富な機能を備えているため、お客様から多岐にわたる質問がやってきます。 そのため、サポート対応チーム内でも『どのように調査をしていけばよいか?』と悩むことがあります。 そうしたなかで、様々な質問に対して正確に調査し、適切に回答ができるように、 オープンソース ソフトウェアであるナレッジ共有ツール *1 を使用して、以下のようなナレッジを共有しています。 サポート依頼の調査観点 ログ調査方法 実際に機能を確認できる端末の使用方法 どのような質問が来ても、正しく内容を把握し、適切な回答を提供できるように努めております。 明確にサポート対応を行う取り組み サポート対応チームの取り組みの一つとして、"回答をいかに分かりやすく伝えるか" もあります。 なぜなら、エンジニアであるサポート対応チームと顧客対応を中心とするカスタマーサポートチームでお互いの専門としている分野が異なります。 さらに、文字ベースでのやり取りを基本としているため、曖昧な表現や一般的ではない用語を利用すると正しい内容が伝わらず、やり取りが増えるだけでなく、 お客様に誤った内容が伝わってしまう可能性がある からです。 そうしたことを防ぐため、サポート対応チームに配属後、わかりやすい回答ができるように以下の取り組みが実施されます。 回答作成レビュー 回答作成時チェックシート 詳細について説明します。 回答作成レビュー カスタマーサポートチームへ回答を伝える前に、 サポート依頼の担当者が作成した回答を一度ベテランの方に見てもらいます。 文章としておかしくないか 表現に揺らぎがないか 誤解を与えるような強い言葉(例えば『絶対』)を使っていないか 一般的ではないIT用語を使っていないか 色々な観点でチェックが入ります。 作成した回答のレビュー依頼 ベテランのチェック あらかじめ設定した基準をクリアし簡潔な回答ができるようになれば、ベテランのレビューがなくなります。 余談ですが、ここで筆者の文章力がグッと鍛えられた気がします。 回答作成時チェックシート また、サポート対応チーム内で、上記レビューであった指摘点について普遍的な内容の場合は、回答作成時のチェックシートに追加されます。 回答作成チェックシート つまり、ここに記載されているものに当てはまらないようにすることで、わかりやすく誤解を与えにくい回答になります。 (副次的にベテランのチェック時間も減るといった効果もあります) こうした文章表現のナレッジ化も取り組んでいます。 終わりに サポート対応チームでは、さまざまな工夫を行っております。 冒頭でもお伝えしたように、 「お客様の疑問・もやもやを無くす」がゴールではありますが、お客さまに、「より満足していただける」ように心がけています。 こうした取り組みは一朝一夕で成し遂げられるものではなく、すぐに成果が見えるわけではありません。 しかし、日々ユーザーが増えていく楽楽販売の商品の強みの一つとして「サポートの品質が高い」となるように、 サポート対応チームは、お客さまと実際にやり取りしていただいているカスタマーサポートチームと力を合わせて、 高品質なサポートを提供できるよう色々な工夫を行い努めていきたいと思います。 *1 : GROWI
はじめに:経費精算業務の現状と課題 当社は、経費精算業務を効率化するプロダクト「楽楽精算」を提供しています。 現在、多くの企業が紙や Excel で経費精算業務を行っており、申請から承認までに膨大な時間を要しています。 特に手作業による申請チェックや書類不備の差し戻しが、 経理 担当者の業務負担を増大させる要因となっています。 このような状況では、 経理 担当者が業績管理や予算策定といったコア業務に集中することが難しくなります。 こうした問題を解決するため、 「楽楽精算」ではプロダクトでのAI活用を一層推進する こととしました。 プロダクト開発に関わるエンジニアの皆さんにとっても、AIを活用したプロダクトの提供価値向上は大きな関心事ではないでしょうか。 一方で、AI導入の際には インパク トの見積もりや、不確実性の扱い方について悩む場面もあると思います。 そこで本記事では 経費精算業務の効率化に向けた、AIを活用した製品戦略と開発ロードマップ をご紹介します。 ロードマップ策定の目的やプロセス、不確実性への対処、期待されるアウトカムにも触れていますので、皆様のご参考になれば幸いです。 はじめに:経費精算業務の現状と課題 なぜAIが必要なのか 変化の激しいAI技術の不確実性をどう扱うか AI開発ロードマップの策定目的 どう実現していくのか AI開発ロードマップの概要 どんなアウトカムを目指すのか プロダクトでのAI活用を成功させるために なぜAIが必要なのか 少子高齢化 による労働力不足が進んでおり、企業はより少人数で高い付加価値を生み出すことが急務 です。 経理 業務をはじめバックオフィス業務も例外ではなく、AIを活用したさらなる効率化が求められます。 またAI活用により、既存プロセスのシステム化を超えた、経費精算のDX(業務の抜本改革)が可能になります。 業務の最適化を通じて、ガバナンス向上と 経理 担当者がコア業務に専念できる状態が期待できます。 変化の激しいAI技術の不確実性をどう扱うか AIは変化が激しく、AI活用によりプロダクトの提供価値がどれほど向上するかは不確実性がついてまわります。 計画を立てても、実際に運用してみると想定外の課題が発生することも少なくありません。 そのため、PoC(概念検証)を行い、最小実用プロダクト(MVP)を経て実運用に至る段階的なアプローチが重要になります。 最初から大きな範囲に適用するのではなく、 最も重要な機能からスモールスタートで進める ことが成功の鍵となると考えています。 AI開発ロードマップの策定目的 ラク スが「楽楽精算」のAI開発ロードマップを策定する主な目的は、 経理 担当者がより戦略的、創造的なコア業務に専念できる状態を創出する道筋を描く ことです。 冒頭で述べた通り、多くの 経理 担当者は経費精算などのノンコア業務に多くの時間を費やしており、企業の売上や利益に直接貢献する業務に注力できていないという課題があります。 AI技術を活用して経費精算業務を効率化し、 経理 担当者の負担を軽減することで、彼らが本来注力すべきコア業務に時間とリソースを割けるようにすることを目指しています。 どう実現していくのか ラク スは、AI開発ロードマップに沿って、以下のステップでAI機能を実装し、上記の目的を実現していきます。 出典: 当社プレスリリース   ステップ1:申請者・承認者向け機能のAI活用 最初のステップとして、 使用頻度が高く、利用ユーザー数が多い「申請者・承認者向け」の機能 からAIの導入を開始します。 申請者が経費申請を行う際の入力ミスを削減し、それに伴う差し戻しを減らすことで、承認者や 経理 担当者のチェック作業を軽減します。 今後リリース予定の「勘定科目の入力補助機能」は、このステップにおける重要な取り組みの一つで、現在PoCを進めているものです。 この機能は、AI- OCR によって領収書や請求書のデータを読み込み、過去の申請履歴や添付された領収書データを基に、AIが適切な勘定科目の入力を補助します。 これにより、申請者がどの勘定科目に分類すべきか迷うことが減り、経費申請時のミスや差し戻しを大幅に削減することが期待されています。 ステップ2: 経理 担当者向け機能のAI活用 次のステップとして、 より専門性が求められる「 経理 担当者向け」の機能 にAIを活用していきます。 具体的には、企業のガバナンス向上のための経費使用規定違反や利用目的が不明瞭な経費申請を検知するAI機能や、 経理 担当者の業務負荷を大きく削減するためのAIによる通常経費申請の自動承認・否認機能などの開発を目指します。 AI開発ロードマップの概要 ラク スは、2025年3月期から2029年3月期までのAI開発ロードマップを策定し、段階的にAI活用を進めることで、 経理 業務全体の生産性向上を目指しています。 出典: 当社プレスリリース     フェーズ1(2025年3月期~2026年3月期): 経理 担当者のチェック作業軽減 経費申請の入力補助機能 申請前の不備をチェックする機能 経費申請内容のチェック機能 承認経路を適切化する機能 フェーズ2(2027年3月期~2029年3月期): 経理 担当者がコア業務に専念できる状態の創出 経費使用規定違反や利用目的が不明瞭な経費申請を検知する機能 AIによる通常経費申請の自動承認・否認機能の開発 開発ロードマップの詳細はプレスリリースにも掲載しておりますので、併せてご覧ください。 https://www.rakus.co.jp/news/2024/1212.html どんなアウトカムを目指すのか ラク スは、これらのAI機能を実装することで、 1社あたりの経費精算作業時間を大幅に削減する ことを目指しています。 現在の 楽楽精算にAI機能を導入することにより、60%以上の作業時間短縮が可能になる と期待されています。 さらに、このロードマップは経費精算領域だけでなく、 他の楽楽シリーズのサービスにもAI活用を視野に入れ、機能アップデートを継続する ことも視野に入れています。 ラク スは、顧客基盤の強みを生かし、バックオフィスサービスへのAI活用を早期に展開することで、顧客企業全体の業務効率化、生産性向上に貢献しようとしています。 そして、 ラク スは バックオフィス業務支援領域で最も頼りにされる存在 を目指しています。 AI技術を駆使して、顧客の要望や法改正などの外部環境の変化に柔軟に対応しながら、常に進化し続けることで、バックオフィス業務のDXを包括的に支援し、業界をリードする存在となることを目指しています。 プロダクトでのAI活用を成功させるために AIを活用してプロダクトを成長させるためには、ユーザー視点でその有効性を評価し、不確実性を考慮しながら現実的な計画を立てることが重要です。 そのために、AIの導入目的を明確にし、単なる技術導入ではなく、 課題解決を中心に据える ことが求められます。 また、PoCからMVP、最適化といった段階を踏みながら、 柔軟に計画を修正していく ことも不可欠です。 そうすることで、AIの力を最大限に活かし、プロダクトの価値を高めることができると考えています。 本記事が、AI活用によるプロダクト成長のヒントとなれば幸いです。
はじめに こんにちは。楽楽精算の サポートエンジニア を担当している梅田です。 サポートエンジニアの役割は、楽楽精算の仕様や技術を理解するだけでなく、問い合わせ対応時に適切な調査を行い、開発チームやインフラチームと連携しながら迅速に解決策を導くことです。そのため、テスト技法や業務知識を学び、最新の技術動向を把握することが大切です。 ラク スでは「 学習し成長し続ける 」ことを行動指針のひとつとして掲げており、 私たちサポートエンジニアもこの指針を大切にしています 。 お客様により良いサポートを提供するためには、製品知識に加えて、技術や法制度の変化を理解し、 常に最新の情報に対応していくことが不可欠 です。 そこで、実務に役立つスキルを学び、 サポートエンジニアチーム内や所属している課の単位で 勉強会を開催・実施しています。本記事では、 これらの勉強会の取り組みを通じて得られた成果 をご紹介します。 はじめに なぜ勉強会が必要なのか? 学習の必要性を実感したお問い合わせエピソード 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート 2023年度:チームの関心が高まり、継続的な学習文化へ 2024年度:業務時間内の勉強会が本格化 スキルアップとチームの成長を目指す「勉強会の目的と取り組み」 目的1. 実務に活かせるスキルの向上 工夫:実務に直結するテーマを選定 目的2. キャリアの方向性を明確にする 工夫:スキルの可視化とフィードバックの仕組み 目的3. チーム内の知識共有を促進する 工夫:持ち回り発表者制によるアクティブラーニングの実践 持ち回り発表者制のメリット・デメリットとサポート体制 メリット デメリット サポート体制 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 「学習し成長し続ける」ために     なぜ勉強会が必要なのか? サポートエンジニアの業務では、技術調査や問い合わせ対応に加え、製品仕様や法制度の理解が欠かせません。しかし、新機能の追加や法改正のたびに、日々の業務だけで知識をすべて習得するのが難しいのが現実です。 学習の必要性を実感したお問い合わせエピソード 定時実行で生成されるファイルに、実行時間直前に作成したデータが含まれていないというお問い合わせがありました。 当初はデータが消失したのではないかと懸念しましたが、調査の結果、次回の定時実行には問題なく出力されることが確認できました。これは、システムの負荷を分散させるために処理タイミングを調整している設計によるもので、仕様どおりの動作でした。 もしシステムの設計を深く理解していれば、落ち着いて対応し、迅速に説明できたと感じました。 この経験を通じ、機能の仕様だけでなく、処理の仕組みや技術的な制約を深く理解し、お客様に的確に伝える力を磨く必要があると改めて認識しました。 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート サポートエンジニアチームの学習文化は、私自身の1人勉強会(自主学習)から始まりました。当時の私は、 システムエンジニア としての経験はあったものの、テスト設計や品質保証の視点が不足しており、 お客様からの問い合わせに対し、システムのどの部分を検証すべきか、影響範囲をどのように調査すべきかを効率的に判断することが難しい状況でした。 そこで、 上司からの勧めもあり 、 業務の幅を広げるためにテスト設計とソフトウェア品質の評価、 ITサービスマネジメント という視点を 体系的に学習 することを決意しました。 JSTQB(ソフトウェアテスト技術者資格) を通じて、テスト設計の基礎やソフトウェアの品質を評価する視点を学びました。特に、「どのような観点で検証すれば、システムの品質を維持できるか」という考え方を学んだことで、問い合わせ対応の精度が向上しました。学んだ知識を活用することで、ログ分析や再現手順を整理し、調査をより明確に進めることができました。 また、 ITIL(ITサービス運用資格 ) を学ぶことで、問い合わせ対応を単発の業務として終わらせず、継続的な改善につなげるという視点も得ることができました。学習の結果、問い合わせ内容を蓄積し、対応フローの最適化やドキュメント整備を進めることで、よりスムーズな対応ができるようになりました。 2023年度:チームの関心が高まり、継続的な学習文化へ 私が取り組んでいた自主学習に興味を持つメンバーが増え、 有志のメンバーが集まり、少人数での勉強会が始まりました 。この勉強会では、 チームとして JSTQB 、 ITIL 、 日商簿記(財務会計資格) 、 OSS-DB(データベース資格) 、 LinuC(Linux資格) などの取得を目的として幅広い分野を学習しました 。 JSTQB ・ ITIL の学習を通して、 品質保証 と ITサービスマネジメント に関する知識を深め、 問い合わせ対応の精度向上 に貢献しました。 日商簿記 の学習では、 経理 業務に関する理解を深め、 運用に沿ったご案内 や 会計に関する適切なご説明 ができるようになり、 サポート品質の向上 に繋がりました。 OSS-DB ・ LinuC の学習では、 データベースやサーバー に関する知識を習得し、 開発・インフラチームとの連携 を円滑化し、 技術的な問い合わせへの対応力 を高めました。特に、 OSS-DB の勉強会では、梅田以外の メンバーが発表者 を務めることで、 「教え合う」文化 が生まれました。発表者を担当したメンバーは、 人に伝えることで知識を深め 、参加者も 積極的に質問 することで、 相互理解を深めました 。 2023年を通して、 個人の学び が チーム全体の成長 に繋がる 学習環境 が整い、 資格取得 のみならず、 業務に直結する知識 を学び、 サポートの質を高める文化 が根付きました。 2024年度:業務時間内の勉強会が本格化 2023年度までの取り組みが評価され、2024年度からは 業務時間内での勉強会開催が本格化 しました。これにより、 資格取得 を目的とした学習から、 実務 や 自分たちのスキル成長 に直結する勉強会へと発展しました。 勉強会開催の推移 この年に新たに取り組んだ勉強会のテーマには、次のようなものがあります。 なぜなぜ分析勉強会 : 問い合わせ対応時の問題を深掘りし、問題の本質に迫るなぜなぜ分析の手法を学びました。この勉強会を通じて、 分析視点が身に付き、問題の本質を捉えた対応と問い合わせの迅速な回答、業務効率化 に繋げることができました。 生成AIの活用事例紹介 :生成AIを活用した問い合わせ対応の効率化や業務改善への応用方法を紹介しました。この勉強会を通じて、 問い合わせ対応の効率化 や 業務改善 に繋がる 具体的なア イデア が得られました。( 前回のブログ記事 で活用事例として紹介 しました) 経理 実務勉強会 :決算業務の流れを理解し、経費精算システム(楽楽精算)や会計システムとの連携について学習しました。この勉強会を通じて、 経理 業務全体の流れ と、 各システムがどのように連携しているか をメンバー全員が理解できました。 こうした実践的な学びを通じて、 業務の効率化や品質向上につながる知識を身につける機会が増えました。 スキルアップ とチームの成長を目指す「勉強会の目的と取り組み」 サポートエンジニアチームでは、 メンバーの スキルアップ と業務の質向上を目的に勉強会を実施 しています。単なる知識習得にとどまらず、 実務に活かし、キャリアにつなげ、チーム全体を成長させる ことを目指し、次の 3つの目的 を掲げています。 目的1. 実務に活かせるスキルの向上 サポート業務では、 製品の検証や運用、品質保証の知識に加え、テスト設計や自動化、影響調査などのスキルが必要です 。これらを実践的に学び、日々の業務に活かせるようにすることが勉強会の第一の目的です。 工夫:実務に直結するテーマを選定 メンバーが「知識を得るだけでなく、すぐに活用できる」よう、勉強会のテーマを業務課題と結びつけています。 たとえば、 Playwright 勉強会 では、E2E(エンドツーエンド)テストの自動化手法を ハンズオン形式 で学びました。参加者はすぐに業務へ応用できました。実際に、勉強会の翌週には 自動化テストの導入を進めるメンバーが現れ 、成果につながりました。 目的2. キャリアの方向性を明確にする サポートエンジニアとしての専門性を高めるだけでなく、品質保証やシステム運用、開発との連携など、多様なキャリアの選択肢を意識できるようにすることを目的としています。そのため、 ITスキル標準 ( IT 分野で働く人がどんなスキルを持っているかを客観的に評価するための基準) を活用してスキルの棚卸しを行い、各メンバーが習得すべきスキルを明確にしています。 習得すべきスキルと達成度を可視化 工夫:スキルの可視化とフィードバックの仕組み ITスキル標準 を基に、サポートエンジニアに必要なスキルを整理し、それに沿った勉強会のロードマップを作成しました。スキル習得の進捗を確認しながら、次に学ぶべき内容を計画し、定期的な1on1で上司と話し合う機会を設けています。 参考:2024年度勉強会のロードマップ 目的3. チーム内の知識共有を促進する 勉強会を通じて、 チーム全体のスキルを向上させ、属人化を防ぐ ことも大きな目的です。知識が特定のメンバーに偏らないようにすることで、 誰もが安心して業務に取り組める環境 を整えています。 工夫:持ち回り発表者制によるアクティブラーニングの実践 サポートエンジニアチームでは、「自分が知りたいことを学び、それを共有することで理解を深める」という アクティブラーニング の考え方を取り入れ、メンバーが交代で発表を担当する 持ち回り発表者制 を導入しています。 勉強会のロードマップは、運営者(梅田)が作成し、各メンバーが必要なスキルを明確にできるよう調整しています。発表者は、このロードマップに沿って準備を進め、知識を深めながらアウトプットの機会を得ることで、実務に直結するスキルを身につけています。 また、「分からなければ聞く」「確認しやすい」というサポートエンジニアチームの文化を活かし、発表後のディスカッションを活発に行うことで、相互理解を深めています。 持ち回り発表者制のメリット・デメリットとサポート体制 持ち回り発表者制は、チーム全員が主体的に学ぶ環境をつくるために導入しました。 しかし、発表者にとっては学びの機会となる一方で、準備の負担や進行の難しさといった課題もあります。そのため、発表者制のメリットとデメリットを整理し、より円滑に進めるためのサポート体制を整えています。 メリット 参加者が受け身にならず、主体的に学ぶ機会が増える 発表者を担当することで、理解を深めるだけでなく、伝える力も身につく 特定の人に負担が偏らず、チーム全体で スキルアップ できる デメリット 初めて発表者を担当するメンバーにとっては、準備に時間がかかる 人によって説明のレベルや進行のスムーズさに差が出る サポート体制 こうした課題を解決するために、 発表者向けのサポート体制 を整えています。初めて発表者を担当するメンバーには、過去の資料を共有し、必要に応じて経験者がフォローすることで、負担を軽減しています。 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 サポートエンジニアチームでは、業務に直結する資格の取得を推奨しており、特に JSTQB ・ITILv4・ 日商簿記 3級 は「3種の神器」として定着しています。これらに加えて、運用管理・技術インフラ・品質保証の知識を深めるため、さまざまな資格取得にも取り組んでいます。 資格取得は スキルの可視化 や キャリア形成の指標 となるだけでなく、実務での適用を意識した学習につながっています。 以下に、取得を推奨している資格を 「品質保証」「運用・管理」「技術インフラ」「業務知識」 のカテゴリに分けて整理しました。 サポートエンジアチーム推奨資格一覧 カテゴリ 資格名 推奨度 説明 品質保証 JSTQB ◎ ソフトウェアテスト に関する国際的な資格認定制度で、品質保証に必要な知識を体系的に学ぶことができる。 品質保証 JCSQE ○ ソフトウェア品質保証に関する知識を体系的に学べる資格。 開発プロセス や品質管理の基礎を理解し、実務での品質向上に役立てることができる。 運用・管理 ITIL ◎ ITサービスマネジメント に関するベストプ ラク ティスを体系化した フレームワーク 。ITサービスの品質向上や効率化を体系的に学ぶことができる。 技術インフラ LinuC ○ Linux のシステム管理や運用に関する知識を証明する資格。サーバー管理やインフラ構築の基礎を体系的に学ぶことができる。 技術インフラ OSS-DB ○ オープンソース のデータベースに関する資格認定制度。特に PostgreSQL の管理や運用に必要な知識を体系的に学ぶことができる。 技術インフラ AWS(SAA) ○ クラウド サービス「 AWS 」に関する技術知識や運用スキルを証明する資格。インフラ設計やセキュリティ対策の理解を深め、 クラウド 環境の適切な活用に役立てることができる。 業務知識 日商簿記 ◎ 企業の運営や会計に関する基礎知識を体系的に学ぶことができる。 サポートエンジニアチーム全体のスキル向上を目的に、資格取得を積極的に推進した結果、 資格取得者は年々増加 しており、以下のような推移を示しています。 資格取得者数の推移 「学習し成長し続ける」ために サポートエンジニアチームの勉強会は、2024年度から本格的に始まり、まだ発展の途中です。現状、発表者が業務多忙や急遽の休みなどで勉強会がスキップになることがあります。 勉強会を停滞させずに継続するには、発表者の負担を分散し、進行を柔軟に調整できる仕組みを整えることが重要です。 今後も、勉強会の着実な継続を続けて、自分たちの成長につなげるために、勉強会運営の工夫を続けていきます。 また、これまでチーム内での学習が中心でしたが、他部署でも専門的な勉強会( リファクタリング 最新技法など)が開催されていることが分かりました。今後は、 社内の知見を活かし、他部署の発表者を招いたり、資料提供を受けたりすることで、学習の幅を広げていきます 。 私自身も、勉強会の質を向上させるために、 LT会 やテックブログへの参加を継続し、資料作成やプレゼンテーションのスキルを磨いていきます。さらに、チーム内で関心の高いコンテンツ(最近であればコンテナ技術やNotebookLMを活用した業務改善等)についても学び、実務への応用を進める予定です。 「 学習し成長し続ける 」 という姿勢を大切にし、新しい知識を積極的に吸収しながら他業界の優れた事例も取り入れ、個人とチームの成長につなげていきます。勉強会を実践的な学びの場へと進化させ、さらに成長を加速させるため、今後も改善を続けていきます。 const ctx = document.getElementById('studyChart').getContext('2d'); const studyChart = new Chart(ctx, { type: 'bar', data: { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格取得向け', data: [1, 3, 0], // 各年の資格取得向け勉強会数 backgroundColor: 'rgba(54, 162, 235, 0.6)' }, { label: '業務知識向け', data: [0, 0, 7], // 各年の業務知識向け勉強会数 backgroundColor: 'rgba(255, 206, 86, 0.6)' }, { label: '技術向け', data: [0, 0, 5], // 各年の技術向け勉強会数 backgroundColor: 'rgba(75, 192, 192, 0.6)' }, { label: '最新技術&ツール', data: [0, 0, 3], // 各年の最新技術系勉強会数 backgroundColor: 'rgba(255, 99, 132, 0.6)' } ] }, options: { responsive: true, scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } } } }); const ctx2 = document.getElementById('radarChart').getContext('2d'); const data = { labels: ['実務/テスト', '実務/プロダクトサポート', '知識/商材理解', '知識/IT共通', '知識/専門知識'], datasets: [{ label: '達成度', data: [10, 59, 67, 50, 82], borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', pointBackgroundColor: 'rgba(54, 162, 235, 1)', pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: 'rgba(54, 162, 235, 1)' }] }; const options = { responsive: true, maintainAspectRatio: false, aspectRatio: 1, // チャートの縦横比を調整(小さく) layout: { padding: 0 // 余白を最小限に }, plugins: { legend: { display: false // 凡例を非表示 } }, scales: { r: { suggestedMin: 0, suggestedMax: 100, ticks: { stepSize: 25, callback: function(value) { return value + '%'; } }, pointLabels: { font: { size: 10 // 軸ラベルのフォントサイズを小さく } }, grid: { lineWidth: 0.5 // グリッドの線を細くして余白を減らす } } } }; new Chart(ctx2, { type: 'radar', data: data, options: options }); const ctx3 = document.getElementById('qualificationChart2').getContext('2d'); const data2 = { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格保有者数(累計)', data: [3, 17, 23], // 各年度の資格保有者数(累計) backgroundColor: 'rgba(0, 115, 207, 0.2)' // 青色 } ] }; const qualificationChart2 = new Chart(ctx3, { type: 'bar', data: data2, options: { responsive: true, plugins: { legend: { display: false } // 凡例を非表示 }, scales: { y: { beginAtZero: true, title: { display: true, text: '資格保有者数' }, ticks: { precision: 0 } // 小数点を表示しない } } } });
こんにちは!株式会社 ラク スで技術広報を担当している川東と申します! 2025年2月27日に開催された 『EMConf JP』 は、エンジニア リングマ ネジメントを実践するEMにとって刺激的なイベントでした! 公式サイト( fortee.jp/emconf-2025 )にもある通り、今回のテーマは 「増幅」 と 「触媒」 。すなわち、EMたちが持つ熱意や知見をより大きく広げ、組織全体に好影響を与えるセッションが盛りだくさんでした。熱気あふれる会場には多くのEMやエンジニアが集まり、リアルな知見や体験を共有する貴重な場となりました。 弊社からはPdMチームのEMである稲垣が登壇しております! 本レポートでは、ホールAとホールBの両方で行われたセッションを、筆者の独断と偏見で学びを感じたポイントに絞って振り返っていきたいと思います! (実際にイベントに参加して技術広報2名で全セッション視聴してきました!) セッションレビュー キーノート エンジニア リングマ ネージャという働き方と知識体系のロードマップ(広木大地/レクター) EMとしての役割は「エンジニアの管理職」から進化し、【4つのP】―人材育成、技術戦略、プロジェクト管理、プロダクト連携―が求められるようになったという視点が印象的でした。また、フェ イルファ ストの精神や 心理的 安全性の確保など、現場で実践すべきポイントを明確に示してくれました。 hirokidaichi.github.io n=1の経験が紡ぐエンジニア リングマ ネジメントの可能性(岩瀬義昌/ NTTコム 、fukabori.fm) 個人的に一番印象に残ったセッションです。実践に基づくリアルな体験談と、EMとしての具体的なノウハウ(「ポエムドリブンマネジメント」など)が盛り込まれており、すぐにでも現場で活かしたい内容ばかりでした。 speakerdeck.com 【ホールA】 Potential EM制度を始めた理由、そして2年後にやめた理由(青木啓剛/estie) 期間限定でEMとしての素養を見極める制度の試み。その中で、EMに求める最重要要件や、常に組織を変革していく必要性が浮き彫りにされました。 speakerdeck.com エンジニアリング価値を黒字化する、バリューベース戦略を用いた技術戦略策定の道のり( Kazuki Maeda/atama plus) 経営戦略と技術戦略の橋渡しとして、バリューベース戦略を活用する方法が紹介され、経営層と対等な目線で議論を進めるための具体的なアプローチが印象に残りました。 speakerdeck.com 1行のコードから社会課題の解決へ:EMの探究、事業・技術・組織を紡ぐ実践知(熊谷遼平/レディーフォー) 事業、技術、組織の3軸からEMの役割を探究している点が印象的でした。特に、学習文化形成のために業務時間の3割を本業以外に充てる施策は、メンバーの成長と組織の活性化に寄与すると感じました。 speakerdeck.com Two Blades, One Journey: Engineering While Managing(ohbarye/スマートバンク) EMとICの両立が可能であるという実体験からの提案。EMとしての柔軟な働き方や、必要に応じた役割転換のヒントが、参加者にとって大いに参考になるだろうと思わせるセッションでした。 speakerdeck.com 「共創型エンジニア リングマ ネジメント」の挑戦と実践(うっしー/kubell) 組織拡大に伴う局所最適やサイロ化の問題に対し、EMグループ全体で課題解決に取り組む姿勢は、共創の精神そのものでした。全員が責任を共有するという考え方は、今後の組織運営においても非常に魅力的です。参考にしたいです! speakerdeck.com サバイバルモード下でのエンジニア リングマ ネジメント(こにふぁー/Kyash) 売上減少や人材流出といった厳しい現場での実践例は、危機時における判断の難しさを改めて認識させられる内容でした。​会場は立ち見が出るほどに盛況でした! speakerdeck.com 【ホールB】 プロダクト部門のマネージャー全員でマネジメントポリシーを宣言した記録(piro takahara/スタンバイ) 部門全体で共通のポリシーを作ることで、情報共有と成功体験のすり合わせが図られ、組織拡大の中での一体感を醸成する重要な取り組みとして参考になりました。 speakerdeck.com 急成長する企業で作った、エンジニアが輝ける制度(池ノ上倫士/SHIFT) EMがいない状況下で導入されたスキルツリーや評価制度が、給与上昇率向上や 離職率 の低下につながった具体例が紹介され、エンジニアが輝ける土台作りの重要性を再認識させられました。 speakerdeck.com 楽しいぞEM拡張パズル!課題と共に役割を広げちゃおう!(笹健太/クリエーションライン) 「EMって何をすればいいのか?」という疑問を、PMトライアングルの視点から整理。完璧なスー パーマン ではなく、得意分野を生かしながら成長する姿勢は、多くのEMにも共感できるセッションだったと思います。 speakerdeck.com 大規模 アジャイル から学ぶ、エンジニア リングマ ネジメントの本質(Kittaka shun/TOKIUM) 複雑化する組織で、 アジャイル 手法を応用した意思決定と役割分担の工夫が紹介され、EMとしての自信とポジティブな取り組み方を後押しする内容でした。 speakerdeck.com わたしがEMとして入社した「最初の100日」の過ごし方(daiksy/ はてな ) パラシュート人事としてのスタートに伴う期待値調整や、既存の文化へのリスペクトの重要性を具体的に語っており、新任EMにとっては必聴のアド バイス となりました。 speakerdeck.com プレイングマネージャー は本当に悪なのか? - 令和時代の プレイングマネージャー を戦略的にハックする(すずけん/カミナシ) 技術力とマネジメントを同時に発揮する プレイングマネージャー のメリットと課題を、実践的な視点で議論。意思決定と実行の迅速さ、評価への納得感が強調されました。 speakerdeck.com EMの仕事がLVアップした3つの視点 ~これまでに出会ったEMから学んだこと~(shinden (新田 智啓)/Datachain) 「WHY(価値を知る)」「WHAT(理想と課題設定)」「HOW(体系的な学び)」という3つの視点で、EMとしてのレベルアップを図る方法がシンプルかつ説得力をもって伝えられました。 speakerdeck.com 多様なマネジメント経験から導き出した、事業成長を支えるEMの4つの コンピテンシー (稲垣剛之/ ラク ス) 弊社(株式会社 ラク ス)からはPdMチームを統括するEMの稲垣が登壇しました。今回、大変多くのCfPの中から弊社EMのCfPを採択していただけたのはとても光栄でした! セッションの内容としては、論理的かつ具体的に、EMが事業成長にどう寄与できるかを弊社の評価軸である コンピテンシー という形で整理しました。実践的なヒントをお届けできていれば幸いです。 © 2025 EMConf JP 2025 実行委員会 speakerdeck.com 「Govtechという巨大な山に挑む」エンジニアが活躍する、世界最強の行政DXチームを目指す(井原正博/廣瀬幸帆/GovTech東京) 新しい公共 価値の創出に向け、内製化と組織文化の再構築を目指す壮大なビジョンが示され、行政領域でのチャレンジとして非常に刺激的な内容でした。 まとめ EMConf JPはオフライン限定のイベントでしたが、会場の熱量にすさまじいものを感じました! 各セッションを通じて感じたのは、確かにEMとしての業務は容易ではないものの、失敗を恐れずチャレンジすることで得られる学びや、組織全体に与えられる インパク トの大きさです。 今後もこのようなイベントが開催されることで、各々の知見や経験を共有しながら、より多くのEMが組織の成長に貢献していく未来を期待せずにはいられません。 EMに興味のある皆さん!ぜひ、この熱い波に乗って、EMとしてのキャリアをさらに進化させてみてはいかがでしょうか?
はじめに エンジニア2年目のTKDSです! 今回は GitHub のデ バイス フローを利用したユーザー認証の方法についてご紹介します。 デ バイス フローはブラウザに直接アクセスできない CLI ツールでも GitHub 側に認証を依頼できる機能です。 今回はデ バイス フローによる認証を経て発行されるアクセス トーク ンを使って、認証・認可を要求したユーザー名を取得し、ユーザー確認に利用するところまでやっていきます。 はじめに GitHub Appsによるデバイスフロー 実際にやってみる GitHub Appsの準備 アプリの準備 実行 まとめ 参考文献 GitHub Appsによるデ バイス フロー 認証リク エス ト時に返ってくるURL・ユーザーコードを使用し、ブラウザで認証を行うとアプリケーション側でアクセス トーク ンの発行を行えます。 アクセス トーク ンのリク エス トには初回の認証リク エス ト時に返ってくるデ バイス コードが必要なため、アクセス トーク ンのリク エス トを行うエンドポイントを単純に叩いてアクセス トーク ンを横取りされる危険性は低いと考えられます。 この方法を利用するメリットとしては、 - ユーザーはURLを開いて、ユーザーコードを入力するだけでいい - ユーザーの認証機能を直接提供せずに、 GitHub に認証を依頼できる - ユーザーに認証させたあと取得できるユーザー トーク ンを使いユーザー情報を取得できるので、認証依頼時点でユーザーの情報を知ってる必要がなく、エンドポイントから返ってきたユーザー トーク ンを使ってはじめてユーザーの特定ができる - GitHub Appsで権限を絞れば、ほとんどの情報取得をシャットアウトできる→ユーザー トーク ンはもちろんもらさない前提で安全性も高い 等があります。 ユーザー側の利便性、アプリ自体はURLとユーザーコードを提供するだけの利便性がいいと思って今回採用してみました。 では次に実装例を示していきます。 実際にやってみる 今回やりたかったのは、ユーザー認証と認証したユーザーのアカウント名の取得です。 まず、今回の処理の流れを以下に示します。 ユーザーがまずアプリを通して、デ バイス フローを GitHub に依頼します。 そして、返ってきた値のうち、URLとuser_codeをユーザーに提示します。 ユーザーが認証するまでの間、アプリ側は GitHub の トーク ン払い出しのエンドポイントをたたきつづけます。 ユーザーは表示されたURLにアクセスし、ログイン後、アクセス トーク ンの入力・権限の認可を行います。 ユーザーによる認可が完了した以降で、アクセス トーク ン払い出しURLにアクセスすると、ユーザーコードを使って認証・認可をしたユーザーに紐付いたアクセス トーク ンを取得できます。 この時点でアプリはユーザーが誰かわからないので、アクセス トーク ンを使ってユーザーの情報を取得します。   最終的にレスポンスのloginキーに含まれてるユーザー名を確認することで、どのユーザーがアクセス トーク ンを払い出したかわかります。 では、実際に準備していきます。 GitHub Appsの準備 今回のデ バイス フロー認証には GitHub Appが必要なため用意します。 New GitHub Appで GitHub Appsの作成をしてください。 設定画面に遷移するので、 Enable Device Flow にチェックをいれてください。 次に、 GitHub Appによって発行された トーク ンになんの操作を許すか設定します。 今回は個人のメールアドレスだけですが、ここで設定を変えれば、所属してるオーガナイゼーションの取得なども可能になります。 今回は一番弱い権限かつ取得しても問題なさそうな情報にしたいので、Email adressesの項目をRead-Onlyで設定します。 これで GitHub Appの設定は完了です。 client_id を控えておいてください、あとで使います。 アプリの準備 CLI アプリを準備していきます。 CLI アプリ側でやりたいことは、デ バイス フローのリク エス ト、ユーザーへのURLとuser_codeの提示、アクセス トーク ン払い出しのポーリング、取得したアクセス トーク ンによるユーザー情報の取得です。 Goによるコードを書きました。 サードパーティ のライブラリ使ってないため、そのまま動くと思います。 今回は先にgo mod initしてしまっていたので、go.modがある状態前提です。 package main import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "strings" "time" ) const ( // clientID = "" // GitHub AppのClient ID deviceAuthURL = "https://github.com/login/device/code" accessTokenURL = "https://github.com/login/oauth/access_token" githubUserURL = "https://api.github.com/user" pollingInterval = 5 * time.Second // intervalより長くしない、とりあえず5秒 ) // デバイスコード type deviceCodeResponse struct { DeviceCode string `json:"device_code"` UserCode string `json:"user_code"` VerificationURI string `json:"verification_uri"` ExpiresIn int `json:"expires_in"` Interval int `json:"interval"` } // アクセストークン type accessTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` Scope string `json:"scope"` Error string `json:"error,omitempty"` } var clientID string func main() { clientID = os.Getenv( "CLIENT_ID" ) // 1. デバイスコードを取得 deviceCodeRes, err := requestDeviceFlow() if err != nil { log.Fatal(err) } fmt.Printf( "verification_uri: %s \n " , deviceCodeRes.VerificationURI) fmt.Printf( "user_code: %s \n " , deviceCodeRes.UserCode) // 2. アクセストークンを取得(ポーリング) timeout := 3 * time.Minute ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() var accessTokenResponse accessTokenResponse loop: for { select { case <-ctx.Done(): default : accessTokenRes, err := requestAccessToken(deviceCodeRes.DeviceCode) if err != nil { log.Fatal(err) } if accessTokenRes.AccessToken != "" { accessTokenResponse = *accessTokenRes break loop } switch accessTokenRes.Error { case "authorization_pending" : fmt.Println( "ユーザー認証待ち" ) case "slow_down" : fmt.Println( "ポーリング感覚短すぎ" ) case "access_denied" : log.Fatal( "認証失敗" ) case "expired_token" : log.Fatal( "デバイスコードの有効期限が切れた" ) default : log.Fatal(accessTokenResponse.Error) } // time.Sleep(pollingInterval) } // time.Sleep(time.Duration(deviceCodeRes.Interval) * time.Second) time.Sleep(pollingInterval) } fmt.Println(accessTokenResponse) userInfo, err := getGitHubUserInfo(accessTokenResponse.AccessToken) if err != nil { log.Fatal(err) } fmt.Println(userInfo) } func requestDeviceFlow() (*deviceCodeResponse, error ) { client := http.Client{} sendData := url.Values{} sendData.Set( "client_id" , clientID) sendData.Set( "scope" , "repo,user" ) req, err := http.NewRequest( "POST" , deviceAuthURL, strings.NewReader(sendData.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) dCodeResp, err := client.Do(req) if err != nil { log.Fatal(err) } defer dCodeResp.Body.Close() var deviceCodeRes deviceCodeResponse err = json.NewDecoder(dCodeResp.Body).Decode(&deviceCodeRes) if err != nil { return nil , err } return &deviceCodeRes, nil } func requestAccessToken(deviceCode string ) (*accessTokenResponse, error ) { client := &http.Client{} data := url.Values{} data.Set( "client_id" , clientID) data.Set( "device_code" , deviceCode) data.Set( "grant_type" , "urn:ietf:params:oauth:grant-type:device_code" ) req, err := http.NewRequest( "POST" , accessTokenURL, strings.NewReader(data.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) tokenResp, err := client.Do(req) if err != nil { return nil , err } defer tokenResp.Body.Close() var tokenRes accessTokenResponse err = json.NewDecoder(tokenResp.Body).Decode(&tokenRes) if err != nil { return nil , err } return &tokenRes, nil } func getGitHubUserInfo(accessToken string ) ( string , error ) { req, err := http.NewRequest( "GET" , githubUserURL, nil ) if err != nil { return "" , err } req.Header.Set( "Authorization" , "Bearer " +accessToken) req.Header.Set( "Accept" , "application/vnd.github.v3+json" ) client := &http.Client{} resp, err := client.Do(req) if err != nil { return "" , err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "" , err } return string (body), nil } 実行 漏れたら困る値もあるのでぼかしつつ実際の流れを載せていきます。 1 . アプリ起動 環境変数 からclient_idを取得するようにしてあるので、 コマンドライン で指定して起動します。 client_idには、先ほど GitHub Appを作ったときに控えておいたclient_idを指定してください。 CLIENT_ID="<控えておいたclient_id>" go run main.go verification_uri: https://github.com/login/device user_code: xxxx-xxxx 2 . ブラウザでユーザーコードを入力 表示されたURLにアクセスし、user_codeを入力してください。 ユーザーコードの認証が成功すると、権限委譲の画面に移行するので、緑のボタンを押してください。 成功すると以下画面が表示されます。 これでユーザー側操作は完了です。 3 . アクセス トーク ンの取得とユーザー情報の取得 アプリ側は2の間ずっと トーク ン払い出しのエンドポイントをポーリングしています。 これには事前に払い出したclient_idと認証リク エス ト時にuser_codeと一緒に返ってくるdevice_codeを使用します。 取得できたアクセス トーク ンをもとにユーザー情報を取得します。 ここで取得したユーザー情報をもとに、アクセスしていいユーザーなのか判別などします。 ※ここの画面のスクショはアクセス トーク ンやユーザー情報が出てしまっているので割愛します。 以上で、デ バイス フローによるユーザー認証ができました。 まとめ 今回はデ バイス フローを利用して、 ユーザー認証を行いました。 今回用意したアプリではclient_idをユーザー側が入力しましたが、サーバー側で行うようにすることでより堅牢にユーザー認証できるのではないかと考えられます。 GitHub 側に認証を任せて簡単にユーザー認証ができるのは非常に便利です。 ここまで読んでいただきありがとうございました! 参考文献 https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-login-with-github-button-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#using-the-device-flow-to-generate-a-user-access-token https://www.authlete.com/ja/developers/device_flow/ https://tex2e.github.io/rfc-translater/html/rfc8628.html?utm_source=chatgpt.com
はじめに ラク スのプロダクトデザイン組織マネージャーの小林です。 私たちの所属する 「プロダクトデザイン課」は、お客様の業務課題を解決すべく、全プロダクトのUI/UXデザインを担うチーム です。 2025年、プロダクトデザイン課はお客様への価値提供をより一層高めるため、新たな挑戦に踏み出します。 そこで改めて組織紹介も兼ね、ミッション・ビジョン、これまでの歩み、現在取り組んでいること、今後の挑戦についてお話したいと思います。 はじめに プロダクトデザイン課のミッション・ビジョン ミッション・ビジョン策定に至る経緯 顧客理解を高めるデザイナーの取り組み 顧客ニーズを踏まえ主体的な提案も増える UI刷新への挑戦 顧客価値を高める今後の取り組み プロダクトデザイン課のミッション・ビジョン 私たちデザイナーに共通するお客様の課題解決への思いと、 その実現へ向けてあるべきチームの状態を宣言しました。 ミッション プロダクト開発の中心となり、顧客課題を解決する優れたUXを生み出す ビジョン 日本を代表するBtoB SaaS の優れたUXを生み出すプロフェッショナルチーム ミッション・ビジョン策定に至る経緯 ラク スのデザインチームは2012年にゼロから立ち上げました。 私は開発マネージャーを務めていましたが、より課題解決につながるプロダクト作りへの思いと共に、 UI設計や ユーザビリティ への熱意も高まり、デザイン専門チームを作ることにしたのです。 立ち上げ期はデザイナーも少なく、 開発プロセス への関わり方、採用・育成方法、UIルールも手探りしながら進めていきました。 プロダクト規模の拡大とともに担当するUI設計規模も拡大しましたが、デザイナーの上流工程への関わり方はまだまだ限定的でした。 開発チームでほぼ決定した要件・仕様・期日に対し、きれいなUIを調整して提供するだけの時期もありました。 より主体的に顧客価値を提供するため、より深く 開発プロセス に関わりたいという思いが強くなっていました。 そのための課題が、 顧客業務や製品仕様の理解を高めること でした。 ラク スのプロダクトは業務 ドメイン にあわせ、個別に深く課題解決に踏み込んでいます。 その分、製品仕様の複雑性や難易度は高くなるとも言えます。さらに運用歴が長いプロダクトは開発経緯への理解も必要となります。 私たちも改めて、顧客課題解決に踏み込んでいくことを決意し、ミッション・ビジョンを策定することにしました。そこでの思いは、主に下記の二つに集約されます。 ・表面上のUIでなく、顧客業務の課題をしっかり理解し、課題解決できるUI/UXをつくることがデザイナーの一番の役割 ・ビジネスサイド、プロダクトマネージャー、エンジニアなど、すべての部署とコミュニケーションをとり、要求を満たすUIをデザイナーが主体的に設計していきたい まとめると、かつてデザイナーは、開発チームが決めた要件・仕様をもとにUIを整える立場でした。 しかし、それでは本質的な課題解決ができないため、 「表面的なデザインではなく、顧客課題を解決するUX設計」 を重視する組織とするために、現在のミッション・ビジョンを定めました。 ただ顧客課題は日々進化しますので、顧客理解に終わりはありません。現在でも上流工程への関わり方についてまだまだ取り組むべきことは多いと考えています。 顧客理解を高めるデザイナーの取り組み 当社のデザイナーは顧客理解を深めるため、以下のような取り組みにも力をいれています。 ・商談動画の視聴 営業やカスタマーサクセスとお客様との会話を 追体験 し、リアルな業務課題を理解する取り組みです。 ・業務見学、 ヒアリ ング 実際に業務現場を把握し、より実用的なUI設計などに活かします。 ・資格取得 ラク スのプロダクトが手掛ける領域には、電子帳票保存法や インボイス 、 労働基準法 など、満たすべき法要件があります。デザイナーもUI設計を行う上で、必ず理解しておく必要があります。基礎知識として、簿記など関連資格の取得に取り組んでいます。 ・勉強会 幅広い顧客業務を体系的に理解するため、 経理 など分野別の勉強会も行っています。 業務の必要に応じ参加するほか、入社オンボーディングプログラム内でも動画視聴できるようにしています。 顧客ニーズを踏まえ主体的な提案も増える これらの取り組みを通じ、顧客ニーズを踏まえて主体的な提案を行えることも増え、より多くのお客様にご満足いただけるようになりました。 ここでは、 メールディーラー「メール対応画面のUI刷新」事例 をご紹介します。 メールディーラーはメール共有管理ツールです。 www.maildealer.jp ビジネスサイドが持つ顧客の声をデザイナーと密に連携し、既存・新規両方のお客様に満足いただけるUIを提供できた事例です。 メールディーラーは運用歴が20年以上の老舗プロダクトで、当時の インストーラ ー型メールUIを参考にしていました。 使い慣れているお客様も非常に多い一方、Web メーラー の時代にあわせたUI/UXのアップデートも必要でした。 そこでデザイナーを中心にプロトタイプを制作し、ビジネスサイドに提案しました。 営業、製品企画、カスタマーサクセスと目指す方向性を合意したのち、製品企画が既存顧客の業務運用に支障がないかを確認、営業が商談での反応を伺いながら顧客の声を集約し、UIUXの設計を実施しました。 さらに、フロントエンドエンジニア、サーバサイドエンジニアともシステム上の実現可能性をすり合わせたのち、無事に新たなUIをリリースすることができました。 UI刷新への挑戦 ラク スは 中長期を見据えた継続的なUX改善 を目指しています。 この一環で、 ラク スのプロダクトを横断するUI刷新 がスタートしました。 背景は、 SaaS の導入が一般的となり、複数の製品を検討する企業が増えてきたことです。 ラク スのプロダクトは 顧客の業務 ドメイン にあわせ、深く課題解決に踏み込むベスト・オブ・ブリード型の開発 を行っています。 そのため各プロダクトのUIも、各業務 ドメイン ごとに個別性が高いものとなっています。 今後はお客様が複数の SaaS を利用することも想定し、 どのプロダクトを利用しても共通体験を提供できるようにしたい と考えています。 現在、UI刷新のゴールや、どのように顧客体験を改善すべきか、役員陣も交えて議論しています。 具体的なUI刷新の進め方については、デザイナーも積極的に発案や取りまとめを行っており、デザイン、開発、ビジネスサイド間で活発に議論している最中です。 顧客課題解決と複数 ドメイン をまたぐ共通体験、両方を提供できるようしっかりと役割を果たしていきたいと思います。 ここでは、刷新後に予定されているUIを少しだけお見せします。 顧客価値を高める今後の取り組み UI刷新の一環で、デザインシステムの構築・導入にも取り組んでいきます。 この詳細は次回の記事でご紹介いたします。 UX向上を実現するためには、さらに深い顧客理解が欠かせません。 より積極的に顧客 ヒアリ ングやユーザーテストにも取り組んでいきたいと考えています。
はじめに プロダクトをつくる私たちエンジニアや組織は 「本当に顧客のために開発できているだろうか?」 と、一度は自問したことがあるのではないでしょうか。 事業成長し、組織が大きくなるにつれ、エンジニアと顧客の距離は遠くなりがちです。 かつては直接届いていた「この機能、助かりました」「ここが使いづらい」といった顧客の生の声も届きづらくなります。 複数チームでの分業や、多くの ステークホルダー が関わる場合、このように感じる方もいるのではないでしょうか。 こうした環境下では、  「リリースした機能は、本当に役に立ったのだろうか?」  「顧客はどんな機能をよく使い、どんな課題に直面しているのだろうか?」  「この対応の優先度は、本当に正しいのだろうか?」 といった疑問がよぎることもあるのではないでしょうか? 私たち ラク スも例外ではありません。 近年、開発メンバーが増え、組織が大きくなる中で、顧客の声がエンジニアに届きづらくなってきました。 しかし、 私たちは創業当初から「顧客志向」を徹底して大切にし 開発組織としては 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 というミッションを掲げ、「顧客志向」での開発に取り組み続けてきました。 結果としてプロダクトが顧客に支持され、 国内 SaaS 市場でARR No.1 を達成することもできました。 今後も顧客に選ばれ続けるプロダクト開発をするため、 私たち開発組織は「顧客志向」を徹底し大切にしていく事が重要 と考えています。 今回は「顧客志向の SaaS 開発組織」であり続けるために 私たちがどんな取り組みを行っているのか その一端を、技術広報からご紹介します。 はじめに 開発組織の「顧客志向」を強化する取り組み 管理職全員でのワークショップ 開発組織全メンバー向けワークショップ 顧客理解を深めるための情報を集約 顧客志向表彰 「顧客志向」を日々実践し、これからも徹底 開発組織の「顧客志向」を強化する取り組み 昨年、当社における「顧客志向」の浸透や日々の実践について 社内調査したところ、組織や人それぞれで大きくバラつきがあることが明らかになりました。 このような現状を管理職たちで議論した結果、 開発組織全体で「顧客志向」の重要性を再認識する 必要があるという事で一致しました。 そのため、まずは管理職から「顧客志向」の重視性を再認識する目的でワークショップを開催することになりました。 管理職全員でのワークショップ このワークショップでは、ゴールを 「顧客志向」の定義と実践方針を 言語化 し、共通認識化すること に設定しました。議論は以下のステップで進めました。 「顧客志向」の重要性の再認識:各自が考える「顧客志向」の重要性と、業務への影響をチーム内で共有・議論し、共通見解をまとめました。 顧客志向の定義策定:顧客志向の浸透にあたり、「顧客志向」をどのように定義するか、共通見解をまとめました。 具体的アクションの検討・実践方針の策定:顧客志向を強化するために、日々の業務や組織全体で実施すべき具体的なアクションと実践方針を議論し、共通見解をまとめました。 ワークショップを通じて管理職が考える顧客志向の定義と実践方針は以下に決まりました。 「顧客志向」の定義 ・顧客のニーズや課題を深く理解し、価値のあるソリューションを提供する ・また、変化するニーズやフィードバックに対して迅速対応と継続改善に取り組むこと 組織全体の実践方針 開発本部内の全社員が顧客がいる事を常に意識し ・顧客理解を深める   ex)利用者と同じ体験をする、一次情報(VoC等)に触れる ・顧客への提供価値を自分の言葉で説明できる このワークショップを通じて管理職たちの中で「顧客志向」の重要性を再認識することができ、 また、浸透や実践を強化していこうという共通認識を持つことができました。 また、組織全体で「顧客志向」をさらに浸透させることで一致し、全メンバーを対象にしたワークショップも開催することになりました。 社内にも議論の内容を共有しました 開発組織全メンバー向けワークショップ メンバー向けのワークショップの目的も 「顧客志向」の重要性に気付きを得る こととにしました。 加えて 担当プロダクトの顧客理解と提供価値を自分たちの言葉で説明できるようになる ことも目指して実施しました。 ワークショップの流れは以下 顧客志向の重要性を考えるワーク 顧客理解と製品理解を深めるディスカッション 課題解決とアクションプラン作成 最終発表 各チームは各チームはPdM、デザイナー、フロントエンド、バックエンド、インフラ、SREの混成で、各チーム4〜5名の異なる役割間でコミュニケーションが生まれるようにしました。 このワークショップを通じて、 参加メンバーたちは自分自身の言葉で議論することで、顧客ニーズを積極的に取りに行く必要性や、顧客業務の理解の重要さ、 顧客接点を持つビジネスサイドやプロダクトマネージャーとの連携強化などの気づきを得ていました。 またワークショップ後半では、顧客理解を深めるアクションのア イデア も出るようになり、市場調査やインタビュー、VoC活用、 ユーザビリティ テスト実施などの具体的な施策の議論も活発になりました。 参加後アンケートと ヒアリ ングから 参加者の9割以上 が「顧客志向」の重要性を再認識し、実践していこうと思ったとの回答を得られました。 メンバーについても「顧客志向」の重要性を再認識することができ、 また、実践を強化していこうと共通認識を持つことができました。 顧客理解を深めるための情報を集約 ワークショップを通じて課題としてあがった、 継続して顧客理解を深めるための仕組み も整えはじめました。 事業成長と組織が拡大するにつれて、顧客理解に必要な情報は分散している状況でした。 最新資料を探すことが難しく、資料の重要性も伝わりにくい状況であったほか、顧客や製品に関する基礎知識も、開発組織向けにまとまったものはありませんでした。 そこで、以下のような情報を開発組織の社内 ポータルサイト へ集約しました。 顧客・製品特性などの基礎情報 ※オンボーディング資料 商談動画 顧客満足度 調査 顧客要望の情報 などなど これにより開発メンバーが顧客理解を深められる情報にアクセスしやすくしました。 集約・公開後は「非常に役立った」「定期的にメンテナンスしていってほしい」などの声が多く寄せられるほか、ここからの情報をベースに製品の重要機能について学び合う勉強会企画も動き出しました。 顧客志向表彰 さらに、顧客志向を体現した個人の行動を称える「顧客志向表彰」も新設しました。 「顧客志向」を高める取り組みを共有し、その価値を認め合う ことで日々の挑戦を後押ししていきます。 「顧客志向」を日々実践し、これからも徹底 「顧客志向」の浸透と実践の強化を図るべく、ご紹介した直近の取り組みを通じて、 組織全体で「顧客志向」の重要性を再認識することができ、また、浸透や実践を強化していこうという共通認識を一気に高めることができました。 しかし、「顧客志向」の実践にゴールはありません。 日々業務の中で意識し、行動し続けることが重要と当社開発組織では考えています。 「顧客志向の SaaS 開発組織」としてあり続けるため 「顧客志向」を徹底して大切にし実践し続けていくため そのための仕組み作りと支援を、技術広報も取り組んでいこうと考えています。
インフラ開発部でテッ クリード を務めております上畑です。 みなさんはAnsibleコードを修正した後に そのAnsibleコードを本番環境へ適用する際、 ドキドキ していませんでしょうか? 前回、 Ansibleをバージョンアップする記事 を執筆し、大量のコード修正が必要になりました。 この記事では、 ラク スがどのようにしてAnsibleコードを ドキドキ せずに本番に適用しているか、その仕組みを紹介します。 目次 目次 1. はじめに 2. DockerによるAnsible自動実行CIシステム 3. その他、CI環境の工夫 3-1. 本番環境Dockerイメージの最新化 3-2. CI実行時のエラー調査 3-3. CI/CDの並列実行を実現するコード化 3-4. 定期Dockerイメージの構築 4. 最後に 1. はじめに 一般的に、Ansibleコードを修正してマージする際には、以下のような事前チェックを行うことが多いのではないでしょうか? Lint(構文)チェック 複数人によるコードレビュー 本番環境へのAnsible Dry-Run実行 ステージングや開発環境でのAnsible実行 特にAnsibleコードの修正で厄介なのは、 AnsibleのDry-Runでは確認できず、実際に実行しなければ挙動が確認できないコードがある という点ではないでしょうか。(例えばcronモジュールなど) そのため、多くの方が以下のような対策を取っているかもしれません。 本番環境の複製やステージング環境でAnsibleを実行し、問題を検証する しかし、これには次のような課題があります。 修正を繰り返すたびに実行が必要 環境準備が大変 作業後の環境復元でミスが発生する可能性 2. DockerによるAnsible自動実行CIシステム ラク スでは上記の対策に加え、次の方法を採用しています。 本番環境に近いDockerイメージを用意し、そのDockerイメージ対してAnsibleを実行する CI環境を構築しました。 これにより、Dry-Runではなく実行時の変更差異をAnsibleコードの修正を行いながら何度でも確認可能です。 Docker環境のため、一部実行できないコードや挙動の違いはありますが、実際にAnsibleを実行できるというのは大きなメリットです。 具体的なCI環境の処理フローは以下の通りです。 AnsibleコードをGitサーバにPush Gitサーバから親JenkinsのCI用ジョブへWebhook通知 CI用ジョブが、Lintチェックジョブを立ち上げてLintチェック(Ansible Lint、 YAML Lint)を実行 CI用ジョブが、Ansibleチェックジョブを立ち上げて、指定した本番相当のDockerイメージを起動し、Ansibleを実行 本番環境に対してAnsibleのDry-Runを実行 3. その他、CI環境の工夫 3-1. 本番環境Dockerイメージの最新化 上記CIを完了後にmainブランチへマージして本番環境へAnsibleの適用を行いますが、 その際、本番相当のDockerイメージへもAnsibleを適用してDockerイメージの更新を行います。この作業も自動化されています。 3-2. CI実行時のエラー調査 Ansibleを実行した際にエラーが発生して調査を行いたいことがありますが、CI環境では実行後にDockerコンテナを停止する為、エラーになった状態を維持することができません。 その為、CI実行後にDockerコンテナを停止しないオプションを用意し、Dockerコンテナにアクセスして調査できるようにしています。 3-3. CI/CDの並列実行を実現するコード化 弊社では、配配メールのサービスだけでも10種類以上のサーバがあり、1つのAnsible リポジトリ でこれらの環境を維持しています。 共通利用のコードに変更を行う際、すべての環境に対して手動でジョブを実行するのは非効率なため、 以下の様な YAML ファイルを用意して並列実行を自動化しています。 CDも同じ仕組みで自動デプロイを行っています。 # Docker CIジョブ ANSIBLE_DOCKER_RUN: - IMAGE: 'serverA:latest' PLAYBOOK: 'serverA.yml' stage: 'production' DOCKER_KILL: false - IMAGE: 'serverB:latest' PLAYBOOK: 'serverB.yml' stage: 'production' DOCKER_KILL: false # 本番Dry-Runジョブ ANSIBLE_HONBAN_DRY_RUN - PLAYBOOK: 'serverA.yml' stage: 'production' - PLAYBOOK: 'serverB.yml' stage: 'production' 3-4. 定期Dockerイメージの構築 Ansibleコードは当然ながら新規から構築を行っても同一の環境である必要があります。 よくあるコード修正の失敗として既存環境に対して適用することはできるが、新規に構築した環境では実行できないコードを作成してしまうことがあります。 例えば、以下のように実行順が逆になっている場合など # 新規に追加したコード - name: Install Package ansible.builtin.dnf: name: "A" enablerepo: "epel" # 修正前からあるコード - name: Enable Epel Repo ansible.buitin.file: src: "epel.repo" dest: "/etc/yum.repos.d/epel.repo" owner: "root" group: "root" これは既存の環境に適用した際はepel.repoが存在する為、エラーになることはありませんが新規に構築した際にはエラーになります。 実際にこんな単純なミスはないでしょうが、例えばrole単位に分かれていてplaybook内でrole名の指定順が逆であったため発生するなど 以外と発生するケースはあります。 これは上記DockerCIでも発見できない為、定期的(毎日)素のOSのイメージからAnsibleを実行して新規から構築することが可能か確認を行っています。 4. 最後に ラク スではAnsibleによるサーバの構成管理の環境を維持する為、その安全性を高める継続的な改善を行っています。 これ以外にもbehaveやmoleculeによる振る舞いテストの追加など強化を行っており、継続的改善を行っていきます。 以上、ありがとうございました!
インフラ開発部でテッ クリード をしております上畑です。 ラク スで利用しているAnsibleコードについて、Ansibleのバージョンアップを行った内容を記事にしました。 この記事が同じような境遇のどなたかの助力になれば幸いです。 1. 背景 2. Ansibleバージョンアップ 2-1. AnsibleとPythonの関係調査 2-2. 各OSの標準Pythonバージョン一覧調査 2-3. Porting Guideによる仕様変更の確認 2-4. バージョンアップ戦略 2-5. Ansibleコード修正内容 [修正対応内容] ansible-2.9.27 to ansible-8.7.0 ansible-8.7.0 to ansible-9.12.0 3. コード修正にはAnsible-Lintの自動修正(autofix)機能を使う 3-1. 実行方法 オプションの使い方 ルール一覧 4. Ansible-Lintバージョンアップ 4-1. Ansible-Lintバージョン推移 4-2. Ansible-Lint各バージョンへの対応 v4.7.0 to v5.4.0 var_naming unnamed-task v5.4.0 to v6.14.0 missing document start "---" (document-start) missing starting space in comment (comments) too few spaces before comment (yaml[comments]) deprecated-module name[casing] ※autofix利用可能 fqcn[action-core]※autofix利用可能 fqcn[action] no-changed-when: Commands should not change things if nothing needs doing. yaml[truthy]: Truthy value should be one of v6.14.0 to v6.22.2 var-naming[no-role-prefix] risky-shell-pipe 5. まとめ 1. 背景 2019 - 2020年に私が作った共通Ansibleテンプレートはansible-2.9.x(旧型Ansible)のコード体系をベースとして構築しており、現在も各サービス環境の構築や設定変更等業務で利用しています。 (記事) ラクスサービスを管理するAnsibleコードの共通テンプレートを作った話 - RAKUS Developers Blog | ラクス エンジニアブログ また、CI/CD環境としてAnsibleの構文チェック(Lint)やAnsible実行環境をJenkinsと連携して整備しており、業務にがっつり組み込まれていることからAnsibleのバージョンアップに伴うサービス影響を鑑みて見送って(半ば放置)しておりました。 しかし、定期的なJenkins(Dockerコンテナ)のバージョンアップを進めていくにあたって、Jenkinsイメージ内のOS更新に伴う Python サポートバージョン変更が上記環境の維持に影響が出たことが今回Ansibleバージョンアップを決心した背景となります。 2. Ansibleバージョンアップ 2-1. Ansibleと Python の関係調査 Ansibleはご承知の通り2.9から2.10に伴いansibleとansible-core(or ansible-base)に分かれましたので現在(2024年11月18日)までの状況をまとめました。 下記の表にもある通り、Ansible実行環境及び実行先の Python のサポートバージョンの範囲がある為、これに伴う影響を確認していきます。 出典: ansible-coreサポートマトリックス Ansible Version base/core Version EOL Ansible実行環境 Ansible実行先 ansible-2.9.x - EOL(2022/05/23) 2.7, 3.5-3.8 2.6-2.7 , 3.5-3.8 ansible-2.10.x ansible-base 2.10.x EOL(2022/05/23) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-3.x ansible-base 2.10.x EOL(2022/05/23) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-4.x ansible-core 2.11.x EOL(2022/11/07) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-5.x ansible-core 2.12.x EOL(2023/05/22) 3.8-3.10 2.6-2.7 , 3.5-3.10 ansible-6.x ansible-core 2.13.x EOL(2023/11/06) 3.8-3.10 2.6-2.7 , 3.5-3.10 ansible-7.x ansible-core 2.14.x EOL(2024/05/20) 3.9-3.11 2.7 , 3.5-3.11 ansible-8.x ansible-core 2.15.x EOL(2024/11/01) 3.9-3.11 2.7 , 3.5-3.11 ansible-9.x ansible-core 2.16.x 2025/05/01 3.10-3.12 2.7 , 3.6 -3.12 ansible-10.x ansible-core 2.17.x 2025/11/01 3.10-3.12 3.7-3.12 ansible-11.x ansible-core 2.18.x 2026/05/01 3.11-3.13 3.8-3.13 2-2. 各OSの標準 Python バージョン一覧調査 いくつか抜粋しますが、各OS標準の python のバージョンを確認しました。 上記の表ではansible-9.xからansible-10.xへ移行する際に、Ansible実行先として python -2.xのサポートを終了しています。その為、 RHEL / CentOS の7系への影響があります。 また、 Python -3.6のサポートも終了している為、ここではAlmaLinux/RockyLinux/ RHEL の8系の影響にも考慮が必要となることが考えられます。 OS OS Version Python Version RHEL / CentOS 7 2.7 AlmaLinux/RockyLinux/ RHEL 8 3.6 AlmaLinux/RockyLinux/ RHEL 9 3.9 Ubuntu 20.04 3.8 Ubuntu 22.04 3.10 Ubuntu 24.04 3.12 ラク スでも社内にあるいくつかの検証用の環境の一部がCentOS7の環境があり、現在リプレイス作業中でした。 その為、影響解決策として以下のいずれかの対応を検討しました。 python -2.7から python -3.xへの変更 OSのリプレイス完了まで待つ 並行で作業を行う為、まずは CentOS7環境のリプレイスが完了するまではansible-10.xへの変更は行わずに、ansible-9.12.0まで対応していくこと としました。 2-3. Porting Guideによる仕様変更の確認 Ansible公式サイトにはPorting Guide(移植ガイド)がありますので、利用しているAnsibleのモジュールにおいて仕様変更等がないか各バージョン毎に仕様変更への影響確認を行います。 その際に各モジュール単位で確認を行いますが、 ラク スのAnsibleコードではAnsible標準の機能による実装が中心となっているため、community.general. , ansible.builtin. などに限定することができ、比較的確認箇所が少なく済みました。 公式 Ansible Porting Guide 2-4. バージョンアップ戦略 一番留意が必要なのは、 「サービスへ影響をしないこと」 が重要である為、ansibleのバージョンアップは段階的に実施していきます。 以下ansible アップデート順になります。 From Version To Version base/core Version target Python Verison 2.9.x 2.9.27(2.9最終バージョン) --- 2.6-2.7, 3.5-3.8 2.9.27 2.10.7 ansible-base-2.10.17 2.6-2.7, 3.5-3.9 2.10.17 3.4 ansible-base-2.10.17 2.6-2.7, 3.5-3.9 3.4 8.7.0 ansible-core 2.15.13 2.7, 3.5-3.11 8.7.0 9.12.0 ansible-core 2.16.13 2.7, 3.6-3.12 一旦ココまで ---- ---- ---- 9.12.0 10.6.0 ansible-core 2.17.x 3.7-3.12 2-5. Ansibleコード修正内容 以下、各Ansibleバージョン間において対応した内容を記載しておきます。 弊社環境では以下2種類の修正のみで、ansible-9.12.0までの修正において、実行時の動作は問題ありませんでした。 [修正対応内容] ansible-2.9.27 to ansible-8.7.0 未定義の変数の使用について、厳格化されました。実際には[]内は文字列の予定でしたが、変数として評価されていた為対応しました。 The task includes an option with an undefined variable. --- - name: "{{ package_name }}" | default([httpd]) + name: "{{ package_name }}" | default(['httpd']) ansible-8.7.0 to ansible-9.12.0 Ansible 2.12(ansible 5.X)からDeprecatedとなっていたinclude:, import:の表記は[Ansible 9系から廃止]]( https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_9.html#id65 )されました。 Removed include which has been deprecated in Ansible 2.12. Use include_tasks or import_tasks instead. --- - - include: setup.yml + - include_tasks: setup.yml 3. コード修正にはAnsible-Lintの自動修正(autofix)機能を使う ansible-lintにはv6.15.0から autofix機能が実装 されました。 この自動修正機能を利用することでAnsibleコードの修正の9割に対応することができましたので紹介します。 3-1. 実行方法 自動修正機能付きで実行するにはansible-lint実行時に--fixを付与するだけです。 渡したplaybookに記載されているroleに対して、引っかかったルールに対して修正が行われます。 ※このオプションは、v6.20.0から追加されておりますので利用時はこれ以降のバージョンを使用してください。 ansible-lint [playbook].yml --fix 実際に修正が行われるとModified * files.と修正されたファイル数が表示されます。 diff機能はない為、git等で差分を確認しましょう。 # ansible-lint ansible-lint.yml --fix --- INFO Identified /etc/ansible as project root due .git directory. INFO Set ANSIBLE_LIBRARY=/etc/ansible/.cache/ansible-compat/0cb87f/modules:/usr/share/ansible/plugins/modules INFO Set ANSIBLE_ROLES_PATH=/etc/ansible/.cache/ansible-compat/0cb87f/roles:roles:/etc/ansible/roles INFO Executing syntax check on playbook ansible-lint.yml (0.77s) Modified 6 files. Passed: 0 failure(s), 0 warning(s) on 39 files. Last profile that met the validation criteria was 'production'. オプションの使い方 --fix ... --fix=allと同等、自動修正できる全てのルールを修正 --fix=,--fix name, fqcn , yaml ... 指定したルールのみ修正 ルール一覧 全てのルールに対して自動修正が行われるわけではなく、以下のルール一覧に記載されているものに対して修正が行われます。 対応しているautofixルール一覧 ざっと紹介すると以下の修正が可能です。 * command-instead-of-shell ... shellをcommandに変換 * deprecated-local-action ... local_actionの代わりに delegate _toを使う * fqcn ... モジュール名を fqcn 形式に変換(ansible.builtin.など一部のみ) * jinja ... jinja表記のformat修正 * key-order ... blockにwhenをつける場合の記載修正 * name ... 先頭が小文字から始まっている場合、大文字に変換 * no-free-form ... one-linerの記載を複数行記載形式に修正 * no-jinja-when ... when文のjinja記載を修正 * no-log-password ... no-logの記載を修正 * partial-become ... becomeやbecome_userの記載を修正 * yaml ... yaml 表記を修正 4. Ansible-Lintバージョンアップ 続いてAnsible-Lint環境についてもアップデートを検討しました。 4-1. Ansible-Lintバージョン推移 現行のAnsible-Lintはv4.3.7を利用しておりましたので、現時点での最新バージョンであるv24.10.0まで段階的にアップデートをおこない影響を確認しながら修正を実施しました。 以下バージョン推移 v4.3.7 > v5.4.0 > v6.14.0 > v6.22.2 > v24.10.0 4-2. Ansible-Lint各バージョンへの対応 ここからはAnsible-Lintのバージョンアップにおいて、各バージョン間における実際に修正が必要だった内容を記載しておきます。 v4.7.0 to v5.4.0 var_naming 変数名として使用できる文字の制限 影響が大きい為、一旦修正を行わず例外追加で対応(大文字を許可) .ansible-lint --- var_naming_pattern: "^[A-Za-z_][A-Za-z0-9_]*$" # 大文字を許可 unnamed-task name定義のないタスクの禁止 - import_tasks: install.yml - import_tasks: setup.yml # 以下に修正 - name: Install import_tasks: install.yml - name: Setup import_tasks: setup.yml v5.4.0 to v6.14.0 missing document start "---" (document-start) yaml ファイルの先頭に---を付与 --- missing starting space in comment (comments) 先頭コメントの後にコメント文の前にスペースが必要 #-name # 以下に修正 # - name: too few spaces before comment ( yaml [comments]) コメントの前のスペースは2つ以上。またはコメントの開始後のスペースがありません command: test #noqa 503 command: test # noqa 503 deprecated-module include:, import:の禁止 - include: # 以下に修正 - name: ... include_tasks: - import: # 以下に修正 - name: ... import_tasks: name[casing] ※autofix利用可能 nameの先頭文字は大文字であること - name: test # 以下に修正 - name: Test fqcn [action-core]※autofix利用可能 module名をansible.builtin.など新しい表記に対応が必要 - name: Test command: echo 'test' # 以下に修正 - name: Test ansible.builtin.command: echo 'est' fqcn [action] module名をansible. posix .やcommunity.general.など新しい表記に対応が必要 - name: Test sysctl: name: ... - name: Timezone timezone: name: ... # 以下に修正 - name: Test ansible.posix.sysctl: name: ... - name: Timezone community.general.timezone: name: ... ただし全ての修正が終わるまでは古いansible-lintでciを動かす為、一旦対象外にしておく .ansible-lint --- skip_list: - fqcn[action] no-changed-when: Commands should not change things if nothing needs doing. command module使用時にはchanged_whenの定義が必要 # コマンド実行結果がrc: 0の場合changedになる ... ansible.builtin.command: echo 'test' register: result changed_when: result.rc == 0 # コマンド実行結果に関わらずchangedにはしない ansible.builtin.command: echo 'test' register: result changed_when: false # コマンド実行結果に関わらずchangedにする ansible.builtin.command: echo 'test' register: result changed_when: true yaml [truthy]: Truthy value should be one of yaml 構文においてboolianの値はtrue or falseである必要がある - name: Test ansible.builtin.command: echo 'test' register: result changed_when: no failed_when: True # 以下に修正 - name: Test ansible.builtin.command: echo 'test' register: result changed_when: false failed_when: true また、上記対応においては修正量が多い為、role単位での修正を行う対応として、 yaml -lintバージョンを1.26.3から1.35.1に更新しignore機能を利用して修正対象を絞り込んで順次対応を実施しています。 .yamllint --- rules: truthy: ignore: - "roles/roleA/*/*.yml" # ロール修正時に削除して対応 - "roles/roleA/*/*.yaml" # ロール修正時に削除して対応 - "roles/roleB/*/*.yml" # ロール修正時に削除して対応 - "roles/roleB//*/*.yaml" # ロール修正時に削除して対応 v6.14.0 to v6.22.2 var-naming[no-role-prefix] ロール内の変数名は role_name_ プレフィックス として使用する必要があります。 プレフィックス の前では下線が使用できます。 Ansibleドキュメントを確認しても、このルールが確認できませんでした。変更に伴う修正影響が大きい為、一旦修正対象外としました。( https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#playbooks-variables ) roles/hoge/vars/main.yml --- version: '0.1' # 以下に修正 hoge_version: '0.1' 今回はこのルールを全体的に対象外にする .ansible-lint --- skip_list: - var-naming[no-role-prefix] risky-shell-pipe shell内でpipeを使用する場合は、pipefailオプションを設定する必要がある - name: Test Shell shell: echo 'test' | tail # 以下に修正 shell: set -o pipefail && echo 'test' | tail 5. まとめ Ansibleバージョンアップ時にはAnsibleのバージョンによって Python のサポートバージョンが変わるので注意。Ansible実行環境、Ansible実行先の環境の Python のバージョンを確認すること Ansibleコードの修正にはAnsible-Lintの自動修正機能が使える。ただしv6.20.0以上を使うこと Ansibleのバージョンアップは段階的に行う Ansible-Lintも段階的にバージョンアップを行い修正範囲を極小化して段階的に行う 実際には、これ以外に弊社にはAnsibleを本番環境に適用する前に実際に実行して、安全性を確認する環境が存在しますが詳細は別記事で紹介しようと思います。 以上長くなりましたが、Ansibleバージョンアップ作業のまとめになります。 ありがとうございました。
はじめに 配配メール開発チームの id:takaram です。 2024年12月22日に、東京・蒲田で PHP Conference Japan 2024 が開催されました。 ラクス はブロンズスポンサーとして協賛させていただいたのに加え、エンジニア3名の トーク を公募で採択いただき、登壇してきました。 今回は ラクス からの参加者によるレポートを紹介させていただきます! はじめに 参加レポート PHPの今とこれから2024 PHP RMは何をする?コア開発者と兼任するメリット/裏話 終了の危機にあった15年続くWebサービスを全力で存続させる〜Twilog・Togetter統合の舞台裏〜 見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理 20年続くレガシープロダクトに10年携わったエンジニアが思う、システム長期運用のカギ PHPerのための計算量入門 情報漏洩させないための設計 PHPConferenceへの参加を後押しするためにしていること どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか ラクスからの登壇セッションのご紹介 PHP開発者が挑むDKIM導入:Googleガイドライン対応の実例と学び Rustで作るPHP拡張モジュール:PSR-7ライブラリ編 20年もののレガシープロダクトに0からPHPStanを入れるまで まとめ 参加レポート PHP の今とこれから2024 report by id:hirobex fortee.jp 廣川さんによる、定例の PHP の動向についての トーク です! PHP8.4は新機能も多く、非常に盛り沢山な内容になっていました! プロパティフックや非対称プロパティ可視性などは、使ってみたいと思っているPHPerの人も多いのではないでしょうか? PHP RMは何をする?コア開発者と兼任するメリット/裏話 report by id:hirobex fortee.jp PHP コア開発者であり、かつリリースマネージャーでもあるSaki Takamachiさんの トーク です。 リリースマネージャーが集まったSlackがあるという話や、リリースマネージャーが PHP リリースのためにどんな作業をするのかと行った、非常に貴重な話を聞くことができました! 世界的な OSS がどのように開発されているのか、その一部を見ることができます! 終了の危機にあった15年続く Webサービス を全力で存続させる〜 Twilog ・Togetter統合の舞台裏〜 report by id:hirobex speakerdeck.com イーロン・マスク に買収されたXの API 廃止に伴う、 Twilog とTogetter統合についての トーク です。 大規模なDBを、どのようにして AWS に移行したのか、そのお話を聞くことができました! SQLite と MySQL が構成図に出てきたときは、どうなることかと思いました。 見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` と SQL 結果のメモリ管理 report by id:takaram speakerdeck.com 武田憲太郎 ( @KentarouTakeda ) さんによるセッションです。武田さんは、 PHP 8.4で導入された pg_result_memory_size() の提案・実装を行った方で、その関数にも関連する「 SQL 実行時のメモリ使用量」についてお話しされました。 普段何気なく使っているPDOの prepare、execute、fetchAll の裏で何が起きているのか、 SQL の結果データがどこでどのように保持、管理されているのかを知ることができました。それと同時に、自分のコードの実装は大丈夫……?と不安になったので、改めて見直してみようと思いました! 20年続くレガシープロダクトに10年携わったエンジニアが思う、システム長期運用のカギ report by id:uemura_rks speakerdeck.com 20年続くプロダクトをモダンな環境に移行してきた取り組みについての トーク でした。 私が担当するサービスは同じような境遇でいまだにレガシーから抜け出せていない状況にあるため、 非常にためになる内容でした。 システムだけでなく開発体制の改善(開発体制に再現性を持たせる)について述べられていたことも印象的でした。 モダンへの移行はタイミングが遅くなれば遅くなるほどやることが増えるので、出来るだけ早めに実施したいものです。 PHPerのための計算量入門 report by id:hirobex speakerdeck.com アルゴリズム において非常に大事な要素になる計算量について、非常にわかりやすく解説していただいた トーク です! データ数が大きくなればなるほど計算量の考えは大事になってくるので、とても参考になりました。 また、最後にチューニングにも限界があるから、仕様変更も視野に入れようねという話も入っていたのが、非常に実務的だと思いました。 情報漏洩させないための設計 report by id:uemura_rks speakerdeck.com ビジネスロジック の実装ミスなどによる情報漏洩を防ぐための設計手法についての トーク でした。 コンテキストごとにEntityを用意することで必要最小限の情報を扱えるようにするといった工夫 分けたEntityを取り違えないための工夫 が参考になりました。 情報漏洩を題材にした トーク でしたが、紹介されていた手法は情報漏洩に限らず普段の開発に活かせる考え方でした。 deptrac という依存関係をチェックできるツールが面白そうなので使ってみたいです。 PHPConferenceへの参加を後押しするためにしていること report by id:takaram fortee.jp 下岡葉子 ( @yoko_94b ) さんによる、 PHPカンファレンス のような技術イベントに参加したことがない人の背中を押すにはどうするか、というお話でした。 行かない理由として「難しそう」「すごい人が行くところでは?」のような気後れや「休みの日の朝から……?」のような声に対し、「若い人もたくさん来てるよ」「知らない単語を知るいい機会だよ」「午後からでも大丈夫」といった声掛けを社内で行って後押しされているそうです。 トーク 中「今回初参加の方いますか?🙋」と質問されたところ、結構たくさんいらっしゃったのが印象的でした。 弊社開発部は東京と大阪に分かれていて、大阪からだと距離のハードルという問題もあったのですが、2024年は全国各地で PHP 系カンファレンスが開催されたおかげでハードルがぐっと下がりました。 2025年も続くようなので、私も参加を後押ししていきたいです! 月刊 PHPカンファレンス 2025! #phpcon #track1 pic.twitter.com/xJYvrkZzs5 — takaram (@takaram71) 2024年12月22日 どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか report by id:takaram speakerdeck.com おかしょい ( @okashoi ) さんによるLTです。 コードレビューをついつい後回しにしてしまう人(私含め)には耳が痛い話ではありました。私は「レビューを優先すべき」という話は聞いていても、理由まで具体的に理解できていなかったのですが、今回そこを知ることができました。 自分のタスクを横に置いてでもレビューを優先していきましょう!💪 ラクス からの登壇セッションのご紹介 PHP 開発者が挑む DKIM 導入: Google ガイドライン 対応の実例と学び report by id:uemura_rks speakerdeck.com トラック4の一発目として、楽楽販売で Google ガイドライン に対応した実録をお話させていただきました。 カンファレンス初登壇でしたが落ち着いて話せたので良かったです。 自分が設計寄りの人間なこともあり技術の話があまり出来なかったため、次回はもう少し技術ネタを持って参加してみたいです。 Rustで作る PHP 拡張モジュール:PSR-7ライブラリ編 report by id:takaram www.docswell.com 通常 C言語 で作る PHP 拡張モジュールを、 ext-php-rs というライブラリを使ってRustで実装してみる、という話をさせていただきました。 Rust自体本格的に触ったことがない状態から始めたため、かなり苦労したのですが、初心者だからこそできる話もあったかなと思います。今回の登壇を通じて、Rustにも PHP にも詳しくなれたのではと思います! 20年もののレガシープロダクトに0からPHPStanを入れるまで report by id:hirobex speakerdeck.com 登壇しました! タイトル通り、レガシープロダクトにPHPStanを入れた話です! PHPStanのオプションや、bootstrapFilesを駆使して、PHPStanを導入した話をしました! ありがたいことに、多くの人が来てくださりました。 発表した会場が、大学の講義室みたいで、人気教授になった気分でした。 まとめ 2024年は全国各地で PHPカンファレンス が行われましたが、歴史の長い PHP Conference Japan(世界最古だそうです!)は今年の締めくくりにふさわしい盛り上がりだったように思います。 2025年は6月28日開催と、実はもう半年後に迫ってきています。来年も楽しみですね!
はじめに こんにちは、 @rs_tukki です。 この記事は、 ラクス Advent Calendar 2024 の25日目の記事です。 今回は、開発中に見つけた重いクエリを改善するための記録と、改善のために使用した見慣れない構文の紹介をしようと思います。 はじめに 開発中の出来事 パフォーマンスチューニング UNNESTとは? チューニング結果 まとめ 開発中の出来事 ある日、私はWebアプリに新しい API を追加するための実装をしていました。 既存のテーブルからデータを取得するための新しいクエリを作成して、そのクエリを呼び出した結果を返すだけの単純な API です。 実装はつつがなく完了し、その後のテストでも特に問題は見られませんでした。 ただ、新規にクエリを作ったため、最後に JMeter を使ったパフォーマンス検証を行うことに。 【図解】はじめてでもわかるJMeterの使い方 - RAKUS Developers Blog | ラクス エンジニアブログ DBにデータを大量に入れて、顧客の利用状況を元にアクセス数の 閾値 を検討し、 その数だけリク エス トを投げる操作を3回繰り返し、それぞれの平均値を取ります。 そして結果がこちら。 平均値 1回目 1,881ms 2回目 1,886ms 3回目 1,929ms 1,900ms = 1.9秒 。 ...流石にちょっと時間がかかりすぎですね。 パフォーマンスチューニング 負荷検証は通常の運用で想定した分をはるかに超えたデータ量と呼び出し回数で行ってはいましたが、 とはいえ一度の API の実行で2秒弱もかかることがあるのではとてもリリースできません。 そのため、 ボトルネック となっている SQL を特定した上でそのチューニングを行うことにします。 実装したコードから呼び出しているクエリを1つずつ コメントアウト してはパフォーマンス検証、という操作を繰り返し、極端に処理が軽くなったタイミングがあれば、その時に コメントアウト しているクエリが ボトルネック ということになります。 そして、それを何度も繰り返している内に、とうとう原因となる一つのクエリに辿り着きました。 それがこちら。 SELECT count ( 1 ) AS count FROM ( SELECT a.column1 FROM tableA a WHERE EXISTS ( SELECT 1 FROM tableB B WHERE a.column1 = b.column1 AND a.column2 = b.column3 - 1 AND b.column4 = 0 AND b.column5 = 0 AND b.column6 IN ( SELECT column7 FROM tableC WHERE column8 = ' AAA ' OR column9 = ' AAA ' OR column10 = ' AAA ' ) ) AND a.column11 = 0 AND a.column12 >= 100 LIMIT 100 ) AS " result " ; このクエリの実行が極端に重いせいで、リク エス ト全体が遅延していたというわけです。 このクエリ……何か変…… 見てもらえれば分かるとおり、このクエリでは欲しい情報を一度に取ろうとするあまり、 サブクエリにサブクエリを重ねて極端にパフォーマンスが悪いクエリになってしまっています。 そのため、サブクエリの中の最も深い部分、tableCから column7 を抽出している部分を先に実行しておき、 後から条件に加えるようにしました。 SELECT column7 FROM tableC WHERE column8 = ' AAA ' OR column9 = ' AAA ' OR column10 = ' AAA ' ; -- 「test1」と「test2」が抽出される --- SELECT count ( 1 ) AS count FROM ( SELECT a.column1 FROM tableA a WHERE EXISTS ( SELECT 1 FROM tableB B WHERE a.column1 = b.column1 AND a.column2 = b.column3 - 1 AND b.column4 = 0 AND b.column5 = 0 AND EXISTS ( SELECT column7 FROM UNNEST(ARRAY[ ' test1 ' , ' test2 ' ]) AS C(column7) WHERE b.column6 = C.column7) ) AND a.column11 = 0 AND a.column12 >= 100 LIMIT 100 ) AS " result " ; UNNESTとは? さて、改善後のクエリに UNNEST という見慣れない構文があります。 これは PostgreSQL で使用できる構文で、配列を引数として渡すと、その各要素を単一列のテーブルとして出力する、という構文になります。 www.postgresql.jp つまり、 SELECT column7 FROM UNNEST(ARRAY['test1','test2']) AS C(column7) を実行すると、 column7 test1 test2 という列を含む C テーブルが結果として得られます。 この結果はそのまま EXISTS に引き渡すことが出来るため、事前に抽出しておいた複数件の値を1つずつIN句に入れるよりも若干パフォーマンスが改善するというわけです。 チューニング結果 さて、改善後のクエリを、改善前と同条件のもとで負荷検証にかけてみたところ... 平均値_改善前 平均値_改善後 1回目 1,881ms 507ms 2回目 1,886ms 518ms 3回目 1,929ms 516ms API リク エス トの実行時間が1/3にまで改善されました! クエリを分割するだけで速度が3倍近くにもなるので、やはりチューニングは大事だと気付かされた一件でした... まとめ 今回は、開発時に発見した ボトルネック とそれを改修するまでの記録を解説しました。 テストのタイミングでは問題なく見えても、大量のデータを投入し大量のリク エス トを投げるとパフォーマンスが落ちることは往々にしてありますので、しっかり検証は行うようにしましょう。 ここまで読んでいただき、ありがとうございました!
目次 リファクタリングに着手するまでの経緯 苦労した点や学び 仕様を理解する 既存コードを読み解く ①目的や仮定を持たずに一気に全体を追ってしまう ②コメントに惑わされてしまう ③効果的な作業メモを取らない 適切な命名 ①コードリーディング時 ②実装時 まとめ リファクタリング に着手するまでの経緯 初めまして!楽楽販売新卒エンジニアのudonrmです。 本記事では、配属後初めての業務として経験した既存コードの リファクタリング について過程と学びを伝えていきます。 弊社では、3ヶ月間の新卒合同研修を経た後にそれぞれの部署に配属され、そこで配属後研修が行われます。私の所属された楽楽販売では、配属後に検証環境を使用した実践的な開発演習や商材理解を中心に2ヶ月半ほどの研修がありました。9月中旬ごろには配属後研修が終了し、そこから実業務を開始しました。 初めての業務として取り組んだタスクが、コード品質の改善を目的とした既存コードの リファクタリング です。 この リファクタリング は、既存のバリデーション アーキテクチャ の改修やクラスの責務の見直し、可読性の向上がメインテーマになります。 バリデーション アーキテクチャ 変更の経緯については、弊社過去ブログをご参照ください。 tech-blog.rakus.co.jp 苦労した点や学び ここからは、 リファクタリング に着手する際に特に苦労した点や学びのあった点を観点別に紹介していきます。 仕様を理解する まず苦労した点に、仕様理解を挙げたいと思います。 楽楽販売は自社の業務フローに合わせて表示項目や操作メニューをカスタマイズできることが特徴の一つで、長所でもあります。ただ、その分どうしても機能数が増えてしまう傾向にあり、仕様理解が難しいことが業務を進めるうえで自分の課題になっていました。 今回のタスクは、配属後研修で軽く触れた程度でほとんど知見のない機能を対象とした リファクタリング でした。そのため、当然ですが仕様の理解にも苦労がありました。 当初の考えとしては、仕様理解は リファクタリング の方針検討やレビュワーとの認識合わせを経て自然と理解していくものくらいの認識を持っていました。 実装の序盤ごろまでは仕様の理解不足による影響は特になかったのですが、時間がたつほどじわじわと仕様の理解不足による影響を感じ始めます。 詳細は省きますが、画面上で使われているとある単語の意味を若干勘違いしていたり、細かい挙動の把握漏れがあったりしました。誤った前提知識を持ったままコードリーディングを進めるので、細部を読み間違えてしまったり、最悪の場合は デグレ が起きていることに気が付かなかったりしてしまいます。 結果的に、今回のタスクでは仕様理解に対して以下の学びを得ることができました。 適切な仕様理解はコードリーディングや実装の精度向上に役立つ 「ながらの仕様理解」には限界がある 進捗がストップするのを恐れずに積極的に時間を作るべき 違和感を持った仕様は問題ないはずと思いこまずに確認する これについては、実装のことで頭がいっぱいで確認を怠ってしまった経緯があります。 正しい仕様を理解しないと実装する必要のない箇所を実装してしまったり、考慮漏れが生まれたりしてしまう可能性があるので特に強く意識していきたいと感じた部分でした。 既存コードを読み解く リファクタリング 着手に当たり、既存コードのロジック把握にもかなり苦労しました。 当然ですが、簡単にスラスラと読めるものではありません。 機能理解が不十分なことや、処理の流れが脳に収まらないことなど原因はいくつもありますが、今振り返るとコードを読む際の取り組み方に問題があったと思います。 ここからは問題点を3点に絞ってふりかえってみたいと思います。 ①目的や仮定を持たずに一気に全体を追ってしまう 業務のコードは単に量が多いだけでなく、複雑な仕様が影響してどうしても複雑になってしまいます。楽楽販売の ソースコード も同様でした。 当初は、右も左もわからず目についた処理をとりあえず追ってしまっていました。上から下に流れるように読んでいくだけなので、修正箇所の特定にも時間はかかるし、何より脳が疲弊して効率がどんどん下がっていきます。ある日、そのモヤモヤを日報に書いてみたら先輩からこんなフィードバックが来ました。 後日実際に ペアプロ をしていただき、以下の学びを得ました。 命名 から役割が自明な関数に対して仮説を立てること 例えば、以下のようなメソッドがあります。ここで 命名 と返り値だけに注目すると、「ユーザーIDを受け取ってHogeArrayを返すんだな」ということが自明だと思います。 次に、 getHogeArrayByUserid() を呼び出す際は以下のようになると思います。 命名 から役割が推測できて返ってくる値も把握できている場合には、わざわざ getHogeArrayByUserid() の中身の処理まで追いに行く必要はなさそうです。 $hogeArray の正体に対して仮説を立てる→ デバッグ を使って検証の繰り返しでコードリーディングが進みます。 当初、目についたメソッドをただひたすら読みに行ってしまっていたので、このアド バイス を受けてコードリーディングが少し楽になったのを覚えています。 こうして書き出してみると当たり前のことだなとは思いますが、当時はコードリーディングがまったく進まないことに焦り、ヒントになりそうなロジックがないかを探そうとして本来読む必要がない箇所まで読んでしまっていたので悪循環になってしまっていました。 ②コメントに惑わされてしまう 命名 と返り値以外にも、コードリーディングの理解を助ける要素としてコメントがあります。 先輩との ペアプロ から得た学びを頼りに、ロジックを深追いしない代わりにコメントを意識して積極的に読むようにしてみました。 結論としては、「仕様理解、機能理解がままならない場合はコメントを読むとかえって混乱する場合がある」という学びを得られました。 実際に経験した例では、実態に即したコメントでなかったり、表現があいまいでなにを指しているのかがわからなかったりするケースがありました。 詳細は省きますが以下のようなコメントがありました。 コメントではターゲットのリストを取得という補足がされていますが、返り値の型がない(リリース当時は型宣言がサポートされていませんでした)ことやターゲットがさす内容がロジックを追わないとわからないことがコードリーディング時の負担になっていました。 実際には中身のロジックを追っていけば取得できるリストは確認できるのですが、仕様が複雑なためターゲットの実態をうまくつかめず不安を抱えたまま読み進めていきました。仕様理解や機能理解があれば、このメソッドの出力結果が使われる場面や使われ方が具体的に想像しやすくなるはずです。 実際には、このターゲットの正体は仕様をしっかり理解していれば特に苦労することなく理解できるものでした。仕様理解や機能理解に不安がある場合は、コメントで感じた違和感を放置せずにひとつひとつ確認していくことが大切だなと感じた場面でした。 ③効果的な作業メモを取らない 既存コードを読み解くうえで、作業メモの取り方に関しても課題感を感じていました。 「①目的や仮定を持たずに一気に全体を追ってしまう」の課題と類似しますが、作業メモも自分の思考の流れを一気に書き出しているだけだったので、読み返すのが辛いだけでなく、作業の進捗状況がわかりづらいアウトプットになってしまっていました。 当時の作業メモを見返してみると問題点は以下の2つが考えられます。 事実や気づきが入り混じっている メモを書いているときは問題ないが、あとから見返す際に信頼すべき情報かどうかの判断にリソースが使われてしまう フォーマットや粒度がそろっていない メモを追記していく際にどこに追記すればいいか迷う→フォーマットや粒度がさらに崩れてどんどん読みづらくなる メモを読み返す際にどこを読めばいいか探すのに時間がかかる 当時のメモをそのまま紹介することは難しいので、動物園に行くためのメモというテーマに変えて当時のメモを再現してみました。(GPT4o生成) このメモは極端な例ですが、 事実や気づきが入り混じっている 駐車場はあるけど使うかわからない。料金高めだった気がする(曖昧)。 この書き方は、書いている最中は思考が整理されて問題はないはずですが、量が増えれば増えるほど自分のメモに信頼が持てなくなってしまいます。気づきとして書いたつもりのメモを事実として誤読してしまうと、作業が直感的に進まずもはやメモを読まないほうがスムーズに進みます。これは実際に体験したので少し痛い目を見ました。 フォーマットや粒度がそろっていない 「目的」と「見たい動物」の見出しの粒度が明らかに揃っていません。適切な階層構造になっていないとメモを読み返す際の障害になってしまいます。メモを追記していく際にどこの階層に記述していくかの判断を誤ってしまうかもしれません。実際に当時のメモを読み返してみると階層構造が全体的に統一されていないため、読むのに時間がかかってしまいました。 せっかくメモを取ったのに、メモを見ないほうが進捗がいい気がするという本末転倒な状況になってしまったのでメモの取り方を見直してみました。 改善後のメモはこんな感じです。 まずは、どんなタスクをするとしても共通のフォーマットを使用するように変更しました。タスク単位でissueを+1していけばいいだけなので追記も楽ですし、読み返す機会も圧倒的に増えました。 メモには気づきベースのメモを書き、補足事項には事実ベースのメモを書くことで情報が一気に整理されて読みやすくなったのを実感できました。 このフォーマットが最適解と言い切れるわけではありませんが、無理なくメモを取る習慣を続けられているので今のところうまくいっている感じがします。 適切な 命名 命名 に関しても、コードリーディング時と実装時双方の場面で何度も頭を悩ませました。 *1 リーダブルコードの購読や配属後の開発演習などで、可読性の低い 命名 がもたらす弊害については認識していたつもりでしたが、修正方法や考え方について ソースコード ベースで学べたのは本当にいい経験だったと思います。ここではコードリーディング時と実装時にわけて適切な 命名 について考えたいと思います。 ①コードリーディング時 今回の既存実装は、汎用的な 命名 や抽象的な 命名 をしている箇所が多くあったため、直感的にコードリーディングをするのが厳しいなと感じる場面が多くありました。 以下のコードは一例です。 どの変数も 命名 が抽象的です。例えばこのメソッドの再利用性が高く、汎用的なロジックを提供している場合は具体的な文脈に依存しない抽象的な 命名 でも問題はないと思います。 ただ、用途が限定されたメソッドで呼び出し箇所が1か所しかないケースなどにおいてはコードリーディングの障害になってしまいそうです。 実体験ですが、中身にどんな情報が入っているかの把握や推測が難しくなってしまいました。 デバッグ で何とか情報を得ようとしましたが、クラス単位で 命名 が不適切な箇所が多くあったので全体の構造理解にはかなり苦労したことを覚えています。 不適切な 命名 の変数を放置したまま実装を進めても、コードリーディング時に無駄なリソースが割かれてしまうので リファクタリング 着手の最初のステップとして全体的に変数名を見直しました。 変数名見直しは主に以下の2点を意識しました。 1.地道に デバッグ して変数に格納されているデータの中身を確認する 格納されているデータの把握ができないと方針が立てられないため、 デバッグ を通じてデータの中身を確認していきます。変更しなくてもよさそうな変数や、不足した情報がある変数をリストアップして修正可能な箇所を洗い出しました。 2. 命名規則 を決めて粒度をそろえる 一例ですが、以下のような 命名規則 を決めました。 リストを扱う配列にはListを付けて $hogeList のような形にする 返り値がリストであることが自明であるにもかかわらず、変数名からそれが読み取れないケースが多くありました。こういったケースは 機械的 に修正する方針に決めました。 情報量を増やせそうな場合は増やす デバッグ した結果、用途が限定的かつ情報量を足せそうな変数に対しては情報量を増やす方針を決めました。メソッドの呼び出し階層が深いケースでも、呼び出し元までたどる必要がなくなるので以降のコードリーディングに大いに役立ちました。 ②実装時 新規で追加したメソッドやクラスの 命名 も、コードレビューのやり取りを通して何度も修正していきました。 PRの指摘などでいただいたものの中で今後も意識しておきたいと思ったものをまとめたいと思います。 変数名を変に省略しようとしない 変数名もそうですが変に省略しようとしない方が大体上手くいきますね。今は IDE が優秀なので長くても別に困りませんし。 下記は簡単な例ですがクラスの 命名 ではできるだけ汎用的な 命名 は避けたほうが無難という学びを改めて認識しました。 変に省略するよりも役割を適切に表現する 命名 にすることで可読性は上がる場合が多そうです。 対称性を意識する *2 書籍プリンシプルオブプログラミングの中で対称原理について触れられている箇所があり、以下のようなメリットが紹介されていました。 コードの形に対称性があると、予測がつきやすく、コード理解のスピードが速まります。また、視覚的にも美しいコードになるので、これもコードの理解のスピードを速めてくれます。 対称性を全く意識しない 命名 にすると、例えば以下のようなコード例になります。 命名 に対称性がなく、可読性が低いです。 例えば、 insert と deleteLast は 命名 の粒度がそろっていません。 insertAnimal はリスト全体に関する操作が想定されますが、 deleteLastAnimal は「最後の動物だけ」という限定的な操作が想定されます。このままだと、コードリーディング時に 命名 から直感的に処理を把握していくことが難しくなってしまいそうです。 次に、ロジックはそのままで 命名 が対称になるように修正されたコードの例を示します。 命名 の粒度をそろえ、 add ⇔ remove と open ⇔ close のようにごく一般的な対称性を意識しました。 命名 から役割も推測でき、「登録削除」「動物園の開閉」と観点が限定的になるのでコード理解のスピードが速くなります。 対称性は 命名 だけでなく設計全般にも役立ちそうな基本的な考え方なので、今後も意識しておきたいです。 まとめ 苦労の多かった リファクタリング ですが、チーム内外の様々な方の手厚いサポートのおかげもあっておよそ1か月半ほどで実装~受け入れが無事完了しました。 今回紹介したもの以外にもたくさんの学びがあったので、今後の業務に積極的に役立てていきたいと思います。 最後までご覧いただきありがとうございました。 *1 : O'Reilly Japan - リーダブルコード *2 : プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則
この記事は ラクス Advent Calendar 2024 の14日目の記事(予定)です。 はじめに シンボリックリンク切替によるデプロイについて 今回の改善における無停止デプロイのスコープ 検証したこと 検証におけるゴール 検証観点 ①realpathキャッシュの動作検証 ②アプリケーションコードの動作検証 ③本番想定のアクセス下の動作検証 アプリケーションの改修内容 おわりに はじめに こんにちは、kasuke18 です。 楽楽販売のDevOpsチームメンバーとして、リリース周りなどインフラチームと協働する部分を主に担当しています。 私達のプロダクトでは、 シンボリックリンク 切替による無停止デプロイを導入しました。 導入にあたって私達のコンテキスト(特にサーバ構成などの アーキテクチャ )に合うか検証のうえ、必要なアプリケーション改修を実施しました。 その検証内容と改修ポイントをご紹介します。参考になれば幸いです。 シンボリックリンク 切替によるデプロイについて このデプロイフローの導入前は、 rsync によるファイルの直接差し替えで対応していました。 PHP のファイル読込は遅延読込されるため、タイミングによっては処理中のプロセスがエラーになってしまうケースがあります。 (例えば新しいクラスを含むコードをデプロイする際、その新ファイルの配置より先に利用個所のロジックが処理されてしまうと未定義としてエラーになる) そのため顧客影響を避けることから、リリース作業を夜遅くに実施するなど運用コストが高い状態が続いていました。 これを改善するため、各所で導入事例のある「 シンボリックリンク 切替によるデプロイ」を検討しました。 上記のようなデプロイフロー・具体的なデプロイの手順( mv コマンドで シンボリックリンク を上書きする)などは、導入事例を参考に、割とそのまま利用できる部分が多かった記憶があります。 https://developers.prtimes.jp/2022/05/31/prtimes-deploy-improvement/ とはいえ単純にデプロイ手順を変えるだけでよいのか、具体的にはアプリケーションの作り込みを変える必要があるのか、といった観点では私達のコンテキストに近しい事例があまり見つからず、導入するにあたって本当に問題ないのかを検証しました。 今回の改善における無停止デプロイのスコープ シンボリックリンク 切替という手法なので、サーバ上のアプリケーションコードの差し替え部分にのみ影響します。改善効果が期待できる範囲もサーバ上の PHP プロセスが担う処理に限定されます。 逆にフロントエンド↔バックエンド間のインタフェースの変更(リク エス トパラメータの増減)や PHP プロセス↔データベース間のインタフェースの変更(DBテーブル定義の変更)には対応できません。これらの改善には別の手段が必要になりますのでご留意ください。 検証したこと まず私達のプロダクトへ導入できないか検討するにあたって、他社の事例などを参考にさせていただきました。 例えば下記の導入事例などを見ると、realpathキャッシュというもの原因で問題が発生するリスクが説明されています。 https://www.klab.com/jp/blog/tech/2016/1062120304.html これだけを見ても単に導入すると、痛い目に遭う可能性が高そうです。 またOPcacheの利用有無やサーバソフトウェアなど、実行環境による違いによる挙動の変化もあり得るため、私達のコンテキストに合った検証が必要でした。 ちなみに今回検証を行った構成は以下のとおりです。 PHP 8.2 OPcache利用なし Apache + mod_ php 検証におけるゴール 今回の検証では、上記のような変更があったとしても「1つの PHP プロセス内では、デプロイ前・後のどちらかのコードのみが実行される」ということを担保することを目的に検証を進めました。 一番リスクなのは新旧コードが入り混じって実行されることで、その場合はなにかエラーが発生したとしてもまともに調査することができません。 新バージョンの反映に多少の遅延が生まれることは許容し、それよりも1つの PHP プロセス内での一貫性を優先しました。 検証観点 以下の3段階で検証を行い、新旧コードが入り混じって実行されることがないかを確認しました。 ①realpathキャッシュの動作検証 ②アプリケーションコードの動作検証 ③本番想定のアクセス下の動作検証 ①realpathキャッシュの動作検証 まずは私達のコンテキストにおけるrealpathキャッシュの効き方を把握するため、ミニマムなサンプルコードを作成しました。 sleep中に シンボリックリンク を切り替えてrealpathキャッシュの内容やロードされたクラスのファイルパスがどうなるかをするかを確認します。 詳細は長くなるので割愛しますが、下記のような関数を利用しました。 realpath_cache_get() : この関数の実行時点のrealpathキャッシュ内容を取得 ReflectionClass::getFileName : クラスが定義されているファイルを取得 // require_once("/app/symlink/SampleClass1.php"); // var_dump(realpath_cache_get()); ["/app/symlink/SampleClass1.php"]= > // キャッシュのキーはrequire_onceに指定したパス array(4) { ["key"]= > int(3616146466484062189) ["is_dir"]= > bool(false) ["realpath"]= > string(28) "/app/before/SampleClass1.php" // シンボリックリンク解決済のパスがキャッシュされる ["expires"]= > int(1733634941) } // var_dump((new ReflectionClass ("SampleClass1"))- > getFileName()); string(28) "/app/before/SampleClass1.php" // シンボリックリンク解決済のパスが取得できる ②アプリケーションコードの動作検証 ここでは実際のアプリケーションが実行されるパターンを洗い出し、新旧コードが混在することがないかを確認しました。 例えば私達のプロダクトでは、Webアクセス・ CLI 実行(バッチ)・メール受信からの起動 などのパターンがありました。 それぞれパターンについて、処理の何処かでsleepを仕込み、実行中に シンボリックリンク の切替を行い検証します。 ③本番想定のアクセス下の動作検証 アプリケーションコードを利用するという点では②と同じですが、検証したいポイントが異なります。 ②ではsleepを仕込んだ単発処理で、理論的上問題がないかを検証しました。 それに対して③では、アプリケーションの通常利用中に シンボリックリンク を継続的に切り替えて問題がないかを検証します。 イメージとしては同じバージョンの ディレクト リを2つ用意し、そのうち片方にメソッド・クラスのインタフェース変更を行い、従来の rsync によるデプロイではエラーになる状態を作ります。 その状態で シンボリックリンク の向き先を新旧切り替え続け、アプリケーション操作でエラーが発生しないかを確認します。 またアプリケーション操作は、リリース時に利用している動作確認ツールがありましたので、それを流し続けることで実現しました。 アプリケーションの改修内容 詳細は長くなるため、検証結果はまた別の機会にします。 特に①のサンプルコードも合わせて記載できるように頑張ります。 検証結果を踏まえて、アプリケーションの改修が必要な点は、 require に指定されるファイルパスが、すべて「 シンボリックリンク 解決済みのパス」となるようにすることです。 例えばComposerで生成されたautoloaderを利用している場合、autoloader経由で読み込まれるファイルは特別な考慮を必要としません。 autoload*.php では __DIR__ を基にパスを組み立てているため、 シンボリックリンク 切替の影響を受けません。( PHP の __DIR__ で取得できるパスは シンボリックリンク 解決済みのパス) 一方で、autoloader自体を読み込む際のパス指定には注意が必要です。 // OK例 require_once(__DIR__ . "/vendor/autoload.php"); // NG例 require_once("/path/to/project/vendor/autoload.php"); require_once(ini_get("user_dir") . "/vendor/autoload.php"); 上記NG例のように シンボリックリンク を含むパスになっていると、その処理タイミングで シンボリックリンク 切替が重なると、新旧入り混じった状態でアプリケーションが動いてしまう可能性があります。 特にWebアクセスにおいては、realpathキャッシュがリク エス トをまたいで共有されるケースがある(処理された Apache のプロセスが同じ場合は共有される)ため、分かりづらい形でエラーになってしまうことがあります。 おわりに 私達のプロダクトで「 シンボリックリンク 切替によるデプロイ」を導入したときの検証内容・アプリケーション改修内容をご紹介しました。 realpathキャッシュの挙動調査や Apache のプロセスとの関連など、記事のボリュームの都合で書ききれなかった部分については、今後別記事で詳しくご紹介します。
こんにちは、あるいはこんばんは。楽楽販売の開発をやっている @taclose です☆ ISUCONに参加するのはこれで2回目ですが、 今回は7位でした! ISUCON14 TOP30 微妙!とか言わないで!頑張った方ですよ!運が良かった方ですよ!(と言いたい!) 今日はそんなISUCON14がどんな感じだったのかを振り返っていこうと思います! 記事の概要・想定読者 ISUCONの準備 前回の反省からはじまる 前回の反省点 練習はISUNARABE! ISUCON当日 ISUCONの初動:初回ベンチマークまでにやる事! 初回後の次の一手:DBのINDEX見直し 各自が怪しいポイントを重点的に攻める 4時以降:サーバ構成を真剣に考える 最後のチューニング 最終結果 今回のISUCONの振り返り 良かった点 反省点 最後に 記事の概要・想定読者 想定読者 今年ISUCON出たけど、悔しい思いをした方 来年ISUCON出てみようかな?と思う方 記事の概要 ISUCON14の細かいチューニングポイントはあまり取り上げません。 Git管理や Makefile の準備やノウハウ集ではありません。 ISUCONに挑戦する準備や練習、当日の困りポイントをまとめた体験談です。 では早速、当日までの準備から振り返ってみましょうっ ISUCONの準備 前回の反省からはじまる 11月ごろ行われた作戦会議では以下の話が出ました。 前回の敗因はずばり準備不足、練習不足!以下のような社内ハンズオンを開催するぐらいの知識量はあったものの、 tech-blog.rakus.co.jp 当日は8時間という限られた時間で実践するのは至難の業でした。 前回の反省点 サーバ構成変更時のコマンド準備が不十分だった( Makefile に ベンチマーク 前のコマンドを準備するなど)。 サーバ構成変更時にネットワーク設定でアタフタして0点になりかけた。 そこで今回は事前準備とサーバ構成変更の練習に重きを置くことにしました。 練習はISUNARABE! ISUNARABE を使い、本番さながらの練習をしました。 特に課題だった AWS での3台サーバの使い方や初動の役割分担を重点的に練習しました。前回に比べると少ない練習時間でしたが、効果はありました。 ISUCON当日 ISUCONの初動:初回 ベンチマーク までにやる事! 私たちのチームでは以下の作業を分担して進めました。 configやアプリをGit管理下に置く makeコマンドで設定ファイルやアプリをデプロイ MySQL やnginxの初期設定(slow queryのログ出力など) 作業が終わったら初回 ベンチマーク を実行。 10時半時点でスコア1000点でした。 初回後の 次の一手 :DBのINDEX見直し nginxログ解析で遅い API は分かりますが、まずはDBのINDEXを整備するのが効率的。 pt-query-digestで解析し、30分~1時間で対応しました。この時点でスコアは3800点。 各自が怪しいポイントを重点的に攻める 我がチームは 全員遊撃部隊 ! nginxログやプロファイラー結果を見て処理時間が長い箇所を探る 怪しい箇所を自由に攻める 13時半でスコア8000点、15時に15000点、16時に18000点まで到達しました。 4時以降:サーバ構成を真剣に考える 3台サーバの使い方を再検討。 1台をnginx+アプリ用、もう1台をDB用に割り当て 16時半でスコア26000点。 最後のチューニング サーバ構成変更に伴い、nginxや MySQL の設定値を見直し。 17時半でスコア37000点。 最 終結 果 39957点 今回のISUCONの振り返り 良かった点 チームメイトに恵まれたこと。感謝感謝です!お疲れ様でした! 反省点 N+1問題への対処が不十分だったこと。 抜本的な解決策を思いついても時間内では躊躇してしまい、キャッシュで逃げてしまった。 場数を踏めばこういった勘所も磨かれると思うので、次回再挑戦したいと思います! 最後に これからISUCON始める人、始めたばかりの人に参考になれば幸いです! もしこれからISUCONはじめるぞ!という方は 目指せISUCON!!社内WEBパフォーマンス改善ハンズオンのすすめ - RAKUS Developers Blog | ラクス エンジニアブログ でも紹介している書籍を読まれたり、ローカルでパフォーマンスチューニングを練習してからやる事をお勧めします! ランク外で終わっても非常に学びが多く楽しいイベントなので、是非みなさんトライしてみてください!!
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「技術推進プロジェクト」 というプロジェクトがあります。 このプロジェクトで過去に検証した「継続的アプリケーションセキュリティ」について共有しようかと思います。 課題の経緯、前提条件 課題の経緯 期待する導入成果 シフトレフトや、脆弱性診断ツールに関する概念 前提条件 実現手法 SASTとIASTの特徴 共通の特徴 SAST IAST SAST/IASTを導入したらDASTが不要になるか どこまでコストをかけられるか ツール所感 → 今後に向けて SAST/IASTツールの製品傾向 SCMサービスに付随するサービス GitHub Code Scanner 調査の中で分かった導入に向けたハードル 最後に 課題の経緯、前提条件 課題の経緯 脆弱性 を含めた不具合の発生タイミングについては、発生するタイミングが開発工程の後であればあるほど、開発全体への影響が大きいです。しかしながら特別な対策を行わなければ開発工程の後半ほど不具合が見つかることが多いというのが現実ではないでしょうか。 それはプログラムが動く状態になってからの方がテストしやすいということが最大の理由かと思いますが、テストのタイミングを開発工程のなるべく早い段階、すなわち不具合発生の原因が生まれるタイミングに移す(=シフトレフト、開発工程を左から右に流れるとイメージして前工程である左側に移すこと)方法が考えられ生み出されています。 (出典: IPA 『 セキュリティ・バイ・デザイン 導⼊指南書 』 p.36) ラク スでもそれほど多いわけではありませんが、リリース前に不具合が見つかることがあり課題感がありました。 本調査ではテストのシフトレフトについて、有効そうな手法の探索と導入可否の検討を行いました。 なお、上で図を引用した IPA から発行されている『セキュリティ・ バイ・デザイン 導⼊指南書』は、このあたりの考え方がまとまっている資料なのでぜひ参考にしてみてください。 www.ipa.go.jp 期待する導入成果 期待する導入効果としては、 開発プロセス 全体で不具合対応にかかっているコストを現状よりも小さくすることです。そのために従来開発工程の後半で見つかっていた不具合を少しでも早い段階で検知できる仕組みを期待しています。 ただし、導入することで今までよりも手間がかかるようでは本末転倒 1 なので、導入後にセキュリティのシフトレフトによるコスト増(ツール費用など)を含めて現状よりも小さくなることが大前提となります。 ※なお実際の不具合対応コストはこんなに割合多くないです🙂 シフトレフトや、 脆弱性 診断ツールに関する概念 CAS: Continuous Application Security 継続的アプリケーションセキュリティ CIに組み込むなどして日常的に意識することなくセキュリティ向上策を実施するプ ラク ティス SAST: Static Application Security Testing 実装中に適用 設計にも適用できるという話もあるが現実的には実装中での適用になると思われる コード診断を行い、Linterのように動作する 製品としては SonarQube があったり、 GitHub やGitLabにも組み込まれている IAST: Interactive Application Security Testing E2Eテスト中に適用 操作と並行して検査する 製品としては Seeker , Contrast Security など DAST: Dynamic Application Security Testing リリース前に適用 DASTはすでに ラク スでも導入していたため今回の調査からは除外 SCA: Software Composition Analysis 利用しているライブラリの既知の 脆弱性 を検出できる SASTツールの機能として組み込まれている場合がある RASP: Runtime Application Self-Protection アプリケーション実行中に異常を検知する仕組み 本稿では直接扱わないが参考として 前提条件 現在の ラク スでは実装工程として 単体テスト 、その後 結合テスト 工程に 脆弱性 診断ツールを用いてのテスト(DAST)を行なっています。 DASTツールの見直しについては別途定期的に取り組んでいるため、シフトレフトするとなると検討すべきはSAST, IASTツールになります。今回はSAST, IASTツールの調査と ラク スで導入しやすそうなツールの検討を行なっていきます。 また、調査当時は ソースコード 管理にGitLab CEを利用しており、 クラウド 版 GitHub Enterpriseへの移行を検討していたこともあり、GitLab, GitHub に依存したGitLab SAST, GitHub Code Scannerについては調査対象外にしていました。 実現手法 SASTとIASTの特徴 共通の特徴 ホワイトボックステスト 対応できるのはメジャー言語に限られる しかし長期的にメンテナンスする想定のサービスで採用されるような言語は対応しているので実用上は問題なさそう コード外由来の 脆弱性 には対応できない ソースコード を対象に捜査するため仕方がない SAST 誤検知が多くなりがち フルスキャン可能で カバレッジ を100%にしやすい IAST 誤検知は少ない 機能操作に伴って カバレッジ が高まるため、 カバレッジ を高めるのは大変 カバレッジ 100%を目指すためには、すべての処理分岐を網羅するようなアプリケーション操作が必要 SAST/IASTを導入したらDASTが不要になるか 結論から言えば、SAST/IASTを導入してもDASTは不要にならなず、現状DASTツールにかけているコストはなくなりません。 なぜならば、DASTで出来ずSAST/IASTで対応可能な観点のテストがある一方で、SAST/IASTで出来ずDASTでしか出来ない観点のテストもあるためです。 例えばSAST/IASTでは ソースコード をフルスキャンしてのテストが可能だが、DASTで行うような外部ライブラリや複数のモジュールを結合してのテストは行うことが出来ません。 既存のDASTで無理してカバーしているような部分がある場合(例えば細かな条件違いの正常系経路の網羅など)は該当箇所をSASTおよびIASTに適切に移行することで、トータルのメンテナンスコストは削減できる可能性はあると思います。 どこまでコストをかけられるか 今回の調査を始めるにあたって少し甘かった部分ですが、調査中盤で改めて 社内で開発工程後半で不具合が発覚することによる手戻りの 工数 がどの程度か確認したところ、想定以上に開発品質が良く手戻りが多いサービスでも「2, 3人日程度」ということがわかりました。この時点で高額なツールの導入は割に合いません。むしろ「まだ導入する必要がない」という選択が現実的だと判断できるボリュームです。 セキュリティ関連のツールは総じて高額な事が多く、今回調査したツール類も有償ツールはすべて導入のコストメリットは見いだせませんでした。一方でCommunity Editionなどの無償でのツール提供が行われているものだと機能面で弱く、これも導入メリットが小さいと判断されました。 ツール所感 → 今後に向けて さて、有償無償問わずツール導入のメリットが薄くなってしまいましたが、こういった状況だとSAST/IASTツールの導入はかなり難しいです。 SAST/IASTツールの製品傾向 無償ツールとして利用できるものは機能制限が多い 自前運用しようとするとCI環境のマシンリソースを確保する問題が発生する 常時稼働するわけではないが、少ないと待ち時間が開発速度の悪化に直結する 多くのツールは クラウド サービスとして展開されている テスト環境での操作情報を外部に送信しなければならない セキュリティが厳しい会社だとこの部分で導入は厳しそう 2 予算が潤沢であれば、機能面でContrast Securityが良さそうに見えました www.contrastsecurity.com SCMサービスに付随するサービス GitHub Code Scanner 調査当時もCode Scanningは存在していましたが、当時弊社の ソースコード 管理はGitLab CEを利用していたため候補から外していました。またGitLab SASTも GitHub 移行の検討が始まっていた頃だったため、除外していました。 現在はほぼすべての ソースコード を クラウド 版 GitHub Enterpriseに移しているのでCode Scanningも候補に入ってきます。 課題として挙げたCI環境のリソースもCode Scanningであれば、(多少のお金の力があれば)なんとかなります。 と思ったら……Code Scannerの利用はプライベー トリポジ トリ対象だと、 GitHub Advanced Securityライセンスが必要で、アクティブコミッター1人あたり月額$49が追加で必要になるので弊社のようにそれなりの規模のチームで開発している場合に導入するのはハードルが高かった…… 3 。 docs.github.com 調査の中で分かった導入に向けたハードル 既存 ソースコード で検出される項目の扱い 導入時が一番多く検出される 検出されたから修正、とすると「今」必要ではない修正が多発する 導入直後に検出された不具合の扱いは事前に定めておかないと混乱のもと 現状の品質に対して期待できるメリットのコスト対比がまだ低い 以下のいずれかの状況にならない限り導入メリットが薄い 今後 ウェブサービス 開発が複雑化して不具合対応コストが増加 今後 ウェブサービス 開発速度が向上して不具合対応コストが ボトルネック となる SAST/IASTツールが コモディティ化 して導入コストが減少 最後に セキュリティのシフトレフト自体は 開発プロセス における理想であることに違いはないと考えています。 ただし商用サービスを提供する立場としては実現するためにはもう少し普及して コモディティ化 (に伴う低価格化)してくれないと導入は正直厳しいというのが現時点での判断です。 一方でライブラリなどを オープンソース で公開する場合など、不具合の影響が広範囲に波及するようなプロダクトに関していえば、 GitHub のパブリック リポジトリ でのCode Scanningが無料で利用できるため積極的に使っていくべきだと感じました。 いわゆるオーバーエンジニアリングになってコスト増を招いてしまうことは、ビジネスとしてサービス開発を行っている以上は避けるべき。 ↩ そしてセキュリティのシフトレフトに興味を持つ会社は比較的セキュリティ意識が高いはず……。 ↩ パブリック リポジトリ であれば無料で導入できるようなので、公開しているライブラリなどにはぜひ導入したいですね。 ↩
こんにちは、あるいはこんばんは。 私は楽楽販売の案件開発チーム(新機能開発チーム)の開発リーダーをしている @taclose です☆ 今回のブログでは、楽楽販売の案件開発チームの構成や業務を紹介しようと思います! 累計導入社数4,400社突破 の楽楽販売がどのような開発体制で、普段どんな事に取り組んでいるのかを知ってもらえればと思います! 楽楽販売の開発チーム構成 チームの紹介 チームのミッションは何か? 普段具体的にはどんな事をやっているのか?どんな事を考えているのか? 最近行った業務改善とは具体的にどんなものがあるのか? 今後の展望 さいごに まずは各チームの業務分担から説明します。 楽楽販売の開発チーム構成 チーム名 業務 DevOpsチーム 運用・保守、リリース作業等を行うチーム SAチーム カスタマーサポートが対応出来ない技術的な問い合わせに対応するチーム VCチーム ベトナム 開発拠点とのオフショア開発をするチーム 案件開発チーム 新機能の開発を行うチーム 楽楽販売開発課のチームは業務毎に大きく分けてこのように分かれています。 私たち案件開発チームはメインとなる開発以外とも言える運用・保守・サポート・定例作業等から独立しており、 メインとなる業務に集中する事ができます! 他のチームの方々に感謝ですね! そのおかげで、リソース調整も非常にやりやすく 計画的に業務を進める事ができています 。 私は 開発に集中できて ワークライフバランス が保ちやすいこのチーム が一番気に入っていますっ! では、もっと具体的にチームの紹介をしていきましょう。 チームの紹介 楽楽販売は既に多くの機能が存在していますが、法改正の影響や顧客のニーズに合わせて常に新しい機能が要望されています。 そんな機能追加を概要設計〜実装/テストまで行うのが案件開発チームになります。 メンバーは8名。だいたい常に2案件の機能開発が同時進行で開発が進められており、新機能開発以外にもバグ改修、 リファクタリング なんかも行っています。 構成としてはこんな感じでしょうか 役割 業務内容 AM(1名) アシスタントマネージャー。メインはリソース調整やスケジュールの調整。課長と開発メンバーの間に入って活躍してくれます。概要設計とかもやってます。 SP(2名) 開発リーダー的存在。コードレビューや設計方針、開発フローの見直し等をメインに行う。直接コーディングする機会は少ないです。 開発メンバー(5名) 実装の要。詳細設計、実装、テストを行う主力部隊です。 一覧にすると上のような立ち回りですが、SPと開発メンバーの垣根はそこまで厳密ではありません。 私としては、「 スケジュールとかリソースとかの調整毎から解放されてメイン業務に集中できる 」これがまたうれしいポイントですね! AMお疲れ様です!! とは言いつつも表面的な事を話しても余りイメージがわかないかもしれないですね! FAQ的な形で魅力を紹介していこうかと思いますっ! チームのミッションは何か? 一つは言わずもがなですが、顧客が求める機能を迅速に実装する事です。 もう一つは 内部品質の向上というのも手が抜けないミッション となっています。 楽楽販売は15年以上開発が続けられている長い商材であるため、内部品質としては問題がある実装というのが存在しています。 紆余曲折とした経緯があるのはエンジニアであれば容易に想像がつくでしょう・・・ でも、楽楽販売がすごいなと思うのは、「 リファクタリング 」「 アーキテクチャ 見直し 」といったような、 内部品質の改善を行う事も機能開発の一環として実施している事 です。 これの何がすごいのか? エンジニアならわかるかもしれませんが、案外ここにコストを割く事を経営側は良しとしないからです。 でも 楽楽販売は違います!今後の更なる成長のためには必要な事なら リファクタリング だって実施します! デメリットとして バグを出すかもしれない(何か機能がグレードアップするわけでもないのに) コストがかかる(何か機能がグレードアップするわけでもないのに) こんな事があるのはわかった上でミッションコンプリートを目指します。 では、「具体的にはどんな事をやっているのか?」を話していこうと思います。 普段具体的にはどんな事をやっているのか?どんな事を考えているのか? ざっくり言うなら、新機能の開発に対して 概要設計を作成する 実装する テストする 当然これが主な業務になります。60%〜80%の時間はこれに使ってます。1週間単位で見るなら 1週間のスケジュールでどこまで開発が進められるか話す 主業務を1週間やる 目標通り進んだか? KPT (Keep, Probrem, Try)で振り返る ここがよかった!今後これはルール化しよう! これは改善しないとまずいよ! こんな感じですね!じゃ 残りの20%ぐらいは何をやっているのか? 「 KPT で出たTryや、積み残されていた業務改善をしています」 最近行った業務改善とは具体的にどんなものがあるのか? 例えばですが、最近は プルリク エス トの粒度を小さくしよう メトリクスを計測して複雑度を意識しよう 機密情報が リポジトリ にpushされるのを事前に防ごう XXX(色々)を自動化しよう リファクタリング を提案しよう とかでしょうかっ!あくまで読者に伝わりやすい例です。他にも色々やってますよ! 今後の展望 これからもまだまだ楽楽販売は進化を続けます 。そのため、 内部品質の改善 というのは重要なテーマとなっています。 案件開発チームは新機能を開発出来る土台作りも担っているわけです。 今後としては 新機能の迅速なリリースが行える体制作り 新機能を不具合なくリリースできる内部品質の更なる向上 これを推し進めていきたいと思います。 さいごに 楽楽販売の案件開発チームについて紹介させてもらいました。 チームが一丸となり ラク スが掲げる リーダーシッププリンシプル を念頭に置きながら、常に発生する課題(理想と現実のギャップ)とどう向き合っていくべきかを考え、「限られたリソースの中で今は何をどのようにするべきか?」を考え、それを行動にうつしているすぱらしいチームです。 時にユーモア、時にエレガント、興味があれば是非楽楽販売の開発チームに来てくださいね!
はじめに ltreeとは ltree型 ltreeの操作 活用法 1. 承認フローの構築 事前準備 テーブル作成 データ追加 2. テーブルに細かくアクセス制御をかける 事前準備 ltreeの有効化 テーブル作成 ポリシー作成 行セキュリティポリシーの有効化 ポリシーの設定 データを追加 ユーザー作成 試す まとめ はじめに こんにちは! エンジニア2年目のTKDSです! 今回はltreeについて調べ、その活用法を考えてみました。 ltreeについて、ltreeの活用法の2段構成です。 ltreeとは 階層ツリー構造を模した構造を格納する機能を提供する 拡張機能 です。 詳しくは ドキュメント をみてください。 ltree型 階層ツリー構造を表す型です。 例)`Company.Department.Team1 ドット区切りで大文字小文字は区別しないようです。 各データはラベルと呼びます(上記でのCompany、Department、Team)。 ltreeの操作 よく使いそうな操作だけ2つ挙げます。 ltree @> ltree : 左辺の引数が右辺の親要素(か同じ)かどうか 例) SELECT name FROM organization WHERE path @> 'Top.IT.Software'; テーブルのpathがTop.IT.Softwareの親に該当する要素をすべて探します。 ltree <@ ltree : 左辺の引数が右辺の子要素(か同じ)かどうか 例) SELECT name FROM organization WHERE path <@ 'Top.IT'; テーブルのpathがTop.ITを親に持つ要素を探します。 このSELECT文では、Top.ITを親に持つ要素をすべて探します。 ~ :一致するパスの検索 右辺に指定したパスに一致するltreeを探します。 SELECT name FROM organization WHERE path ~ ' Top.IT.* ' ; SELECT name FROM organization WHERE path ~ ' Top.I*.* ' ; 活用法 2種類ほど活用例を考えてみました。 1. 承認フローの構築 ltreeを使って、承認フローに使えるテーブルを構築してみます。 事前準備 compose. yaml ces : db : image : postgres:16.4-bullseye container_name : db environment : POSTGRES_USER : postgres POSTGRES_DB : postgres POSTGRES_PASSWORD : postgres ports : - "127.0.0.1:5432:5432" volumes : - db_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck : test : [ "CMD-SHELL" , "pg_isready -U postgres -d postgres" ] interval : 30s timeout : 10s retries : 5 start_period : 10s volumes : db_data : init. sql -- ltree拡張の有効化 CREATE EXTENSION IF NOT EXISTS ltree; テーブル作成 CREATE TABLE approval_flow ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, flow_path ltree UNIQUE , -- 階層構造を表現するためのltreeカラム status text, -- 各承認ステップのステータスを格納 approver text -- 承認者(ユーザーIDや名前) ); データ追加 INSERT INTO approval_flow (flow_path, status, approver) VALUES ( ' 1.approver.userA ' , ' open ' , ' userA ' ), ( ' 2.approver.userB ' , ' pending ' , ' userB ' ), ( ' 2.approver.userC ' , ' pending ' , ' userC ' ), ( ' 3.approver.userD ' , ' closed ' , ' userD ' ); ここまででデータ投入までできました。 では、2の承認者でpendingのひとを検索してみましょう。 SELECT approver FROM approval_flow WHERE flow_path ~ ' 2.approver.* ' AND status = ' pending ' ; userCを approved にしてみましょう。 UPDATE approval_flow SET status = ' approved ' WHERE flow_path = ' 2.approver.userC ' ; 1ユーザー検索結果から消えてるのがわかります。 階層構造を表現できるので、1列で承認が必要な人を管理できて便利です。 2. テーブルに細かくアクセス制御をかける 次に活用例2です。 もともと考えてたのはこの使い方でした。 スキーマ よりも細かく、グループを区切ってアクセス制御できないかなと考えたことがありました。 そのときに、調べてみつけたのが階層構造を扱うltreeでした。 この活用方法では、ltreeでグループを作り、 Row Level Security (行セキュリティポリシー) の有効化で参照単位を制限してアクセス制御を実現します。 テーブルの関連のイメージは以下の図です。 基本的にすべてのテーブルをテナントIDで紐付けて、事故ってテナント外のデータを参照しないようにしました。 では、実際に環境を構築してきます。 事前準備 1のときと同じです。 docker compose down -v、docker compose up で環境作り直しておきます。  docker exec -it db psql -U postgres でログインします。 ltreeの有効化 -- ltree拡張の有効化 CREATE EXTENSION IF NOT EXISTS ltree; ついでにtest スキーマ を作って参照するようにしておきます。 ltreeの設定はpublic スキーマ にインストールされるようなので、publicも追加しておきます。 publicを含めないとltreeが参照できず、ltree型がないエラーになります。 CREATE SCHEMA test; set search_path to test, public ; テーブル作成 tenantテーブル CREATE TABLE test.tenants ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL ); usersテーブル CREATE TABLE test.users ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, username TEXT NOT NULL , email TEXT NOT NULL , tenant_id BIGINT NOT NULL , CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES test.tenants(id) ); rolesテーブル ロールの名前を設定するテーブル CREATE TABLE test.roles ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL , tenant_id BIGINT NOT NULL , CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES test.tenants(id), CONSTRAINT unique_role_name_per_tenant UNIQUE (name, tenant_id) ); role_permissionsテーブル:ロールに紐づく権限を設定するテーブル ltreeでアクセス制限を設定します。 CREATE TABLE test.role_permissions ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, role_id BIGINT NOT NULL , access_ltree LTREE NOT NULL , tenant_id BIGINT NOT NULL , CONSTRAINT fk_role FOREIGN KEY (role_id) REFERENCES test.roles(id), CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES test.tenants(id) ); user_rolesテーブル:ユーザーとロールの紐付けを管理するテーブル CREATE TABLE test.user_roles ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, user_id BIGINT NOT NULL , role_name TEXT NOT NULL , tenant_id BIGINT NOT NULL , CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES test.users(id), CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES test.tenants(id) ); documentsテーブル:アクセス制限するデータを投入するテーブル CREATE TABLE test.documents ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, title TEXT NOT NULL , content TEXT NOT NULL , tenant_id BIGINT NOT NULL , access_ltree LTREE NOT NULL , CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES test.tenants(id) ); すべて作成したらテーブル一覧を確認してみます \dt test.* ポリシー作成 アクセス制御を実現するためのルールを作っていきます。 行 セキュリティポリシー の有効化 ALTER TABLE test.documents ENABLE ROW LEVEL SECURITY; ALTER TABLE test.documents FORCE ROW LEVEL SECURITY; 有効になっているか確認します。 SELECT relrowsecurity, relforcerowsecurity FROM pg_class WHERE relname = ' documents ' ; ポリシーの設定 詳しくは ドキュメント をみてください CREATE POLICY tenant_rbac_ltree_policy ON test.documents USING ( EXISTS ( SELECT 1 FROM test.user_roles ur JOIN test.roles r ON ur.role_name = r.name AND ur.tenant_id = r.tenant_id JOIN test.role_permissions rp ON r.id = rp.role_id WHERE ur.user_id = current_setting( ' session.authorization_user_id ' )::BIGINT AND rp.tenant_id = test.documents.tenant_id AND rp.access_ltree @> test.documents.access_ltree ) ); CREATE POLICY tenant_rbac_ltree_policy_insert_update ON test.documents WITH CHECK ( EXISTS ( SELECT 1 FROM test.user_roles ur JOIN test.roles r ON ur.role_name = r.name AND ur.tenant_id = r.tenant_id JOIN test.role_permissions rp ON r.id = rp.role_id WHERE ur.user_id = current_setting( ' session.authorization_user_id ' )::BIGINT AND rp.tenant_id = test.documents.tenant_id AND rp.access_ltree @> test.documents.access_ltree ) ); session.authorization_user_idを設定することで実際のアクセス制御を実現します。 データを追加 テナントデータの投入 INSERT INTO tenants (name) VALUES ( ' Tenant A ' ); INSERT INTO tenants (name) VALUES ( ' Tenant B ' ); ロールデータの投入 -- Tenant Aのロール INSERT INTO roles (name, tenant_id) VALUES ( ' Admin ' , 1 ); INSERT INTO roles (name, tenant_id) VALUES ( ' User ' , 1 ); -- Tenant Bのロール INSERT INTO roles (name, tenant_id) VALUES ( ' Admin ' , 2 ); INSERT INTO roles (name, tenant_id) VALUES ( ' User ' , 2 ); ロール権限の投入 -- Tenant AのAdminロールの権限 (Sales部門全体にアクセス可能) INSERT INTO role_permissions (role_id, access_ltree, tenant_id) VALUES ( 1 , ' TenantA.Sales ' , 1 ); -- Admin role for Tenant A (Sales部門全体) -- Tenant AのUserロールの権限 (Sales部門のTeam 2のみアクセス可能) INSERT INTO role_permissions (role_id, access_ltree, tenant_id) VALUES ( 2 , ' TenantA.Sales.Team2 ' , 1 ); -- User role for Tenant A (Team 2のみ) -- Tenant BのAdminロールの権限 (Marketing部門全体にアクセス可能) INSERT INTO role_permissions (role_id, access_ltree, tenant_id) VALUES ( 3 , ' TenantB.Marketing ' , 2 ); -- Admin role for Tenant B (Marketing部門全体) -- Tenant BのUserロールの権限 (Marketing部門のTeam 2のみアクセス可能) INSERT INTO role_permissions (role_id, access_ltree, tenant_id) VALUES ( 4 , ' TenantB.Marketing.Team2 ' , 2 ); -- User role for Tenant B (Team 2のみ) ユーザーデータの投入 -- Tenant Aのユーザー INSERT INTO users (username, email, tenant_id) VALUES ( ' user_a1 ' , ' user_a1@example.com ' , 1 ); INSERT INTO users (username, email, tenant_id) VALUES ( ' user_a2 ' , ' user_a2@example.com ' , 1 ); -- Tenant Bのユーザー INSERT INTO users (username, email, tenant_id) VALUES ( ' user_b1 ' , ' user_b1@example.com ' , 2 ); INSERT INTO users (username, email, tenant_id) VALUES ( ' user_b2 ' , ' user_b2@example.com ' , 2 ); ユーザーとロールの紐付け -- Tenant Aのユーザーにロールを割り当て INSERT INTO user_roles (user_id, role_name, tenant_id) VALUES ( 1 , ' Admin ' , 1 ); -- user_a1 is Admin in Tenant A INSERT INTO user_roles (user_id, role_name, tenant_id) VALUES ( 2 , ' User ' , 1 ); -- user_a2 is User in Tenant A -- Tenant Bのユーザーにロールを割り当て INSERT INTO user_roles (user_id, role_name, tenant_id) VALUES ( 3 , ' Admin ' , 2 ); -- user_b1 is Admin in Tenant B INSERT INTO user_roles (user_id, role_name, tenant_id) VALUES ( 4 , ' User ' , 2 ); -- user_b2 is User in Tenant B ドキュメントデータの投入 -- Tenant Aのドキュメント (会社=Tenant A, 部門=Sales, チーム=Team 1) INSERT INTO documents (title, content, tenant_id, access_ltree) VALUES ( ' Document A1 ' , ' Content of Document A1 ' , 1 , ' TenantA.Sales.Team1 ' ); INSERT INTO documents (title, content, tenant_id, access_ltree) VALUES ( ' Document A2 ' , ' Content of Document A2 ' , 1 , ' TenantA.Sales.Team2 ' ); -- Tenant Bのドキュメント (会社=Tenant B, 部門=Marketing, チーム=Team 1) INSERT INTO documents (title, content, tenant_id, access_ltree) VALUES ( ' Document B1 ' , ' Content of Document B1 ' , 2 , ' TenantB.Marketing.Team1 ' ); INSERT INTO documents (title, content, tenant_id, access_ltree) VALUES ( ' Document B2 ' , ' Content of Document B2 ' , 2 , ' TenantB.Marketing.Team2 ' ); ユーザー作成 postgresユーザーのままだとすべてのレコードがみえてしまうので、別のユーザーを作成します。 CREATE USER access_user WITH PASSWORD ' password ' ; -- スキーマへのアクセス権限(USAGE)を付与 GRANT USAGE ON SCHEMA test TO access_user; -- テーブルに対するCRUD操作権限を付与 GRANT INSERT, SELECT, UPDATE, DELETE ON ALL TABLES IN SCHEMA test TO access_user; 試す 最初に全データをpostgresユーザーでみておきましょう。 各記事に access _ltreeが設定されているのがわかります。 この権限通りにアクセス制限できているか確認していきます。 ログインします。 docker exec -it db psql -U access_user -d postgres どの権限でアクセスするかは、 session.authorization_user_id で決められます。 まずは設定なしでやってみます。 権限がないのでみれません。 次はuser_id=1でやってみます。 権限はTenantAのSales部門全体にアクセス可能です。 次はuser_id=2でやってみます。 権限はTenantAのSales部門のTeam 2のみアクセス可能です。 次はuser_id=3でやってみます。 権限はTenantBのMarketing部門全体にアクセス可能です。 次はuser_id=4でやってみます。 権限はTenantBのMarketing部門のTeam 2のみアクセス可能です。 次はuser_id=5でやってみます。 紐付けられるユーザーはいません。 しっかり操作権限が絞られているのが確認できました! まとめ ltreeについて紹介と活用方法を考えてみました。 階層構造を簡単に扱えて、パターンマッチで検索もできるので非常に便利です。 記事をみてくださったかたもぜひ活用方法を考えてみてください!(社内のかたはこっそり教えていただけるとありがたいです) ここまで読んでいただきありがとうございました!
こんにちは、あるいはこんばんは。 だいたいサーバサイドのエンジニアの( @taclose )です☆ GitHub Copilotが活躍している昨今、弊社では GitHub で更に開発効率を良くしていこうという流れで日々自動化が行われております。 今回はそんな時代だからこそ求められている GitHub Actionsについて、初心者向けにワークフロー作成の際に知っておきたいコツと教訓について紹介します。 GitHub Actionsのワークフローを読めるけど、まだ自信がないという方はぜひ参考にしてください! 「それもっと早く知っておきたかった!」「初心者がつまづきがちなポイント!」を解説します! 読者ターゲット ワークフロー作成のコツ 1. run セクションで式 ${{}} は極力使わない 危険その1:コードインジェクションのリスク 危険その2:データのサニタイズ不足 2. workflow_call を使う際の注意点 注意点その1:github コンテキストは引き継がれる 注意点その2:secrets の扱い 3. GITHUB_TOKEN で行った操作は再度トリガーされない 補足: workflow_run トリガーを活用する まとめ 参考記事・お勧めサイト 読者ターゲット GitHub アクションなんとなく書いてるけど、 最適な書き方が出来ているか自信がない 方とか! ワークフロー作成のコツ 1. run セクションで式 ${{}} は極力使わない 例: jobs : example-job : runs-on : ubuntu-latest steps : - run : echo "Hello, ${{ github.event.inputs.name }}!" このように、 run セクションで ${{ }} 式を使われるのはよく見かけますが、実は避けるべきです! 理由は主に2つあります。 危険その1:コードインジェクションのリスク ${{ github.event.inputs.name }} などを直接 run に渡すと、もしその値が悪意のあるものであった場合、 意図しないコマンドが実行される危険 があります。 例えば、もしシークレットの中に rm -rf / なんてあったら…ッ!! 危険その2:データの サニタイズ 不足 データを サニタイズ (無害化)せずに渡すことで、特定の記号や文字列が予期しない動作を引き起こすことがあります。 GitHub Actions内で式を使う場合は、 env 変数や他の手段を使ってデータの安全性を確保しましょう。 良い例: jobs : example-job : runs-on : ubuntu-latest steps : # "${NAME}" このダブルクォーテーションで囲んでいるのがミソ!何を代入されてもこけません! - run : echo "Hello, ${NAME}!" env : NAME : ${{ github.event.inputs.name }} このように、式を直接 run に渡さず、 環境変数 ( env ) に渡してから使うと、 "(ダブルクォーテーション) が混じっているかも?とかの考慮が要らず安全です。 2. workflow_call を使う際の注意点 workflow_call を使うと、他のワークフローを再利用できて便利 です。 時には リポジトリ を跨いで共通のワークフローなんて事もやりたいですよね。 しかし、いくつか気を付けるべき点があります。 注意点その1: github コンテキストは引き継がれる 呼び出し元のワークフローで使用していた github コンテキストは、そのまま呼び出し先にも引き継がれます。 例えば、 github.event や github.actor などは呼び出し元のものが反映されるため、何か 特定のイベントやユーザーで動作させたい場合には注意が必要 です。 例: on : workflow_call : inputs : name : required : true type : string jobs : example-job : runs-on : ubuntu-latest steps : # ここで出力されるevent_nameは呼び出し元になります! - run : echo "Running as ${{ github.actor }} for event ${{ github.event_name }}" 注意点その2: secrets の扱い workflow_call では、 secrets が自動で渡されるわけではなく、呼び出し側で明示的に渡す必要があります 。 忘れがちなポイントなので注意しましょう! 例: # workflow_callを呼び出してる側の実装 jobs : call-reusable-workflow : uses : ./.github/workflows/reusable-workflow.yml secrets : # secretsはこのように渡してあげないと参照出来ません! MY_SECRET : ${{ secrets.MY_SECRET }} 呼び出し元でこのシークレットを適切に設定しないと、ワークフローがうまく動かない可能性があるので、しっかりと設定してください。 3. GITHUB_TOKEN で行った操作は再度トリガーされない 何言ってるんだろ?って思うかもしれませんが、ワークフローで色々自動化していると案外詰まるポイントです! GitHub Actionsでは、デフォルトで GITHUB_TOKEN が提供されており、これを使って API 操作や リポジトリ への変更を行うことができます。 しかし、ここで一つ覚えておきたい仕様があります。 それは、 GITHUB_TOKEN で行った操作は再度ワークフローがトリガーされない という点です。 例: # GITHUB_TOKENを使ってissueを投稿しているサンプルです。 jobs : example-job : runs-on : ubuntu-latest steps : - name : Create a new issue run : | curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -d '{"title":"New Issue","body":"This issue is created by GitHub Actions!"}' \ https://api.github.com/repos/your-repo/issues このように GITHUB_TOKEN を使って何かissueを投稿した場合、その操作自体が新しいワークフローを起動することはありません。 つまり、「issueが作成されたら通知するワークフロー」とかを作っていたとしても通知が飛ばない結果になります。 これを知らないと「なんでワークフローが動かないんだろう?」とハマることがあるので覚えておきましょう! 補足: workflow_run トリガーを活用する もし、 GITHUB_TOKEN での操作後にワークフローを自動的に起動したい場合は、 workflow_run トリガーを活用することを検討してみるのも良いかもしれません。 これで他のワークフローの完了をトリガーにできます。 まとめ GitHub Actionsでワークフローを作成する際には、いくつかの落とし穴や仕様に注意する必要があります。 今回紹介した3つのポイントを押さえておけば、初心者でも安全かつスムーズにワークフローを作成できるはずです。 run セクションで式 ${{}} は使わず、代わりに env を活用しよう。 workflow_call では github コンテキストと secrets の扱いに注意! GITHUB_TOKEN の操作では再度ワークフローがトリガーされない仕様を覚えておこう。 このコツを頭に入れて、快適な GitHub Actionsライフを送ってくださいね! 参考記事・お勧めサイト 最後に参考にした記事やお勧めのサイトを紹介しておきます。 actionlint playground workflowの構文チェックが出来ます。pushする前にサッと確認すると無駄が減ります! GitHub CI/CD実践ガイド――持続可能なソフトウェア開発を支えるGitHub Actionsの設計と運用 (エンジニア選書) 初心者から上級者まで幅広い人にお勧めできる良書です! zenn.dev