TECH PLAY

株式会社LIFULL

株式会社LIFULL の技術ブログ

652

エンジニアの松尾と申します。LIFULL HOME'S で中古住宅売買領域の開発を担当するチームのマネージャーとして働いています。 2024年は新卒1名、中途1名の新入社員が自部署に仲間入りしました。今回はそれらの内容をふりかえり、LIFULLのオンボーディングの工夫や今後の伸びしろについて整理してみたいと思います。 エンジニアのオンボーディング 企業の魅力因子 ふりかえり ⭐️ Philosophy(理念・方針) 1. 会社/上位部門/自部署のビジョンを自分のことばで伝える 2. 会社/上位部門/自部署の目標やミッションを伝える 3. 自部署におけるルールやコミュニケーションのスタンスを伝える 🚀 Profession(活動・成長) 4. 開発環境の構築をサポートする 5. できるだけ早く初リリースをしてもらうサポートをする 6. 成長曲線をイメージするための指針を作る 🧑‍🧑‍🧒 People(人材・風土) 7. 自己紹介をし合う場を作る 8. 業務上で関わりを持ちそうな組織/人と引き合わせる 🎁 Privilege(待遇・特権) 9. 会社のルールや福利厚生を伝える 10. エンジニア職におけるルールや福利厚生を伝える 今後の伸びしろ 完了までのスケジューリング 周囲のメンバーとの分担 まとめ エンジニアのオンボーディング あらためてオンボーディングとは、新しく組織に加わったメンバーの職場への順応を促進することで、早期の戦力化を目指す取り組みです。 エンジニアとしては 開発環境/使用する言語/ドメイン知識/プロジェクト管理方法/ステークホルダー などを理解し、身につける必要があります。 私はLIFULLの魅力を多面的に知ってもらうことで、本人を取り巻く環境に安心を感じ、前向きに取り組んでいける流れを作りたいと考えています。 「採用の過程で伝えてきたLIFULLの魅力を、入社後に再認識してもらえるオンボーディング」について検討してみます。 企業の魅力因子 人が企業に感じる魅力を、下記の4点に分類します。 Philosophy(理念・方針) Profession(活動・成長) People(人材・風土) Privilege(待遇・特権) 人によってどの要素を重視するかは異なりますが、採用の候補者や新入社員には漏れなく伝えていく必要があります。 ここからはオンボーディングにおいて行った取り組みをこれらに分類して整理し、不足がなかったかをふりかえります。 ふりかえり 取り組んだ内容は次回以降やほかのどなたでも活用できるように、チェックリスト風に10項目でまとめてみました。 ⭐️ Philosophy(理念・方針) 1. 会社/上位部門/自部署のビジョンを自分のことばで伝える LIFULLでは経営理念を頂点として、それを落とし込んだビジョンが組織ごとに存在します。「LIFULLエンジニア像」という「エンジニアとしてありたい姿」もインプットする必要があります。これらをどのように体現していくかを自分のことばで伝えます。 note.com ビジョンは半期ごとにふりかえり見直しているため、ふりかえりの場でほかのメンバーのビジョンへの想いも聞いてもらうことで、理念を腹落ちさせていきます。 2. 会社/上位部門/自部署の目標やミッションを伝える 組織の粒度に応じた目標や戦略があるため、会社の方向性を知ってもらう意図でそれぞれを伝える必要があります。社内でのKGIやKPIの定義やとらえ方も合わせて伝えていきます。 戦略については、弊社には「戦略DAY」という各部門が戦略を発表する場があるため、この内容を視聴して理解してもらいます。直接スピーカーに質問することもできますし、必要に応じて1on1などで補足します。 3. 自部署におけるルールやコミュニケーションのスタンスを伝える 下記のようなグループ内で決めているルールを共有します。 グループメンバーの大まかな役割 カレンダーに定例で入っている会議の説明 グループで出社する曜日、リモートワークのお作法 把握していなければ翌日から困ることが多いため、配属初日で必ず伝えるようにします。 🚀 Profession(活動・成長) 4. 開発環境の構築をサポートする 基本的にはドキュメントを参考に進めてもらいますが、ドキュメントのメンテナンス不足により「ここがうまく動きません…」が起こるため、専用のSlackチャンネルを作成して伴走します。 出社時は近くに座っていつでも質問を受けられるようにし、リモートでも声をかけやすいようにSlackのハドルに常駐します。チームで声を掛け合い、新メンバーが1人になる状況をできるだけ作らないようにします。 5. できるだけ早く初リリースをしてもらうサポートをする チームの一員であることを「成果」によって感じてもらうため、軽めのバグ改修などをお願いして短期でリリースしてもらいます。周囲からの歓迎をあらためて伝えるために、リリース直後には「〇〇さんの初リリース完了しました!!!」と全体メンションで発言し、リアクションを集めるようにします。 「案件がない…」とその場で困らないために、普段からGood First Issueを整えておくことが大切だと感じます。 6. 成長曲線をイメージするための指針を作る 入社から1年間のマイルストーンを作成します。「1年後にこの領域の設計ができるようになってほしいです」「この時期にはこの規模の実装ができるようになっていると思います」という階段を作り、本人には具体の部分を埋めて目標設定に臨んでもらいます。 入社段階では、新しい環境で本人の能力が最大限発揮できるかは未知です。まずは焦らず順応してもらうため、活躍は期待していること/ただし直近での大きな成果は計算には入れていないことを伝えます。 🧑‍🧑‍🧒 People(人材・風土) 7. 自己紹介をし合う場を作る 自部署において、既存のメンバーと新メンバーがお互いに経歴や嗜好を語り、気になるところを質問し合う会を設けます。エンタメ好きなメンバーには「好きなYouTuberは?」、中途入社のメンバーには「前職のユニークな社内用語は?」など質問はさまざまです。 また、LIFULLでは4半期に1回の頻度で全エンジニアが集まる総会もあり、新入社員にはその場で自己紹介をしてもらう風土があります。 8. 業務上で関わりを持ちそうな組織/人と引き合わせる 新卒のメンバーには配属前の研修で関わった社員や同期がいますが、中途社員は相対的につながりが少ないです。今年はフロントエンドのメンバーを迎えたため、他部署のフロントエンドエンジニアやデザイナーのメンバーとのランチや飲みを調整しました。バックエンドの場合はインフラやアーキテクトの部門との連携を検討します。 一方で、 START という、別の部署の先輩社員がメンターになる制度もあります。「万一上司の私に言いづらいことがあれば、さらに上司やSTARTのメンターに相談してね」と伝えるようにしています。 🎁 Privilege(待遇・特権) 9. 会社のルールや福利厚生を伝える 大枠は入社時にバックオフィスの社員から伝えられますが、評価制度の詳細や勤務ルールなどは必要に応じて伝えていきます。 社内には 多様な取り組み があり、上司である私が認識していないものもあります。本人に近い属性の人が「こういうのもあるよ」と伝えてくれるとありがたく、そのためにも前述のつながりを増やす取り組みは重要だと思います。 10. エンジニア職におけるルールや福利厚生を伝える LIFULLには keelaiという社内のAIチャットボット や エンジニアいつでも相談 というQ&Aフォーラムなど、利用すると効率的に/安心して開発を進められるしくみがあります。 また アクセシビリティに専門性を持つメンバーの1on1 や エンジニアキャリアクリニック のように本人の成長のために活用すべきものもあり、知らなければもったいない情報が多々あります。 LIFULLでは「入社の手引き」という社内ドキュメントに入社後知っておくべき情報をまとめているため、そちらの継続的な整備を行っていきます。 今後の伸びしろ 完了までのスケジューリング 早期の戦力化を期待しているとはいえ、一気に伝えるには多すぎる情報量だと思っています。 「今伝えないといけないか」を考慮して無理のないスケジュールを組みつつ、なるべく早く戦力になってもらえるように徐々に短縮していきたいと感じています。 周囲のメンバーとの分担 あらためて、これらを上司が1人で抱えるとたいへんだと思いました。 実際にチームメンバーが「Slackハドルに常駐しておくので、開発のフォローは任せてください」「他部署のあのメンバーもランチ誘えるけどどう?」と声をかけてくれて助かっています。 特に「Profession(活動・成長)」と「People(人材・風土)」の部分はチームの総員で行うのが望ましいと感じました。スケジューリングの段階で「誰がやるか」も決めておけば効率的かもしれません。 まとめ 2024年の自部署でのオンボーディングについてふりかえりました。今年の進め方が満点だったかというとそんなことはありませんが、2人とも元気で前向きに開発をすすめてくれていて何よりだと感じています。 これは偶然ですが、先日は他職種(プロダクトマネージャー)においてもオンボーディングの取り組みが共有されました。 note.com LIFULLでは職種を問わず新メンバーを大切にし、ともに成長していく文化があります。一緒に働く仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
こんにちは。 LIFULL Tech Vietnam (以下、LFTV)CEOの加藤です。 2023年10月にLFTVのCEOに就任し、早1年が経過しました。 この1年LIFULLグループでは「グローバル開発体制強化」を掲げ、その実現に向け多くの検討と取り組みを繰り返してきました。 そうした活動を繰り返す中で、我々は目指すべきグローバル開発を進めるためには以下の「3つの壁」を超えていくことが重要であるという結論に至りました。 「意識・理解」の壁 「仕組み」の壁 「言語」の壁 今回はその中でも主に「意識・理解」の壁を越えるためにLFTVとして行った取り組みを3つ紹介します。 3つの壁については当社CTOの以下の記事でも紹介しています。 note.com また、LFTVにおける今後のビジョンやグローバル開発体制強化の詳細は以下の記事で紹介していますので、よろしければご覧ください。 www.lifull.blog 関心・理解を深める「# times-lftv」 取り組みの概要 ベトナムからの情報発信 双方向のコミュニケーション カルチャーを醸成する「ガイドライン研修」 研修の構成 第1部:全体把握 第2部:ケーススタディ 第3部:プレゼンテーション 研修後の定着に向けた取り組み 深く戦略を理解する「OneTeam OpenTalk」 OneTeam OpenTalkの特徴 質問の事前収集 双方向の対話 隔週30分、継続的な開催 最後に 関心・理解を深める「# times-lftv」 組織の垣根を越え一つのチームとして連携を深めていくためには、お互いの理解が重要です。 物理的な距離が離れた中で、いかに身近な存在として意識できるようになるかが、結束力を強める鍵となると考えています。 そのような機会を生み出すため、ベトナムからの情報発信を行うSlackチャンネル「# times-lftv」を開設し、運用してきました。 # times-lftvでの投稿 取り組みの概要 ベトナムからの情報発信 # times-lftvでは、LFTVでの業務や取り組みに加えて、ベトナムの文化や習慣、日常生活に関する情報も幅広く発信しています。 ベトナムランチの紹介 ベトナムの交通事情 15時のTea Breakの様子 テト休暇のお祝い など こうした日常の話題を通じて、メンバーがベトナムそのものに興味を持ち、親近感を抱くことを狙いとしています。 双方向のコミュニケーション # times-lftvは単なる一方通行の情報発信にとどまらず、双方向のコミュニケーションが生まれる場としても機能しています。 運用を続ける中で、以下のような動きも見られるようになりました。 日本やマレーシアのメンバーからのコメントをきっかけに会話が広がる。 日本のメンバーからベトナムのメンバーへ向けて、日本に関するトピックが投稿される。 こうした交流を通じて、物理的な距離を越えたつながりが築かれ、メンバー間の関心が高まり、それに伴い理解もより深まっています。 カルチャーを醸成する「ガイドライン研修」 プロダクトチームとして高い成果をあげるためには、一人一人が同じ価値観を共有し、同じ行動規範によって判断することが重要です。 LIFULLグループでは社是の「利他主義」や経営理念を始めとし、日々の行動規範を定めたガイドラインなど共通して持つべき価値観が定められています。 ※ガイドライン含む企業理念は以下を参照 lifull.com これら理念を浸透し、組織の文化として定着させるため、LFTVでは「ガイドライン研修」を実施しました。 研修は大きく3部構成となっており、段階的に理解を深めるプログラムが設計されています。 研修の構成 第1部:全体把握 社是や経営理念、ガイドラインそれぞれのつながりについて理解を深めるセッション。 これにより、日々の行動規範として意識すべきガイドラインがLIFULLグループの理念全体とどのようにリンクしているかを把握します。 第2部:ケーススタディ 各ガイドラインごとに実務を想定したケーススタディを実施。 参加者はチームごとに分かれ、特定の状況においてガイドラインに沿った行動を考え、答えを導き出す形式で進行しました。 これにより、抽象的な概念を具体的な行動に結び付けています。 ケーススタディの様子 第3部:プレゼンテーション 各グループに分かれ、代表者が最も好きなガイドライン項目に対し、自身の経験や考えを結び付けたプレゼンテーションを実施。 実際のエピソードと紐づけて深掘りをすることで、ガイドラインが実際の業務や行動にどのように結び付くかを深く理解する機会となりました。 プレゼンテーションの様子 研修後の定着に向けた取り組み 今回の研修終了後も4半期に一度を目安とし、継続してプレゼンテーションの機会を設けることを予定しています。 また、10月より人事評価にガイドラインの要素を組み込んでおり、定期的な振り返りとフィードバックの機会を創出することで定着を図っています。 深く戦略を理解する「OneTeam OpenTalk」 組織全体が一体となって目標を達成するためには、ビジョンや戦略の理解が欠かせません。 しかしながら、従来のビジョンシェアリングだけでは、CEOから社員への一方通行の形式に偏りがちで、細かな部分や背景までは伝わりきらないという課題があります。 そこで、LFTVでは双方向の対話を通じてビジョンや戦略を深く理解し合うための取り組みとして、「OneTeam OpenTalk」を開始しました。 OneTeam OpenTalkの特徴 質問の事前収集 社員がビジョンや戦略に対して持つ疑問を事前に集めることで、具体的なトピックに基づいた対話を実現しています。 これにより、社員一人一人が抱く「なぜ?」や「どうして?」を解消する機会を提供しています。 双方向の対話 当日は集めた質問に対してCEOが回答しながら、テーマについて深掘りする形式で進行。 単なる説明にとどまらず、所々で社員からの追加の質問や対話を交えながらビジョンや戦略の理解を促しています。 隔週30分、継続的な開催 OneTeam OpenTalkは隔週30分という短時間で、1回につき1~2つの質問をテーマに設定して実施しています。 このように小規模なテーマ設定を行うことで、特定の内容に集中して理解を深めることができ、日々の業務に負担をかけることなく継続的な実施が可能となっています。 OneTeam OpenTalkの様子 最後に 我々LIFULLグループではLIFULL本社および海外開発拠点であるLFTV、 LFTM の垣根をなくし、一体となって開発を推進するグローバル開発を目指し日々挑戦しています。 今回はその実現に向けた「意識・理解」の壁を超える取り組みを3つ紹介させていただきましたが、LIFULL、LFTV、LFTMでは一緒に働く仲間を募集しています。 LFTVでは日本とは異なる文化や価値観に触れ、日々刺激を受けながら成長する機会に溢れています。 海外のメンバーと直接連携しプロダクト開発に携わることで、コミュニケーションの取り方や考え方の違いなど多くの面で新たな発見が得られます。 また、ベトナムならではの社内イベントも多数開催しているため、ベトナム文化の中で働きたいと考える方にとっても魅力的に感じていただけるはずです。 そして、グローバル開発を推進する中で、国や所属する開発拠点にとらわれない関わり方を目指し、幅広い領域や役割を担う機会を生み出していきます。 幅広い経験が得られる機会の中から、一人一人に合ったキャリアパスを実現するための支援も行っていきます。 グローバル開発に共感いただき、同じ志のもと挑戦したいと感じて下さった方がいらっしゃいましたらぜひともご応募お待ちしています! hrmos.co hrmos.co lifull-tech.vn lifull-tech.my
アバター
こんにちは。テクノロジー本部アーキテクトグループの福留です。最近気になっているものは model-checking/kani です。 アーキテクトグループは、LIFULL のエンジニアの開発生産性を、エンジニアに寄り添いながら向上させていくことをミッションとして活動しています。 そこで今回は、昨年から今年にかけて行った LIFULL HOME'S のリリースフローの改善、通称「即日リリースプロジェクト」について紹介します。 このプロジェクトでは、 LIFULL HOME'S のリリース作業を完全自動化し、リリースにかかる時間を 1/5 に短縮しました。 その取り組みについて、順を追って説明します。 従来の LIFULL HOME'S のリリースフロー リリースフローの課題 課題 1: リリーサーの負担が大きい 課題 2: リリースまでの時間が長い 即日リリースの目標 即日リリースの適用 Phase 1. リリーサーの確認業務自動化 Phase 2. リリース作業の自動化 Phase 3. リリースフローの高速化 まとめ 従来の LIFULL HOME'S のリリースフロー LIFULL HOME'S はいくつかのマイクロサービスで成り立っています。 このマイクロサービスでの機能の公開、いわゆるリリースは「リリースチケット」と呼ばれる Jira チケットと GitHub の PullRequest によって管理されています。 リリースチケットは PullRequest ごとに作成される Jira チケットで、開発グループ長と、担当企画のリリース承認と、リリース状況の管理を行います。 また、LIFULL HOME'S のステージング環境を含めた環境は、テスト環境、プール環境、本番環境の 3 種類があります。 それぞれの環境は以下のような役割を持っています。 テスト環境 develop ブランチの内容が反映される テストデータによる動作確認が可能 プール環境 release ブランチの内容が反映される 個人情報等のセンシティブ情報をマスクした、本番環境相当のデータによる動作確認が可能 本番環境 master ブランチの内容が反映される ユーザーに提供される本番環境 従来のリリースフローでは、これらを用いて以下の流れでリリースが行われていました。 1 日目 (Jira) リリース承認を開発グループ長・企画担当者からもらう (GitHub) PullRequest を develop ブランチにマージし、テスト環境に反映 (テスト環境) テスト環境で動作確認 2 日目 (プール環境) 開発者がプール環境で動作確認 3 日目 (本番環境) 開発者が本番環境で動作確認 以上をまとめると、従来は次の図のようなリリースフローでリリースが行われていました。 従来のリリースフロー このフローの中では、「リリーサー」と呼ばれる人たちが、以下のような作業を行っていました。 PullRequest とリリースチケットの突き合わせ develop ブランチを release ブランチにマージし、プール環境へ反映 release ブランチを master ブランチにマージし、本番環境へ反映 反映された PullRequest に対応するリリースチケットのリリースステータスを「プール反映完了」「本番反映完了」などに変更 リリーサーの作業 リリースフローの課題 このリリースフローには、以下のような課題がありました。 課題 1: リリーサーの負担が大きい リリーサーの作業はすべて手作業で行われている上、上記のリリースフローで運用されているリポジトリは 5 つあります。ゆえに、リリーサーの確認作業はとても煩雑なものでした。 このリリーサーが手作業で行う作業は、月曜から木曜まで、1 日 4 時間ずつ、合計で 16 時間もかかっていました。 しかも、リリーサーはリリースフローと、リリースされるアプリケーションについて大まかな知識を持っている必要がありました。そのため、比較的社歴の長い、経験豊富なエンジニアがリリーサーを担当する傾向にありました。 つまり、 経験豊富なエンジニアの時間がリリーサー業務に、週 16 時間も使わなければならない という、もったいない事態が起こっていました。 課題 2: リリースまでの時間が長い 図の通り、開発者が変更を develop ブランチにマージしてからリリースされるまでには、約 3 日が必要です。 リリースまでのリードタイムは、最短で約 40 時間かかっていました。 本来なら新しい価値をどんどんユーザーに提供していきたいはずが、 価値提供のボトルネックがリリースフローにもある ことは明白でした。 即日リリースの目標 このような課題から、アーキテクトグループでは、以下の目標を立て、リリースフローの改善を行うことにしました。 リリーサー業務を自動化し、リリーサーという存在を不要にすることで、エンジニアが開発に集中できる環境を作る リリースフローを効率化することで、develop マージ後 1 日以内でのリリースを可能にする 2 番目の目標から「即日リリースプロジェクト」と名付け、3 つのフェーズに分けて取り組むことにしました。 即日リリースの適用 Phase 1. リリーサーの確認業務自動化 最初に、リリーサーの作業で最も時間を取られていた確認業務を自動化することから始めました。 ところが、リリーサーはリリースチケットの上で、「リリース承認されていること」以外に何をチェックしているのか、具体的には分かっていませんでした。 というのも、リリーサーは部署をまたいで持ち回りで担当が決まっていたため、公式な引き継ぎ資料がなかったためです。 非公式な引き継ぎ資料はあったものの、あくまでリリーサーが所属する部署内での資料だったため、チェックしている内容はリリーサーによってまちまちになっていました。 たとえば、あるリリーサーは最低限承認されていることをチェックしているが、あるリリーサーは PullRequest の内容まで入念にチェックしているらしい、という状況のようでした。 そこで、リリーサーのチェックを一緒にやってみることで、実際のチェック内容を把握しつつ、標準となるチェック項目を作成しました。 具体的なチェック項目については割愛しますが、人間でなければ判断できないチェック項目はリリーサーやエンジニアとの合意の上で削減しました。そして、自動化しやすく、かつ守るべきところは守れるチェックリストに落とし込みました。 最終的にできたチェック項目について、手元で動かせる cli ツールを作成してリリーサーに使ってもらうことで、作業の負担を大幅に減らしました。 Phase 2. リリース作業の自動化 Phase 1 ではリリース前の確認作業を自動化しました。ここから各環境に反映していく作業、つまりリリース作業も自動化することで、リリーサーの存在を不要にできます。そこで、リリース作業を自動で行ってくれるアプリケーションの開発を計画しました。 リリース作業とは単に GitHub のマージボタンを押すだけかと思いきや、実はそうではありません。 LIFULL HOME'S を構成するアプリケーションは API 層・BFF 層・フロントエンド層に分かれており、それぞれに依存関係があります。 この依存関係を踏まえると、前段のアプリケーションがリリースされたことを確認してから、次のアプリケーションをリリースしなければなりません。 たとえばフロントエンドと BFF に同時に変更を行った場合、BFF の方が先にリリースされてしまうと、フロントエンドの参照部分が壊れてしまい、障害につながります。 つまりリリーサーは、「前段のアプリケーションのリリースが完了したこと」を確認してから、次の段のアプリケーション(リポジトリ)のマージボタンを押さなければなりません。 ところで LIFULL には、 KEEL という全社で横断的に利用しているアプリケーション実行基盤があります。KEEL のワークロード監視ダッシュボードからは、現在のアプリケーションの状態、具体的には Kubernetes の Pod の状態を確認できます。 KEEL については、ぜひ以下をお読みください。 www.lifull.blog KEEL チームに依頼し、Pod の状態として、現在デプロイされている Git のコミットハッシュを追加してもらいました。これで、リポジトリの master ブランチのコミットハッシュと Pod のコミットハッシュを比較できるようになりました。 PodのステータスにSHA-1ハッシュを表示 リポジトリのコミットハッシュと Pod のコミットハッシュが同じになったのを見れば、アプリケーションがリリースされたかどうかを自動で判断できるようになりました。 また、リリースの進捗状況を Slack へ通知するようにしました。 通知内容は以下のようなものがあります。 現在どのアプリケーションをリリースしているのか GitHub の障害などによるマージの失敗 カナリアリリースの失敗 これらの内容を、対処方法も含めて通知することで、リリースの進捗状況をほぼリアルタイムで把握できるようにしました。 slack通知の例 同時に、このアプリケーションに、リリーサーが行っていたチェックを自動化するツールを組み込むことで、リリースのすべてを自動で行えるリリースシステムが完成することになりました。 リリースシステムによって自動化 Phase 3. リリースフローの高速化 リリーサー業務の自動化とは別に、リリースフローの高速化にも取り組みました。 従来のリリースフローをおさらいすると、次の図のようになっていました。 従来のリリースフロー(再掲) プール環境とは、本番環境のデータから個人情報をマスクしたデータを利用できる環境で、主に本番相当の環境としてテストを行うために利用されています。 この反映と確認のために 1 日かけることが、結果としてリリースまでのリードタイムを長くしていました。 そこで、プール環境にテスト環境と同じ develop ブランチを参照させることで、 テスト環境とプール環境へ同じ日に反映を行い 、リードタイムを短くすることを計画しました。 まずエンジニア全体にアイデアの共有を行い、そこで受け取った質問に対して回答や説明をしました。 当時の質問票 そして、機能追加に責任を持つ企画・エンジニアの代表の方にプレゼンを行い、合意を得ることで、リリースフローの変更を行うことに決まりました。 リリースフロー変更のプロポーザルの一部 最終的にできたリリースフローは以下のように、シンプルなものになりました。 新しいリリースフロー 1 日目 (Jira) リリース承認を開発グループ長・企画担当者からもらう (GitHub) 開発者が PullRequest を develop ブランチにマージし、テスト環境とプール環境に反映 (JIRA) リリースシステムがリリースチケットのステータスを変更 開発者がテスト環境とプール環境で動作確認 2 日目 リリースシステムが develop ブランチを master ブランチにマージし、本番環境に反映 (JIRA) リリースシステムがリリースチケットのステータスを変更 開発者が本番環境で動作確認 このリリースフローの変更に合わせて、以下のようなリリースのルールを設けました。 (即日リリース) 当日 11 時までにテスト環境とプール環境で動作確認できていれば、その日のうちにリリース可能 (即時リリース) プール環境での確認が必要なく、KEEL の機能で作成される Ephemeral Environment *1 で十分にテストできていれば、 直接 master ブランチにマージしてもよい ルール 1 によって、従来のリリースフローでリリースされるまでに約 40 時間かかっていたものが、最短 8 時間、つまり 約 5 倍速く リリースできるになりました。 また、ルール 2 に従えば、リリースまでのリードタイムはより短くなり、 もはやリードタイムは存在しません。 これらの改善だけが原因ではないと思いますが、年間のリリース数は、 即日リリース適用以前と比べて約 20 %増加しました 。 まとめ LIFULL HOME'S のリリースフローを改善することで、リリーサー業務を自動化し、リリーサーという存在を不要にできました。その結果、開発者が、開発業務により集中できるようになりました。 また、リリースフローを効率化したことで、リリースまでのリードタイムを大幅短縮し、価値提供が従来よりも速くなりました。 即日リリース PJ は、アーキテクトグループならではの「開発者に寄り添った開発生産性の向上」ができた、良い例だと思います。 今後も、エンジニアの生産性向上に向けて、さまざまな取り組みを行っていきます。 最後に、LIFULL ではともに成長していける仲間を募集しています。 12 月 2 日現在、アーキテクトグループでもメンバーを募集中です!よろしければこちらのページもご覧ください。 hrmos.co hrmos.co hrmos.co *1 : Firebase のプレビュー環境のような、PullRequest に紐づいて作成されるテスト環境
アバター
こんにちは。クオリティアーキテクトグループ(以下、QAG)でQAエンジニアをしている片野です。 QAGでは横断組織として自動テストやツール開発、プロセス改善などのしくみ作りに取り組んでいます。 今回は、横断QA組織が開発組織の品質に関する課題を見つけるための取り組みについて紹介します。 QA組織の紹介 背景 やったこと QAサークル QA通信 QA探検隊 まとめ QA組織の紹介 LIFULLでは設計や実装を行っている開発エンジニアがテストも行っています。 ですので、QAエンジニアのプロジェクトの関わり方としては、下記のような開発者への支援活動を行っています。 プロジェクト支援 テスト計画サポート 自動テスト相談 テストのお悩み支援 提供ツール・データ テスト仕様書の共通テンプレート Bucky 開発プロセス・そのたの活動 Bucky Shift-Left 探索的テスト実施・導入支援 組織の成り立ちについてはこちらの記事をご覧ください。 https://www.lifull.blog/entry/2019/12/15/000000 背景 横断QA組織の立場としては次のような課題があります。 社内には、さまざまな開発や保守のプロジェクトが同時に動いているので、QAGがすべてのプロジェクトへ均等な支援を行えない 各プロジェクト・組織の品質面で困っている事や改善したい事がQAGから見えにくい 各プロジェクトの困りごとは相談を受けてから気付くことが多く、QAGは受け身になることが多い やったこと 上記に書いた課題を解消するために、下記の3つの活動を実施しました。 QAサークル QAサークルは、QAGと開発チームのコミュニケーションの機会を増やす事を目的に、チームや個人でたまっている品質に関する課題やモヤモヤとした問題点の意見交換を行う場です。 開催形式はQAGがコンテンツを作成して発表する講義形式やみんなで意見を交わすディスカッション形式など、毎回試行錯誤しています。   ※過去に実施した探索的テストをテーマにした会の目次、チャーター作成ワークの結果 実施データ 開催回数:3回 開催頻度:3ヵ月に1回(1時間)程度 延べ参加人数:26人 開催テーマ:テスト全般、 テスト仕様書の共通テンプレート 、探索的テスト 狙い QAエンジニアと開発者が定期的にコミュニケーションをとり、相談がしやすい下地づくりをする QAGについて知ってもらう QAGが行っている施策の普及 工夫している点 タイムリーな話題をテーマに選定することや地道な広報活動を行った 開催テーマに関する良い事例を持っている方にインタビューし、QAサークル内で事例紹介をした ワークで実際に手を動かしてもらい理解度を高めた 効果 当初は想定していなかったエンジニア以外(企画職)の方の参加 少しずつ着実に、個別の相談や支援につながっている 参加者はイベントを通して、下記のような学びや気付きがあったそうです。 今まで知らなかった新しいテスト手法(探索的テスト)について理解が深まった  困ったらQAに相談しようという心持ちを作れた ※探索的テストについては別の機会にブログを書いてみたいと思います。 QA通信 Slackに最近のQAGの取り組みについての紹介記事を毎月投稿しています。 実施データ 投稿回数::16回 開催頻度:1ヵ月に1回 狙い QAGの認知度向上 QAGへのサポート依頼数増加 工夫している点 Block Kit Builder でフォーマットを統一し可読性を向上させた 絵文字を効果的に使用することで、書き手の感情を読み手に伝えるとともに情報をコンパクトにまとめて伝達した 効果 投稿した記事を読んだ人が、同僚に読むことを薦めているslackメッセージが複数見受けられ、QAGや開発したツールの認知度向上に寄与した QAGのツールやお知らせを気軽に発表できる場になった QA探検隊 Slackに投稿された品質に関するキーワード(たとえば「バグ」、「テスト」)を自動で定期的に検索し、困っている人がいないかを探しました。 実施データ 実施期間:2024/1〜2024/5 不定期に実行(社内イベント開催直後など) 狙い slack上に流れている品質課題に関する情報を能動的に取得する 工夫している点 社内で開発したツールを用いてslack api経由でエゴサーチを実施した 膨大な検索結果を効率的に仕分けするしくみを作成した 効果 テスト仕様書の共通テンプレートについての困り事を素早く拾う事ができ、改修や機能追加の優先度付けに役立った 各組織のSlackチャンネル毎のキーワード発話量を可視化でき、プロジェクト支援先を決める一助となった 下記の理由のため、現在QA探検隊は定期的な活動をしていません。 膨大な検索結果を効率的に仕分けするしくみを作成しましたが、完全な自動化ができていなく、手作業の部分もあるため費用対効果が見合わなかったため。 まとめ 横断QA組織が開発組織の品質に関する課題を見つけるための取り組みについて紹介しました。 活動を通して、課題を見つけるためには定期的に開発組織とコミュニケーションを取ることと、QAGが何をしているかを定期的に広報することが大切だと感じました。 今後も今回説明した活動やブログなどを活用して、QAGが実施している事を広めていきたいと思います。 最後まで読んでいただきありがとうございました。
アバター
こんにちは、エンジニアの中島です。 この記事はフロントエンドのパフォーマンス調査の記録を記事にしたものです。 弊社は自社プロダクトのパフォーマンス劣化を検知するために WebPageTest を導入し、数時間に一度自動計測をするようにしています。 そこでとある日から TBT (Total Blocking Time) が劣化しているようなので原因を調査してほしいという依頼が来ました。 現象確認 まずは本当に問題が起きているかの確認です。 WebPageTestのタイムラインを見てみます。 たしかにいくつかのタイミングで大きめのスタイルの再計算が走っていることがわかりました。 しかしながら、これらが"ある日を境に"、TBTの大幅な劣化を引き起こした犯人かどうかはまだわかりません。 数日分のタイムラインをみて確認する必要があります。 タイムライン情報を一つ一つ目でみて確認するのも大変だなと思ったのでこのタイムラインの元になっているjsonファイルをパースして導くことができないかと考えました。 生データを見るとあまり詳しくはわかりませんが以下のように配列で50万行ほどログがつまっているようでした。 { ... " traceEvents ": [ { " args ": { ... } , " name ": " XXXX ", " dur ": 2 , .... } , { " args ": { ... } , " name ": " XXXX ", " dur ": 1 , .... } , { " args ": { ... } , " name ": " XXXX ", " dur ": 3 , .... } , { " args ": { ... } , " name ": " XXXX ", " dur ": 1 , .... } , ... ] } nameにはどういうログなのかが記載されており、具体的にはFunctionCallやUpdateLayoutTree、ResourceFinishといった値がみられました。 またdurはdurationの略のようで1/1000ms単位の数値が入っていることがわかりました。 ざっくりとこのログに対応する モデル を作り、そこから10ms以上のUpdateLayoutTreeだけを抽出してその実行時間を出力してみれば少なくとも発生の根拠になるかなと考えました。 WebPageTestを簡単にスクレイピングして該当日の前後数日のtimeline情報のjsonファイルのurlを抽出し、そのコンテンツを取得してパースしていきます。 records = Performance :: Timeline :: Record .from_timeline( " path/to/timeline.json " ) p ([date, records .select(& :update_layout? ) .select{|record| record.duration > 10 } .map(& :duration )] (タイムラインデータのモデル) Performance::Timeline::Record class Performance :: Timeline :: Record def initialize (record) @record = record.deep_symbolize_keys end def name @record [ :name ] end def duration ( @record [ :dur ] || 0 ) / 1000.0 end def update_layout? name == ' UpdateLayoutTree ' end def self . from_timeline (file) result = JSON .parse( File .read(file)) records = result[ ' traceEvents ' ] records.map {|record| new(record) } end end 実行結果 [YYYY-MM-DD 19:41:43 +0900, [15.948]] [YYYY-MM-DD 19:41:43 +0900, [15.984]] [YYYY-MM-DD 19:41:48 +0900, [20.741]] [YYYY-MM-DD 19:41:09 +0900, [11.888]] [YYYY-MM-DD 19:42:24 +0900, [25.136]] [YYYY-MM-DD 19:41:59 +0900, [14.453]] [YYYY-MM-DD 19:42:15 +0900, [14.962, 13.14, 11.3871, 11.410]] [YYYY-MM-DD 19:41:49 +0900, [15.190, 15.919, 10,873, 11.733]] [YYYY-MM-DD 20:57:31 +0900, [16.211, 10.861, 15.085, 11.209]] [YYYY-MM-DD 20:59:48 +0900, [26.105, 19.971, 17.264, 11.762]] [YYYY-MM-DD 20:55:14 +0900, [18.890, 15.410, 12.570, 14.569]] [YYYY-MM-DD 19:37:16 +0900, [17.059, 11.72, 11.735, 13.011]] [YYYY-MM-DD 19:38:57 +0900, [14.816, 17.057, 13.122, 10.301]] 実行結果を見ると、確かにある日を境にスタイルの再計算が急増してることが確認できました 原因調査 ある日を境にということであれば、リリースだったり、GTMなどのツール経由のタグ挿入などが疑われます。 WebPageTestは数時間に一度回しているので、同じ手順で問題発生の時間を特定します。 まずはその特定された時間にリリースされたものを一つずつ洗っていきます。 しかしながら今回は対象となるページに影響を与えるような実装はどこにも含まれていませんでした。 そうなるとツール経由のタグ挿入の影響の線が濃くなります。 WebPageTestとchromeのdevtoolsで計測したPerformanceのログは中身が基本的に同じなので実際に手元のブラウザでそれらのツールをネットワークブロックした時とそうでない時のログを見比べて判断していきます。 とはいえ、色々なものの影響をうけるためtimelineが完全に安定することはなく、複数回分のログを取得して判断していく必要があります。 その工程が面倒なのでPuppeteerを使って自動化していきます。 require ' puppeteer-ruby ' ... Puppeteer .launch( headless : false ) do |browser| page = browser.new_page block_list = [ # タグ挿入系のサービスのパス (ex: gtm) # ... # ... ] # ネットワークリクエストのインターセプトを有効化 page.request_interception = true page.on( ' request ' ) do |request| if block_list.any? {|uri| request.url.include?(uri) } request.abort # リクエストを中止してブロック else request.continue end end # とありえず10回分計測する ( 1 .. 10 ).each do # トレースの開始 page.tracing.start( path : ' trace.json ' ) # 検証対象のページに移動 page.goto( ' https://www.homes.co.jp/path/to/target/ ' ) # トレースの停止 page.tracing.stop # トレース結果の読み込み trace_data = File .read( ' trace.json ' ) trace_json = JSON .parse(trace_data) records = Performance :: Timeline :: Record .from_timeline( ' trace.json ' ) p records.select(& :update_layout? ) .select{|record| record.duration > 10 } .map(& :duration ) end end このように検証したところ、今回はとあるタグ挿入ツールをブロックすると問題のスタイルの再計算はなくなることが判明しました。 ツールから挿入されてるタグを列挙し、二分探索でブロックしていき原因となるスクリプトを特定します。 今回はこのような手順で現象の確認と原因の特定ができました。 最後に この調査方法が王道的な調査方法かはわかりませんが、生ログを見る調査方法は汎用性の高いアプローチなので何かの参考になれば幸いです。 LIFULL ではともに成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co https://hrmos.co/pages/LIFULL/jobs/010-9999 hrmos.co
アバター
こんにちは、エンジニアの中島です。 この記事は2024年10月のLIFULL社でのアクセシビリティ改善およびやっていき活動の報告です。 この活動報告は月次で出すかもしれないし出さないかもしれないくらいの温度感で運用されています。 目次 目次 サービス改善 LIFULL HOME'S iOSアプリのお気に入り画面のアクセシビリティ対応 アクション操作物件にチェックを入れる例 育成・啓発の取り組み 各開発グループを対象にした勉強会実施 LIFULLアクセシビリティガイドラインのWCAG2.2対応の開始 アクセシビリティ1on1 お知らせ サービス改善 本期間中はアクセシビリティ推進部署による修正はなかったため、各サービス開発部署の取り組みのみを紹介させていただきます。 LIFULL HOME'S iOSアプリのお気に入り画面のアクセシビリティ対応 iOSアプリ開発チームが、お気に入り追加した物件の一覧画面のアクセシビリティ対応を行いました。 修正内容は以下の通りです。 ナビゲーション内の各ボタンの読み上げ内容を意味の伝わるものに修正 お気に入り解除や物件チェックなどの機能をVoiceOverで操作できるように修正 コントラストが不足していた一部テキストのコントラスト調整 各ボタンの十分なタップ領域確保 物件コンテナ内の各機能はアクションに登録することでVoiceOverからも操作ができるようになりました。 アクション操作物件にチェックを入れる例 アクション起動 チェック機能を呼び出し チェック実行 育成・啓発の取り組み 各開発グループを対象にした勉強会実施 もともとフロントエンドエンジニアやデザイナー向けに1対1のアクセシビリティ育成を行っているのですが、それに加えて社内の大半の開発部署がアクセシブルなアプリケーション開発を自力で行っていけるようになることを目的として部署ごとに学習機会を設けることにしました。 対象となる開発部署は20グループ程度で、本期間中は3つのグループに受講いただきました。 内容は スクリーンリーダーを利用している当事者の動画の視聴 動画視聴であらわになった問題点とそれに言及しているWCAG項目の対応確認 どのように実装すれば支援技術でUIの役割や状態を伝えることができるかの学習 となっています。 LIFULLアクセシビリティガイドラインのWCAG2.2対応の開始 現在のLIFULLアクセシビリティガイドラインはWCAG2.1をベースに作っており、新しく認知分野やモバイルデバイスへのサポートを含んだWCAG2.2の内容を取り込めておりませんでした。 ワーキンググループ内で各追加項目の読み込みを行い、各項目をどのように反映させるかを決定し、具体的な作業に入り始めました。 アクセシビリティ1on1 本期間中はフロントエンドエンジニア9人、アプリケーションエンジニア1人、デザイナー1人に対して行いました。 内容はWCAGの解説、APG(aria authoring practices)の解説、coga-usableの解説、実際のアプリケーション開発時でのアクセシビリティ配慮に関する相談などが主なものとなります。 お知らせ LIFULLではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
グループデータ本部データサイエンスグループの嶋村です。 LIFULLは、2024年10月15日(火)から開催される 国土交通省初となるデータコンペ 「 第1回 国土交通省 地理空間情報データチャレンジ ~国土数値情報編~ 」に対して 協賛 をしており、LIFULLの主力サービスの一つである LIFULL HOME’Sのデータセットを提供 しています。今回、その取り組みを紹介したいと思い記事を書きました。この記事をご覧の皆様も是非コンペにご参加いただけると嬉しいです。 本コンペでの企画運営は一般社団法人不動産建設データ活用推進協会(PCDUA)になりますが、LIFULLはPCDUAに特別会員として加入しており、​不動産および建設分野のデータ活用やデジタル推進のエコシステム構築を目指しています。その一環として、不動産・建設分野におけるデータ活用の活性化になればと思い、LIFULL HOME’Sのデータセットを提供することになりました。 これまで、学術機関向けに対しては、国立情報学研究所(NII)の情報学研究データリポジトリ(IDR)を通じて、「 LIFULL HOME’Sデータセット 」を公開してきました。当時の公開の経緯などは是非下記の過去の記事をご覧いただきたいと思いますが、多くの研究者の方々に利用していただき、たくさんの研究成果の創出にも貢献できたと実感しています。 「HOME'S」の物件・画像データセットを研究者に提供開始します! - LIFULL Creators Blog 「HOME'Sデータセット」に高精細度の間取り図画像データを追加しました - LIFULL Creators Blog 「IDRユーザフォーラム2023」参加報告 - LIFULL Creators Blog 一方で、民間に対して広く公開する取り組みは 今回が初めて となり、どの程度のコンペ参加者が集まるのか、コンペ参加者によってどのようなデータ分析がされるのか、とてもワクワクしています。社内のデータを外部へ提供することはリスクにもなりえるのですが、一方で多くの社外の方々との連携につながる良い機会でもあり、多くの方にデータに触れていただきLIFULLを少しでも知っていただくきっかけになればと願っています。 また、今回他の企業も協賛としてデータセットや分析環境を提供をして下さっており、 オープンデータと複数の民間企業のクローズドなデータを組み合わせて分析できる機会は非常に稀 だと思います。詳細は コンペ特設サイト をご覧下さい。 最後になりますが、データサイエンスグループでは「活用価値のあるデータを創出」し「データを活用した新たな機能やサービスの研究開発」を加速して下さる シニアデータサイエンティストを募集 しています。また、事業部内でもデータ分析で事業に貢献する データアナリストも募集 しております。 興味お持ちいただける方は、 カジュアル面談 も行っていますのでお気軽にご連絡ください。 hrmos.co hrmos.co
アバター
こんにちは、エンジニアの中島です。 この記事は2024年9月のLIFULL社でのアクセシビリティ改善およびやっていき活動の報告です。 この活動報告は月次で出すかもしれないし出さないかもしれないくらいの温度感で運用されています。 目次 目次 サービス改善 売買物件登録画面の各コントロール・グループへの名前設定 育成・啓発の取り組み LIFULL Creative School WCAG輪読会 アクセシビリティ1on1 お知らせ サービス改善 本期間中の改善取り組みのターゲットはLIFULL HOME'S 賃貸・流通マネージャーサイトです。 こちらはLIFULL HOME'Sをご利用いただいている会員様(不動産会社様)が、物件を掲載する際にお使いになる管理画面になります。 諸事情で発表できないものもありますが公開可能な取り組みを紹介させていただきます。 売買物件登録画面の各コントロール・グループへの名前設定 前回のお知らせでは賃貸の物件登録画面におけるコントロールの名前設定についてご紹介しましたが、売買物件登録画面についても同様の作業を行いました。 名前のないコントロール・及びグループに適切な名前を設定し、支援技術ユーザーにとってもコントロールの目的が理解しやすいように修正を行いました。 育成・啓発の取り組み LIFULL Creative School 弊社のデザイナーや広報・企画職の一部を含むクリエティブ本部のメンバーは、LIFULL Creative Schoolという一回2時間程度の勉強会を不定期で行っています。 9/13に行われた会では、啓蒙活動の一貫としてアクセシビリティ推進WGのメンバーが講師を担当し、アクセシビリティに関する基本的な知識を共有しました。 用語の整理から始まり、弊社ビジョンや品質基準、Design Philosophyとの照らし合わせ、障害当事者のUT動画の視聴、WCAGの簡単な解説、すぐに身につけられるチップスの共有・自社事例を交えた座談会などを行いました。 WCAG輪読会 アクセシビリティやっていき勢向けにWCAGの輪読会を隔週で行っています。 本期間中は9月19日に行われました。 範囲は達成基準1.4.6〜1.4.7です。 アクセシビリティ1on1 本期間中はフロントエンドエンジニア1人、アプリケーションエンジニア1人、デザイナー1人に対して行いました。 過去の育成対象者が育成する側に回るといった循環ができるようになってきました。 内容はWCAGの解説、APG(aria authoring practices)の解説、coga-usableの解説、実際のアプリケーション開発時でのアクセシビリティ配慮に関する相談などが主なものとなり ます。 お知らせ LIFULLではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
プロダクトエンジニアリング部の海老澤です。 普段は LIFULL HOME'S の 賃貸領域 のフロントエンド開発をしています。 今回はLIFULL HOME'S の UI 構築を手助けするコンポーネントカタログ(以下「カタログ」)を作った取り組みについて紹介します。 最初にお断りしておくと、この「カタログ」はいわゆる 「デザインガイドライン」「デザインシステム」のことではありません🙇 背景 LIFULL では日々プロダクトの改善を行っており、UI の改修もそのひとつです。 しかし以前から複数のフロントエンドエンジニアが開発生産性と品質面での課題を感じていました。 開発生産性の課題 LIFULL HOME'S の UI はすべて、サービスのベースとなる「LIFULL HOME'S デザインガイドライン」をもとに設計されています。 このデザインガイドラインは現在 Adobe XD で公開されており、コードベースのものはありませんでした。 LIFULL HOME'S ではマイクロサービス化が進んでいるため、必然的に各リポジトリで同じような HTML/CSS をゼロから再生産しています。 共通利用できるコードがあれば開発生産性の向上にもつながるのに、それがないのはもったいないと感じました。 品質面の課題 UI の改修のすべてをフロントエンドエンジニアで受け持っているわけではなく、普段あまり HTML/CSS を触らないエンジニアが担当することもあります。 そのためマークアップやスタイリングの品質がまばらでレビュアーの匙加減で決まっているところがありました。 また、アクセシビリティ品質の担保にも課題がありました。 社内ではアクセシビリティを重視する文化が定着しており、 LIFULLアクセシビリティガイドライン に準じてコーディングをしています。 しかしその品質をすべてのエンジニアが担保するのは非常に難易度が高いです。 フロントエンドエンジニアが改修する場合でも、複雑な js とアクセシビリティ要件が絡むコンポーネントの場合は開発に時間がかかってしまいます。 このような問題を解消するため、「誰もが汎用的に使える実装リファレンス集」がほしいと考えました。 実現したいこと このカタログで実現したいことは以下です。 プロダクトの技術スタックに依存せずコピー&ペーストできる マークアップとアクセシビリティの品質を備えていること カスタマイズに制約は設けないこと 1. プロダクトの技術スタックに依存せずコピー&ペーストできる コンポーネントの展開方法には大きく分けて2つあります。 npmパッケージを配布して利用箇所でimportするやり方と、ファイルまたはコードのコピー&ペーストで展開するやり方です。 今回はコピー&ペーストを採用しました。 パッケージ化のデメリット パッケージ化は以下の理由から開発生産性を損ねる可能性が高いと判断しました。 カスタマイズの自由度が制限される サービスごとに多様な意思決定・UI 個別最適を行っているため、微調整がしづらくなる しくみが複雑化する カスタマイズ用のパラメータを追加し続けるとコンポーネント自体の使い勝手が悪くなる 改修の意思決定のためのコストがかかり、スピード感が損なわれる コピー&ペーストであればコードだけ持っていって各サービスへの最適化もしやすいですし、必要な機能だけのせていけるため複雑化も避けられます。 採用技術とコピー&ペーストの親和性の高さ LIFULL HOME'S はマイクロサービスが進んでおり、フロントエンドではさまざまな技術選定がされています。 ただ Tailwind CSS と Stimulus はほとんどのプロダクトで採用されており、既存の技術構成に後載せすることも比較的容易です。 そのためこれらをベースにコードを掲載していくことにしました。 Tailwind CSS は付与されたクラスから自動的に CSS を作成してくれるため、サービスに合わせてスタイリングの微調整が可能です。 Stimulus も以下のような特徴からコピー&ペーストとの親和性がとても高いです。 バックエンドのフレームワークを問わない 依存関係がなくビルドシステムを必要としない アタッチも HTML に data 属性を付与するだけで良い 汎用的な振る舞いを切り出したコントローラの組み合わせで作れる これらの理由からコピー&ペーストの方が開発生産性の向上のアプローチには良いと考えました。 2. マークアップとアクセシビリティの品質を備えていること コンポーネントカタログに掲載されるパーツは、最低限のマークアップとアクセシビリティの品質を備えていなければなりません。 品質の高いものがコピー&ペーストできることで、利用するプロダクトの品質レベルが底上げされます。 アクセシビリティ要件を満たす実装はしばしば難しく知識と経験を要しますが、コピー&ペースト元のコードがアクセシブルであることで、ジュニアレベルのエンジニアでもアクセシブルなプロダクトづくりに貢献することができます。 3. カスタマイズに制約は設けないこと 使用にあたって改変の制限はありません。 このカタログでは 素 HTML + Tailwind CSS + Stimulus をそのまま載せているので、スペーシングや振る舞いの調整も容易です。 そこからコンポーネント化するのは各サービスの開発者に委ねており、特に縛りは設けていません。 このカタログはあくまでも「実装リファレンス集」としての機能のみを提供し、UI 実装の初動を速く正確に行うことを目指しています。 成果物 LIFULL HOME'S Frontend Recipes こうしてできあがったのが LIFULL HOME'S Frontend Recipes です。 開発は社内の有志3名で行いました。 フレームワークは Astro を使っています。 軽量かつ mdx でサクサク書けるためカタログサイトにはぴったりのフレームワークでした。 現在は以下のコンテンツを配信しています。 Foundation カラートークン、アイコン、フォントなど最小限の要素 Objects ボタン、カード、リスト、フォームパーツなどの Component role, aria-* の付与のほか、フォーカス時や強制カラーモード時でも見やすい状態を担保 ちなみに英語で作成されていますが、これは近年海外拠点にある子会社と一緒に開発することが増えているためです。 ポイント コード出力形式の設定が可能 プロダクトに合わせて以下の設定が可能です。 HTML <-> JSX の切り替え インデント形式の指定 タブ or スペース、およびその数 Tailwind CSS の prefix の指定 後から Tailwind CSS を入れたリポジトリで prefix を指定しているパターン があるため コードの出力形式を設定できる機能 アクセシビリティに関する注意事項の記載 コードをコピー&ペーストするだけでなく、見て学べるような作りにしました。 checkbox コンポーネントのアクセシビリティ注意事項 フォーカスリングの可視性や強制カラーモード時の表示など、ピンと来にくいものはサンプルコードで比較できるようにしています。 フォーカスリングの可視性の比較 強制カラーモード時の表示 デザインガイドラインに掲載されていないコンポーネントも掲載 Q&Aコンポーネント たとえばこちらの Q&A 方式のコンポーネントですが、デザインガイドラインには載っていません。 しかし実際の業務ではたびたび見かけるパターンのため、カタログには収録しています。 とにかくあるあるなパターンを集めていくことで開発生産性の向上を狙っています。 ちなみにガイドラインに記載されているコンポーネントについてはラベリングを行っていますので、そこで区別は可能です。 テキストリンクのページ 終わりに カタログの公開後に自分でも使ってみましたが、どんな環境であろうとコピー&ペースト→クラス微調整だけで済むためかなりスピーディなコーディングができました。 既存のプロダクト改修はもちろんですが、新規のプロジェクトでプロトタイプをさっと作る時にも役立ちそうです。 今後は DatePicker などの難易度の高いコンポーネントや、 Behavior としてコンポーネントのスタイルに依存しない Stimulus Controller を拡充していく予定です。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
こんにちは! LIFULLエンジニアの吉永です。 普段はLIFULL HOME'SのtoC向けCRMチームにてエンジニアリングマネジャーをやっています。 本日は私がCRMチームにジョインしてからの4年間で行ってきたチームとしての技術負債解消活動について紹介します。 技術負債解消活動の変遷、技術負債解消と新規開発がどのようなバランスで進められてきたのか、現状はどうなっているか、今後取り組んでいきたいことについて触れていきます。 アジェンダ 技術負債解消活動の変遷 技術負債解消と新規開発のバランス 現状について 今後取り組んでいきたいこと 最後に 技術負債解消活動の変遷 2020年~2021年ごろ 当時はレコメンド物件をユーザー毎に取得してメールコンテンツを作成するサーバ、LINEサーバからのWebhookで応答するチャットボットサーバ、メールを送る為のマーケティングオートメーションツールなどさまざまなシステムがプログラミング言語もプラットフォームもバラバラな状態で構築されていました。 EC2インスタンスもたくさんあり、デプロイもほとんどが手動対応が必要な状態でした。 これらの課題を解消しようと動き始めたのが2020年ごろの話です。 役割が細かくなりすぎてしまっていたアプリケーションサーバ群を一つにまとめ、プログラミング言語にGoを採用、デプロイはGitHub Actionsで自動的に行えるようにシステムをリニューアルして2021年の初頭にファーストリリースされました。 この時リリースされたシステムの名称がeos(Elastic Omni channel Service)と言います。 その当時の活動については下記の記事にて紹介されています。 www.lifull.blog www.lifull.blog www.lifull.blog 2022年~2023年ごろ eosリリース以降はレガシーシステムをeosへ移植していきました。 さまざまなプラットフォームかつプログラミング言語もバラバラだった状態は少しずつ解消されていき、この頃にはeosの開発時にGo、マーケティングオートメーションツールでは専用のプログラミング言語といったようにサーバサイドに関しては開発時に使用する言語は2種類になりました。 eosはクリーンアーキテクチャを採用したことで初期実装は苦労しました。 しかし機能拡張はしやすく、LINEやメールの動的コンテンツを作成する為の開発はeos上で完結するようになり、生産性が上がって開発速度はどんどんと速くなっていったように思います。 その当時の活動については下記の記事にて紹介されています。 www.lifull.blog www.lifull.blog 技術負債解消と新規開発のバランス 2020年~2022年ごろまではおおむね50:50くらいの比率だったと思います。 個人で見ると、技術負債解消タスクが8割くらい、また別の人は新規開発系が8割くらいなどメンバーによってどちらに比重を重く持っているかの違いはあれど、チーム全体では半分ずつの割合でした。 2023年5月ころからはConventional Commitsを採用して、コミットメッセージベースでの技術活動バランスの可視化ができるようにという取り組みも始めました。 チームによって適切な状態は異なると思いますが、コミットメッセージベースで集計、可視化することで直近の活動は意図した活動になっているか?の一つの判断材料には使えると思っています。 Conventional Commitsを採用してみて得た知見などは下記の記事にて紹介しています。 www.lifull.blog 現状について 重めの技術負債解消タスクはここ数年でだいぶ片付いてきたので、直近だと新規開発の割合が少しずつ増えています。 技術負債解消はシステムリニューアルではなく、リファクタリング系タスクの割合が増えていて、ソースコードの保守性向上を目指した取り組みが多いです。 新たなチャレンジをする機会も増えており、機械学習に挑戦してくれるメンバーがいたり、イベントに参加して新技術のキャッチアップを行ったりができています。 www.lifull.blog www.lifull.blog 今後取り組んでいきたいこと マーケティングオートメーションツールの初期導入から約10年が経過していますが、メールを配信する為の顧客情報連携アーキテクチャは基本的にあまり変わっていません。 配信するコンテンツの内容や、コンテンツを配信する為の仕掛けはマーケティングオートメーションツールのバージョンアップや企画チームの各種施策で改良されていっていますが、ベース部分の改良まで踏み込めていないのが現状です。 ベース部分のところではコンテンツを配信するトリガーとなる情報やトリガーの引き方に制限があり、現状だと「サイトで問合せしてからn日経過した人にこのコンテンツを」という施策はやりやすいのですが、サイトで起こしたアクション(例えばサイトで特定の物件をお気に入り登録してからn日後、サイトの最終訪問からn日後など)を起点としてトリガーに対応させようとするとちょっと工数がかかってしまい、工数がボトルネックとなってそのような施策は優先度が上がりづらい課題があります。 現在はこのベース部分の改良がしやすいようにする為のリファクタリングや一部リニューアルを少しずつ進めており、この活動がゆくゆくはさまざまなコンテンツ配信トリガーを実現することに繋がっていくと思っています。 LIFULLのプロダクトエンジニアリング部 部長の河津は下記インタビューの中で「思いついたらすぐにリリースできるように、開発の速度をもっと速めていきたい」と語っています。 techplay.jp 私達のチームでもこの考え方に沿って各種施策を素早くリリースできるように継続的に技術負債解消と新規開発に取り組んでいきます。 最後に 技術負債解消と新規開発のバランスはそのチームの置かれている状況やフェーズによって最適なものが変わると思います。 このような時間配分になっていることが望ましいというバランスは常に意識し、定性的な面だけでなく定量的なデータを可視化して定期的にチームメンバーと認識合わせをしておくことが必要だと思います。 プロジェクト管理ツールのチケットへ入力した工数、日々創出されていくコミットメッセージなど活動のバランスを可視化する為のデータはあるけど、活用しきれていないというのはどこの組織でも多かれ少なかれあると思いますので、LIFULLで取り組んでみて得た知見を今後もブログなどで共有できればと思います。 最後まで読んでいただきありがとうございました。 LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
フロントエンドエンジニアの嶌田です。 アイコンは、UIデザインにおいて欠かせないパーツです。弊社が提供する不動産情報サイトであるLIFULL HOME'Sでも、多数のアイコンが使われています。 ウェブページにアイコンを埋め込む手段は無数にあります。あなたはいくつ言えますか? それぞれのメリットとデメリットについて説明できるでしょうか? この記事では、LIFULL HOME'Sのフロントエンドで選ばれているアイコン表示方法およびその理由を説明します。その後、アイコンの管理や実装にまつわる不便な点をサービス横断的に解消するために作られた社内ツールと配信システムである、 LIFULL Icon CDN を紹介したいと思います。 私たちのアイコン実装方法 無数にあるアイコン実装方法のうち、私たちが選んだ実装方法は極めてシンプルです。 < img src = "assets/icon-star.svg" width = "24" height = "24" alt = "" > そう、 img 要素を使うのです。これまでさまざまなアイコン実装方法を試してきましたが、結局これが一番だろうということになりました。 なぜなのか? いくつかの観点から見てみましょう。 極めて手軽に使える アイコンを使いたい場面で img 要素を埋め込むだけなので、事前の準備や仕組みの構築が不要です。ブラウザがHTMLをパースしながら img 要素に出会うと、外部リソースとして画像データを読みにいき、自動的にその部分にはめ込んで表示してくれるのです。当たり前ですね! SVGスプライト *1 のようなテクニックは、ページ内で使う可能性のあるアイコンをあらかじめひとまとめにしておく必要があります。不要なアイコンを読み込まないようにする等の最適化を想定すると、トリッキーな仕組みを構築しなければならないでしょう。 img 要素を埋め込むだけなら、 どのプロダクトでも 極めて手軽に使えるのです。 十分なパフォーマンス 表示速度は十分に速いです。 img 要素のぶんだけ別々にHTTPリクエストが発行されることから、パフォーマンスがよくない印象を持たれるかもしれませんが、実際に速いです。 リクエスト数が増える問題は、HTTP/2のストリーム多重化により気にする必要がなくなっています。インラインSVG *2 に比べると、画像のデコード処理を別スレッドに逃がすことができるので、メインのHTMLのパースを素早く終えられるというメリットもあります。 標準的なHTTPキャッシュの恩恵を受けられることも大きな利点です。2度目の表示にはブラウザキャッシュが参照され、ネットワークリクエストは発生しません。 また、 loading="lazy" 属性を使った最適化も期待できるでしょう。アイコンは得てして装飾的に使われるので、読み込みを遅延させても問題ない場面が多いでしょう。 誰にとっても分かりやすい どストレートなやり方なので、HTMLのごく基礎的な知識さえあれば仕組みを理解できます。エンジニアと他職種との連携の円滑さにもつながるはずです。 アクセシビリティ アイコンの代替テキストは、 img タグの alt 属性によって提供します。まったくひねりのない方法ですが、問題なく動作します。 アイコンは、実装方法によってはアクセシビリティ上の問題を引き起こしてしまうことがあります。 たとえばアイコンフォントは、フォントデータが読み込めなかったときやユーザーが意図的にフォントを上書きしていた場合、アイコンが表示されなくなってしまう場合があります。SVGスプライトやインラインSVGでのアプローチは、代替テキストの指定に難があり、幅広いサポートを目指すためにWAI-ARIAに頼らざるを得ない場合があります。 他にも、OSのハイコントラストモード(強制カラーモード)への対応や、色反転モードに対する対処が望まれます。それについては記事の後半で触れたいと思います。 色の変更 SVGスプライト、インラインSVG、アイコンフォント *3 の利点は、色変更がCSSで手軽にできることでした。 img 要素に戻るのは、色変更の手軽さを放棄することにならないでしょうか? どうということはなく、私たちはこのようにします。 < img src = "assets/icon-star--red.svg" width = "24" height = "24" alt = "" > 色を変えたアイコンを読み込めばよいのです。 あっ、まだブラウザの戻るボタンは押さないでください。本気か?と思うかもしれませんが、本気です。たしかに手軽とはいえないでしょう。使う色が増えるたびにファイルを増やさなければいけません。 でもたとえば、クエリパラメーターで色が指定できたらどうでしょう? < img src = "assets/icon-star.svg?color=ff0000" width = "24" height = "24" alt = "" > 裏側の仕組みはともかく、少なくとも色の指定はやりやすくなりそうですよね。 アイコンをもっと手軽に使いたい! 正直なことを言うと、マークアップエンジニアにとって、アイコンは非常に面倒くさいです。まじめに取り組むと、アイコンを書き出して最適化し、配信の仕組みに乗せ、アイコン用コンポーネントを実装しなければなりません。いずれの工程もノウハウの塊です。 LIFULLは多数のサービスを提供しています。不動産情報サイトのLIFULL HOME'Sだけをとっても、いくつものサービスの集合体です。これら一つひとつのサービスごとに、別々のエンジニアがアイコンのお決まり手順を踏んでいると考えると、とても非効率な気がしませんか。 そこで私たちは、アイコンにまつわるこれら実装上の課題をサービス横断的に解消するために、 LIFULL Icon CDN と呼ばれる仕組みを構築しました。 LIFULL Icon CDNの特色 LIFULL Icon CDNは大きく分けて2つのコンポーネントで構成されています。 APIと呼ばれるコンポーネントは、ドメイン名を持ち、エンドユーザーからのリクエストに応えます。前段にCDN *4 を配置しており、静的キャッシュを持つことで高速化とオリジンサーバーの負荷軽減をしています。 Appと呼ばれるコンポーネントは、社内向けのアイコン管理アプリケーションです。外部からのアクセスはできません。アイコンを一覧し、必要なアイコンをページに埋め込むためのコードを取得することができます。 ここからは、スクリーンショットをまじえつつ、LIFULL Icon CDNでどんなことができるのかを見ていきます。 定義済みアイコンを一覧できる LIFULL Icon CDNが取り扱うアイコンは、デザインガイドラインで定義されたアイコンです。プロダクト横断的に利用されることが想定されたアイコンがずらっと並んでいます。執筆時点で300余りのアイコンが登録されています。 開発者は、この中から使いたいアイコンを選びます。 埋め込みコードをコピーできる 詳細ページからはエンドユーザーがアイコンにアクセスするためのURLと、埋め込むためのコード(HTML形式およびJSX形式)が取得できます。 アイコンをサービスで利用するための開発者の手順はとてもシンプルです。HTMLのコードをコピーして、コードに貼り付ければ終わりです。アイコン利用のための事前準備は不要です。 色を自由に変更できる アイコンの詳細ページ上からアイコンの色を設定できます。色を変更するとクエリパラメーターに反映され、指定した色のアイコンのURLが取得できます。 たとえば日本地図のアイコンはデフォルトでオレンジと黄色の2色から構成されていますが、黒に塗りつぶしたり、 https://icon.lifull.com/lh/japan-map-twotone?fill=black 2色を別々に塗り替えることも可能です。 https://icon.lifull.com/lh/japan-map-twotone?modify[yellow]=mono-300&modify[orange]=accent-blue 無秩序に使われることを避けるため指定できる色には制約があり、デザインガイドラインで定義された色トークンの中から選定する仕様にしています。 高パフォーマンス 配信用アプリケーションの前段にCDN(Amazon CloudFront)を設け、静的キャッシュを担わせることで、ほとんどすべてのリクエストをCDNから応答するようにしています。そのため動作は非常に軽快です。 各アイコンのSVGデータはオブジェクトストレージ(Amazon S3)に保管されていますが、すべての色指定に対する物理的なSVGデータが準備されているわけではありません。アプリケーション層で応答時に色変換の処理を適用しています。CDNには処理後のSVGレスポンスをキャッシュさせているため、軽快な動作を保っています。 アクセシビリティを高める工夫 いくつかの配慮を加えることでアイコンの使いやすさが向上します。 1つは、 強制カラーモードへの配慮 です。WindowsにはOSレベルの強制カラーモード、いわゆるハイコントラストモードが搭載されています。有効にすると、ウェブページを含むOS全体の配色が事前に定義された色で描画されるようになります。 img 要素を通じて埋め込まれている画像は、デフォルトでは強制色が適用されません。一方、アイコンの背景色は透過されているため、たとえば 黒基調の強制色における黒いアイコンは背景に溶け込んで見えなくなってしまう という問題が起こります。 2点目は、 色反転モードへの配慮 です。特に配慮が必要なのはスマート反転モードと呼ばれるもので、macOSやiOSなどのAppleのOSに搭載されています。スマート反転モードでは、OS全体の色をネガポジ反転しますが、画像や動画は反転されない挙動をとります。 この場合も、たとえば白の背景に置かれた黒のアイコンは、スマート反転モードを適用すると、背景色のみが反転するため、 背景に溶け込んで見えなくなってしまう 状況に陥ってしまいます。 2つの問題に対処するため、各SVGファイルには次のようなCSSを宣言しています。 < svg xmlns= "http://www.w3.org/2000/svg" ...> < style > @media (forced- color s: active) { @media (prefers- color -scheme: dark) { [ fill ] :not([ fill = "none" ]) { fill: #fff !important } } @media (prefers- color -scheme: light) { [ fill ] : not([ fill = "none" ]) { fill: #000 !important } } } </ style > ... </ svg > そして、LIFULL Icon CDNを使用する側には次のようなCSSを適用します。 /* 強制カラーモードが有効なとき、img要素経由で読み込まれるSVGに prefers-color-schemeメディアクエリが引き継がれない問題がある。 SVG側からカラースキーム文脈を取得するために、以下のスタイルを アイコン読み込み元に記述する必要がある。 */ @media (forced- color s: active) { @media (prefers- color -scheme: light) { img [ src ^= "https://icon.lifull.com/" ] { color -scheme: light; } } @media (prefers- color -scheme: dark) { img [ src ^= "https://icon.lifull.com/" ] { color -scheme: dark; } } } /* macOSのスマート反転モードが有効のとき、img要素は反転対象から除外される。 背景が透過されているアイコンが色反転から除外されると意図せずコントラストが 不足する可能性があるため、以下のスタイルにより反転に含まれるようにする。 */ @media (inverted- color s: inverted) { img [ src ^= "https://icon.lifull.com/" ] { filter : invert ( 0 ); } } 細かく書くと1記事分の分量になってしまうため省略しますが、ブラウザ側の不具合に対処しつつ、LIFULL Icon CDNを使う img 要素にだけアクセシビリティの向上のためのスタイルを適用しています。 デザイン上の工夫 LIFULL Icon CDNはエンジニアのデザイナーとエンジニア発案のプロジェクトとして始まりましたが、デザイナーも巻き込み、アイコンの統制や管理の仕組みを含む全体的なプロジェクトになりました。その意味ではアイコンのデザインシステムと呼べるものかもしれません。 デザイナーからみたLIFULL Icon CDNについては、別記事として公開されています。ぜひご一読ください! note.com おまけ 実装方法のバリエーション 以下はLIFULL Icon CDNのドキュメントに記載されている内容で、アクセシビリティに配慮したいろいろな実装パターンを解説しています。 もっとも基本的な使い方 アイコン詳細ページで取得できるHTMLをコピーして、そのままプロダクトのコードに貼り付けます。LIFULL Icon CDNチームはこれが ベストプラクティス であると考えており、推奨しています。 < img src = "https://icon.lifull.com/l/chevron-right" width = "24" height = "24" alt = "" /> 使用例1:ボタン内のアイコンとして使用する(装飾としての利用) テキストラベルの横に配置される場合や、単なる装飾としてのアイコン利用の場合、代替テキストは不要です。 alt 属性は空文字列に設定してください。なお、 alt 属性自体は省略しないでください。 次へ < button class = "flex items-center rounded border border-mono-400 bg-white px-4 py-2" > < span > 次へ </ span > < img src = "https://icon.lifull.com/l/chevron-right" alt = "" width = "24" height = "24" class = "ml-2 size-4" > </ button > 使用例2:ボタン内のアイコンとして使用する(機能ラベルとしての利用) テキストラベルが併記されず、アイコンがボタンやリンク内における唯一の要素である場合、代替テキストが必要です。 alt 属性に機能や状態を表す代替テキストを指定してください。 < button class = "flex items-center rounded border border-mono-400 bg-white p-3" > < img src = "https://icon.lifull.com/lh/star-twotone?fill=orange" alt = "お気に入り" width = "56" height = "56" class = "size-6" > </ button > 使用例3: 背景画像として利用する(非推奨) 背景画像には代替テキストが設定できず、強制カラーモードで非表示になってしまうため非推奨です が、装飾としての用途に限っては使用しても構いません。 次へ < button class = "button" > 次へ </ button > < style > .button { min-height : 24px ; padding-right : 32px ; background : url( 'https://icon.lifull.com/l/chevron-right' ) no-repeat right center / 24px ; } </ style > 使用例4:インタラクションに応じて色を切り替える シンプルさを重視し、切り替え前後のアイコンをあらかじめ埋め込んでおくアプローチを推奨します。 以下は、ボタンへのマウスオーバーで色が若干濃くなるアイコンのコード例です。 < button class = "group flex items-center rounded border border-mono-400 bg-white p-3" > < img src = "https://icon.lifull.com/lh/star-filled" alt = "お気に入り" width = "56" height = "56" class = "size-6 group-hover:hidden" > < img src = "https://icon.lifull.com/lh/star-filled?fill=orange-800" alt = "お気に入り" width = "56" height = "56" class = "hidden size-6 group-hover:block" > </ button > 使用例5:任意の色を使用する 原則として、定義済みのカラートークンを使用します 。どうしても任意の色を使用する場合は、 mask-image プロパティを使うテクニックが使用できます。 < span class = "mask-icon" style = " background-color: rebeccapurple --mask-icon: url('https://icon.lifull.com/lh/star-filled'); " ></ span > < style > .mask-icon { display : block ; width : 24px ; height : 24px ; mask: var( --mask-icon ) no-repeat center / 100% ; } </ style > Tailwind CSSを使う場合は以下のようになります。 < span class = "block size-6 bg-[rebeccapurple] [mask:var(--mask-icon)_no-repeat_center/100%]" style = "--mask-icon: url('https://icon.lifull.com/lh/star-filled')" ></ span > 次期バージョンも進行中 LIFULL Icon CDNはすでに社内エンジニアに便利に使われていますが、社内向けアプリケーションの機能拡充が予定されています。 現在のアイコンの追加・更新作業は、AWSコンソールにログインしS3に直接アップロードする運用フローになっています。かかる手間やヒューマンエラーのリスクに対処するために、管理アプリケーション上から直接アップロードや更新等の作業が行えるようになっていく予定です。 クレジット <LIFULL Icon CDN運営チーム> LIFULL HOME'S事業本部 エンジニア Dongjae Park、Shin Mingyu クリエイティブ本部 デザイン部 ぼこ、URITA RIKI スペシャルサンクス 鄭 在淳、曺 承鉉、そめ お知らせ LIFULLは主体性を重んじる社風で、LIFULL Icon CDNのようなエンジニア発案のプロジェクトが多数進行しています。他職種の協力も手厚いです。転職先に弊社をぜひご検討ください! hrmos.co hrmos.co *1 : SVGスプライト……ページ内で使うアイコンセットを事前にsvg要素でまとめて定義しておき、利用箇所でフラグメント参照を使って手軽にアイコンを使えるようにする仕組み。CSSで色変更がしやすい利点がある。 https://css-tricks.com/svg-sprites-use-better-icon-fonts/ *2 : インラインSVG……svg要素を使いSVG画像をHTMLにインラインで記述すること。CSSで色変更がしやすい。 *3 : アイコンフォント……アイコンデータをフォントファイルの字形データとして格納し、文字としてアイコンを表示するテクニック。CSSで色変更がしやすい。 *4 : CDN……Content Delivery Network。ウェブコンテンツをユーザーに高速で配信するために、世界中の複数のサーバーにデータを分散させる仕組み。
アバター
こんにちは、エンジニアの中島です。 この記事は2024年8月のLIFULL社でのアクセシビリティ改善およびやっていき活動の報告です。 この活動報告は月次で出すかもしれないし出さないかもしれないくらいの温度感で運用されています。 目次 目次 サービス改善 賃貸物件登録画面の各コントロール・グループへの名前設定 取扱物件一覧ページの詳細条件設定ダイアログ内のコントロールの名前設定 育成・啓発の取り組み WCAG輪読会 アクセシビリティ1on1 お知らせ サービス改善 本期間中の改善取り組みのターゲットはLIFULL HOME'S 賃貸・流通マネージャーサイトです。 こちらはLIFULL HOME'Sをご利用いただいている会員様(不動産会社様)が、物件を掲載する際にお使いになる管理画面になります。 諸事情で発表できないものもありますが公開可能な取り組みを紹介させていただきます。 賃貸物件登録画面の各コントロール・グループへの名前設定 賃貸物件登録画面には物件情報を入力するためのフォームがありますが、その中には名前の設定されていないコントロールや、名前は設定されているものの文脈が不明瞭なラジオボタン・チェックボックスを含んだグループなどがありました。 これらのコントロール・及びグループに適切な名前を設定し、支援技術ユーザーにとってもコントロールの目的が理解しやすいように修正を行いました。 取扱物件一覧ページの詳細条件設定ダイアログ内のコントロールの名前設定 不動産会社様がLIFULL HOME'Sに掲載しようと登録した物件の一覧ページには、確認したい物件を絞り込むための検索フォームが設定されていますが、より詳細な条件設定を行えるダイアログが存在しています。 このダイアログ内の各コントロールも名前が未設定のものが多くありましたので適切な名前を指定する修正を行いました。 育成・啓発の取り組み WCAG輪読会 アクセシビリティやっていき勢向けにWCAGの輪読会を隔週で行っています。 本期間中は8月22日に行われました。 範囲は達成基準1.4.4〜1.4.5です。 アクセシビリティ1on1 本期間中は先月と同じくフロントエンドエンジニア6人、デザイナー1人に対して行いました。 内容はWCAGの解説、APG(aria authoring practices)の解説、coga-usableの解説、実際のアプリケーション開発時でのアクセシビリティ配慮に関する相談などが主なものとなります。 お知らせ LIFULLではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
こんにちは。プロダクトエンジニアリング部でAndroidのネイティブアプリエンジニアをしている久野です。 今回はイマーシブモデルルームというApple Vision Pro向けのアプリを日本発売日に合わせてリリースした話をします。 なぜAndroidのネイティブアプリエンジニアの私がApple Vision Proの開発を担当したかというと、元々学生の頃からインターンや趣味でVRコンテンツ作成に携わっていた中、Apple Vision Proの発売を聞き「これを使ったアプリの開発をしてみたい!」と強く思い、上長や会社に挑戦してみたいと相談したところ、快く承諾して頂けたのがきっかけです。 イマーシブモデルルームについて 苦労したこと 技術調査および実機開発 リリース時期 デザイン、モデルの確認 工夫した機能 空間ビデオ スプラッシュの作成 まとめ イマーシブモデルルームについて イマーシブモデルルームは没入感のある高精細かつリアルなモデルルームや臨場感のある3D映像(空間ビデオ)で物件の魅力を体験することができるApple Vision Pro向けのアプリです。 2024年6月28日(金)にApple Vision Pro向けアプリにおいて国内不動産ポータルで初めて *1 の提供です。 App Storeにおいて公開していますので、Apple Vision Proをお持ちの方は体験してみてください。 prtimes.jp イマーシブモデルルーム LIFULL Co., Ltd ナビゲーション 無料 apps.apple.com 苦労したこと 技術調査および実機開発 今回イマーシブモデルルームはUnityを利用して開発を行いました。 UnityのApple Vision Pro用SDKであるPolySpatialは2023年の11月中旬に公開されましたが、情報が非常に少なくアプリのビルド等を進めるのも困難でした。また、SDK自体の更新も頻繁に行われていたのでそれに合わせてUnityを利用してApple Vision Proのアプリで何が実現できて何が出来ないかということの検証を進めていました。最新の情報をキャッチアップすることが重要だったため、Unityのフォーラムや個人の方が公開していらっしゃる記事を常にチェックしながら開発を進めました。 Apple Vision Proの実機に関連することでも苦労しました。Apple Vision Proの基本的な操作は目線と手を利用して行います。そのため、アプリケーションの開発においても実機を利用して、目線と手を使った操作の確認が必要でした。ただ、SDKが公開された11月段階ではアメリカにおいても実機は発売されていないので、Unity Editor及び Apple Vision ProのSimulatorを利用して確認を行ないました。 しかし、Unity EditorやApple Vision ProのSimulatorでは両手の入力をするようなコンテンツ(イマーシブモデルルームの機能ですと3Dモデルのマンションを回転、サイズの変更をする)といったジェスチャーの確認を行うことが困難です。そのため、弊社で3月にアメリカで発売された実機を入手して技適申請を行うまでの間はデベロッパラボを月に一度程の頻度で応募をして利用させて頂きました。それによって開発中のアプリを実機で挙動確認させて頂きました。 developer.apple.com リリース時期 リリース日を日本での発売日に合わせるために行っていた取り組みを説明します。 Apple Vision Proが日本で発売される時期がわからない状況で、発売日にリリースすることを目標にして進めていました。 そのことから、アプリのリリース時期を3段階に分けてそれぞれの段階においてどのようなアプリをリリースするかということをチームで定めていました。 チームの計画としては下記3パターンを考えていました。 7月リリースパターン 9月リリースパターン 12月リリースパターン 7月のリリースパターンでは時間も少なかったため必要最小限のプロダクトを開発し、そこからブラッシュアップしていくという方法を取りました。早い段階から動く形にしておくということを意識しながら進めることで想定よりも早い段階でのリリースに対応することが出来ました。 デザイン、モデルの確認 まずUnity Editorを用いてビジュアルのチェックを行なっていました。しかし、Apple Vision Proの特性を最大限に活かすためにはこれだけでは不十分です。実際にアプリをApple Vision Proにビルドし、モデルがどのように見えるか、ユーザー体験がどのように実現されるかを確認することが不可欠でした。 この確認作業ではデザイナーの方と密に連携し、デザイン及び体験の確認を何度も繰り返しました。特にリリース直前にはデザイナーの方とUnity Editorの画面を一緒に見ながら確認及び細かい修正を行いました。 またモデル更新の反映など軽微な確認にはPolySpatialの Play to Device 機能を活用しました。Play To Deviceを利用することでBuildをせずにUnity Editorから再生したものをApple Vision ProのSimulatorで確認できます。これにより開発プロセスの効率が大幅に向上しました。 工夫した機能 空間ビデオ 今回アプリの中で空間ビデオを表示する機能も提供しています。 空間ビデオはUnityのPolySpatialの VisionOSVideoComponent でも対応しています。上記のドキュメントに従って設定を行なっていたのですが、動画が再生されないという問題にぶつかりました。そのため以下のような対応を行いました。 初めに、Volume cameraの設定です。 以下二つの設定に対して同じものを指定する必要がありました。 Project Settings > PolySpatial > Default Volume Camera Window Config Scene内の Volume Camera > Volume Window Configuration この二つに設定しているVolume cameraを同じものにすることで、Apple Vision Proの実機で空間ビデオの再生をすることが出来ました。 また、動画はコーデックが空間ビデオに対応されているものである必要がありました。iPhone等で撮影した後でPCに送る際にはオートコーデックが発生して別のコーデックに変更がかからないように注意する必要がありました。 スプラッシュの作成 今回のアプリの中でユーザーがアプリを開始するたびにスプラッシュを表示しています。 Unityにはデフォルトで Splashを表示する機能 が存在していますが、今回のPolySpatialでは利用することが出来ないということをフォーラムで見かけ、実際に調査を行ったのですが利用することが出来ませんでした。 そのため、デフォルトの機能を利用せずにスプラッシュの代わりにアプリ起動時にアプリのキービジュアルを表示する機能を作成しました。 実装面では、Volume cameraの WindowEvent を活用しました。 こちらを利用して、アプリが開かれた際にキービジュアルを数秒間表示してから、アプリのコンテンツを体験出来るように実装しました。 まとめ Apple Vision Proの日本発売日に合わせて、「イマーシブモデルルーム」を無事リリースしました。技術調査や実機開発、デザイン確認など、数々の課題はありましたが、3Dモデルデータを利用した住まい探しの新たな可能性を広げるプロダクトを生み出す第一歩になったのではと思います。これからも、住生活を革進することの出来るプロダクトを開発していこうと思います。 LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co hrmos.co *1 : 2024年6月28日時点、自社調べ
アバター
KEELチーム の相原です。 前回のエントリは「LLMを利用したPlatform Engineering」でした。 www.lifull.blog 今回は、小さい経路最適化ミドルウェアを実装してAZ間通信を削減した話を書きたいと思います。 背景 我々KEELチームはKubernetsベースの内製PaaSであるKEELを開発しており、LIFULLのほとんどのサービスがこのKEEL上で動いています。 www.lifull.blog そして、KEELは巨大なマルチテナントのKubernetesクラスタとしてAWSの複数のAvailability Zone(以下AZ)に展開されていて、多くのmicroservicesが互いに通信しあっています。 そのためAZ間通信はプラットフォームとして重要な関心事の一つです。 レイテンシやAWSのAZ間通信に対する課金を最小限に抑えるため、なるべくAZ間通信を減らしたいという要求があります。 我々のプラットフォームでは現在LLMのサポートを強めており、それに伴い扱うペイロードのサイズも増加傾向にあることからも対応の優先度が上がってきています。 www.lifull.blog 実際にKubernetesの Topology Aware Routing をプラットフォーム側で配布してデフォルトで有効にしたり、Istioの Locality Load Balancing をクラスタ全体で有効にして、クラスタ内の通信の経路最適化に努めてきました。 ※Istioが有効になっているPodではTopology Aware Routingは機能しないため、全てのPodにIstioを導入できていない場合はクラスタ全体として両方を有効にする必要があります 一方で、KEELはステートレスなクラスタとして永続性が必要とされるデータストアは基本的にクラスタ外に依存しており、未だ最適化できていない経路も存在しています。 そこでその残されたAZ間通信も削減すべく、小さい経路最適化ミドルウェアを実装する判断に至りました。 背景 実装 シンプルなConnection Pool 余談: Connection Storm Topology Aware Routing 注意点 最後に 実装 さて、KubernetesやIstioが実現するクラスタ内通信の経路最適化はKubernetesのService Discoveryに依存しているため、クラスタ外への通信で真似することはできません。 つまりクライアントはサーバがどのトポロジに配置されているかを知る共通の手段を持っていないということです。 サーバ側がDNSのSRVレコードなどに対応していればそれをそのまま利用できますが、そうでもない場合全てのサーバに新規にService Discoveryを入れるということはあまりに大変です。 そこで我々はTCPサーバとして振る舞うConnection Poolのミドルウェアとして経路最適化を実現することに決めました。 LIFULLにはRubyのUnicornなど1リクエスト1プロセスなプロセスモデルのアプリケーションサーバがいくつかあり、元々プロセスをまたいだConnection Poolを実現したいという要求があります。 しかし、プラットフォームとしてProxySQLを提供するなど特定のユースケースでは対応できていたものの、 あらゆるAZ間通信 には対応できていませんでした。 Connection Poolでの経路最適化のアイデアはシンプルで、維持している接続をトポロジごとに管理して、なるべくクライアントと同じトポロジの接続を取り出して使うだけです。 つまりConnection Poolが維持している接続を疑似的にService Discoveryとして利用するということです。 これが実現できれば色々と都合が良さそうなのでやっていきましょう。 シンプルなConnection Pool まずは普通のTCPプロキシにシンプルなConnection Poolを実装していきます。接続を取り回すだけのソフトウェアになるのでGoが無難でしょう。 MinIdleConnections , MaxIdleConnections でアイドルな接続をコントロールできるようにして MaxIdleTime , MaxLifetime で生存期間を設定できるよくあるやつです。 設定は大体こういうインタフェースでしょうか。 Connection は MaxIdleTime の判定で利用する returnedAt を持てるようにしただけの net.Conn の薄いラッパーです。 type ConnectionPoolStrategy int const ( FIFO ConnectionPoolStrategy = iota LIFO ) type ConnectionPoolOption struct { MaxConnections uint MaxIdleConnections uint MinIdleConnections uint MaxIdleTime time.Duration MaxLifetime time.Duration Dialer func (context.Context) (net.Conn, error ) ConnectionPoolStrategy ConnectionPoolStrategy } type ConnectionPool struct { option *ConnectionPoolOption connectionMutex sync.Mutex connections []*Connection idleConnections []*Connection } コンストラクタは省略するとして、接続を取り出す部分はこんな感じになります。 func (p *ConnectionPool) Get(ctx context.Context) (*Connection, error ) { p.connectionMutex.Lock() defer p.connectionMutex.Unlock() defer func () { go p.prepareIdleConnection(ctx) }() for { connection, err := p.getIdleConnection(ctx) if err != nil { return nil , xerrors.Errorf( "failed to get idle connection: %w" , err) } if connection == nil { break } now := nowFunc() if (p.option.MaxIdleTime > 0 && now.Sub(connection.returnedAt) > p.option.MaxIdleTime) || (p.option.MaxLifetime > 0 && now.Sub(connection.createdAt) > p.option.MaxLifetime) || !connection.isHealthy() { _ = connection.Close() p.removeConnection(ctx, connection) continue } return connection, nil } connection, err := p.newConnection(ctx) if err != nil { return nil , xerrors.Errorf( "failed to create connection: %w" , err) } return connection, nil } ロックを取りながらアイドルな接続を取り出し、生存期間を満たしていれば死活監視をしてから接続を返します。 アイドルな接続がなければ新規に接続を作成して返します。 prepareIdleConnection は MinIdleConnections を満たすようにアイドルな接続を用意していて、 isHealthy はソケットに read(2) して EAGAIN or EWOULDBLOCK であることの確認です。 func (c *Connection) isHealthy() bool { // A zero time value disables the deadline. _ = c.conn.SetReadDeadline(time.Time{}) syscallConnection, ok := c.conn.(syscall.Conn) if !ok { return false } rawConnection, err := syscallConnection.SyscallConn() if err != nil { return false } healthy := false if err := rawConnection.Read( func (fd uintptr ) bool { b := make ([] byte , 1 ) _, err := syscall.Read( int (fd), b) if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EWOULDBLOCK) { healthy = true } return true }); err != nil { return false } return healthy } そして、使い終わった接続を維持するためのPutを実装します。 MaxIdleTime で判断するための returnedAt の更新も忘れないようにしましょう。 func (p *ConnectionPool) Put(ctx context.Context, connection *Connection) error { p.connectionMutex.Lock() defer p.connectionMutex.Unlock() if p.IdleConnections() < int (p.option.MaxIdleConnections) { connection.returnedAt = nowFunc() p.idleConnections = append (p.idleConnections, connection) } else { _ = connection.Close() p.removeConnection(ctx, connection) } return nil } ちなみに、ここまでの内容のTCPプロキシでの利用イメージはこんな感じになります。 func main() { ... semaphore := make ( chan struct {}, maxConnections) shutdown := make ( chan struct {}, 1 ) wg := sync.WaitGroup{} go func () { ctx := context.Background() for { local, err := listener.AcceptTCP() if err != nil { select { case <-shutdown: return default : continue } } semaphore <- struct {}{} wg.Add( 1 ) go func () { defer func () { <-semaphore wg.Done() }() defer local.Close() remote, err := connectionPool.Get(ctx) if err != nil { return } defer connectionPool.Put(ctx, remote) defer remote.Cancel() c := make ( chan struct {}, 2 ) f := func (c chan struct {}, dst io.Writer , src io.Reader ) { _, _ = io.Copy(dst, src) c <- struct {}{} } go f(c, remote, local) go f(c, local, remote) select { case <-c: case <-shutdown: local.CloseWrite() } }() } }() // Graceful shutdown quit := make ( chan os.Signal, 1 ) signal.Notify(quit, syscall.SIGTERM) <-quit time.Sleep(time.Duration(lameduck) * time.Second) close (shutdown) listener.Close() wg.Wait() } 余談: Connection Storm この実装では、維持している接続が MaxLifetime に達した時に強制的にその接続を破棄します。 古い接続が使われ続けることを防ぐためのものですが、これには比較的大きなトレードオフがあります。 それは Connection Storm と呼ばれる現象で、維持されていた接続が一斉に MaxLifetime によって切断されることで、新規の接続が大量に確立して接続先に大きな負荷がかかるというものです。 それを防ぐためには リトライなどでよく知られるJitter を入れるとよいです。 Jitterはランダム性を注入する概念で、生存期間の判定箇所にJitterを入れて一斉に切断されることを防ぎます。 リトライにおけるJitterは、接続先の障害発生時などに複数のリトライが同時に行われるとカスケード障害を起こしてしまうため、リトライにランダム性を持たせるために利用されています。 type ConnectionPoolOption struct { MinIdleConnections uint MaxIdleTime time.Duration MaxLifetime time.Duration + Jitter func (time.Duration) time.Duration Dialer func (context.Context) (net.Conn, error ) ConnectionPoolStrategy ConnectionPoolStrategy } now := nowFunc() - if (p.option.MaxIdleTime > 0 && now.Sub(connection.returnedAt) > p.option.MaxIdleTime) || - (p.option.MaxLifetime > 0 && now.Sub(connection.createdAt) > p.option.MaxLifetime) || + if (p.option.MaxIdleTime > 0 && now.Sub(connection.returnedAt) > p.option.Jitter(p.option.MaxIdleTime)) || + (p.option.MaxLifetime > 0 && now.Sub(connection.createdAt) > p.option.Jitter(p.option.MaxLifetime)) || !connection.isHealthy() { _ = connection.Close() p.removeConnection(ctx, connection) 実際のJitterの実装はこんな感じになると思います。 jitterPercentage でどの程度ランダム性に開きを持たせるかを調整できるようにするイメージです。 テストの時には引数の duration をそのまま返せばいいでしょう。 func (duration time.Duration) time.Duration { jitter := time.Duration( float64 (duration) * jitterPercentage * (rand.Float64()* 2 - 1 )) return duration + jitter } 閑話休題。 Topology Aware Routing ここまででシンプルなConnection Poolの実装が完成したので、ここからようやく本題の経路最適化の実装に入ろうと思います。 アイデアは先に書いた通りシンプルで、維持している接続をトポロジごとに管理して、なるべくクライアントと同じトポロジの接続を取り出して使うだけのものを目指します。 トポロジはIPをベースに判断するとして、今回はサイドカーとしてクライアントのプログラムと同じサーバで動かすことを想定しているため、クライアントのトポロジは自身のIPから判断することができそうです。 トポロジの一覧を --topologies=a=192.168.0.0/24,b=192.168.1.0/24,c=192.168.2.0/24 のように受け取り、プログラム内では以下のように取り扱うことにします。 type Topology struct { Name string CIDR net.IPNet } var topologyList []Topology var topologyName string if topologyAwareRouting { for _, t := range strings.Split(topologies, "," ) { if t == "" { continue } parts := strings.Split(t, "=" ) if len (parts) != 2 { log.Fatalf( "invalid topologies: %s" , topologies) } _, cidr, err := net.ParseCIDR(parts[ 1 ]) if err != nil { log.Fatalf( "failed to parse CIDR %s: %+v" , parts[ 1 ], err) } topology := Topology{ Name: parts[ 0 ], CIDR: *cidr, } topologyList = append (topologyList, topology) if topology.CIDR.Contains(net.ParseIP(ownIP)) { topologyName = topology.Name } } } CIDRを持ったトポロジの一覧が topologyList として管理されていて、自身のIPから判断したクライアントのトポロジを事前に topologyName として定義しておきます。 ( topologyName を事前に定義するのは計算コストを嫌っているだけで、当然クライアントの接続から都度判断することも可能なのでサイドカーではなく独立してデプロイすることも可能です) Kubernetesでは自身のIPを Downward API から取得可能で、AWSでも インスタンスメタデータ から取得できるのでこれを使います。 それでは先ほどのConnection Poolの実装にこれらを与えてTopology Aware Routingに対応していきましょう。 まずは topologyList を受け取るれるようにしつつ、維持している接続をHashMapでトポロジごとに管理できるように構造体を変更します。 type ConnectionPoolOption struct { MinIdleConnections uint MaxIdleTime time.Duration MaxLifetime time.Duration Jitter func (time.Duration) time.Duration + TopologyList []Topology Dialer func (context.Context) (net.Conn, error ) ConnectionPoolStrategy ConnectionPoolStrategy } type ConnectionPool struct { connectionMutex sync.Mutex connections []*Connection - idleConnections []*Connection + idleConnections map [ string ][]*Connection } 次に、接続を取り出す Get でクライアントのトポロジ topologyName を受け取って、同じトポロジの接続を取り出す部分です。 - func (p *ConnectionPool) Get(ctx context.Context) (*Connection, error ) { + func (p *ConnectionPool) Get(ctx context.Context, topologyName string ) (*Connection, error ) { p.connectionMutex.Lock() defer p.connectionMutex.Unlock() defer func () { go p.prepareIdleConnection(ctx) }() for { - connection, err := p.getIdleConnection(ctx) + connection, err := p.getIdleConnection(ctx, topologyName) if err != nil { return nil , xerrors.Errorf( "failed to get idle connection: %w" , err) } if connection == nil { + if n := p.pickRandomIdleTopologyName(); n != nil { + topologyName = *n + continue + } break } now := nowFunc() if (p.option.MaxIdleTime > 0 && now.Sub(connection.returnedAt) > p.option.Jitter(p.option.MaxIdleTime)) || (p.option.MaxLifetime > 0 && now.Sub(connection.createdAt) > p.option.Jitter(p.option.MaxLifetime)) || !connection.isHealthy() { _ = connection.Close() - p.removeConnection(ctx, connection) + p.removeConnection(ctx, topologyName, connection) continue } return connection, nil } connection, err := p.newConnection(ctx) if err != nil { return nil , xerrors.Errorf( "failed to create connection: %w" , err) } return connection, nil } pickRandomIdleTopologyName はフォールバックの処理で、同じトポロジの接続がなかった際に適当なトポロジの接続を代わりに使います。 フォールバックの戦略を外から与えられるようにしてもいいですね。 他はそれぞれ先のHashMapを扱って接続を取り出したり削除したりしてるだけです。 細かい話ですが、 idleConnections がHashMapになることで総接続数を得るための計算コストが O(1) でなくなってしまうため、別でAtomicなカウンターを用意しておいた方がよいです。 最後に Put の実装です。 func (p *ConnectionPool) Put(ctx context.Context, connection *Connection) error p.connectionMutex.Lock() defer p.connectionMutex.Unlock() + topologyName := p.topologyName(connection.RemoteAddr()) + if p.IdleConnections() < int (p.option.MaxIdleConnections) { connection.returnedAt = nowFunc() - p.idleConnections = append (p.idleConnections, connection) + p.idleConnections[topologyName] = append (p.idleConnections[topologyName], connection) } else { _ = connection.Close() - p.removeConnection(ctx, connection) + p.removeConnection(ctx, topologyName, connection) } return nil } net.Conn の RemoteAddr() で接続先のIPを取得し、以下の関数で topologyList として受け取ったCIDRから適切なトポロジを取得します。 func (p *ConnectionPool) topologyName(addr net.Addr) string { for _, t := range p.option.TopologyList { switch addr := addr.( type ) { case *net.TCPAddr: if t.CIDR.Contains(addr.IP) { return t.Name } case *net.UDPAddr: if t.CIDR.Contains(addr.IP) { return t.Name } } } return "" } これで経路最適化を行うTopology Aware Routingも完成です。 簡易的な実装による楽観的な経路最適化ですが、接続先のService Discoveryに依存しない あらゆるAZ間通信 に対応するものができました。 例えばAWSのElastic Load BalancerはAZごとにIPを持っており、それがDNSラウンドロビンで返却されます。 このミドルウェアをクライアントに導入することで、AWSのロードバランサに対する接続であってもAZ間通信を削減することに成功しました。 なお、AWS Application Load Balancerはデフォルトで クロスゾーン負荷分散 が有効になっていて、これを導入してもロードバランサとUpstreamの間ではAZ間通信が発生する可能性があります。 Upstreamが十分にAZに分散されていればクロスゾーン負荷分散を無効にする判断ができるかもしれません。 注意点 このミドルウェアに限らずこういった経路最適化で考慮しなければならないことがあります。 それはトポロジ間の分散です。 クライアントとサーバでトポロジ間の分散が不十分な場合、例えばクライアントが一つのトポロジに集中してデプロイされてしまっている場合に、そのトポロジのサーバに負荷が集中してしまいます。 これを防ぐためには慎重にトポロジ間で分散させる必要があり、それにはKubernetesの Pod Topology Spread Constraints を利用することができます。 これはデプロイ時にトポロジ間のばらつきに対する制約をかけられる機能で、LIFULLではKubernetes Manifestを生成するためのコードジェネレータでKubernetesのTopology Aware Routingの設定とともにデフォルトの設定として配布しています。 www.lifull.blog ただしこれはあくまでデプロイ時の制約で、Podがスケールインした後のリバランスまでは面倒を見てくれません。 そこで、あわせて kubernetes-sigs/descheduler を導入して RemovePodsViolatingTopologySpreadConstraint を有効にすることをお勧めします。 これにより Pod Topology Spread Constraints に違反しているPodを再スケジュールしてくれるため、その後の再デプロイで再度制約をかけることができます。 またこの制約を機能させるためにはノードのトポロジ間のバランスも重要で、 cluster-autoscaler などで適切に設定する必要があることにも注意してください。 (AWSのAuto Scaling Groupはスポットインスタンスを使っていなければ いい感じにやってくれる ので特に気にする必要はない気がします) 最後に Connection Poolのミドルウェアに簡易的なTopology Aware Routingを実装することで あらゆるAZ間通信 を削減することに成功しました。 これによりレイテンシの改善とAZ間通信に対する課金を抑えることができます。 AZ間のレイテンシはLIFULLの環境では最悪ケースで5msにも及ぶことがあり、それなりのインパクトを期待しています。 マルチプロセスなアプリケーションサーバを運用しているとそれっぽいConnection Poolが欲しくなることはあって、手前味噌ですがこのアプローチはちょうどいい塩梅なのではないでしょうか。 最近は調子に乗ってこれにRedisのプロトコルパーサを載せて、RedisへのクエリのRead/Writeの分離機能を実装するなどもしてしまっています。 KEELではRead Heavyなキャッシュなどの用途に向けて運用が簡単なRedis Sentinelクラスタを提供していまして、クライアント実装に手を入れづらい際にこれを入れるだけでいい感じに動くというちょうどいいTCPプロキシとしての立ち位置を確立しつつあります。 KEELチームでは、いくつかの機能をProxy-Wasmで実装してIstio経由で配布していたりもしますが、こういう実装はProxy-Wasmでは実現しづらく第二のService Meshとして今後もこのちょうどいいTCPプロキシを改善していく予定です。 (オーバーエンジニアリングとの境目を気にしながら、)プラットフォームとして必要なものがあれば何でも実装するKEELチームにもし興味を持っていただけた場合は、是非カジュアル面談をさせてください! hrmos.co hrmos.co 次のエントリでは、KubernetesのPodの終了際のログが欠損してしまう問題をeBPFで unlink(2) を bpf_override_return して対処した問題について書こうと思うので購読してお待ちください :D
アバター
KEELチーム の相原です。 これまでKEELチームではKubernetesベースの内製PaaSであるKEELを開発・運用しながら、合間で社内で汎用AI(仮)と呼ぶAutoGPT実装 keelai を開発してきました。 www.lifull.blog 我々はあくまでプラットフォーマーであり、目指すところはLLMを利用したPlatform Engineeringです。 いくつかの取り組みが実を結んできたのでここで紹介したいと思います。 APIの提供 GitHub ActionsからのLLM利用 Slack AutomationsによるLLMノーコードツールの提供 Chrome Extensionで実現するシームレスなLLM体験 コマンドラインツールでのLLM活用 Jupyter Notebookでの高度なLLM活用 まとめ APIの提供 何はともあれまずはAPIの提供でしょう。 ここでは我々が開発するAutoGPT実装である keelai をAPIとして提供しています。 移植性のためにOpenAI互換のインタフェースになっていて、当然Server Sent Eventsにも対応しています。 KEELチームで開発しているSAMLベースの認証基盤の下で社内に公開しており、Kubernetesクラスタ内通信も含めてIstioのAuthorizationPolicyで認可しています。 FastAPIを使っていてちょうどよくWebインタフェースが用意されていることもあり、開発者はAPI Keyなどを発行することなくすぐにAPIを利用することができます。 IstioのRequestAuthenticationで任意のJSON Web Tokenを検証することもでき、あらゆる場所から安全に呼び出すことが可能です。 また、セグメントごとのトークンベースのRate Limitを実装してあり、SAML認証済みのユーザやJSON Web Tokenの任意のペイロード・呼び出し元ワークロードに応じてヘッダを付与し、それをもとにSliding Windowで流量制限しています。 呼び出し元ワークロードの判定をヘッダから行うためには、まずIstioのPeerAuthenticationでmTLSを強制して呼び出し元のEnvoyを保証し、Envoyが付与する x-envoy-peer-metadata をProxy-Wasmでパースしてワークロードを取得するなどちょっとハックしているので、この辺はまた別のエントリで書くとしましょう。 当然サイト信頼性に責任を負う立場として、各種メトリクスやOpenTelemetryによるトレースの収集、適切なリトライやエラーハンドリングによってProduction Readyな品質となっており、コンシューマ向けのプロダクトからもこのAPIが呼ばれています。 各種ベクトルデータベースの提供 や Semantic Search用のコンポーネントの提供 もしていますが、嬉しい誤算で汎用AI(仮)ということもあり基本的に現在はこのAPIを介してあらゆる機能を実現しています。 (OpenAIへの課金を懸念して llama.cpp ベースのオープンソースLLMのAPIサーバも提供していますが、これも嬉しい誤算でLLMの価格競争による低コスト化が進んでいるので今のところ出番はなさそうです) 今後はKubernetesクラスタであるKEEL自身のLLMを使ったAIOps(?)を強化していく予定で、Alertmanagerから発火する運用自動化用のKnative Serviceが存在しているため、そこから同様に keelai のAPIを呼ぶことで更なる自動化を進めていきます。 keelai はSelf-hostingされた汎用AI(仮)として社内の監視基盤とも連携できるのでここは色々と遊べそうです。 GitHub ActionsからのLLM利用 APIには前述した通りIstioのレイヤでJSON Web Tokenの検証と認可が実装されているため、同じく我々が提供する GitHub Actions Self-hosted runners を利用することでGitHub Actionsから以下のように呼び出すことができます。 www.lifull.blog $ ID_TOKEN = $( curl -sSL -H " Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN} " " ${ACTIONS_ID_TOKEN_REQUEST_URL} &audience= ${GITHUB_ACTION_PATH} " | jq -r .value ) $ curl -sSL -X POST -H " Authorization: Bearer $ID_TOKEN " -H " Content-Type: application/json " https://keelai-api.keelai.svc.cluster.local:8080/v1/chat/completions -d " $( echo $@ | jq -sc --arg model " $MODEL " ' . as $messages | {"model": $model, "messages": $messages} ' ) " これには GitHubのOpenID Connect を利用していて、サーバ側はこのようにGitHubから送られてきたJSON Web Tokenのclaimsをもとに認可することが可能です。 apiVersion : security.istio.io/v1beta1 kind : AuthorizationPolicy metadata : name : keelai-api spec : action : ALLOW rules : - when : - key : request.auth.claims[iss] values : - https://token.actions.githubusercontent.com - key : request.auth.claims[repository_owner] values : - lifull selector : matchLabels : app.kubernetes.io/name : keelai-api これにより各リポジトリにAPI Keyを配布することなく、気軽にLLMを利用した自動化を始めることができています。 さて、LLMによるソフトウェアエンジニアリングの生産性向上でやはり最初に思いつくものはコードレビューでしょう。 既にOpenAIのAPIを叩いてコードレビューするようなものはありふれていますが、目指すべき先は社内のコーディングガイドラインや過去の障害実績をもとにしたコードレビューだと思います。 そのためには社内情報にアクセスできる必要があり、そこで汎用AI(仮)を目指す keelai が役に立ちます。 keelai はAutoGPT実装として、目的達成のため自律的に複数の関数を使い分けながら回答を作成できるため、(今はまだ精度に改善点はあるものの)必要に応じて過去の障害情報を参照しながらコードレビューすることが可能です。 我々はプラットフォーム戦略としてKubernetes Manifestからドキュメントまでを一手に生成する コードジェネレータも開発しており 、これによって多くのGitHub Actionsを配布しています。 その中でコードレビューの機能も実験的に提供しており、プロンプトに以下のような指示を加えて、 git diff の出力をもとにコードレビューの結果を reviewdog の形式で出力させています。 The output MUST be `%f:%l: %m` format. <snip> %f: File name %l: Line number %m: Message </snip> Line number MUST be the line number of the file that calculated from the diff. こうしておくことで reviewdog を使って雑にPullRequestへのインラインコメントが可能です。 echo " $body " | reviewdog -efm =' %f:%l: %m ' -reporter = github-pr-review -filter-mode = nofilter このメソッドは手軽な割に意外と汎用的で、セキュリティに特化したコードレビュー機能が社内で配布され始めたりと好感触です。 他にも、利用しているOSSのバグチケットをGitHub Actionsのスケジュール実行で監視して翻訳・要約してIssueとして起票するチームがあったりとGitHub ActionsからのLLM利用が進んでいます。 Slack AutomationsによるLLMノーコードツールの提供 次はSlack Automationsを利用した疑似的なLLMノーコードツールを提供している話です。 Slack AutomationsとはSlackの有料プランで利用できるワークフロービルダーです。 リアクション時やスケジュール実行でチャンネルへの発言からスプレッドシートへの追記まで割となんでもできます。 api.slack.com Deno Slack SDK を使ってステップと呼ばれる任意の処理も自作可能で、組み込みのステップは多くないものの自分で作ってしまえば問題ありません。 LIFULLではこのSlack AutomationsからLLMを呼び出せるようにして、LLMノーコードツールとして職種問わず広く社員が業務を自動化できる環境を整えています。 とは言うものの、Slack Automationsは2024年8月現在でステップのSelf-hostingに対応していません。 つまりプライベートネットワーク上で公開されているAPIにアクセスすることができません。 幸い我々のAutoGPT実装である keelai はSlack Botとしても実装されており、Slack AutomationsからメンションすることでLLMを発火させることができました。 www.lifull.blog 実際の利用例は以下のようなイメージです。 基本的にはリアクションなどを起点に組み込みの Reply to a message in thread を使いながらメンションしてLLMを発火させているだけです。 リアクション時にペイロードとして渡ってくるメッセージのURLからメッセージ本文を取得するステップを実装していますが、これはFew-shot promptingをしやすくするためのものであり必須ではありません。 そして設定したリアクションをすると以下のようにLLMが呼び出されます。 これもなかなか好評で、プログラミングなしでLLMによる業務の自動化が可能であることから職種問わず広く利用されています。 ちなみに、回答結果へのリアクションは keelai 自身が行っており、Slackの chat.update を通してServer Sent Eventsを表現しているためそれの完了通知という意味もありつつ、ここから更にリアクション起点のSlack Automationsを発火させるためという意味もあります。 Slack Automationsには簡易的なフォームを作成する機能もあり、 総務部門など各部署が自分達で相談の一次受けのフォームを用意するといったことが行われています 。 汎用AI(仮)である keelai は当然社内でのこれまでの相談内容や社内ドキュメントをSemantic Searchできるようになっているため、そこそこの精度で回答することが可能です。 そして、 Submit すると事前に定義されたプロンプトにフォームの入力を埋め込んでLLMが呼び出されます。 Slack Automationsにはスプレッドシート連携の機能もあるため、完了通知のリアクションを起点に質問と回答結果のペアをスプレッドシートに追記しておいて、それをもとにプロンプトの改善に繋げるといった運用になっています。 他にも経理部門が為替の日次確認の自動化を実現していたり、RSSによって通知されるニュースをリアクションで要約するワークフローが普及していたりと、こういったことをエンジニアでなくてもすぐに実現できる点が汎用AI(仮) + Slack Automationsの強みです。 元々Slack Botとして keelai を開発し始めた狙いでもありますが、コミュニケーションツール内でLLMを利用することにより社員が日頃からLLMの活用を目にしやすく、利用者の拡大にも一役買っていると思います。 我々はプラットフォーマーとして多くのステップを Deno Slack SDK で開発しており、ループ機構を実現するステップやSlack Automations側でOAuthのサポートがあるためそれを使ったGitHubやGoogle Apps Scriptへアクセスするステップなどが既に実装されています。 (Self-hostingのサポートを待ちつつ)今後もLLMノーコードツールとして機能開発を続けていきます。 Chrome Extensionで実現するシームレスなLLM体験 LIFULLのプラットフォームを広く開発する我々KEELチームは、プラットフォーム戦略の一環としてChrome Extensionも開発しています。 KEELチームが開発する汎用AI(仮)が keelai なので、このChrome Extensionの名前は keelext です。 当初はGrafanaのユーザ体験を補完するなどプラットフォームの利用補助が主なスコープでしたが、汎用AI(仮)の開発に伴いそのスコープが拡大してきています。 LLM周りでの主な機能としては、対応している社内サービスなどのページを開くと keelai のアイコンが表示され、それをクリックするとモーダルが開いて例えばConfluenceの場合はそのページの要約が生成されます。 他にはGrafana上のエラーメッセージの解決方法を過去のエラー対応のやり取りをもとに提案してくれる機能や、右クリックのメニューからは選択範囲の文字列の翻訳・要約するなどありがちな機能も実装してあります。 生成結果の言語はブラウザの設定言語に従いますが、モーダル内でそのまま翻訳することも可能です。 このシームレスなユーザ体験により、利用者は keelai のアイコンを見かけたら とりあえずクリックするだけで何かがいい感じに生成される ということが実現されています。 他にもフォームの自動入力など社内の各種プラットフォームのLLM連携が実現されており、LLMを利用したPlatform Engineeringを支える重要なコンポーネントとなっています。 当然これも keelai のAPIを利用することで実現されていて、 APIの提供 で紹介した社内の認証基盤の認証フローを chrome.identity.launchWebAuthFlow で踏んでいます。 そのため利用者が手元にAPI Keyを用意することなく安全に呼び出すことができています。 あとは愚直にひたすら実装していくだけです。あえて言うとすれば生成結果をServer Sent Eventsでそのままモーダルにストリーミングしていて、性質上複数回クリックするなどしてリクエストが重複する可能性が高いので丁寧にAbortControllerで重複を防いでることくらいでしょうか。 Gmailで利用できるメール返信文の自動生成を実装しているなど本家と衝突して危ういものもありますが、メールは職種によっては評判がよかったりするのでとりあえず短期の生産性向上を目指してあらゆるページにLLM連携を実装していっています。 とはいえChrome Extensionだと権限の問題で実現できる範囲に制約があり、現在は鋭意デスクトップアプリケーションを開発中です。 (プラットフォーマーとして、Kubernetesベースの内製PaaS・GitHub ActionsからKubernetes Manifest, ドキュメントまでを握るコードジェネレータを中心としたコマンドラインツール・Chrome Extensionとやってきたので次はデスクトップアプリケーションかなというところです) コマンドラインツールでのLLM活用 我々は keelctl と呼ばれるコマンドラインツールも開発しており、先に紹介したコードジェネレータもこの keelctl に実装された機能の一つです。 www.lifull.blog 実はコマンドラインツールでのLLM活用は以前に似た内容のエントリを書いていて、社内の認証基盤の認証フローをコマンドラインから踏んでAPIを利用するという大枠は変わっていません。 www.lifull.blog こちらも順調に機能拡充が進んでいて、Conventional Commits形式のコミットメッセージの自動生成の他にも色々なタスクをコマンドラインツールからLLMを活用することで実現しています。 中でも、CSVのカラムを入力として他のカラムを埋める keelctl llm fillcsv という機能が好評です。 System: You are an AI assistant. Given a question and its answer, your task is to generate alternative expressions (paraphrasing) for that question to provide more options. User: Paraphrasing provides a new perspective on the question and aids users in obtaining the answer. Please generate at least 10 paraphrasings. Paraphrasing MUST be joined semicolons. question: keelaiの利用方法 answer: keelaiを利用する際には対象のSlackチャンネルにkeelaiを招待してメンションするか、keelaiのSlack Appsに対してDirect Messageで話しかけてください paraphrasing: keelaiを利用するには;keelaiの使い方;keelaiのドキュメント;LLMを使いたい question: %s answer: %s paraphrasing: 例えば上記のようなプロンプトテンプレートを与えると、以下のCSVを行ごとに処理して paraphrasing カラムを question と answer から生成した想定される質問の別バリエーションで埋めることができます。 question,answer,paraphrasing LIFULLに興味がある場合の次のアクション,お気軽に[カジュアル面談](https://hrmos.co/pages/lifull/jobs/010-9998)をお申込みください, ... 利用イメージは以下です。 コマンドを実行するだけでLLMを使ったタスクを並行で一括実行することができます。 中身は汎用AI(仮)なので、社内情報へのアクセスはもちろんのこと検索や画像生成まであらゆるタスクをこなすことができます。 $ keelctl llm fillcsv input.csv question,answer paraphrasing --prompt-template /path/to/paraphrasing.tmpl --concurrency 10 先のプロンプトテンプレートは社内知識をEmbeddingに変換する時に検索精度向上のために利用しているもので、他にも社内のよくある質問集の多言語対応をする際にも似た要領で一括実行しています。 最近では膨大な業務データのLLMを利用したタグ付けにも使用されたりと、簡単に使える一括処理として広く使われるようになってきました。 Jupyter Notebookでの高度なLLM活用 一方で、CSVの行ごとの一括処理ではまとまった単位で処理をしたい時や複雑な前処理が必要な時に対応することができません。 KEELチームはプラットフォームの機械学習サポートとして JupyterHub を運用しており、そこでそういった高度なユースケースに対応しています。 JupyterHubは社内の認証基盤と統合されており、社員であればURLにアクセスするだけでJupyter Notebookを利用可能です。 そして認証基盤との統合により keelai のAPIをNotebook上から認証なしに実行することができ、すぐにプログラムからLLMを使い始めることができます。 余談ですが、Jupyter Notebookは比較的リッチなUIを実現できるため、このような簡易的な画像生成用のNotebookも提供しています。 これとは別にStable Diffusion WebUIのホスティングもしていて、利用者は用途に応じて画像生成AIを使い分けることが可能です。 このJupyter Notebookの表現力を活かし、Slack上の keelai でCode Interpreter相当の機能を実現するためのインタフェースとしても利用されます。 また、コマンドラインツールの実行者はソフトウェアエンジニアに限られてしまいますが、Jupyter Notebookであれば誰もが簡単に実行できます。 共有ストレージをNotebookにマウントして、作成したJupyter Notebookの配布機構も備えたことで、作成したLLMの高度なユースケースを配布するプラットフォームとしても機能するようになりました。 まとめ 今回はLIFULLでのLLMを利用したPlatform Engineeringを紹介しました。 LIFULLのプラットフォームを開発する我々KEELチームは、Kubernetesベースの内製PaaSであるKEELを開発・運用しながら、社内で汎用AI(仮)と呼ぶAutoGPT実装 keelai も開発しています。 そして、コードジェネレータを備えた keelctl 、プラットフォームのユーザ体験を補完するChrome Extensionである keelext といった複数のチャネルを活かして、LLMを利用したPlatform Engineeringを進めてきました。 Slack Automationsを利用した疑似的なLLMノーコードツールやJupyter Notebookの提供では、利用者が自律的にLLMを用いた業務改善を行い更にそれを配布する仕組みも作ることで、社内のLLM活用にレバレッジを効かせることにも成功しています。 今後は根幹を担う汎用AI(仮)を中心に機能を増やしつつ、KubernetesクラスタであるKEEL自身のAIOps(?)を強化し、あらゆる面からLLMを使ってLIFULLの生産性向上に貢献していきたいと考えています。 もし興味を持っていただけた場合は、是非カジュアル面談をさせてください! hrmos.co hrmos.co
アバター
エンジニアの小林と申します。 LIFULL HOME'S の横断領域の開発を担当しています。 私たちの開発しているLIFULL HOME'Sでは、A/Bテストの実施によって市場学習(≒PDCA)の回数を増やし、より良いプロダクトを作り上げることを目的として、日々多くのA/Bテストが実施されています。 しかしながら、いくつかの問題があります。 今回はLIFULL HOME'SにおけるA/Bテストの成熟度と課題、そして現在の取り組みをご紹介します。 A/Bテストの成熟度と課題 A/Bテスト成熟度モデル 課題 新しい取り組み 構成図 A/Bテスト一覧 A/Bテスト詳細(概要) A/Bテスト詳細(結果) 解決された課題 これからの展望 まとめ A/Bテストの成熟度と課題 A/Bテスト成熟度モデル Kohavi, R., Tang, D., & Xu, Y. (2020).によるA/Bテスト成熟度モデルに照らすとLIFULL HOME'SのA/BテストはWalkフェーズへ移行を目指している段階です。 Walkフェーズへ移行するにあたり課題となったのは A/Bテスト設計の標準化 結果の信頼性の確立 の2点です。 table { border-collapse: collapse; } th { border: solid 1px #666666; color: #000000; background-color: #ff9999; } td { border: solid 1px #666666; color: #000000; background-color: #ffffff; } フェーズ 概要 Crawl A/Bテストで利用するユーザー識別子の発行と結果を集計するための基盤が整っている。 Walk 標準的な指標の定義ができている。 ユーザーのサンプル比率のミスマッチ(SRM)が起きないように仕組み化されている。 A/Bテスト結果の信頼性が確立されている。 Run 評価基準が合意され、システマチックな意思決定するプロセスが確立されている。 Fly テスト集計~プロダクトへのロールアウトまで全てのプロセスが自動化されている。 参照: Kohavi, R., Tang, D., & Xu, Y. (2020). Experimentation Platform and Culture. In Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing (pp. 58-78). Cambridge: Cambridge University Press 課題 EDDの導入を済ませ、次の課題は更なる標準化と結果の信頼性の確立となりました。 ここで課題となるのが集計・分析の過程です。 A/Bテストのプロセスを整理すると以下の図のようになります。 A/Bテストの各プロセス 1. テストの設計 の標準化についてはEDD(Experimental Design Doc)の導入により一定の改善が行われています。 EDD導入については以下のnoteをご覧下さい。 note.com 2. テストの実装 、 3. テストの実施 については半年ほど前にA/Bテスト用社内packageが開発され、新規マイクロサービスにおいてはA/Bテストを低コストで実装できるようになりました。 www.lifull.blog 4. テストの計測 については主にBigQueryに接続したGoogle スプレッドシートにおいて行われています。 このGoogle スプレッドシートのテンプレートが部署(賃貸・分譲 etc...)によって異なり、BigQueryに対するクエリやベイズ計算のロジックもそれぞれに実装されています。 スプレッドシートで表示している値は汎用性を持たせられており、EDDで判断指標として指定したGoal MetricsやGuardrail MetricsなどのMetrics以外にも多くの指標を取得しています。 また、この知見を蓄積するドキュメントはテストごとにConfluence(アトラシアン社製企業向けWiki)にまとめられていますが、Confluenceは検索性が低くInstitutional memoryの蓄積(組織全体が過去の経験から学んだことを、どれだけ覚えているか)という観点では質が低い状態です。 新しい取り組み 前項で挙げた課題を解決するために A/Bテストをはじめとしたコントロール実験を正確に測定し、意思決定を明確化すること 結果・分析を記録し、施策の知見を未来に向けて蓄積すること を目指したコントロール実験実行・分析基盤としてexp-libraryというA/Bテスト管理基盤を作成しました。 構成図 exp-library構成図 メトリクス定義の複雑な入力を受け付けるために、Next.jsを採用しました。 Python製の集計用バッチを定期実行してBigQueryからユーザーの行動データを取得し、集計・ベイズ計算を実行してBigQueryに保存しています。 A/Bテスト一覧 テスト一覧 登録されたA/Bテストの一覧です。 検索と様々な絞り込みが可能です。 後述するテスト情報の詳細の内容も含めて文字列検索をするため、Confluenceで感じていた過去の施策の検索しにくさを解消します。 A/Bテスト詳細(概要) A/Bテスト詳細画面 登録されたA/Bテストの詳細情報です。 こちらで計測のオン・オフが可能です。実施期間中のみ自動で計測する仕様にもできましたが、過去の施策の結果も収集するために、手動で切り替えられるようにしています。 こちらから施策のドキュメントにアクセス可能です。 A/Bテスト詳細(結果) A/Bテスト詳細(結果)画面 計測結果を表示しています。 解決された課題 今回作成した集計バッチで全てのA/Bテストを集計することで、全社で集計フローが統一されました。 また、これまでは部署ごとのスプレッドシートの中にあったクエリや計算ロジックがPythonのコードとしてGitHubで管理されることで、統計に知見のあるエンジニア・データサイエンティストが内容を精査・改善するハードルが下がりました。 これによりテストの計測について、一定の標準化が成され、結果の信頼性を担保に向けて検証が容易になりました。 これからの展望 現在このexp-libraryは既存のスプレッドシートから移行するべくβテストと称して社内の一部の施策担当者からFBを受け、改善を行っています。 今後はエンジニア・企画の工数を削減し、A/Bテストの回数を増やすべく、以下の機能実装を行っていきます。 A/B テスト勝敗判定の自動化 A/B テストの開始・終了の自動化、A/B寄せの自動化 Feature Flag 多腕バンディットアルゴリズムによるコントロール実験機能 まとめ exp-libraryの命名の由来は以下のとおりです。 市場調査の数を指数関数的(exponentially)に増やし、あらゆる実験(experiments)を収集し、経験(experience)を蓄積し、公開する。 「公共図書館の本質的な機能は、資料を求めるあらゆる人々やグループに対し、効率的かつ無料で資料を提供するとともに、住民の資料要求を増大させるのが目的である」 中小都市における公共図書館の運営(1963) 会社の資産とも言える施策の試行錯誤を正確に記録した資料を残し、今後に活かしていくことを目的に基盤を作成しました。 LIFULL HOME'SのA/Bテストをより良い状態にしていくために今後も改善をしていきます。 ともに良いプロダクト作りをしてくれる仲間を募集しています。 hrmos.co hrmos.co
アバター
こんにちは、グループデータ本部データサイエンスグループの清田です。 5月28日から31日にかけて静岡県浜松市にて開催された 人工知能学会全国大会(JSAI 2024) に参加してきました。 LIFULLでは、今年もシルバースポンサーとして協賛しております。 今年は、学会理事および副実行委員長として運営にも関わっていましたので、舞台裏の部分も少しお伝えします! なお、昨年熊本で開催されたJSAI 2023の様子も書かせていただいています。 www.lifull.blog 過去最多の参加人数とハイブリッド開催のメリット 人工知能学会 は、1986年に設立された学術研究団体で、「人工知能(AI)に関する研究の進展と知識の普及を図る」ことを目的としたさまざまな活動を行っています。 この記事でお伝えする全国大会のほか、以下のようなさまざまな事業を行っています。 学会誌・論文誌の発行 研究会の開催 各種セミナーの開催 産業界と学術界の連携シンポジウム(SIAI)の開催 今回のJSAI 2024は、約3800名の参加者が集まり、過去最多の参加人数となりました。 現地での熱気から、ChatGPTに端を発する生成AIブームを受け、AIに対する関心がますます高まっていることを肌で感じました。 JSAI 2024のクロージングセッションにて発表された参加者数の推移 上のグラフに示されているように、全国大会の参加者数は、コロナ禍によるフルオンライン化を経て、コロナ禍前を超える参加人数となっています。 2020年と2021年は完全オンライン開催でしたが、2022年からは現地会場での開催が復活するとともに、ハイブリッド開催となりました。 グラフを見ると、オンラインで参加できるようになったことで、これまで仕事や家庭の都合などの諸事情で参加できなかった方々が参加者層に加わったように思えます。 また、全国大会では、Zoomによるオンライン参加だけでなく、「録画の後追い再生」など、便利なサービスも提供されています。 オンライン参加の方だけでなく、現地参加の方からも、これらのハイブリッド開催ならではのサービスは非常に好評であることが、アンケートの結果からわかっています。 ハイブリッド開催は、設備や運営スタッフの配置などで大きなコストがかかるものの、ニーズも非常に大きいため、来年以降も継続される方針です。 JSAI 2024におけるダイバーシティ&インクルージョンへの取り組み 人工知能学会全国大会は、毎回多様な分野の方々が参加するイベントであり、実際に参加してみると、非常にダイバーシティの大きなコミュニティであることを肌で感じます。 ただ、参加者の属性についていえば、まだ大きな偏りがあります。 代表的な属性である「性別」について見ると、女性の参加者の割合は15%未満であり、依然として男性の参加者の方が圧倒的に多い状況です。 (人工知能学会の会員比率については、さらに偏りがあり、女性の割合は約6%です) 社会を根本から変革する可能性を持つAI分野のコミュニティには、さまざまなバックグラウンドの人材が集まることが求められます。 もし、AI分野の研究者の属性が、極端に偏ってしまった場合、構築するAIが実社会で活用できないものになってしまう可能性も指摘されています。 私が人工知能学会の編集委員長を担当していた時期に、学会誌2020年9月号にて企画した特集「ダイバーシティとAI研究コミュニティ」でも、この問題を取り上げました。 特集の共同企画者の伊藤貴之先生(お茶の水女子大学)へのインタビュー記事がAINOWに掲載されていますので、もしよろしければぜひご覧ください。 ainow.ai 人工知能学会では、この問題への取り組みを進めるため、「多様性・包摂推進委員会」を2023年に設立しました。 sites.google.com 委員長の高野雅典さん(サイバーエージェント)が、コミュニティにおける属性の偏りがなぜ起こるかを解説されています。 結果の不平等と機会の不平等のフィードバック構造(多様性・包摂推進委員会のWebサイトから引用) こうした状況を改善するため、多様性・包摂推進委員会では、以下のような方針を掲げて活動を行っています。 コミュニティ醸成・支援 学会参加者・会員・運営者の啓発 制度・慣習に関する課題の共有と解決策の提案 具体的な活動の一つとして、2023年11月に開催された人工知能学会合同研究会2023、および今回のJSAI 2024にて、「女性向けランチ会」を開催しています。 ダイバーシティの課題として挙げられるマイノリティの人々の「ロールモデルの少なさ」、つまり「職業・生き方において参考にする/したい人と出逢う機会が少ない」という問題を改善するため、マイノリティの方々どうしが交流する場を創出することが、このランチ会の目的です。 今回、学会理事として冒頭にご挨拶をしましたが、参加者の方々が交流を心から楽しまれている様子を拝見することができました。 株式会社onerootsの榊原 美穂さんが、参加者として報告記事を投稿されていますので、こちらもぜひご覧ください。 www.wantedly.com オーガナイズドセッション「不動産とAI」 今回のJSAI 2024では、2017年から2019年まで3回にわたって開催した「不動産とAI」オーガナイズドセッションを、4年ぶりに開催することができました。 sites.google.com 今回は、以下のようなテーマで発表募集を行い、応募があった6件の研究発表とパネルディスカッションでプログラムを編成しました。 不動産分野への生成AI技術の応用 不動産価値の推定 マルチメディアとしての不動産ビッグデータの活用 経済学、建築学、地理学、都市学など、不動産と密接に関連をもつ分野との連携 パネルディスカッションでは以下のようなテーマで、多岐にわたる議論を行いました。 生成AIへの期待や課題提起 既存ストックの価値を高めるためにAI技術が果たせる役割 不動産領域の多様なデータへの要望や課題提起 学際である不動産の他業界との関わりについて 来年も、引き続き「不動産とAI」をテーマとしたセッションを企画していく予定です。 ご期待ください! 来年は大阪での開催 来年の全国大会(JSAI 2025)は、 EXPO 2025 大阪・関西万博 の会期中の大阪で、2025年5月27〜30日に開催を予定しています。 来年の全国大会会場は大阪・中之島にあるグランキューブ大阪です JSAI 2025では、実行委員長という役割で関わる予定です。 大阪・関西万博との連動企画など、より社会に開かれた学会への変革に向けた取り組みを進めてまいります。 参加される皆様に満足していただける全国大会になるよう、微力ながら尽力してまいりますので、皆様どうぞよろしくお願いいたします! LIFULLでは、共に成長しながら働く仲間を募っております。 現在、以下の職種を募集しております。LIFULL HOME’Sデータセットなど、豊富な研究開発資源を活かしながら、多様な社会課題の解決に向けた研究開発やプロダクト創出に取り組んでみませんか? hrmos.co ご興味をお持ちの方、ぜひお気軽にご相談ください!
アバター
こんにちは、エンジニアの中島です。 この記事は2024年7月のLIFULL社でのアクセシビリティ改善およびやっていき活動の報告です。 この活動報告は月次で出すかもしれないし出さないかもしれないくらいの温度感で運用されています。 目次 目次 サービス改善 ログイン画面の入力フォームの名前設定 物件登録画面のステップ情報のスクリーンリーダー対応 売買物件登録画面のテーブル文脈の排除 物件登録画面内の物件画像編集モーダルのアクセシビリティ対応 取扱物件の一覧ページにある各コントロールへの名前設定 育成・啓発の取り組み アクセシビリティ1on1 社内表彰 お知らせ サービス改善 本期間中の改善取り組みのターゲットはLIFULL HOME'S 賃貸・流通マネージャーサイトです。 こちらはLIFULL HOME'Sをご利用いただいている会員様(不動産会社様)が、物件を掲載する際にお使いになる管理画面になります。 諸事情で発表できないものもありますが公開可能な取り組みを紹介させていただきます。 ログイン画面の入力フォームの名前設定 ログイン画面の入力フォームには一部名前が設定されていないコントロールがあり、支援技術ユーザーにとっては何の入力が求められているのかがわかりにくい状況がありました。 これらのコントロールに適切な名前を設定する修正を行いました。 物件登録画面のステップ情報のスクリーンリーダー対応 物件登録画面は入力ページ、確認ページ、登録完了ページの3ステップで構成されています。 そのステップの現在地情報は画面上部に画像で表示されていますが、代替テキストが「進捗状況」とだけ書かれていて、ステップの一覧、現在地情報を支援技術から読み取ることができない状況にありました。 そこで、代替テキストを空にし、スクリーンリーダー用にaria-currentを設定した順序リストで補うように修正しました。 売買物件登録画面のテーブル文脈の排除 売買の物件登録画面はレイアウトのためにテーブルを使っており、また余白のために空白のセルを使っていたりとセマンティクスが不適切であり、スクリーンリーダーでの読み上げが壊滅的であるという問題がありました。 前回賃貸物件登録画面でも同様の問題があり、role設定でテーブル文脈を破棄する対応を行いましたが、売買側にも同様の修正をしました。 物件登録画面内の物件画像編集モーダルのアクセシビリティ対応 賃貸・売買ともに物件登録画面内に登録する物件画像を編集するためのモーダルがあります。 モーダルがモーダルとして認識されない、モーダル内のコントロールにフォーカスが適切に移動しない、フォーカスがトラップされない、モーダル内のコントロールのフォーカスインジケータが見えないなど様々な問題がありました。 これらを解消すべく、求められるrole/stateの設定およびフォーカス管理、フォーカスインジケータの可視化対応などを行いました。 取扱物件の一覧ページにある各コントロールへの名前設定 不動産会社様がLIFULL HOME'Sに掲載しようと登録した物件の一覧ページには、確認したい物件を絞り込むための検索フォームが設定されていますが、これらのコントロールには名前が設定されていないものが多くありました。 それぞれのコントロールに名前を設定し、支援技術ユーザーでもコントロールの目的を理解できるよう修正しました。 育成・啓発の取り組み アクセシビリティ1on1 本期間中は先月と同じくフロントエンドエンジニア6人、デザイナー1人に対して行いました。 内容はWCAGの解説、APG(aria authoring practices)の解説、coga-usableの解説、実際のアプリケーション開発時でのアクセシビリティ配慮に関する相談などが主なものとなります。 社内表彰 LIFULL社ではクォータに一度、エンジニアがそろうエンジニア総会というものがあり、そこにアクセシビリティへの取り組みを表彰する枠が用意されています。 4Q(2024/07)の表彰はLIFULL HOME'S アーカイブ開発チームと売買開発チームでした。 アーカイブチームはマンションブランドごとの物件一覧ページのリニューアルにおいてフルキーボード操作、及びロストすることのないフォーカスマネージメントを実現した点が評価されました。 また売買開発チームは売買物件の一覧ページ末尾にある不動産会社情報カルーセルのタブストップ数を減らし、キーボード操作をより用意にした点が評価されました。 お知らせ LIFULLではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター
グループデータ本部データサイエンスグループの嶋村です。 データサイエンスグループが主催でデータサイエンス系の自社イベント『 LIFULL AI Hub 100 ミニッツ #2 「ファクトブック」 』を開催しました。第1回目の『 LIFULL AI Hub 100ミニッツ #1 「LLM(大規模言語モデル)の研究開発」 』に引き続き、オフライン・オンラインともに盛況となり、今回も講演を聴講していて学びがありました。当日は、 X(旧Twitter)でも実況 しておりましたので、当日の様子が少しでも伝わればと思います。 主催のデータサイエンスグループはグループデータ本部配下の研究開発組織になります。グループデータ本部は、LIFULLグループで生まれる新たなデータを安全かつ効果的に活用できるようにし、事業の変化と持続的な成長を促進することを目指している組織です。その中で、データサイエンスグループは「活用価値のあるデータを創出」し、「データを活用した新たな機能やサービス」の研究開発に取り組んでいます。 今回のテーマである「ファクトブック」は、以前本ブログでも記載した「 LIFULLファクトブックで目指す真のドメイン知識獲得 」に関する発表となります。弊社LIFULLの事例だけではなく、先行してファクトブック活動を続けられている東北一円にドラッグストアを展開する薬王堂さまの事例に関する発表もありました。 第一部 トークセッション 「ファクトブック事例」 第一部では、まず、 LIFULLデータサイエンスパートナである鹿内氏 から企業における分析の実情や、ファクトブック活動の概要について説明がありました。 データの利活用に関する調査結果があり、その中で「特に課題・障壁はない」と回答している企業も多いが、本当にそうだろうか?という問いは大変興味深かったです。実は課題に気付けておらず、真のデータ利活用は進んでいない、という可能性もあると感じました。データサイエンスグループも様々な部署と連携して社内でのデータ利活用に向けた取り組みをすることがありますが、大小問わず課題は山積でまだまだ伸びしろがある状態なため、日々革進させていきたいです。 次に、LIFULLデータアナリストの羽賀と、薬王堂の西郷さまから、データを企業運営にどのように活かしているかについて講演がありました。 LIFULL羽賀からは社内で実施しているファクトブック活動の事例や、これまでの実績や課題に関する発表がありました。作成したファクトブックコンテンツに基づいて考察をする読み会の開催数は15回にわたり、のべ参加者数は約200名であることや、読み会での考察をもとに社内で新たな発想が生まれた事例が紹介されました。一方で、熱量ある参加者を継続的に確保するにはどうしたら良いか、という課題も提起されていました。 また、西郷さまからは薬王堂さまが提唱する 薬王堂PBMA(Purchase Behavior Modification Analytics) が紹介され、その薬王堂PBMAの実現に向けてファクトブック活動が重要な取り組みであることが話されていました。初期の時点では数名で実施していたファクトブック活動も、営業メンバやインターンシップを交えて、徐々に拡大されている事例は、弊社でもとても参考になりました。 第二部 クロストーク パネルディスカッション クロストークではファシリテータの鹿内氏と、登壇者である羽賀・西郷さまを交えて、ファクトブック活動をどのように発展させていくかというディスカッションがありました。 ファクトブック活動は読み会が重要ではありますが、ファクトブックコンテンツそのものが整っているのが大前提です。そのコンテンツで、どのような項目を、どのように見せるのか、試行錯誤の話がありました。たとえば、統計分野ではお馴染みの累積分布を見せるにしても、累積分布に馴染みのない参加者も居るため、丁寧に説明をしたりと工夫がありました。 ファクトブックコンテンツを作成していく上では、データが整備されていることも欠かせず、データクリーニングの重要性についても語られていました。データがバラバラで集約できていないケースや、データはあるがフォーマットがバラバラで分析しづらいケースなどもあるため、いかに使いやすいデータを作るかが鍵となることは間違いありません。 ファクトブック活動を発展させていく上では、キーマンが欠かせず、そのキーマンは誰なのかという問いは興味深く参考になりました。薬王堂さまでは、役職や年齢は関係なく、熱意があり行動を起こせる方を起用していました。薬王堂さまの場合、代表取締役である西郷さまのトップダウンのコミットメントに合わせて、熱量ある人材によるボトムアップのコミットメントが合わさることで、発展されていったと感じました。 ファクトブックコンテンツというアウトプットで満足するのではなく、アウトカムにつなげることが重要だ、というディスカッションもありました。確かにアウトプットだけをしていると、「これは何のための分析だろう」となってしまうことは往々にして起こりがちです。そのため、ファクトブック活動を通じてアウトカムにつなげるというイメージが共有できていると、地道な分析であっても意義が理解でき、また分析結果に基づいたネクストアクションも生まれやすいと感じます。 クロストークの最後に総括として、鹿内氏から、ファクトブック活動は「各職種の領域にまたがるデータについて、コミュニケーション(ディスカッション)をするきっかけになる」という話がありました。読み会を通じてデータについて相互理解を深めることで、データドリブンな文化の形成につながっていくと思うため、改めてファクトブック活動の重要性を再認識できました。 以上のようなディスカッションがあり、運営側の立場ではありましたが、聴講者の一人として大変興味深く学びになりました。今回の学びをLIFULLファクトブックでも活かしていきたいです。 おわりに 今回はデータサイエンス系の自社イベント「LIFULL AI Hub 100ミニッツ」の取り組みについて紹介しました。次回は第3回のイベントも企画しており、気軽にご参加いただけると嬉しいです。 LIFULL Co., Ltd. - connpass 最後になりますが、データサイエンスグループでは「活用価値のあるデータを創出」し「データを活用した新たな機能やサービスの研究開発」を加速して下さるシニアデータサイエンティストを募集しています。また、今回のファクトブック活動を推進している羽賀の部署でも、データアナリストを募集しております。 hrmos.co hrmos.co 興味お持ちいただける方は、 カジュアル面談 も行っていますのでお気軽にご連絡ください。
アバター
こんにちは、エンジニアの中島です。 この記事は2024年4月〜6月のLIFULL社でのアクセシビリティ改善およびやっていき活動の報告です。 この活動報告は月次で出すかもしれないし出さないかもしれないくらいの温度感で運用されています。 目次 目次 サービス改善 取扱物件検索の検索方法選択タブのボタン化 物件登録画面(賃貸のみ)のタブのボタン化とテーブル文脈の破棄 物件登録画面(賃貸のみ)内のダイアログのキーボード操作を可能に 物件登録画面(賃貸のみ)内の郵便番号からの住所入力補完機能をキーボード操作可能に 物件登録画面(賃貸のみ)内の画像登録UIをキーボード操作可能に 取扱物件一覧(賃貸のみ)内のフォーカス不能なボタンを修正 育成・啓発の取り組み 新入社員研修 アクセシビリティ1on1 WCAG解説書 輪読会 社内表彰 お知らせ サービス改善 本期間中の改善取り組みのターゲットはLIFULL HOME'S 賃貸・流通マネージャーサイトです。 こちらはLIFULL HOME'Sをご利用いただいている会員様(不動産会社様)が、物件を掲載する際にお使いになる管理画面になります。 諸事情で発表できないものもありますが公開可能な取り組みを紹介させていただきます。 取扱物件検索の検索方法選択タブのボタン化 マネージャーにログイン後に表示されるページの取扱物件検索の検索方法選択タブはdivで実装されており、フォーカス不能・選択不能であるという問題がありました。 いったんそれをボタン化し、キーボード操作を可能にしました。 またタブ内に表示される各フィールドに適切な名前を設定しました。 物件登録画面(賃貸のみ)のタブのボタン化とテーブル文脈の破棄 物件情報を登録する画面では、多岐にわたる入力をする必要があり、そのためタブを使って情報を分割しています。 しかしながらこのタブもdivで実装されており、フォーカス不能・選択不能であるという問題があったため、こちらも同様にボタン化する対応を行いました。 また、この物件登録画面はレイアウトのためにテーブルを使っており、また余白のために空白のセルを使っていたりとセマンティクスが不適切であり、スクリーンリーダーでの読み上げが壊滅的であるという問題がありました。 こちらは組み直しが大規模改修となり困難なためrole設定でテーブル文脈を破棄する対応を行いました。 物件登録画面(賃貸のみ)内のダイアログのキーボード操作を可能に 物件登録画面内にはダイアログが表示されるボタンが随所に設置されています。 しかし、多くは click ではなく mousedown イベントによって起動するように実装されており、キーボード操作でのダイアログ表示ができないという問題がありました。 これらをクリックに変更し、キーボードでのダイアログ開閉を可能にしました。 またダイアログのrole設定不備・および名前設定の不備・キャンセル、送信ボタンのフォーカス不能不備などの問題も合わせて修正しました。 物件登録画面(賃貸のみ)内の郵便番号からの住所入力補完機能をキーボード操作可能に 物件登録画面内に住所入力を簡単に行うために郵便場号から住所入力を行う機能があります。 しかしこちらも機能を呼び出すボタンにフォーカスが当たらないといった問題ありました。 こちらも適切なrole、振る舞いの設定を行い、キーボード操作での利用を可能にしました。 物件登録画面(賃貸のみ)内の画像登録UIをキーボード操作可能に 物件登録画面には物件の画像を登録するUIがありますが、こちらも画像の選択ボタン、削除ボタンなどいくつかのボタンがキーボード操作不能という問題がありました。 他と同様に適切なrole、振る舞いの設定を行い、キーボード操作での利用を可能にしました。 取扱物件一覧(賃貸のみ)内のフォーカス不能なボタンを修正 会員様の取扱物件を一覧表示する画面には、表示された物件の編集や削除を行うためのボタンなどが数多くあります。 いずれも名前の設定漏れがあったりキーボード操作ができないといった問題がありました。 他と同様に適切な名前設定、role設定、振る舞いの設定を行い修正を致しました。 育成・啓発の取り組み 新入社員研修 弊社では毎年6月ごろに新入社員(新卒・中途)向けにアクセシビリティの理解を深めるための研修を行っています。 この研修は今年で3回目で、内容は freeeさんのアクセシビリティ研修 の資料を参考にさせていただいており、毎年LIFULLに合わせてマイナーチェンジを行っています。 アクセシビリティとは何か、基本的な考え方など説明する会は営業職等を含めた全職種対象で行い、その後より具体的な内容をものづくり職種を対象に行いました。 サービス作りに直結する学びになったという声だけでなく、営業資料作成時にも気を付けていきたいなど、多岐にわたる感想をいただきました。 アクセシビリティ1on1 本期間中は先月と同じくフロントエンドエンジニア6人、デザイナー1人に対して行いました。 内容はWCAGの解説、APG(aria authoring practices)の解説、coga-usableの解説、実際のアプリケーション開発時でのアクセシビリティ配慮に関する相談などが主なものとなります。 WCAG解説書 輪読会 アクセシビリティやっていき勢向けにWCAGの輪読会を隔週で行っています。 本期間中は4月1日、5月30日、6/27日の3回行われました。 範囲は達成基準1.3.4〜1.4.3です。 社内表彰 LIFULL社ではクォータに一度、エンジニアがそろうエンジニア総会というものがあり、そこにアクセシビリティへの取り組みを表彰する枠が用意されています。 3Q(2024/04)の表彰はLIFULL HOME'S 賃貸開発チームでした。 LIFULL HOME'S(賃貸)の問い合わせフォームのアクセシビリティ改善に取り組んでいただいたことが評価されました。 キーボード操作時の細かい不備や、Web標準に準拠したフォームの入力支援などさまざまな修正が行われました。 お知らせ LIFULLではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アバター