検索エンジンチームの加藤宏脩です。 今回は、LIFULLの検索エンジンであるSolrのバージョンアップについて紹介します。 Solrを含むミドルウェアの最新バージョンへのアップデートには多くの工夫と努力が必要です。 この記事では、私たちがLIFULL HOME`Sを支える物件検索エンジンのバージョンアップにどのように取り組んでいるのか、またv9.2.1へのバージョンアップ対応の詳細ついて紹介します 課題: Solrバージョンアップ移行時の課題 Solrのアップデートは単純な作業ではありません。 特に、古いバージョンからの大きなジャンプでは、多くの非互換性や変更点が存在します。以下は、私たちが移行時に遭遇した主要な課題です。 非互換性の問題: 既存の機能や設定が新バージョンで動作しない可能性。 テスト環境の構築: 新バージョンの動作確認に必要なテスト環境のセットアップ。 既存のカスタム機能の移行: LIFULL独自のカスタム機能を新バージョンに適応させる。 パフォーマンスの違い: 新旧バージョンの間でのパフォーマンス差。 新機能の取り込み: どの新機能を採用し、どう利用するかの判断。 影響が不明: 自社で利用している機能が把握できず、影響があるのかが分からない。 継続的にバージョンアップするために普段行っていること LIFULLでのSolr運用は日々の変更調査から始まります。 日々の変更調査 SolrやLuceneの変更内容を追跡し仕様変更や非推奨・廃止機能の調査します。 LIFULLの利用環境への影響を調査します。 調査結果をスプレッドシートにまとめます。 バージョンアップをブロックする可能性があれば、はやめにコミットします。 バージョン毎の調査 新バージョンのリリース毎に変更内容を確認し、動作確認を行います。 LIFULLの検証手順で動作確認をします。 テスト結果や修正項目、留意点をまとめます。 バージョンの選定 変更が多くなると作業量が増えるため、こまめにバージョンアップをします メジャーバージョンアップ等変更が多い場合は、 その一つ前のバージョンに上げるなどして、一つあたりのバージョンアップの作業量を減らすようにします。 バージョンアッププロジェクトで行うことについて バージョンアップは以下のような手順で進められます。 バージョンアッププロジェクトの開始 日々の調査やバージョンの調査結果をもとに、対応内容を確認。 調査計画、デプロイ計画、テスト計画を作成。 システム変更タスクの消化: 新バージョンに対応するための変更タスクを実施。 検証 LIFULLでは、物件検索エンジンの比較、検証用にテストを用意しています。 これがあることにより、 頻繁に物件データの更新がある環境で、データをそろえた2つの環境を用意したり 任意の量のクエリを抽出して性能や結果を比較するような手間のかかるタスクが簡単に行えるようになっています。 性能テスト 実際に本番環境で投げられているクエリを用意して、 新旧バージョンのクラスタにクエリを投げて、そのレスポンス速度を比較します。 回帰テスト 実際に本番環境で投げられているクエリをサンプリングして、 新旧バージョンのクラスタにクエリを投げて、レスポンスの差分を比較します。 データの差分確認テスト 新旧バージョンのクラスタに同じデータを投入して、 全件の出力を比較します。 混沌テスト(カオスエンジニアリング的な観点から行われるテストを略して混沌テストと呼んでいます) SolrのLeaderがfailoverして切り替わるのを確認します。 Zookeeperを落としてクラスタにクエリを投げて、そのレスポンスを確認します。 SolrのインスタンスにOOMを起こさせて、意図した挙動をするのかを確認します OOMは、自社で作ったOOMを起こさせるプラグインを導入してテストしています。 ディスクの使用量テスト Zookeeper、Solrのインスタンスのディスク使用量が新旧で大きく違っていないかを確認します。 Solrの台数を増減させてリクエストを受け付け続けられるかを確認します。 ログ出力テスト サーバー内のログにエラーやワーニングがないかを確認します。 cloudwatch logsに送信できているかを確認します。 デプロイ 問題がなければ、本番環境にデプロイ。 バージョンアップの運用をしたメリット バージョンアップを始めるときのハードルが低くなる バージョンアップが始まった段階で、変更内容やプロジェクトの手順など必要なタスクが分かります。 バージョンアップのPJのテンプレート化をしているので、PJの進め方も分かります。 共通のテスト手順が決まっているので、テストの手順を考える必要がなくなります。(変更内容を見て別途テストの設計はします) 知識が増える 変更調査を毎日やっているため、 メンバーが徐々にSolrの仕様変更に詳しくなります。 気になることをSlackでつぶやくと、誰かが知っているということが何度かありました。 リリースの安心感 検証は、過去の障害事例を元にしたテストも含んでおり そのテストがパスしている状態でのリリースには安心感があります。 Solr v9.2.1へのバージョンアップについて v9.2.1を選んだ理由。 当時最新のSolrバージョンであったことと、v9.x以降ベクトルが利用できるようになるため v9.2.1へのバージョンアップを行いました。 v8.x系の最新バージョンへの変更も考えましたが、 v9.2.1の検証もできており問題ないと判断したため、v9.2.1へのバージョンアップを行うことにしました。 Solrの変更について 調査や検証をする中で、いくつか問題や気を付けることがあったため その点についての紹介をさせていただきます。 ※具体的な変更については、Solrの公式の変更点を参照してください。 https://solr.apache.org/guide/solr/latest/upgrade-notes/major-changes-in-solr-9.html experimentalなAPIの機能廃止 zookeeperがlog4jを廃止して、logbackを使うようになりました logの出力先が変わるため、awslogsなどログの収集元を変更する必要があります。 FastLRUCache廃止に伴いCaffeineCacheへ変更 SolrのbooleanClausesの設定を参照する箇所の変更 節の長さが設定値を超えるようになり、バージョンアップ前まで成功してたクエリが失敗するようになるということが起きます LegacyBM25SimilarityFactoryの廃止に伴いBM25SimilarityFactoryへの変更 フリーワード検索による類似スコアでのブースト計算が変わりソートのための重みが下がります 類似度によるスコアの開きが少なくなるので、fqなど他の条件でのスコア計算の影響を受けやすくなります バージョンアップの効果 高速化 レスポンス速度が向上。 LIFULLの環境では、p99が大幅に改善しました。 CPU使用率の低下 CPU使用率が低下して、さばけるクエリ数が増えたので インスタンス数を3~40%ほど減らせました。 Solr v9というより、Javaのバージョンアップによる影響が大きかったのもあるかもしれません。 新機能の活用 新バージョンに含まれるベクトルを使った検証ができるようになりました。 まとめ 今回は、LIFULLが行っているSolrを継続的にバージョンアップする仕組みと、Solr v9.2.1へのバージョンアップについて紹介しました。 少ない工数かつ、比較的安全にバージョンアップできるようになりました。 また、最新のSolrにバージョンアップしたことで、新機能の活用やパフォーマンスの向上やコストカットなどのメリットも得られました。 ミドルウェアのバージョンアップは疎かにしがちですが、 LIFULLでは上記のような運用を行うことで、物件検索エンジンのSolrを最新バージョンに追随しています。 最後に、 このような効率化をしたいまたは得意なエンジニアの方々、 LIFULL では一緒に働く仲間を募集しています。この記事を読んで LIFULL に興味ができた方は求人情報も御覧ください。 hrmos.co hrmos.co
こんにちは。フロントエンドエンジニアの根本です。 LIFULL HOME'Sのプロダクト開発と、スポーツ関連の新規事業開発に携わっています。 ちょうど1年前のブログ( UXエンジニアとは?新規事業での取り組み - LIFULL Creators Blog )では、UXエンジニアという職種と新規事業開発でどのような取り組みを実践しているか概略を紹介しました。 今回はUXリサーチを取り入れた実際のプロダクト開発について、既存サービス改善と新規サービス開発それぞれに分けて紹介します。 既存サービス改善 インタビューの定期実施 私たちのサービスでは会員の方に常時インタビュー募集をしており定期的にインタビューを実施しています。 インタビューでは下記の共通事項を聞きながら必要があればその時に確認したい点や検討中の施策があればそれに対してヒアリングを行います。 ・サービスを認知した場所と会員登録した理由 ・現在の利用状況と登録前に期待していた事とのギャップ ・実際に使ってみて使いづらい点や要望 ユーザーストーリーマッピング作成 既存サービス改善を行うにあたって、サービス全体のユーザーストーリーマッピングを整理し直しました。 整理結果をもとに不足している機能群を洗い出し、優先度の重みづけをし重点的に対応すべき機能をピックアップしました。 また、会員には様々なユーザー属性がありその属性毎に必要な機能差分があれば色分けし整理します。 施策への落とし込み ユーザーストーリーマッピングを整理し見えてきた優先度高の機能について、インタビュー結果をもとにユーザー行動の洗い出しと発話を整理し改善のアイディアを洗い出します。 このように定期的にインタビューを実施することで、既存機能の改善をする際のインプットとして活用できPDCAを高速に回せることを実感しています。 新サービス開発 元々指導者向けにスポーツの練習メニューを提供するサービスを開発運営していましたが、今回選手向けの新規サービスの企画を進めてきました。 コンセプトテスト1回目 新規サービスを開発するにあたり、課題仮説とそれに対するコンセプトを用意し選手の保護者11名を対象にユーザーインタビューしました。 今回のイタンビューでは課題共感とコンセプト及びソリューションへのマッチ度を五段階で評価して頂き、自分達の検討している新規サービスがユーザーに受け入れられるのか検証しました。 選手年齢・競技経験・競技意欲・練習頻度など様々な基本情報をヒアリングすることで、今回の初期顧客となるペルソナ像の肉付け作業も合わせて実施しました。 初期顧客へのコンセプトテスト2回目 コンセプトテスト1回目から見えてきた初期顧客ユーザーへ追加インタビューを実施し、利用前UX・利用中UX・利用後UXを聞くことでユーザー体験設計時のインプット材料を整理しました。 【利用前UX】 ・サービスを知ってから導入するとしたらどんな流れで導入するのか ・こういったサービスや教材はどこで認知することが多いか ・認知した時に、どんなプロセスで検討するか ・導入する時の決め手は何か(期待値は何か) 【利用中UX】 ・全体感としてどのような利用の仕方をするか・いつ利用するか(曜日や時間帯など) ・誰が利用するか(本人、保護者、両方) ・利用するデバイスは何か(スマホ、PC、タブレット) ・どこで練習するか(家の中、家の前、公園など) ・練習する時にネックになりそうなことはあるか(端末など) 【利用後UX】 ・保護者として導入してよかったと価値を感じる要素は何か ・選手が価値を感じる要素は何か ワイヤーフレーム作成 プロダクトのワイヤーフレーム作成は自分が担当していますが、今回UXリサーチに時間をかけ様々なユーザーの声を聞いたことで、一つ一つの機能がなぜ必要なのか不要なのか言語化することが容易になりました。 本実装&リリース 本実装を終え、今年8月に無事プロダクトリリースを致しました。 UXリサーチを通常よりも念入りに実施したこともありリリースまでの期間は少し時間を要してしまいましたが、リリース後1ヶ月を迎えユーザー登録数も順調に推移しており、想定ペルソナのユーザーに利用いただいています。 また次のフェーズとしては、会員に対してインタビューを継続的に実施し実際の使い勝手について追加リサーチしていく予定です。 最後に LIFULL HOME'Sのプロダクト開発においても、UXリサーチは積極的に取り入れられており、リサーチ内容によって様々な検証が実施されています。 この1年様々なプロジェクトのUXリサーチに参画させていただく中で、改めてユーザーの行動や意識を理解することで、より良い体験を生み出せることを実感しました。 また、実際に自分のサービスの向こう側にいるユーザーと対話することでユーザーのためにもっと良いプロダクトにしなくてはという使命感も感じます。 これからも日常的にUXリサーチを取り入れながらプロダクトに愛を持って開発を続けていきたいと思います。 LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは!LIFULL HOME’S iOSアプリエンジニアの山川・佐藤です。 今回は、2023年の9月1日(金) 〜 9月3日(日)の3日間で開催された、iOSに関連した技術をコアテーマにしたテックカンファレンス「iOSDC Japan 2023」に参加してきました。この記事では、3日間で行われたセッションの中から我々の印象に残ったセッションやイベントの様子について振り返ります。 iOSDC Japan 2023について iOSDC Japanは2016年に初開催され、今年で8回目となるiOSエンジニア向けのテックカンファレンスになります。 iOSDC Japan 2023の公式サイトはこちら https://iosdc.jp/2023/ 本イベントのテーマは「コミュニケーション」であるということで、オフラインの会場ではスポンサー企業ブースでさまざまな企業の方と交流したり、セッションで登壇した人に質問したり議論ができる場が用意されています。今回からはポスターセッションも始まりました。 そして、コロナ禍中は様々な行動制限があってできなかったことが多かったそうですが、今年は「フルスペックなiOSDCが帰ってきた」ということで、ビールが飲み放題な懇親会も復活しました!🍺笑 このように、学べて楽しめる要素が盛りだくさんなイベントです! 印象に残ったセッション Appleにおけるプライバシーの全容を把握する Appleが近年力を入れているプライバシーに関して、Appleが考えるプライバシーの基本原則や開発者が対応しなければいけないことを一通りおさらいするといった内容でした。 例えば2021年には、ユーザの許可を得ずにユーザの行動を追跡することを禁止するポリシー「App Tracking Transparency(ATT)」が制定されました。これによって、アプリはユーザの行動を追跡するには予めユーザに取得する旨を伝えた上でトラッキングの許可をもらう必要が出てきました。また、WWDC2023ではXcode15が発表され、Privacy Manifestsが導入されます。この対応が2024年春までに必須になります。必要な対応の要点の一つにRequired reaspon APIというものがあり、アプリで利用するのに理由が必要なAPIがあればMainifestに設定しておかなければいけません。 このAPIにはUser DefaultsのAPIも含まれており、これを利用していないアプリはほとんどないだろうということで、弊社を含めた多くの企業で対応が求められそうです。早めのキャッチアップと対応が求められるので、随時関連情報は見ていきたいですね。(山川) speakerdeck.com 法改正を乗り越えるiOSアプリのリリース戦略 運営中のサービスにダイレクトな影響のある法改正が施行されることになった際のリリース戦略について、株式会社LUUPでの対応事例の紹介でした。 2023年7月1日に電動キックボードの走行ルールが大きく変わりました。法改正施行日が決まっている中で、対応項目の洗い出しや、ユーザーに新しい交通ルールを理解してもらう方法の検討、極力サービスを止めないためのスケジュール管理など、怒涛の半年間であったことが伝わってきました。 計画的に準備を進めた結果、法改正施行当日にリリース作業を行う必要もなく、見守るのみだったというお話が非常に印象的でした。 不動産業界でも、法律はもちろんのこと、公正取引委員会の表示規約が毎年改正されます。これに合わせて弊社のサービスでも対応が必要になってくるため、非常に参考になりました。(佐藤) speakerdeck.com SwiftUI + KMM 開発で見えたそれぞれの長所と短所 Kotlin Multiplatformを導入することでiOS / Androidのビジネスロジックを共通化し、UIレイヤーの実装のみを各OSに委ねるという開発方法が登場しました。 ※最近、タイトルにもある「KMM」という呼び方はKotlinを開発するJetBrains社より、公式に「KMP」であるとされたため、以降はKMPで統一します。 📣 Update on the name of Kotlin Multiplatform From now on, “Kotlin Multiplatform” (KMP) is the preferred term when referring to the Kotlin technology for sharing code, regardless of the combination of platforms being discussed. We are deprecating the “Kotlin Multiplatform… — Kotlin by JetBrains (@kotlin) 2023年7月31日 iOS側でKMPを導入する際にはいくつか課題があるとのことでした。その一つとして、KMPはObjective-Cのモジュールを生成するため、いくつかのSwiftの機能は利用できません。そのため、KMP側で複数のクラスを持つ階層構造のものを定義したとしても、Swiftで見ると深い階層は浅い階層のクラスのオブジェクトにまとめられてしまったりするそうです。とはいえ、他にも癖のある部分もあるものの、総合的に見ると開発体験は悪くないそうでした。 実は弊社でもKMPを導入してサービス開発し始めていたので興味深い内容だったため、貴重な事例を知れて非常に参考になりました。(山川) speakerdeck.com Human Interface Guidelinesから読み解く標準アプリの素晴らしい体験 AppleのHuman Interface Guidelines(以下、HIG)を読んだことはあるでしょうか?2023年6月に公式の日本語版も公開されましたが、全てを読むのはなかなか大変ですよね。そこで、既存のApple標準アプリからHIGの理解を深めてみようという内容でした。 例えば、設定アプリは項目が多いにも関わらず、一貫したパターンやグループ分けによって整理されています。常に前の画面に戻れるようになっていたり、UIがテキストサイズの変更に対応していたりと、様々な観点での工夫が施されています。標準アプリには、Appleの知見が凝縮されています。特に標準アプリのアップデートされた箇所は、新たな発見につながるため積極的に触っていきたいですね! エンジニアだけでなく職種の壁を超えて、HIGを読んだり、標準アプリを研究しながら、サービスにとっての最適なUIを考えていきたいと思いました。(佐藤) speakerdeck.com こういうのは標準APIでいいよね サービス内で利用している外部のライブラリ数が多かったので、不要なライブラリを削減してその結果どうなるのかを説明したセッションでした。そもそも利用されていない・利用用途が大きすぎる・標準APIで充分担保できるといった理由から、元は44あったライブラリを15にまで削減されていました。例えば非同期処理でPromiseKitやRxSwiftを使っている箇所も、近年だとCombineやSwift Concurrencyの標準APIで簡単に実装できるようになってきています。 ライブラリの棚卸しをすることでメンテナンス性が向上するだけでなく、ビルド速度やアプリのサイズも改善される副次的効果もあるので、継続的に外部ライブラリと標準の機能との比較をしてリファクタリングしていく視点を持つことは事業インパクトには繋がりづらく、地道な対応ではあるものの重要なのだと改めて感じました。(山川) speakerdeck.com SwiftUIに適した新アーキテクチャの導入に挑む SwiftUIを導入するためのリアーキテクチャの検討から導入までの軌跡と、新しいアーキテクチャ「SVVS」の紹介でした。 リアーキテクチャを実施するにあたり、技術的負債の洗い出し・課題とサービス戦略にあったアーキテクチャの検討・リアークテクチャの進め方の考察の3段階の準備が行われていました。この対応はiOSチームのメンバーで行い、共通認識を持つことを意識して進めていったそうです。 採用された「SVVS」は、Store、View、ViewStoreの3つのコンポーネントで形成されます。依存関係は、ViewからViewStateへの依存、ViewStateからStoreへの依存の一方向と非常にシンプルな構成です。データの流れもシンプルになり、データ不整合の発生を防止することも期待できます。 既知のアークテクチャにこだわらず、チーム内での課題や今後の運用のしやすさを考慮して新しいアーキテクチャを生み出す姿勢を見習っていきたいですね。(佐藤) speakerdeck.com イベントの雰囲気 トーク会場は4部屋あり、自由に行き来ができるようになっていました。 今回はオンラインでも生配信されており、なんと中にはオフラインで参戦しつつも別室のセッションを聴く強者までいました! トーク会場の様子 Day1、 Day2に行われたLTでは、会場でサイリウムが配られ、それを振りながら応援していたのでライブのような雰囲気で楽しかったです。 LTはサイリウムをみんなでフリフリ! スポンサーブースでは、各企業のiOSアプリや事業の紹介のほか、iOSエンジニアならではのソースコードレビューやUIKitとSwiftUIの利用状況に関するアンケート調査などを実施していました。それらの結果を見ながらワイワイ議論できるもの、オフラインならではの醍醐味ですね⭐️ また、スポンサー企業様から技術書やバスタオル、ルービックキューブ、知恵の輪、中濃ソース!?など、個性豊かなノベルティをたくさんいただきました。 スポンサー企業ブース さらに、会場には軽食やドリンクも充実しており、その周りでは登壇者の方々に質問したり、ポスターセッションに参加したりと、コミュニケーションが活発に行われていました。 ロゴ入りのお菓子も食べ放題! X(旧:Twitter)でも楽しそうな投稿をしている方がちらほら見受けられました🤩 とても楽しく、勉強になり、本当によいカンファレンスでした😊 ありがとうございます!来年も楽しみにしています #iosdc pic.twitter.com/eMXC2fH3UC — こばやしよしのり🍎iOSエンジニア転職・オリジナルアプリ開発スクール運営 (@yoshiii514) 2023年9月3日 高まってきたな…! #iosdc pic.twitter.com/Hxpirq2H6X — Roku🐉 (@66nylon_y) 2023年9月3日 #iosdc スポンサーstmn社のTUNAG iOSアプリはVIPER使ってるようでVIPER研究読本がおすすめとおっしゃってましたー。感謝ぁ! pic.twitter.com/4XKvhYLk0B — y.imajo (@yimajo) 2023年9月3日 まとめ たくさんのセッションを聴講して、各企業の取り組みや技術的なトレンドを肌で感じることができました。iOSアプリ開発は従来UIKitを用いた開発がメインでしたが、どの企業でもSwiftUIへの移行を開始したり、SwiftUIでゼロから開発を始める事例が多く、イベントを通してiOSの主流な採用技術も大きく変わってきていることを特に強く感じました。弊社iOSアプリチームでも今後SwiftUIがメインとなる実装がどんどん入ってくることが見込まれるため、今回得られた知見をもとに弊社でのアプリ開発に活かしていきたいです。 最後に、LIFULLでは一緒に働いていただける仲間を募集しています。単にサービス開発をするだけでなく、自分たちの知見を深め・スキルを伸ばす機会がたくさんある職場です。カジュアル面談も実施していますので、もし興味があればそちらもご覧ください。 ※記事執筆時点ではアプリのエンジニアも募集していますよ😊 hrmos.co hrmos.co
プロダクトエンジニアリング部の二宮です。 私たちは「 強い個人・最高のチームになることで、価値創造を加速させ続ける 」というビジョンを掲げ、LIFULLのサービスをプロダクト開発でリードできる強い組織を目指しています。そして「最高のチーム」を目指すための取り組みをいくつか行なっています。 その活動の一環として、ベトナムの開発拠点の LFTV(LIFULL Tech Vietnam) とのハンガーフライト(雑談会)を開催しました。この記事では、その開催の背景と目的についてご紹介いたします。 ハンガーフライトについて LFTVハンガーフライトの準備段階 当日の様子と振り返り まとめと展望 ハンガーフライトについて LIFULLのエンジニア組織では、ここ数年間コミュニケーション活性化のための草の根施策としてハンガーフライト(雑談会)を行なっています。以前このブログでも「 リモートワーク下でどうやって偶発的なコミュニケーションを生み出すか: Discordを使ったコミュニケーション(ハンガーフライト編) 」で紹介した通り、次のような問題意識から始まりました。 コロナ禍の影響でリモートワークが中心になり、自部署以外のメンバーと話す機会が激減したため、ちょっとでも話す機会を作ろうと思ったのがきっかけで、私が所属する部署でもZOOMを使ってハンガーフライトを始めてみました。毎週決まった時間に自部署以外のメンバー含む数名でおしゃべりをしていました。 コロナ禍が長期化し、リモートワークが常態化しました。そのことによって、会社でも他部署の人と交流する機会が減ったことに対して課題意識が強まってきました。そこで、自分たちの周りでしかやっていなかったハンガーフライトを、エンジニア組織全体でやってみてはどうだろうと思い立ち、実施することにしました。 この活動は今ではエンジニア組織の文化として定着しており、現在は数人のメンバーで、次のような運営をしています。 色々な人に参加してもらえるように企画をしっかり練り、みんなが参加したくなるようなゲストを招待する。 無理の無いペース(月1回)でコンスタントに開催する。 参加者からチャットや口頭で質問やコメントを拾って、できる限り双方向コミュニケーションにする。 形式は座談会形式や質問形式など様々。 気軽に参加できるように聞き専もOK。 そして、7月の会ではLIFULLのベトナムの開発拠点の LFTV(LIFULL Tech Vietnam) の方々をゲストにお迎えしました。この企画の背景として、「 LFTVおよびその他海外拠点との協力開発の推進 」がLIFULLの開発組織のテーマの一環として掲げられており、実際に以前より業務連携が増えています。また、以前は単なる「案件の委託先」のような捉え方もされがちだったのですが、徐々にプロダクトチームの一員として参加するようなスタンスにシフトしようとしています。 先に結果を述べると、多くのメンバーが参加した盛況な会となり、いくつか反省点はあったものの、また同じテーマで開催したい良い会になったと思います。 LFTVハンガーフライトの準備段階 今回は普段に比べてどんな会になるのか想像できなかったため、準備をしっかり行いました。具体的には次のような形でやってみることにしました。 特に日本との関わりの強く、日本語にも詳しいブリッジエンジニアの方々を呼ぶ。 他の方々には「来てくれたら嬉しい」と伝える。 そこでベトナムの開発文化・生活の様子を聞ける会にする。 日本とベトナムでそれぞれ相手への事前質問を用意し、交互にその話題を振る形でファシリテーションする。 LFTVの社長に就任された加藤さんがハンガーフライトの運営メンバーの中にいたこともあり、意外に両方の国での広報や連絡で苦しむことは少なかったです。少し先回りした話ですが、「現地のエンジニアに楽しんでもらえる内容になるか不安なので、まずは日本側にベトナムに親しみを持ってもらおう」という方向性だったのですが、この不安は的中せず、実際にはブリッジエンジニア以外の方々にも積極的に参加して頂けました。 準備したスライド 当日の様子と振り返り 結果として、日本とベトナムが合わせて57人も参加する過去最大の参加人数の会になりました。ベトナム側の開発者はおよそ70名で、かなりの割合の方にこの会に参加してもらえました。当初の目論見通り、ベトナムや日本の文化の話題が中心となる会になりました。 アンケート結果などを踏まえ、後日の振り返りでは次の点が良かったこととして挙げられました。 参加人数も多く、アンケートでは満足度が高かった。 ベトナム側で、事前にGoogle Meetsのエクステンションで英語の字幕が出るようなものを共有してくれていた。 日本の運営側は「Google Meetsの公式機能には無い」程度しか確認できていなかった。 日本語で会話したため、日本語の堪能なブリッジエンジニアエンジニア以外に伝わるか心配だったが、ある程度は理解できていたらしい。 参加者の自発的なサポートがありがたかった(後述)。 より改善できそうな点としては次のようなものがありました。 スムーズな進行のためには英語の対応がもう少し必要だった。スライドに英語の記載があるとよかった。 Googleアカウントの権限の違いのため、LFTVのメンバーがGoogle Calendarの登録ができなかった。 暗黙の前提が違って、LFTVメンバーが質問の意図にピンと来ていなかったものもあった。 自発的なサポートで「コメント欄で発言してもらって、それを翻訳して拾う」という形になったが、明示的にその形にするとスムーズに会話が進みそうだった。 当日参加できなかった人にも録画を共有するつもりだったが、Google Meetsの録画をうっかり忘れてしまった。 当日は予想を上回るベトナム側の開発者が集まりました。これは嬉しい反響ですが、当日用意したスライドやアンケートが日本語だけだったり、非日本語話者の考慮漏れが発生したことが反省点の一つになりました。当日、自発的に同時通訳に近いことをしていただけて、かなり助かりました。 内容にピンと来ていなかった質問としては、例えば「今まで参加した中で楽しかったプロジェクトの話を教えてください」というようなものがありました。これは「LFTVでは開発タスク単位で仕事を振られることが多く、(今では変わりつつあるものの)プロジェクト単位で関わることが少なかった」という理由もあったようです。ただ、当日はベテランのエンジニアがこうした事情を補足説明していただいて、それぞれで見えている景色の違いが分かるきっかけになったとも言えるかもしれません。 こうして書くと真面目な話が多かったように思われそうですが、日本のYouTuber(ヒカキンさん)の話になったり、ベトナムオフィス周辺の美味しい料理の話だったり、カジュアルな話題でも活発に盛り上がることが出来ました。 まとめと展望 以上が今回開催したハンガーフライトの報告です。「最高のチーム」を作るためには、それぞれが異なる文化のメンバーとの交流を深めることが重要で、そのための一つの取り組みとして参考にしていただけたら嬉しいです。 「またベトナムの開発拠点との交流会を行いたい」という声が多かったのも嬉しいです。次回は今回の反省点も活かしてよりスムーズに交流できるように改善を図っていきます。LIFULLにはマレーシアの開発拠点( LFTM )もあり、3ヶ国での交流会も企画したいと思っています。 LIFULLのエンジニア組織では「越境」という言葉がよく使われていて、現実にもより良いチームワークを築くための国や職種を越えた様々な取り組みが行われています。興味を持たれた方はぜひ採用ページもご覧いただければ幸いです。 hrmos.co hrmos.co
こんにちは! LIFULLエンジニアの吉永です。 普段はLIFULL HOME'SのtoC向けCRMチームにてエンジニアリングマネージャをやっています。 本日はチームでGitのコミットメッセージ書式を Conventional Commits に準拠するようにしてから得た知見を紹介したいと思います。 コミットメッセージに書式を導入することでどんなメリットがあるのか?導入前後でどんな変化があったのか?今後の展望についてご興味のある方に参考になれば幸いです。 アジェンダ Conventional Commitsとは? チームで導入するにあたってどんな工夫をしたか? 自身が率先して規約に準拠する 定期的に自身のコミットメッセージを振り返る時間を設けた 実際に運用してみて得た知見を仲間にも共有する時間を設けた 導入してみてどうか? リリースノートの内容を分かりやすく自動生成できるようになった コミットの粒度が個人でばらつきにくくなる レビューをしやすくなった feat系の同じ文脈のコミットはレビュー完了後はまとめた方がよいかも 今後の展望 コードリーディングがしやすくなる(と思っている) コミットtypeやscopeの内訳を集計できるようになる まとめ Conventional Commitsとは? 人間と機械どちらから見ても読みやすい形式のコミットメッセージ規約です。 公式サイトに分かりやすく概要がまとまっていますので、良かったらこちらも参照してください。 www.conventionalcommits.org 規約と言われると、少し躊躇される方もいるかもしれませんがそこまで複雑な規約ではありません。 よって、導入するにあたってのハードルはさほど高くなく、気楽に始められると思います。 基本形は下記の形になります。 <type>[optional scope]: <description> [optional body] [optional footer(s)] optionalの部分は必須ではないので、必要最低限の規約を満たす為には <type> と <description> を記載すれば良いです。 例えばAという新機能を追加したコミットがあるとします。 その時のコミットメッセージは下記のようになります。 feat: A機能を追加 また、上記機能をリリース後一定期間経過後にA機能のバグを修正した場合は下記のようになります。 fix: A機能で数値に変換できない文字列を入力した場合に例外が発生するバグを修正 こんな感じで、人間から見てもそのコミットでどんな変更がコードに加えられたのかが分かりやすい規約になっています。 公式サイト上で定義されている <type> はfixとfeatのみなのですが、私達のチームではこれらに加えてAngularの規約も取り入れて運用しています。 github.com チームで導入するにあたってどんな工夫をしたか? 自身が率先して規約に準拠する まずは導入しようというチームに提案した私自身がきちんと規約を理解し、自身が行うコミットコメントを規約に準拠したものにしました。 定期的に自身のコミットメッセージを振り返る時間を設けた GitHubの検索APIを利用して、過去1週間以内のチームメンバーそれぞれのコミットコメント一覧を週一で開催しているチームの定例時間内で規約に準拠したコメントになっていたかを確認するようにしました。 最低でも1週間に一度は振り返る機会を設けたことで、徐々に規約に従うことが当たり前になっていく効果があったと今は思います。 実際に運用してみて得た知見を仲間にも共有する時間を設けた 得た知見もチームの定例で共有する時間を設けました。 このコミット <type> はrefactorかfixかchoreか分類に少し迷った、 <scope> をどこで切るか少し迷った、など実際に利用し始めて気づく疑問も数多くあったので、様々な事例を共有することで、次回からの指針になったり徐々に効率的になっていくと思っています。 導入してみてどうか? リリースノートの内容を分かりやすく自動生成できるようになった 私達のチームではリリース時に セマンティックバージョニング 形式に準拠したタグをインクリメントして自動生成していました。 約2年前に自前で作成したGitHub Actionsで行っていましたが、メジャー、マイナー、パッチをインクリメントする際のアルゴリズムがいまいちで、全ての場面においてチームメンバー全員が納得できるバージョニングになっていたかと言われると少し怪しい出来でした。 そんな中、チームのメンバーが下記のGitHub Actionsを探してきてくれ、Conventional Commitsに準拠していれば、タグのインクリメントとリリースノートも自動で生成してくれるという、既存のものよりも優れたものでした。 github.com 導入に当たってのハードルも低かったので早速採用して、運用を開始しました。 こちらのGitHub Actionsは個人的にも非常に気に入っており、Conventional Commitsに準拠することで得られる具体的なメリットとして、チーム内外にも共有しました。 コミットの粒度が個人でばらつきにくくなった Conventional Commitsを採用し、オプションである <scope> もなるべく含めようというローカルルールを採用したところ、コミットの粒度が個人でばらつきにくくなりました。 私達のチームのプロダクトではクリーンアーキテクチャを採用しているのですが、 <scope> にクリーンアーキテクチャの各レイヤー名( feat(repository):hoge とか feat(domain):fuga みたいな感じです)を入れて運用してみたところ、おのずと一つのコミットでの変更箇所が狭まったからだと思います。 また、レビューでもらった指摘を修正した後も、今まではついつい下記のようなコミットメッセージにしてしまっていました。 レビュー指摘修正 このコミット履歴は後でコードを読み返す時にはノイズにしかならないので、本来この変更を一緒に含めるべきコミットと統合すべきという意識にConventional Commits採用後は変わりました。 具体的には下記のようなコミット履歴になった場合は、 1 feat: A機能を追加 2 test: A機能のテストコードを追加 3 fix: A機能実装部分に対するレビュー指摘修正 下記のように1のコミットに3のコミットを統合するべきですね。 1 feat: A機能を追加 2 test: A機能のテストコードを追加 このようにコミット履歴をマージ前であれば、本来あるべき履歴へマージ前にリベースするということが自然と浸透しました。 レビューをしやすくなった コミットの粒度がばらつきにくくなったことで、レビューもコミット単位で行いやすくなり、この変更は何で行ったのか?をレビューアが理解しやすくなったと思います。 結果として、レビューがしやすくなったと感じる人が多く、今後も継続していこうと思える一つの要因になっています。 feat系の同じ文脈のコミットはレビュー完了後はまとめた方がよいかも レビューしやすくなったと感じる件と少し関りがあるのですが、私達のチームでは先述したようにクリーンアーキテクチャのレイヤーに沿った <scope> でコミットを分けて運用してました。 先日、Conventional Commitsを採用してから初めて大規模な実装を行い、上記の運用でコミット粒度を分けていたのでレビューは非常にやりやすかったです。 ただし、リリースノートにのるfeat一覧としてみると、正直このリリースで何の機能をリリースしたのか?が細分化されすぎているため、初見では分かりづらかったです。 コミット履歴としては下記のような感じになっていました。 feat(domain): A機能用のエンティティと変換処理を実装 test(domain): A機能用のエンティティの変換処理のテストを実装 feat(repository): A機能用のhogehogeデータを外部から取得する処理を実装 test(repository): A機能用のhogehogeデータを外部から取得する処理のテストを実装 refactor(repository): A機能用に追加した処理と既存処理とで共通化できる個所をまとめる feat(usecase): A機能用のインタラクターを実装 test(usecase): A機能用のインタラクターのテストを実装 feat(controller): A機能用のコントローラーを実装 test(controller): A機能用のコントローラーのテストを実装 コミット上ではコントローラー、リポジトリ、ユースケース、ドメインそれぞれ分けた方がレビューはしやすかったのですが、リリース後しばらくたってからコードリーディングをするときのコミット履歴としては下記が良いのではないか?とチームのメンバーと話していて、意見が出ました。 feat(api): A機能を追加 refactor(repository): A機能用に追加した処理と既存処理とで共通化できる個所をまとめる 結論、feat系はレビュー完了後は同じ機能に関するものであれば一つにまとめ、それら以外のものはそのまま個別のコミットのままで良いかもとなりました。 時間が経過して、後からコードリーディングする際のコミットコメントしても feat(api): A機能を追加 の方が、「このソースはA機能追加時に改修されたものなのか」と理解しやすく、その際にクリーンアーキテクチャのレイヤーの情報はいらないだろうからという理由です。 ※この辺りは、私達の出した結論が必ずしも正解とは限らないと思いますので、ケースバイケースかなと思いますが、一つの知見として参考になれば幸いです。 今後の展望 コードリーディングがしやすくなる(と思っている) Conventional Commitsに準拠することで、コミット粒度や形式がある程度まとまっていることから、時間が経過した後で該当個所のコードは何の作業でどんな理由が合って改修されたのか?をコミット履歴から情報を得やすくなり、結果としてコードリーディングがしやすくなると思われます。 また、自身だけでなく第三者にとっても同様の効果はあると思うので、効果を実感できる時期が来ることを今から楽しみにしています。 コミットtypeやscopeの内訳を集計できるようになる 既に少しだけ運用を開始したのですが、ある期間にチームで行った開発の割合は新機能開発系とリファクタリング系どんなバランスだったか?そのバランスは適切か?などをConventional Commitsに準拠していくことで定量的に集計することができるようになります。 今まではプロジェクト管理ツールへ日々の工数入力で行っていた集計をGitのコミット履歴の観点からも分析できるようになり、チームとしてより正確なステータスを把握しやすくなることを期待しています。 下のグラフは私達のチームの2023/06~2023/07のConventional Commitsの <type> を集計した結果です。 現時点では、この割合が健全な状態なのか?はまだあまり良く分かっていませんが、いずれデータが蓄積されて期間比較できるようになっていくことで、より活用の幅が広がっていくと思います。 まとめ Conventional Commitsについて、採用後しばらく運用してみてチームで得た知見について紹介しました。 チームで導入するには少しハードルが高いなど、それぞれ事情はバラバラかと思いますが、そのような状況の場合に私からお勧めするのは、まずは自分ひとりだけでもいいから準拠してみることだと思います。 私自身、実際に自分で採用してみて得たことをメンバーにも共有したり、Qiitaに個人として得た気づきをアウトプットしたりして理解を深めていったことで、良さに気づけた気がします。 qiita.com まずはスモールスタートでやってみることで得られることもあると思いますので、是非お試しください! 最後に、LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
アクセシビリティ推進グループの中島です。 過去同グループの発信した記事の中で、弊社がJavaScriptライブラリとしてStimulusを採用していると何度か紹介させていただきました。 www.lifull.blog 今回はその中で、どんな粒度で、どんな機能のStimulusコントローラを書いているのか少しばかり紹介しようと思います。 (全て書くととても長くなってしまうのでvol.1としてますが、vol.2以降を書くかどうかは今の所わかりません。) button_controller.js 適用例 参考 disclosure_controller.js 適用例 参考 inlay_controller.js 適用例 参考 anchor_sinon_controller.js 適用例 tabs_controller.js 適用例 参考 最後に button_controller.js まずは最頻出のコントローラです。 このコントローラは37signalsから拝借したコントローラの一つです。 button_controller.jsの実装を見る export default class extends Controller { keyboardClick( event ) { if ( [ 'Enter' , ' ' ] .includes( event .key)) { event .preventDefault(); event .stopPropagation(); this .element.click(); } } } このコントローラは要素にボタンとしての機能を簡単に提供するために使っているものです。 ボタンは通常、スペースキーやエンターキーといったキーボード操作でも操作可能であるべきです。 button要素で実装していればこのキーボード操作は標準で提供されるので、こちらがコードを書く必要はないのですが長く運用されたサイトでは往々にしてdivやaタグで実装されたボタンが目立ちます。 そういったものを要素自体を差し替えずに正しくボタンのように振る舞わせるのに役立ちます。 適用例 <!-- before --> < div > click me </ div > <!-- after --> < div # フォーカス可能に tabindex = "0" # 支援技術にボタンであることを教える role = "button" data -controller= "button" # スペースキーとエンターキーをクリックイベントとして処理する data - action = "keydown->button#keyboardClick" > click me </ div > 参考 www.w3.org disclosure_controller.js 次によく使うコントローラはディスクロージャー(開閉UI)を実現するコントローラです。 disclosure_controller.jsの実装を見る export default class extends Controller { static classes = [ 'collapsed' ] ; toggle(evt) { evt?.preventDefault(); if ( this .isExpanded) { this .hide(); return ; } this .show(); } show() { this .control.classList.remove(... this .hiddenClasses); this .element.ariaExpanded = 'true' ; } hide() { this .control.classList.add(... this .hiddenClasses); this .element.ariaExpanded = 'false' ; } get hiddenClasses() { return this .hasCollapsedClass ? this .collapsedClasses : [ '!hidden' ] ; } get isExpanded() { return this .element.ariaExpanded === 'true' ; } get control() { return document .getElementById( this .element.getAttribute( 'aria-controls' ).trim() ); } } 最近ではdetails要素で実現可能なディスクロージャーですが、例によって過去に作られたディスクロージャーはdiv等で作られ、ディスクロージャーとしての機能要件を満たしてないものが多くあります。 ディスクロージャーはトリガーである要素がフォーカス可能で、ボタンとして振る舞い、視覚だけでなく支援技術等でも開閉状態を把握できるように作られるべきです。 このコントローラを使うとそういった部分を担保したディスクロージャーを簡単に実装することができます。 適用例 <!-- before --> < div > < p > ほげほげについて </ p > < div class = "!hidden" > ほげほげはふがふがのことで 一般的にぴよぴよとも 呼ばれています。 </ div > </ div > <!-- after --> < div > < p # フォーカス可能に tabindex = "0" # 支援技術にボタンであることを教える role = "button" # 関連要素のidを指定することで支援技術でジャンプ機能等を提供する aria-controls = "content" # 開閉情報を支援技術に公開する aria-expanded = "false" data -controller= "button disclosure" data - action = "keydown->button#keyboardClick click->disclosure#toggle" > ほげほげについて </ p > < div id = "content" class = "!hidden" > ほげほげはふがふがのことで一般的にぴよぴよとも呼ばれています。 </ div > </ div > 参考 www.w3.org inlay_controller.js 主にウェブで見かけるUIに「もっと見るボタン」を押したら、ボタンは消滅しつつ、隠れた要素が表示されるというものがあります。 このパターンはARIA Authoring Practices Guide等でベストプラクティスが紹介されているわけではないのですが、我々はそういった振る舞いをinlayとよび、対応するコントローラを用意しています。 inlay_controller.jsの実装を見る import { tabbable } from 'tabbable' ; export default class extends Controller { show() { this .nextContent.classList.remove( '!u-hidden' ); this .element.classList.add( '!u-hidden' ); this .firstTabbableItem.focus( { preventScroll: true } ); } get nextContent() { let control = this .element.getAttribute( 'aria-controls' ); return document .getElementById(control); } get firstTabbableItem() { return tabbable( this .nextContent) [ 0 ] || this .nextContent; } } このパターンは押したボタンそのものが消滅することで、フォーカスが失われる(bodyに戻ってしまう)問題に対処するため、表示されたコンテンツ、あるいはそのコンテンツ内の最初のフォーカス可能な要素にフォーカスを移すことが重要と考えています。 そういったことが考慮されてない古いコードをこのコントローラに差し替えることで簡単にキーボード操作の要件を満たすことができるようになります。 適用例 <!-- before --> < ul > < li >< a href = "..." > a </ a ></ li > < li >< a href = "..." > b </ a ></ li > < li >< a href = "..." > c </ a ></ li > </ ul > < div > もっと見る </ div > < ul class = "!hidden" > < li >< a href = "..." > d </ a ></ li > < li >< a href = "..." > e </ a ></ li > </ ul > <!-- after --> < ul > < li >< a href = "..." > a </ a ></ li > < li >< a href = "..." > b </ a ></ li > < li >< a href = "..." > c </ a ></ li > </ ul > < div # フォーカス可能に tabindex = "0" # 支援技術にボタンであることを教える role = "button" # 開閉可能なUIであることを支援技術に公開する aria-expanded = "false" aria-controls = "more-content" data -controller= "button inlay" data - action = "keydown->button#keyboardClick click->inlay#show" > もっと見る </ div > < ul class = "!hidden" id = "more-content" > < li >< a href = "..." > d </ a ></ li > < li >< a href = "..." > e </ a ></ li > </ ul > 参考 accessible-usable.net anchor_sinon_controller.js 主にサイトアナリティクスの文脈で流入元を特定する目的でリンクにパラメータを付与することがよくあります。 ただ、googlebot等のクロールバジェットの消費を抑制する観点で、「パラメータ付与はJavaScriptでクリック時に行ってください」ということが要件に盛り込まれることが稀にあります。 目的は違えどGoogleAnalyticsのLinker機能などが類似の動きをしますね。 そういった時のためにURLの差し替えをさっさと行えるコントローラを用意しています。 anchor-sinon_controller.jsの実装を見る export default class extends Controller { static values = { url: String } ; trick() { this .element.setAttribute( 'href' , this .urlValue); } } 私自身SEOの専門家ではないのでどの程度効果があるのかは詳しく分かってませんが、汎用的なURL差し替えを実現できるようになっています。 適用例 <!-- before --> < a href = "/path/to/page/?from=xxxx" > some page </ a > <!-- after --> < a href = "/path/to/page/" data -controller= "anchor-sinon" data - action = "click->anchor-sinon#trick" data -anchor-sinon- url - value = "/path/to/page/?from=xxxx" > some page </ a > tabs_controller.js 複数のコンテンツをタブ付きインタフェースとして表現するケースがたまにあります。(特にPCサイトに多いような気がします。) 弊社でも家賃相場情報を間取りタイプごとにグルーピングしてタブで切り替えて閲覧できるといった機能があったりしますが、これはそういったUIパターンに適合するコントローラです。 tabs_controller.jsの実装を見る export default class extends Controller { static targets = [ 'tab' , 'tabpanel' ] ; static values = { index: { default : 0, type: Number } } ; select(evt) { let tab = evt.currentTarget; let index = this .tabTargets.indexOf(tab); this .indexValue = index; } selectAt(index) { this .indexValue = index; } next(evt) { evt.preventDefault(); this .indexValue = this .indexValue === this .lastIndex ? this .indexValue : this .indexValue + 1; this .tabTargets [this .indexValue ] ?.focus( { preventScroll: true } ); } prev(evt) { evt.preventDefault(); this .indexValue = this .indexValue === 0 ? 0 : this .indexValue - 1; this .tabTargets [this .indexValue ] ?.focus( { preventScroll: true } ); } first(evt) { evt.preventDefault(); this .indexValue = 0; this .tabTargets [this .indexValue ] ?.focus( { preventScroll: true } ); } last(evt) { evt.preventDefault(); this .indexValue = this .lastIndex; this .tabTargets [this .indexValue ] ?.focus( { preventScroll: true } ); } get lastIndex() { return this .tabTargets.length - 1; } indexValueChanged(current, prev) { let tabs = this .tabTargets; let tabpanels = this .tabpanelTargets; tabs [ prev ] ?.setAttribute( 'aria-selected' , 'false' ); tabs [ prev ] ?.setAttribute( 'tabindex' , '-1' ); tabpanels [ prev ] ?.classList.add( '!hidden' ); tabs [ current ] ?.setAttribute( 'aria-selected' , 'true' ); tabs [ current ] ?.setAttribute( 'tabindex' , '0' ); tabpanels [ current ] ?.classList.remove( '!hidden' ); } } タブ付きインタフェースはタブシーケンス中に一つだけタブストップを持つといった要件や、左右キーでタブ切り替えができる、 Home/Endキーでタブの先頭、末尾切り替えができるといった要件が存在します。 そういったタブナビゲーションをこのコントローラを利用すれば簡単に実現することができます。 適用例 <!-- before --> < h2 > 〜区の家賃相場 </ h2 > < div > < ul > < li > 1人暮らし向け </ li > < li > 2人暮らし向け </ li > < li > ファミリー向け </ li > </ ul > < div > 1人暮らし向け物件の家賃相場 ...円 </ div > < div class = "!hidden" > 2人暮らし向け物件の家賃相場 ...円 </ div > < div class = "!hidden" > ファミリー向け物件の家賃相場 ...円 </ div > </ div > <!-- after --> < h2 id = "tab-label" > 〜区の家賃相場 </ h2 > < div data -controller= "tabs" > < ul role = "tablist" aria-labelledby = "tab-label" > < li id = "tab1" # 初期選択の要素だけタブシーケンスにタブストップを設定 tabindex = "0" role = "tab" # タブが選択中であることを支援技術に公開 aria-selected = "true" data -tabs- target = "tab" data - action = " keydown.left->tabs#prev keydown.right->tabs#next keydown.home->tabs#first keydown.end->tabs#last click->tabs#select" > 1人暮らし向け </ li > < li id = "tab2" tabindex = "-1" role = "tab" aria-selected = "false" data -tabs- target = "tab" data - action = " keydown.left->tabs#prev keydown.right->tabs#next keydown.home->tabs#first keydown.end->tabs#last click->tabs#select" > 2人暮らし向け </ li > < li id = "tab3" tabindex = "-1" role = "tab" aria-selected = "false" data -tabs- target = "tab" data - action = " keydown.left->tabs#prev keydown.right->tabs#next keydown.home->tabs#first keydown.end->tabs#last click->tabs#select" > ファミリー向け </ li > </ ul > < div role = "tabpanel" aria-labelledby = "tab1" data -tabs- target = "tabpanel" > 1人暮らし向け物件の家賃相場 ...円 </ div > < div role = "tabpanel" class = "!hidden" aria-labelledby = "tab2" data -tabs- target = "tabpanel" > 2人暮らし向け物件の家賃相場 ...円 </ div > < div role = "tabpanel" class = "!hidden" aria-labelledby = "tab3" data -tabs- target = "tabpanel" > ファミリー向け物件の家賃相場 ...円 </ div > </ div > 参考 www.w3.org 最後に 今回は記事の物量の関係上、よく使う5つのコントローラに絞って紹介しました。 他にもダイアログ関連のコントローラ、コンテンツの非同期読み込みコントローラ、コンボボックスを実現するコントローラ等、さまざまなコントローラがあるのでvol2があればそこで紹介しようと思います。 最後までお読みいただきありがとうございました。LIFULL では共に働く仲間を募集しています! hrmos.co hrmos.co
こんにちは。プロダクトエンジニアリング部の武井です。 普段はLIFULL HOME'Sの賃貸領域の開発をしています。 現在、LIFULL HOME'Sの賃貸領域ではシステムの基盤刷新を行っています。 詳細については 以前の記事 をご覧ください。 今回はこの基盤刷新に伴い、新たなABテスト実施システムを構築したので、その概要を紹介したいと思います。 LIFULL HOME'S におけるABテスト LIFULL HOME'Sでは、ユーザーにとってより使いやすいポータルサイトを目指し日々UI/UXの改善を行っています。 その効果測定の手段としてABテストを取り入れており、常時いくつものABテストを実施しています。 旧基盤でのABテストのしくみはシステム立ち上げ当初に実装されて以来、現在まで10年以上に渡って使われ続けています。 今回の基盤刷新に伴い、このABテストを実施するしくみを新基盤上に構築し直す必要がありました。 ABテストのシステムは旧基盤と同様に今後10年以上使われ続ける可能性があり、非常に影響度の大きいシステムになることが予想されます。 そのため今後の開発効率を左右する重要な開発プラットフォームを作るという意識で開発に臨みました。 インフラ層での振り分け システムの構築にあたって、A/Bのユーザーをどのように振り分けるかをまず考えました。 最初に出た案は、KubernetesにおけるIstioの加重ルーティングを利用したインフラ層での振り分けです。 IstioはKubernetes上のトラフィック管理などを担うservice meshです。LIFULL HOME'Sの多くのプロダクトはKubernetesの共通基盤上で動作しており、service meshとしてIstioを利用しています。 このIstioのVirtualServiceという機能を使うことでユーザーの振り分けができます。 たとえば、以下のようにmanifestを記述すると v2 のバージョンに 25% 、 v1 のバージョンに 75% のユーザーをそれぞれ流すことができます。 apiVersion : networking.istio.io/v1alpha3 kind : VirtualService metadata : name : reviews-route spec : hosts : - reviews.prod.svc.cluster.local http : - route : - destination : host : reviews.prod.svc.cluster.local subset : v2 weight : 25 - destination : host : reviews.prod.svc.cluster.local subset : v1 weight : 75 Istio VirtualService しかしこの方法では、複数のテストを同時に並行して実施する際、運用上の問題が予想されました。 たとえばテスト1とテスト2を同時並行すると、A/Bそれぞれの掛け合わせで以下の4種類のバージョンのPodが必要になります。 テスト1A * テスト2A テスト1A * テスト2B テスト1B * テスト2A テスト1B * テスト2B このようにテストの同時実施数を増やしていくと、O(2 N )で用意するべきPodの種類が増えていき、管理が非常に困難になることが予想されます。 さらに一つのテストについて振り分けのパターンがA/B/C...と3パターン以上になることもあり、こうなるとより複雑性が増します。 このような運用上の懸念から、Istioを用いたインフラ層での振り分けは断念しました。 アプリケーション層での振り分け 他にも複数の案を検討した結果、インフラ層での振り分けは行わず同一のバージョンのPodでリクエストを受けた後、アプリケーションの中でA/Bのユーザーを振り分けコードレベルで処理を分岐させる案に落ち着きました。 詳細は割愛しますが、コードのイメージは以下のようになります。 システム側で設定ファイルとユーザー情報の突き合わせを行い、A/Bテストの情報が集約された ab というオブジェクトを生成します。このオブジェクトが持つ isB() のようなメソッドを用いてコード上でパターン分岐を行う形になります。 if ( ab. get( TEST_1_ID ) .isB ()) { // テスト1でのBパターンの処理 ... return } // テスト1でのAパターンの処理 ... この場合、アプリケーションのバージョン自体は一つで済みますが、if文で分岐を行う必要があります。複数のテストを同時並行するとより分岐条件が増え、コードの複雑性が高くなりますが、この複雑性を現時点では許容することにしました。 ABテストを実施する以上、必ずどこかでユーザーを振り分けるための複雑性を引き受ける必要があります。この複雑性をインフラ層という離れた場所ではなく、アプリケーションのコードという我々が普段開発していて変更しやすい場所に持ってきたことは暫定的に良い選択だったと感じています。 使いやすいインタフェースの追求 新システムを構築する上で、このシステムを利用する際の開発者体験や使いやすさを意識してインタフェースをデザインしました。 以下は開発したシステムにおける設定ファイルの一例です。 このように設定を定義すると、アクセスしてきたユーザーを自動的に振り分けるシステムになっています。 タイトルや資料へのリンクなども含めて宣言的に定義することで一目してテスト内容を把握できるようにしています。 export const abTestConfig: AbTestConfig = { [ AB_TEST_IDS.sampleProject ] : { title: '【賃貸事業部】サンプルテスト' , specUrl: [ 'https://jira.jp/wiki/XXXXX' , 'https://docs.google.com/spreadsheets/YYYYY' , ] , startDatetime: '2023-08-01 11:00:00' , endDatetime: '2023-08-14 13:30:00' , classification: [ { patternValue: 'a' , weight: 50 , measurementValue: 'sample_test_a' , } , { patternValue: 'b' , weight: 50 , measurementValue: 'sample_test_b' , } , ] , beforePattern: 'a' , afterPattern: 'b' , } , } ; この設定はTypeScriptのObjectとして定義し、型を当てるようにしています。 また、この設定ファイルに対するlinterを実装し、細かい設定内容もチェックしています。 これらのチェック機構によって、PullRequest作成時点で設定漏れや間違いに気付けるようになっており、誤った設定でデプロイしてしまうことを防げます。 このほかにも、利用開始方法や注意点などを記載した詳細なドキュメントを作成したり、テスト実施状況を可視化する簡単なダッシュボードを作成したりしました。 開発工数の削減 このシステムを構築するにあたって、旧基盤の単純な模倣ではなく少しでも開発工数を削減することも意識しました。 なぜなら今回構築するシステムは今後長い間使われる可能性があり、わずかな違いでもレバレッジが効き、結果的に大きな効果をもたらすためです。 旧基盤でのABテスト実施システムを分析したところ、振り分け部分と分析部分が連動していないことが改善点として挙がりました。振り分け部分とはユーザーをA/Bのように振り分ける部分、分析部分は振り分けた後のユーザーがコンバージョンに至ったかなどの分析用メトリクスを送信する部分を指します。旧基盤ではこれらが連動しておらず、テストを実施するたびにメトリクスを送信する処理を書く必要がありました。 そこで新システムでは設定ファイルに分析用の項目 measurementValue を設け、これを読み取って自動的にメトリクスを送信する設計としました。 export const abTestConfig: AbTestConfig = { [ AB_TEST_IDS.sampleProject ] : { title: '【賃貸事業部】サンプルテスト' , ... classification: [ { patternValue: 'a' , weight: 50 , measurementValue: 'sample_test_a' , // この値を自動的に送信する } , { patternValue: 'b' , weight: 50 , measurementValue: 'sample_test_b' , // Bパターンのときはこちら } , ] , ... } , } ; この改善によって、実装や確認の工数削減、実装漏れの防止などにつながります。 まとめ 普段はユーザーが直接触る機能の開発が中心となるため、今回のように社内の開発者に向けたシステムの開発は新鮮で貴重な経験ができたと感じています。最近ではこのABテストの実運用が始まっており、前よりも使いやすい!という声をいただいています。 このように、LIFULL ではユーザーや社内の開発者など多様なステークホルダーの体験を考えたものづくりを行い、ともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは、AI戦略室の清田です。 2023年3月に岐阜で開催された DEIM 2023 に続き、6月に熊本で開催された人工知能学会全国大会(JSAI 2023)に参加いたしました。 www.ai-gakkai.or.jp 今年は、恒例の「不動産とAI」をテーマとした企画セッションにも関わりましたので、その内容も合わせて報告します。 生成AIブームがAI研究コミュニティにもたらした影響 今回のJSAI 2023は、過去最高となる3,566名もの参加者があり、大盛況でした。 2022年に相次いで登場した生成AI技術を用いたサービス(Stable Diffusionなどの絵画生成、ChatGPTなどの自然言語生成)の影響で、AI研究が改めて大きな注目を集めていることを感じます。 ディープラーニングの登場などを受けて10年前ほどから始まった 第3次AIブーム は、ある程度の落ち着きを見せていましたが、「冬の時代」を迎える前に、第4次ブームが始まったのかもしれません。 生成AI技術は、大きな期待を集めている一方、著作権の問題、情報の信頼性の問題など、さまざまなリスクが指摘されてもいます。 私がオブザーバーとして関わっている 人工知能学会 倫理委員会 でも、マスメディアからの取材依頼などが多数入っている状況を受けて議論を行い、「人工知能学会としての大規模生成モデルに対してのメッセージ」を公表しました。 www.ai-gakkai.or.jp 本大会では、「日本は生成AIを起爆剤にできるのか」をテーマとした緊急企画セッション、「アートにおいても敗北しつつある人間 〜人の美意識もAIにハックされるのか?〜」と題した倫理委員会主催セッションなどが実施され、多くの参加者を集めていました。 その様子は、NHKなどのメディアでも報道され、生成AIへの社会からの関心の高さを実感しました。 www3.nhk.or.jp 生成AI技術をめぐっては、大規模言語モデルなどの技術面だけでなく、倫理や法律、収益の配分、人の創造性など、さまざまな課題が入り交じっていて、正しい理解に基づいた議論がなかなか成立しづらいように思います。 正しい理解に基づいた生産的な議論を成り立たせる上で、学会というコミュニティの役割に、大きな期待が寄せられています。 こうした役割を果たす上で、生成AIの開発に関わる各企業がより主体的に関わることが求められているように感じます。 企画セッション「少子高齢化と「住まい」産業のDXを考える」 LIFULLでは、名古屋で開催されたJSAI 2017から、継続的に「不動産とAI」をテーマとしたセッションを、同じ関心を持つ大学や企業の方々と共同で開催しています。 今回は、「少子高齢化と「住まい」産業のDXを考える」と題した企画セッションを開催しました。 少⼦⾼齢化の進展に伴う「住まい」の課題に取り組まれている方々をお招きし、さまざまな事例を共有いただきました。 不動産、AIにかかわる方々だけでなく、これからの時代に求められている新たな「住まい」の価値を追求されている方や、人々の行動データをさまざまな社会課題の解決に活用する取り組みをされている方も交えた議論を行うことで、「不動産とAI」の研究領域をさらに広げることを目指しました。 sites.google.com LIDAR測量、簡易BIMによるリノベ・維持管理へのデータ活用 スターツ社の清水哲志様・城戸祐一様からは、既存建築物のリノベーションにかかる膨大な手間を、スマートフォンに搭載されたLiDAR(ライダー)機能を活用して作成された簡易BIMモデルを用いて削減する取り組みなどを発表いただきました。 (国土交通省の「令和4年度住宅生産技術イノベーション促進事業」として、LIFULLも開発に協力しています) xtech.nikkei.com リノベーション事業の最前線の話題 福岡を中心に築古ビルのリノベーションを多数手がけられているスペースRデザイン代表の吉原勝己様からは、「共感価値」によって、築数十年の築古ビルの資産価値を向上するという興味深い事例を発表いただきました。 www.reizensou.com www.space-r.net これらの事例は、本セッションに参加されていたAI研究者の方々からも驚きをもって受け止められました。 地⽅⾃治体における⾼齢社会デザインへのビッグデータ活⽤ソリューション事例 ヤフー社でビッグデータを活用したデータソリューション事業を担当されている大屋誠様からは、 DS.INSIGHT を用いた分析事例をご発表いただきました。 パネルディスカッション 皆さまからのご発表を受けて、以下のようなテーマで多岐にわたる議論を行いました。 既存ストックの価値を高めるためにAI技術が果たせる役割 2025年問題にどのように向き合うべきか 九州をフィールドとした協働の可能性 今回の議論は非常に刺激的で、お互いに接点の少なかった領域の人々どうしが対話を重ねることが、「住まい」産業の形を大きく変革するきっかけになるのではという大きな可能性を感じました。 来年に浜松で開催予定のJSAI 2024でも、引き続き「不動産とAI」をテーマとしたセッションを企画予定です。 多くの方々のご参加をお待ちしております! おわりに LIFULLでは、生成AI技術を活用したプロダクトの開発を加速するため、専門部署として ジェネレーティブAIプロダクト開発室を2023年5月に設置しました 。 www.lifull.blog AI戦略室も引き続き研究開発に取り組んでおり、今後はジェネレーティブAIプロダクト開発室とともに連携して生成AIの活用に取り組んでいく予定です。 ChatGPTを活用した「 AIホームズくん LINE版 」、ChatGPTプラグインなど、国内不動産ポータルとして初めてとなるサービスを続々とリリースしています。 lifull.com lifull.com 生成AI技術は強力な技術であるがために、ユーザーにとって多くの利便性をもたらす一方、使い方によってはユーザーの利益を損ねるリスクもあります。 AI戦略室では、『創造と革進で喜びを届ける』というチームビジョンを掲げ、AI技術シーズの創出と活用を通じて、多様な社会課題や事業課題の解決に挑戦しています。 今回のJSAI 2023への参加を通じて得られた生成AI技術をめぐるさまざまな課題を踏まえつつ、社会課題や事業課題の解決につながる活用のあり方を考えてまいりたいと思います。 AI戦略室では、共に成長しながら働く仲間を募集しています。ご興味をお持ちの方のご応募をお待ちしております! hrmos.co
KEELチーム の相原です。 今回は流行に乗ってLLM(Large Language Models)の話です。 とは言うもののLLMは単なる流行ではなく新たなパラダイムと言っていいでしょう。 解けるタスクの幅は未だ底が知れず、機械学習とは求められる能力も多少異なることからソフトウェアエンジニアである私の周りでも大きな変化が起きていると感じます。 LIFULLでもこの変化をコーポレートメッセージである「あらゆるLIFEを、FULLに。」の実現に繋げるべくジェネレーティブAIプロダクト開発室が新設され、 一発目としてLIFULL HOME'SのChatGPT Pluginをリリース しました。 さて、我々KEELチームはKubernetesベースの内製PaaSであるKEELを開発・運用するチームです。 www.lifull.blog 我々にはプラットフォームというレバレッジの効くソフトウェアを通して「あらゆるLIFEを、FULLに。」の実現にスケーラブルに貢献する責任があります。 これまでKEELでは コードジェネレータによるPaaS体験 を軸に、可観測性やセキュリティ, デリバリーパイプライン, MLOpsから各種データストア・認証基盤に加えてアプリケーションの参考実装の提供に至るまで必要なことはすべてやってきました。 LLMという新たなパラダイムでも同様にプラットフォーマーとしてやれることがあるはずです。 Platform Engineeringからのアプローチということで、社内でのLLM活用を促進するためにこれまでやってきたことを紹介します。 ベクトルデータベースの提供 Redis Qdrant Embeddings APIのキャッシュプロキシの提供 ChatGPT Retrieval Pluginの構築 コマンドラインツールでのLLM活用 Conventional Commits形式のコミットメッセージの自動生成 今後の展望 ベクトルデータベースの提供 ベクトルデータベースはLLMの文脈では長期記憶を実装するために利用されるデータストアで、個人的にこれから最も熱い領域の一つだと思っています。 LLMはモデルによって扱えるトークン数に限界があり、単体ではこのトークン数を超えて処理することはできません。 つまり、例えばChatGPTで長期のやり取りに応じた返答をさせたり、LLMに対して未知の大量の前提知識を与えてそれに基づいた回答をさせられないということです。 これを解決するために用いられる手法がベクトル表現を用いたSemantic searchです。 Semantic searchは情報検索の分野で知られる手法で、キーワードで検索するKeyword-based searchに対してベクトルによって表現された"意味"で検索します。 LLMの長期記憶は、あらかじめ長期に及んだやり取りや前提知識をベクトル表現に変換してベクトルデータベースに格納しておき、同様に変換したクエリからSemantic searchでそれらを取り出すことによって実現されます。 繰り返しLLMを呼ぶことで複雑なタスクを解く AutoGPT にも長期記憶が必要でありこの手法が用いられていました。 モデルに対する知識の追加はFine-tuningでも実現可能なはずですが、私の理解では過去の学習を忘却してしまうCatastrophic Forgettingなどが問題で期待する性能が出づらいという認識です。 そのため、LLMに複雑なタスクを解かせるための長期記憶を実装するにはベクトルデータベースは不可欠であり、プラットフォームとして提供することを決めました。 Redis これまで内製PaaSであるKEELではいくつかのデータストアをコードジェネレータ経由で利用できるようにしてきていて、Redisもその中の一つです。 www.lifull.blog Redisには RediSearch という全文検索に対応するモジュールがあり、これはSemantic searchにも対応しています。 そして、このRediSearchをはじめとした高品質なモジュールをバンドルした Redis Stack というパッケージがあったため、素直にこれに差し替えるだけで既存のRedisクラスタをSemantic searchに対応できそうだということでまずはRedisから始めました。 redis.io 提供していたRedisクラスタはRedis SentinelによるクラスタリングとHAProxyによるLeaderのService Discoveryによって実現されていて、書き込みがスケールしないことから主にキャッシュなどのRead Heavyなワークロード向けに提供しています。 LLMの長期記憶に関しても読み込みクエリが支配的であると考えられたためこれをRedis Stackに差し替えるだけで問題ないと判断しました。 しかし、ご存じの通りRedisは高速であることのトレードオフとしてすべてのデータをメモリに載せる設計です。 AutoGPTなどから利用されるデータの生存期間が明確な長期記憶としては問題ありませんが、ドメイン知識を記憶させて自社独自の回答をさせるためには膨大なメモリが必要となってしまいます。 取り扱うデータ量の増加によってこの問題が顕著になってきたため次の選択肢を探しました。 それがQdrantです。 Qdrant QdrantはSemantic searchを備えたRust製の検索エンジンです。 対抗馬としてはMilvus, Weaviate辺りでしょうか。 qdrant.tech QdrantはMilvus, Weaviateと比較して機能が少ない反面、構成が非常にシンプルでパフォーマンスに優れています。 前述の通り想定しているユースケースでは書き込みのスケールはそれほど必要ないため、動的なシャーディングや書き込みと読み込みのパスを分けるmicroservicesモードは不要でした。 (我々が得意なRustで書かれているので、構成がシンプルなこともあり何かあっても自分でどうにかしやすいというのもあります。) これもRedisと同様にコードジェネレータから利用できるようにして提供しました。 KEELにはコードジェネレータがあるため、Kubernetesではあるもののインタフェースが固まるまでは オペレーターパターン では提供しておらず、コードジェネレータのインプットとなるyamlにこう書くだけでQdrantクラスタが起動するようになっています。 spec : feature : qdrant : enabled : true replicas : 3 このタイミングで適切なダッシュボード・アラートから運用ドキュメントまでがコードジェネレータによって自動生成されているのでこの作業だけで既にProduction Readyです。 コミュニティからHelm chartも提供されていましたが、厳格な SecurityContext や TopologySpreadConstraint , Topology Aware Routing , mTLSなど我々がKubernetes Manifestsに要求する基準を満たすことが難しいため、基本的にHelm chartは利用しない方針でやっています。 Embeddings APIのキャッシュプロキシの提供 ここまででLLMの長期記憶を実装するために、データの生存期間が明確なユースケース向けにメモリ上で高速なSemantic searchを実現するRedisクラスタと、メモリに乗りきらない知識を記憶するためのディスクをバックエンドとしたQdrantクラスタを提供できました。 今後もユースケースに応じてサポートするデータストアを増やしていくことになるはずで、そうなるとデータストア間の移行コストが気になります。 ベクトル表現である Embeddings の生成には結局OpenAIのモデルを使うことになることが多く、愚直に新しいデータストアに再インデックスしてしまうと決して安くない金額がかかってしまいます。 そこでプラットフォームからのアプローチとして、OpenAI及びAzure OpenAI ServiceのEmbeddings APIのキャッシュを透過的に行うプロキシを開発しました。 キャッシュがなければUpstreamのAPIにリクエストしてその結果をキャッシュ、キャッシュがあればそのまま返すというよくあるやつです。 (この図はChatGPT PluginのShow Me Diagramsを利用して作りました。) OpenAIの公式クライアントである openai/openai-python などは OPENAI_API_BASE 環境変数でAPIの向き先を変更することが可能です。 インターフェースは揃えてあるので、これを利用して向き先をキャッシュプロキシに変えることでクライアントへの変更なしに透過的にキャッシュを挟むことができるといった具合です。 Embeddings APIのレスポンスはJSONなので素直にgzipしてオブジェクトストレージに保存するだけのシンプルなソフトウェアになりました。 Embeddingsを生成するために利用するモデルの名前がリクエストボディに入ってくるのでキャッシュのキーは普通にリクエストボディのハッシュ値でよくて、キャッシュのExpirationはオブジェクトストレージ側に任せてしまっています。 OpenAIとAzure OpenAI Serviceの差異は openai.util.api_key_to_header などでOpenAIの公式クライアントが吸収してくれているのでこの辺をそのまま利用すると楽ができます。 我々はObservability Platformも提供する内製PaaSのチームなので、(布教も兼ねて)このキャッシュプロキシにもちゃんとOpenTelemetryを入れてUpstreamに投げられたトークン数の監視と分散トレーシングをしました。 OpenAIはAPI Keyごとに利用料を追えないため、このレイヤでトークン数を監視することで細かく利用料を確認することができます。 これをプラットフォームから提供することで、単なるデータストア移行のコスト削減だけでなくサービスをまたいだEmbeddingsの共有みたいなところも狙っています。 ChatGPT Retrieval Pluginの構築 LLMの長期記憶のユースケースとしてすぐに思いつくのはやはり社内のQ&A Botでしょう。 社内のドメイン知識をもとにChatGPTが回答できるようになれば、いわゆる社内質問窓口の一部を代替できるはずです。 そのためのソフトウェアをOpenAIがChatGPT Pluginの発表と同じようなタイミングで公開しています。 openai/chatgpt-retrieval-plugin です。 これはベクトルデータベースをバックエンドとして文書のインデックスとSemantic searchをするChatGPT Pluginで、PDFのパースなどLangChainの Document Loader のような部分も内包していて、これさえあればすぐにChatGPTに社内ドキュメントをもとにした回答をさせることができます。 これを適当な場所で動かして、社内ドキュメントをインデックスするだけでやりたいことができそうです。 ただ、この手の誰のJob Descriptionにも書かれていないような仕事は往々にして進みが悪くなりがちです。 我々が開発する内製PaaSであるKEELを利用すればすぐにでも動かすことができるため、社内のQ&Aフォーラムを運営していてRetrievalに関心があった二宮の協力も得ながらプラットフォームの一環としてこの仕事を始めました。 www.lifull.blog 現在はQdrantをバックエンドにしたChatGPT Retrieval PluginがKEEL上で稼働しており、同様にKubernetesのCronJobで社内Q&Aフォーラムをはじめとした社内ドキュメントを定期的にインデックスするバッチプログラムが動いています。 それをSlack BotからLangChainの Retrieval QA 経由で呼びだして、社内のドメイン知識をもとに回答するChatGPTを実現しました。 残念ながら今のところはChatGPT Retrieval Pluginはforkして利用してしまっており、運用上で見つかったいくつかの課題はタイミングを見てパッチを送るつもりではあります。 QdrantのReplication FactorやShard数を外から与えることができない( datastore/providers/qdrant_datastore.py#L275 ) (Redisを代わりに使う場合)Connection PoolなしにRedisの接続を持ち回すため retry_on_error を書かないと接続先の入れ替わりなどに対応できない( datastore/providers/redis_datastore.py#L92 ) Chunk分割のロジックで日本語の文末が考慮されていない( services/chunks.py#65 ) Chunkのサイズは言語ごとのトークン数の消費具合を考慮して決定した方がよい( services/chunks.py#L15 ) などです。 コマンドラインツールでのLLM活用 一気に毛色が変わってコマンドラインツールでLLMを利用した機能を提供している話です。 我々はコードジェネレータをはじめ内製PaaSのKEELを利用するために便利な機能が詰まった keelctl というコマンドラインツールを提供しています。 keelctl self-update というコマンドで簡単に最新にバージョンアップすることができ、コードジェネレータである keelctl gen を実行するとKubernetes ManifestsからGitHub Actions, 運用ドキュメントまで最新のベストプラクティスが生成されるというようなソフトウェアです。 www.lifull.blog こうした機能を持つため大抵の開発者の手元には常に最新の keelctl が入っているような文化を作ることに成功しました。 これによりプラットフォーマーとしてKubernetesクラスタ経由での機能提供だけでなく、コマンドラインも握れているため開発者のローカルの環境にも影響力を持つことができています。 活用を促進するならまずは背中を見せようということで、その keelctl では keelctl llm というサブコマンドでLLMを利用した機能をいくつか提供してきました。 今回はそのうち keelctl llm conventional-commits というConventional Commits形式のコミットメッセージを自動生成する機能を紹介します。 Conventional Commits形式のコミットメッセージの自動生成 正直この辺の開発環境でのLLM活用を考えると大抵GitHub Copilotとバッティングすることになります。 しかしConventional Commitsは組織でルールを微調整したかったりするため自前でやる価値があると判断しました。 Conventional Commits とは人間と機械が読みやすく、意味のあるコミットメッセージにするための仕様です。 www.conventionalcommits.org 人間が手書きするには少し体力のいる仕様で、ルールも細かく定義されているためこれの自動生成はLLMが得意そうなタスクです。 プロンプトは後述しますが、トークン数節約のためLIFULLでは不要な制約を少し消しているのとコミットの型を明示しています。 自動生成の機能としては非常に単純で、あらかじめ与えておいたプロンプトをもとに、標準入力として受け取った git diff の出力結果からConventional Commits形式のコミットメッセージの候補を指定した数だけ生成し、Fuzzy Finderで良さそうなコミットメッセージを選択するとそれを標準出力するというものです。 このように使います。 $ git diff --cached | keelctl llm conventional-commits --select 3 | git commit -F - プロンプトの role: system はこんな感じです。 Please create an appropriate commit message for the given diff according to the following specifications. --- ## Specifications 1. Commits MUST be prefixed with a type, which consists of a noun, feat, fix, etc., followed by the OPTIONAL scope, and REQUIRED terminal colon and space. 2. A scope MUST be provided after a type. A scope MUST consist of a noun describing a section of the codebase surrounded by parenthesis, e.g., fix(parser): 3. A description MUST immediately follow the colon and space after the type/scope prefix. The description is a short summary of the code changes, e.g., fix: array parsing issue when multiple spaces were contained in string. 4. A longer commit body MAY be provided after the short description, providing additional contextual information about the code changes. The body MUST begin one blank line after the description. 5. A description MUST be kept within 72 characters. 6. The first letter of a description MUST be capitalized. 7. A longer commit body MUST be kept within 400 characters. 8. MUST not contain any footer. 9. The appropriate type/scope MUST be determined from the diff. 10. The commit message MUST be written in English. ## Available Types - feat: Addition of new features - fix: Bug fixes - docs: Documentation-only changes - style: Changes that do not affect the meaning of the code (whitespace, formatting, missing semicolons, etc.) - refactor: Code changes that do not modify existing functionality or add new functionality, such as changing variable or function names - perf: Performance improvements - test: Modifications or additions to existing tests - chore: Changes that are not important to developers, such as changes to the build process or library dependencies この機能を配布するにあたって、利用者の手元に OPENAI_API_KEY がないと使えないということは避けたいです。 API Keyの共有はセキュリティ的に論じるまでもないですし、それぞれがAPI Keyを発行することは利用のハードルが高すぎます。 そこで、我々が用意している認証基盤を利用することにしました。 内製PaaSのKEELではLIFULLで利用しているSSOのサービスをIdPとしたSAMLの認可プロキシを用意しています。 この認可プロキシのUpstreamとしてOpenAIのAPIを設定して、このプロキシのレイヤでOpenAIのAPI Keyをヘッダに入れることでOneLoginでログイン済みのユーザであれば OPENAI_API_KEY なしにOpenAIのAPIが利用できます。 認可プロキシなのでログも取れており、これもキャッシュプロキシの章で述べたOpenAIではAPI Keyごとに利用料を管理できない問題を解決できました。 ただしこの認可プロキシはCookieをもとに認可を行うため、Cookieを持たないコマンドラインからは素直に利用することができません。 こういう時にはよくある認証パターンを利用することができます。 コマンドラインツールが 0.0.0.0:0 でHTTPサーバとして待ち受ける( :0 で待ち受けるとランダムな空いているポートを割り当てることができます) コマンドラインツールが待ち受けているアドレスを redirect_url クエリ文字列に付与して <Authorization Proxy URL>/callback を xdg-open で開く( xdg-open はコマンドラインからブラウザを開くためのプロトコルです) Authorization Proxyで認可を行い必要に応じてSSOで認証する Authorization Proxyが認証済みのCookieの値をクエリ文字列に付与して redirect_url にリダイレクトする コマンドラインツールが受けたリクエストのクエリ文字列からCookieの値を取得する そのCookieを利用して認可プロキシにリクエストを投げる という流れです。 これにより、既に開発者の手元に入っているコマンドラインツールから一切の設定を必要とせずにLLMを利用した機能を提供することができました。 この機能は結構好評で、後続のリリースでConventional Commitsをもとにしたバグ発生率などのレポーティング機能も実装したこともあり、既に多くのチームでConventional Commitsが採用され始めています。 GitHub Copilotの隙間を縫っただけのような気もしますが、これで一つLLM活用の方向性を示すことができたと思います。 今後の展望 LLM活用促進に向けてPlatform Engineeringから行ってきたアプローチをいくつか紹介しました。 ですが、実際のところLLMのインパクトに対しては社内の活用はまだ不足していると感じています。 ジェネレーティブAIプロダクト開発室は新設されたものの、LLMは陳腐な言い方をすれば民主化されたAIであり、専任部署だけのものではないどころかソフトウェアエンジニアに限らず多くの人が活用してしかるべきです。 今後もプラットフォーマーとして活用の下支えをしながら一層LLMを「あらゆるLIFEを、FULLに。」の実現に繋げていこうと思います。 直近では組織にまだ活用のイメージが不足していると思っていて、 AI戦略室 と主要なアプリケーション開発者とともに、実際のアプリケーションでLLMを利用する生きた参考実装を用意しようと動いています。 個人的にはそろそろ(概念としての)AutoGPTによるアプリケーション実装の自動生成と真剣に向き合う頃合いかなとも思っています。 Platform EngineeringではこれまでSREを中心に価値あるプラクティスを組織に適用してきました。 これはプラットフォームがレバレッジの効くソフトウェアであるからで、LLMのような新たなパラダイムが出てきた時にそれを組織に適用する責任もプラットフォーマーにはあるはずです。 そして我々KEELチームはその結果として「あらゆるLIFEを、FULLに。」の実現にスケーラブルに貢献していくことを目指しています。 そんなKEELチームにもし興味を持っていただければ是非カジュアル面談しましょう! hrmos.co hrmos.co
こんにちは。エンジニアの渡邉です。普段はLIFULL HOME'Sの売買領域のエンジニアチームにて開発を担当しています。好きなGCPのサービスはCloudRunです。 今回は、LIFULL HOME'Sの物件画像を次世代画像フォーマット「WebP」形式に動的変換して配信できるようにした取り組みについて紹介します。 WebPとは WebP導入の背景 画像変換サーバの基盤刷新 主要なブラウザのWebPサポート 実現方法の検討 工夫した点 WebP対象外のブラウザからリクエストが来た場合に対する対応 CloudFrontのキャッシュ条件を変更 成果 パフォーマンスの改善 運用コストの軽減 全アプリケーションへの一括WebP対応 終わりに WebPとは WebP (ウェッピー)はGoogleがWebサイトの表示速度短縮を目的として開発した静止画像フォーマット画像形式のことを指します。 画質の劣化を最小限に抑え、画像サイズを軽くできます。表示速度改善によりエンドユーザーに好影響を与えるため、現在JPEGやPNGに変わる画像形式として注目されています。 WebP自体は2010年に仕様が公表され、多くのブラウザでサポートされています。 WebP導入の背景 サイト上で配信する画像をWebP形式に変換するしくみを導入する主な目的は、コストカットや表示速度改善、ユーザー体験向上のためです。 LIFULL HOME'SにおけるWebPの導入を長らく検討してきましたが、 画像変換サーバの基盤刷新 主要なブラウザのWebPサポート という2点において環境が変化したため、導入を進めることができました。 画像変換サーバの基盤刷新 LIFULL HOME'Sでは、物件画像のサイズやクオリティ、フォーマットなどを利用するアプリケーションに合わせて動的に調整する内製の「画像変換サーバ」を用いて画像を配信しています。 もともと私は画像の最適化に興味があり、LIFULL HOME'Sの画像を最適化し、最高の画像をユーザーに届けることを目標にしていました。 そのための地盤として、開発しやすいように画像変換サーバを新基盤に移行していました。 以前紹介した画像変換サーバの基盤刷新についてはこちらをご覧ください。 www.lifull.blog 主要なブラウザのWebPサポート WebP化を導入しようと考えていたときに抱えていた課題として、主要ブラウザの一部がWebPをサポートしていないことがありました。 当初WebP化を検討していた際にはInternet ExplorerとSafariにてWebP形式をサポートしていませんでした。 LIFULL HOME'Sを利用しているブラウザのうち、Internet ExplorerとSafariの利用率は無視できず、WebPを導入しても中途半端な成果になってしまうことが懸念だったのです。 しかしながら、現在Internet Explorerはサポートを終了し、SafariはWebPをサポートするようになり懸念点が解消しました。 そのため、WebP化の恩恵を強く受けられる算段が立ったので、導入に踏み切ることができるようになりました。 実現方法の検討 今回WebP配信化させるにあたって以下の二択の方法を考えていました。 すでに存在する画像ファイルを事前にWebP形式にしてそれを配信する方法 現在利用しているngx_small_lightを使用した画像変換サーバにて動的に変換させる方法 両者を比較したところ、今回は後者のngx_small_light側で動的に変換することを選択しました。 その理由としては次のような理由がありました。 ストレージに保存する画像のコストを下げたい 多様な環境からのリクエストに対応するため、配信する形式を動的に選択できるようにしたい 工夫した点 今回WebP化することを決めた上で、苦労したことがいくつかありましたので紹介します。 WebP対象外のブラウザからリクエストが来た場合に対する対応 LIFULL HOME'Sを利用されるユーザーは多種多様なため、Internet ExplorerなどのWebPが表示できないブラウザを利用されている可能性があります。 その場合、画像がまったく表示されないサイトになってしまうので、それを回避するためのしくみが必要でした。 今回どのように対応したかというと、Acceptヘッダの中身を見て変換するかを決定するプロセスを設けました。 ChromeなどのWebPを表示可能なブラウザの場合、以下のような image/webp をAcceptヘッダに持っています。 image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 この image/webp の有無によって元の画像をそのまま返すか、動的に変換したWebPを返すのかを選択しています。 Acceptヘッダに対応した画像フォーマットであればブラウザ側は問題なく表示できます。したがって、利用するアプリケーションはWebPが来る場合とそれ以外の画像形式が来る場合をそれぞれハンドリングする必要なく利用できます。 CloudFrontのキャッシュ条件を変更 LIFULL HOME'Sでは多くの画像を表示するため、負荷を軽減するためにCloudFrontによるCDNキャッシュを採用しています。 今までは画像を取得するURIに対して1:1になる形でCloudFrontにキャッシュできていました。しかしながら、今回の変更により、同一のURIであっても変換されたWebP形式か元の画像形式かの二択になってしまうことが予想されました。 もしWebP形式でキャッシュされていた場合に、Internet Explorerで表示しようとすると画像が表示されないといったことにつながります。それを回避すべくCloudFront側もAcceptヘッダの中身を確認したうえでキャッシュする機構に変更しています。 成果 苦労したこともありつつ、なんとかWebP対応を完了させたことで、いくつか大きく成果を出すことにつながりましたので紹介させていただきます。 パフォーマンスの改善 当初の目的にあった通り、画像の軽量化を行うことができました。 画像は平均して約20%ほどの軽量化に成功し、画像の表示スピードにも好影響を及ぼすことに成功しています。 運用コストの軽減 こちらのコストカットが今回だとかなり大きな成果としてつながりました。 もともと多くの画像を保有し表示することになるLIFULL HOME'Sでは大量の画像をCloudFrontにキャッシュしています。 多くの画像をキャッシュしているために高いコストを毎月かけて運用していましたが、WebP形式にしたことで画像サイズが20%軽減し、実績としてコストカットをすることにつながりました。 また、自社でWebP化を内製できたことで、もともと外部のサービスを利用する等の検討もありましたが、その必要がなくなったことも大きかったです。 全アプリケーションへの一括WebP対応 今回改修を加えた画像変換サーバは弊社の運営している多くのサービスが利用しているアプリケーションとして運用しています。 そのため、汎用的にWebPを返すしくみを作ることができたことによって、すべてのアプリケーションに対してWebPフォーマットでの画像を配信することに成功しました。 各アプリケーションで対応することなく、さまざまなメリットのあるWebPを一斉に適用できたことは、LIFULL HOME'S全体のパフォーマンス向上につながる成果となりました。 終わりに 今回はLIFULL HOME'Sにて画像最適化の文脈で多くのメリットがあるWebPフォーマットでの配信を実施でき、期待通りの成果を上げることができました。 もともと画像の最適化に興味があり、いつかLIFULL HOME'Sの画像をすべてWebPにするぞ!と意気込んでいた私としては感無量でした。 画像最適化はまだまだ多くの手法がありますので、これからも実践していければなと考えています。 弊社ではWebPを配信するにあたり画像変換サーバに対して手を加えるのが一番効果的であると判断しました。しかしながら、アプリケーションの特性によっては事前に変換しておく運用であったり、外部サービスを用いるといった手法も効果的であると思います。 WebP化することでの恩恵は大きいと思いますので、ぜひご検討してみてはいかがでしょうか。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは。AI戦略室 主席研究員の清田陽司です。 LIFULLが取り組んでいるさまざまな研究開発の課題を、より多くの社外の方々(とくに大学の研究者や学生)に共有することで、LIFULLだけではなし得ないより大きな研究成果につなげる、「産学連携」という活動を行っています。 実は、 LIFULL HOME'S 3D間取り というサービスも、産学連携の長年の取り組みの成果の一つです。 このたび、3月に開催された 第15回データ工学と情報マネジメントに関するフォーラム(通称 DEIM 2023) に、協賛企業の担当者、かつ「産学連携委員長」という役割で関わりましたので、その様子を報告します。 event.dbsj.org 開催会場の長良川国際会議場(岐阜県岐阜市) DEIMは、いわゆる「学会イベント」の一つで、 日本データベース学会 、 情報処理学会データベースシステム研究会 、 電子情報通信学会データ工学研究専門委員会 による共催です。 前身の「データ工学ワークショップ」から30年以上続いていて、例年600名以上の参加者を集めています。 LIFULLはもちろん、多くの企業がこのような学会イベントに協賛・参加しています。 この記事では、コロナ禍を経て4年ぶりに実現した現地開催の様子や、企業がこのような学会イベントに関わる意義について、お伝えしたいと思います。 コロナ禍と学会イベント 2020年初頭から続くコロナ禍は、多くの人が集まることに意義のある学会イベントに、大きな打撃を与えました。 2020年3月に開催された DEIM 2020 は、まさにコロナ禍の直撃を受けた学会イベントの一つでした。 DEIMは、例年合宿形式で開催され、参加者間の濃密な議論や交流が行われることを、大きな特色としていました。 私自身も毎年参加し、昼のさまざまなセッションでの発表や参加だけでなく、夜の美味しい料理やお酒を嗜みながらの多くの方々との交流も楽しみにしてきました。 しかし、新型コロナウイルス感染症が急激に拡大する中、このような濃密な交流をすること自体が許されない状況となり、実際に、多くの学会イベントが開催中止に追い込まれました。 DEIM 2020は、磐梯熱海温泉での開催が予定されていましたが、急遽現地開催から、当時ほとんど実績やノウハウのなかったオンライン会議システムによる開催に切り替えられました。 多くの学会イベントが中止に追い込まれる中、質疑応答を含む発表セッションをフルオンラインで実施できるシステムを短期間で構築し、フルオンライン開催を実現したDEIM 2020の挑戦は大きな注目を集め、メディアでも取り上げられました。 www3.nhk.or.jp www.asahi.com www.businessinsider.jp 私自身も協賛企業担当者としてDEIM 2020に参加していました。 オンライン開催のシステムを構築された関係者の方々の奮闘を目の当たりにし、大きな感銘を受けたことを鮮明に覚えています。 LIFULLでも、オンライン開催への切り替えを受けて、協賛を継続するか取りやめるかの判断を迫られましたが、このような危機的状況だからこそ継続して学会イベントの成功を支える、という決断に至りました。 いま振り返っても、この決断をしてとても良かったと思います。 国立情報学研究所(NII)に急遽設置されたDEIM 2020運営事務局 これに続く DEIM 2021 、 DEIM 2022 も、当初は現地(それぞれ磐梯熱海温泉、名古屋市)での開催が計画されましたが、コロナ禍での感染拡大のリスクが懸念されたため、いずれもフルオンラインでの開催を余儀なくされました。 オンラインでの学会イベントにいくつも参加してきて、オンライン開催にはメリット、デメリットの両方があることを感じてきました。 「参加者の幅が拡がる」ことは、オンライン学会イベントの良さの一つです。 参加費用、子育てや介護などの個別の事情により、これまで参加をあきらめてきた方々が多く参加し、議論や交流を楽しまれている様子を、私自身も数多く見ることができました。 より多様な方々が参加することで、議論の幅も拡がることは、オンライン学会イベントならではのメリットだと思います。 DEIM 2022では、過去最大となる1376名の方々が参加し、大盛況でした。 一方で、オンライン学会イベントには、「参加者間の交流が減ってしまう」という課題もあります。 現地開催の学会イベントでは、休憩時間中に顔なじみの方とたまたま出会っての雑談など、数多くの交流機会がありますが、オンライン開催ではどうしても減ってしまいます。 より多くの交流機会をもつことがメリットとなる協賛企業にとっても、交流機会が減ってしまうのは大きな悩みでした。 「直列ハイブリッド開催」という挑戦 オンライン開催と現地開催の良いとこ取りをする「ハイブリッド開催」は、すでにいくつかの学会イベントで取り入れられています。 私自身も、 人工知能学会全国大会(JSAI 2022、京都) など、いくつかのハイブリッド開催の学会イベントに参加し、その良さを実感しました。 一方で、ハイブリッドのイベント開催は本当に大変です。 現地参加者とオンライン参加者がスムーズに交流できるようなシステムは大がかりになりますし、トラブルが起きたときに対応できるスタッフも多数必要となります。 そこで、DEIM 2023では、3年間続いた完全オンライン開催の経験を踏まえて、新たに「直列ハイブリッド」という開催形式に挑戦しました。 具体的には、最初の3日間(3月5日〜7日)に一般発表セッションを完全オンライン開催で実施し、その後(3月8日〜9日)に現地会場(岐阜市)に集まってインタラクティブセッション(ポスター発表)などの交流イベントを実施することになりました。 このように、オンライン開催と現地開催を時間的に分ける(これを「直列ハイブリッド」と呼ぶことになりました)ことで、ハイブリッド開催の「コストや労力がかかる」というデメリットを最小化しながら、オンラインイベントだけ参加したい方々、現地で多くの参加者と交流したい方々の、両方のニーズを満たすことを目指しました。 結果として、直列ハイブリッド開催という形式は大成功だったという感想をもちました。 オンラインでの一般発表セッションで、より多くの発表をオンラインで視聴した上で、現地のインタラクティブセッションで、興味のある研究内容について、発表者とポスター前でじっくりと議論する、という、新たな学会イベントの形が実現できたように思います。 開催期間が5日間と長くなってしまうのが課題ですが、「学会の目的は何か」という原点に立ち返り、時代の変化に合わせてイベントのあり方を柔軟に変えていくことは、とても大切なことだと感じました。 「直列ハイブリッド」という前例のない開催形態に挑戦された実行委員長の鈴木優先生(岐阜大学)を代表とする幹事団の皆さまに、心からの敬意を表したいと思います。 この写真は、現地にて開催されたネットワーキングセッションの様子です。 Open Space Technology(OST) という枠組みで、参加者が話したいテーマをシェアし、それに興味をもつ人々が自由に集まって対話するという時間となり、大変盛り上がりました。 産学連携委員長としてのお仕事 今回のDEIM 2023では、産学連携委員長という役割を担当しました。 企業向け協賛プログラムの枠組みを設計するとともに、協賛企業各社と、大学の研究者を中心とする委員の方々とをつなぎ、産学連携をより密接にするという役割です。 今回は、「直列ハイブリッド」という開催形態のもとで、どのような協賛プログラムにすると多くの方に価値を感じていただけるか、幹事団や協賛企業の担当者など、多くの方々と相談しながら模索しました。 その結果、「4年ぶりの現地開催の機会を生かして、企業ブースに多くの参加者が集まる仕掛け」、および「協賛企業各社からの技術報告発表を、一般発表と同じオンラインセッションの時間に組み込むことで、参加者からより多くの質問が集まる仕掛け」に注力した協賛プログラムとすることになりました。 非常に有り難いことに、今回も19社もの企業から協賛をいただくことができ、充実した協賛プログラムを参加者の方々にご提供することができました。 とくに、最終日の常設ブースは、各社のブースに多数の参加者が集まり、大変盛況でした。 私自身も、LIFULLのブースにポスターを出して不動産分野でのAI関連の研究テーマなどをお伝えし、さまざまな反響をいただきました。 何よりも、参加者の方々が4年ぶりの現地での交流を楽しまれている様子が感じられ、主催者としても大変嬉しかったです。 協賛いただいた各社の皆さま、技術報告発表や常設ブースにて交流いただいた参加者の皆さまに、心より御礼申し上げます。 常設ブースの様子 オンライン会議用に参加者に配布されたバーチャル背景 企業が学会イベントに関わる意義 多くの企業がこのような学会イベントに積極的に関わっているのはなぜでしょうか? 優秀な学生の採用につなげたいという目的は、すべての企業が共通してもっています。 しかし、学会イベントを単に自社の魅力をアピールする場として活用するだけでは、効果も薄く、また長続きもしないように感じています。 学会イベントでは、学生さんだけでなく、大学の研究者の方々、他社の研究者や人事担当者など、さまざまな方々と交流することができます。 大学の先生方とお話しすることで、いまの学生が関心をもっているテーマなどについて詳しく知ることができます。 他社の方々とお話しすることで、他社がどのような考え方をもとに研究開発を進めているかなどについて、理解を深めることができます。 学会イベントの参加者との交流を深める上で非常に大切なのが、学会イベントの価値を高めるために、自社としてどのような貢献ができるのかという視点です。 LIFULLは、 利他主義 を社是として掲げています。 目の前にいる人をHAPPYにすることで自分もHAPPYになれる という考え方を、私達は学会イベントに関わる上でも大切にしています。 最後になりますが、LIFULLでは共に成長しながら働く仲間を募っております。 AI戦略室では シニアデータサイエンティスト を募集中です。 hrmos.co カジュアル面談もありますのでご興味ある方は是非ご応募ください! hrmos.co 今後も、LIFULLでの産学連携活動について本ブログで発信していきます。どうぞよろしくお願いいたします!
社内でChatGPTの普及のためハッカソンを開催しました こんにちは。クリエイターの日運営委員の花岡です。 4/20にLIFULLでChatGPTハッカソンを実施したので、その模様について報告します。 近年、ChatGPTによる技術革新はめざましいものがあります。 LIFULLのサービスとしては、先日ChatGPTを活用したAIホームズくんbeta LINE版をリリースをしています!! lifull.com このような動きの中で、社内向けとしてもChatGPTの可能性を探るべく、ハッカソンを開催しました。 急な開催ではあったものの、22名の方にご参加いただきました! 今回のハッカソンの狙いは以下の通りです。 ChatGPTを使ったハッカソンを実施することで、新しいアイデアの種にしてもらう。 LIFULLでの、ChatGPTへの関心を高めてもらう。 エンジニア・非エンジニアに限らずChatGPTを社内で使ってもらうことにより、ChatGPTという技術に慣れ親しんでもらう。 社内にプロンプトのノウハウを蓄積する。 今回は入門編ということで、エンジニア以外にも広く参加してもらう形式をとりました。 ハッカソンの概要 今回は、ChatGPTが盛り上がってまだ日が浅いことや、エンジニア以外の参加者も多いことからハンズオン、ハッカソンの二部形式を取ることにしました。 また、今回のハッカソンでは弊社のAI戦略室で作成いただいたChatGPTツールを使用しました。 第一部: ハンズオン LLMの概要について ChatGPTと社内向けツールの説明 第二部: ハッカソン ルール説明 ハッカソン 成果発表 表彰 第一部 ハンズオン LLMの概要説明 ハンズオンの前半では、弊社の加藤さんにLLMの概要説明をしてもらいました。 LLM概要説明 ChatGPTと社内向けツールの説明 今回のハッカソンでは、弊社AI戦略室で作成した社内向けツールを用いてハッカソンを行いました。 なお、社内ツールは以下の目的で作成されました。 プロンプトを入力しやすいGUIを作成してOpenAIのAPIを手軽に使えるようにすること。 プロンプト(コンテキスト)を共有できるようにしてノウハウが横展開されやすいようにすること。 後半では、谷山さんにChatGPTや社内ツールの使い方について説明してもらいました。 ChatGPTについて 社内ツールについて 第二部 ハッカソン 今回のハッカソンでは 「LIFEをFULLにするモノ」 をテーマにプロンプトを作成してもらいました。 先ほど説明した社内ツールを使用して2時間ほどの時間でテーマに沿ったプロンプトを作成してもらいました。 表彰作品 今回のハッカソンでは、投票による「ハッカソン優秀賞」と、弊社のCTOの長沢さん、取締役の山田さんに選んでもらう「長沢賞」「山田賞」の2つの特別賞を用意しました。 ハッカソン優秀賞 武田 裕子「発注したい」 〜選定理由〜 実際に運用できると思った。結構良い感じにほかのタスクにも適用できそう。 こんなに詳しく段階的に正確な返しができるようになるとは、と感動しました 業務支援で使えそうだから。社内のchatbotで使いたい etc.. 発注したい 長沢賞 二宮 健「タスク内容相談くん」 〜選定理由〜 どれも使ってみましたが、一番自然かつ使えそうな答えが帰ってきました、 タスクを進める際にどの観点を気を付ければ良いのか、はっきりさせておいたほうが良いところはないか?など、経験の浅い人にも良いメンターになりそうなためです。 タスク内容相談くん 山田賞 羽賀 崇史「振返り精度UP」 〜選定理由〜 対話botの基本的な使い方と言えるかもしれないが、実用的であり使い続けることで人間の成長にもつながるAIとの良い関係が作れそうと感じたため。 振り返り精度UP ここでは紹介しきれませんでしたが、表彰作品以外にもユニークですばらしい作品がたくさんありました。 ハッカソンを振り返って 今回のハッカソンの時間は短めだったにもかかわらず、ユニークでおもしろいプロンプトがたくさん出てきました。 また、エンジニア以外にもたくさんの方に参加いただき、ChatGPTを使ってどのようなサービスを作っていくかの発想の手がかりになったと感じています。 今後社会課題の解決に向けて、ChatGPTの大きな可能性を感じることができるハッカソンになりました。 最後に LIFULLでは、冒頭で紹介したAIホームズくんを始め、ChatGPTやLLMのサービス適用を広げるべく積極的に取り組みが進められています。 また、今回のハッカソン以外にも 熱海ハッカソン などさまざまなイベントを開催しています。 LIFULLに興味のある方、ぜひ一緒に働きませんか。 よろしければこちらのページをご覧ください。 hrmos.co
こんにちは、フロントエンドエンジニアの嶌田です。 アクセシビリティは今まで以上に大きな関心を寄せられるトピックになってきたように思います。個人で関心がある人、企業のなかで周りを巻き込み推進しようとしている人、すでに組織全体での取組みに変わりつつある企業など、状況は様々だと思います。弊社はというと、内側からの推進活動は広がりを見せつつも、まだ組織一丸となった取組みには至っていない、といったところです。 そんな状況の私たちですが、社外のアクセシビリティを推進する同志たちに、ほんの少しでも力を分け与えられたらと思い、このたび「LIFULLアクセシビリティガイドライン」を公開しました。取組み状況が様々ある中でどのように活かしていけるか、まずは一度ご覧いただければ幸いです! lifull.github.io アクセシビリティとは? アクセシビリティとは、高齢者や障害者を含むできるだけ多くの人々に対して、プロダクトやサービスを利用可能にすることです。アクセシビリティを高めると、利用者の母数が増えるだけではなく、けがや病気、一時的に不利を負った状況でもプロダクトやサービスが使いやすくなり、ユーザー体験の向上につながります。 LIFULLアクセシビリティガイドラインとは? LIFULLアクセシビリティガイドラインは、LIFULLの全てのプロダクトやサービスのアクセシビリティを高めていくために策定されたものです。 LIFULLのプロダクトに関わる全ての人を対象にしていますが、内容は普遍的で、多くのデジタルプロダクトやサービスの制作現場において活用できるものになっています。 LIFULLアクセシビリティガイドラインの特徴 コンセプトは「 自分がいまやるべきことがわかるガイドライン 」です。もう少し具体的にすると、次のような特徴を持つように編成されています。 工程に応じた項目 ガイドラインの項目は工程(≒職種)ごとに大きく分けられています。 デザイン コンテンツに関するものや、UIやインタラクション、ビジュアル表現についてのガイドラインがまとめられています。 実装 HTMLやCSS、JavaScriptの実装方法に関するガイドラインがまとめられています。 明確な優先度 重要度、コスト、LIFULLの制作事情を総合的に判断した「レベル」と呼ばれる優先度を導入しています。レベルは3段階に分かれており、ひとまずこのレベルに従って対応を進めていくように制作者に求めています。 レベル1…必ず達成 ユーザーに大きな影響があり、どのサービスでも必ず達成したい重要なタスク。 レベル2…可能な限り達成 レベル1に次いで重要で、できるだけ達成してほしいタスク。 レベル3…できれば考慮 できれば考慮してもらいたいタスク。 レベル3までのガイドラインにすべて対応すると、WCAGのA, AAの基準に(おおむね)対応できるようになっています。 わかりやすい記述 初学者でも理解できるように、図表や例を使って具体的に記述しています。ガイドラインが必要とされる背景や、恩恵を受けるユーザーについても簡潔に記載しています。 項目によっては、厳密な正しさや網羅性を備えていないものもありますが、その役割は別のリソースを参照してもらうということで、本ガイドラインは簡潔さを保っていく方針としています。 関連リソースへのアクセス 理解の促進に役立つリソースや、関連するリソースへのリンクを設けています。 なぜ新しいアクセシビリティガイドライン? W3Cが勧告しているウェブコンテンツアクセシビリティガイドライン(WCAG)をはじめ、freeeやサイバーエージェントといったアクセシビリティで先行している企業が一般公開している質の高いガイドラインがすでに存在しています。それでも私たちは独自のアクセシビリティガイドラインを作成・公開する動機がありました。 主な理由は以下の通りです。 WCAGは難しすぎる WCAGは人類の叡智ともいうべき素晴らしいガイドラインですが、抽象度が高く、知識や経験がないと理解することが難しいです。アクセシビリティ専門でやっていない現場の人全員に、これを読んで理解することを求めるのはハードルが高いと思いました。 「今何をすればいいのか」がすぐわかるガイドラインがない 現場で働く人たちが理解しやすく、実践しやすいガイドラインが求められていると考えました。先行する企業発のガイドラインはその観点では私たちが求めるガイドラインとは少し違ったものでした。 借り物のガイドラインを使い続けることには限界がある WCAGを除けば、企業発のガイドラインはその企業としての方針や現場感が反映されたものですし、そうあるべきだと思います。取組みの初手として既存のガイドラインをそのまま取り入れることは良い判断な一方で、現場からのフィードバックを受けて改良を加えられるようにするには新しいガイドラインを作るのがよさそうだと思いました。 制作裏話 工程ごとに分かれたガイドラインを見てピンときた方がいるかもしれませんが、LIFULLアクセシビリティガイドラインは、 IBMのEqual Access Toolkit に大きく影響されています。現場に寄り添うガイドラインを作りたいと思っていろいろ見ていた時に目につき、コレほしかったやつ~~と思い、真似させてもらいました。 このガイドラインはもともと、社内のドキュメントツールでひっそりと公開されていたものでした。すでに述べてきたような理由や、社外に公開することで社内にも改めて本気度を示すというような副次的な効果も見込んで、公開することにしました。 ガイドラインのデザインは、社内のアクセシビリティ推進ワーキンググループのメンバーである狩野さんが手がけました。挿絵のディレクションもしていただき、ガイドラインに魅力が加わっています。 狩野さんは先日、LIFULLのアクセシビリティへの取組みについてのブログ記事も書いてくれています。こちらもぜひお読みください! note.com LIFULLアクセシビリティガイドラインの今後 IBMのEqual Access Toolkitのように、ガイドラインをより実用的で日常的に使いやすいものにしていきたいと考えています。現在はデザイン、実装についてのガイドラインしか含まれませんが、たとえばプロジェクト計画の初期段階からアクセシビリティを導入していくためのガイドが作れるかもしれません。目標レベルにあわせたチェックリストを自動的に生成する仕組みを作っても便利そうです。 また肝に銘じなければいけないのは、ガイドラインを作成し、メンテナンスすることだけが目的ではないということです。メンバーの問題に常に向き合い、それを解決するツールとして機能し続けることが重要であることを忘れずにおきたいです。 フィードバックください LIFULLアクセシビリティガイドラインはGitHubでオープンソースソフトウェアとして公開しています。誤字脱字や内容の妥当性、わかりやすさや事例の推薦など、さまざまな観点からのフィードバックを歓迎しています! github.com お読みいただきありがとうございました。LIFULL では共に働く仲間を募集しています! hrmos.co hrmos.co
こんにちは。LIFULL ネイティブアプリエンジニアの佐藤麗奈です。 業務では LIFULL HOME'SのiOSアプリ (以下、LHアプリ)の開発を担当しています。 私が新卒で入社してから、早くも1年が経ちました。 今回は、LIFULLに入社してから今日までの歩みを振り返ってみたいと思います。 これからエンジニアとして働くけれど、未経験でもやっていけるか心配だなぁと感じているような方々の背中を押せる記事になれば幸いです。 配属前 情報系の学部を卒業しましたが、正直得意な言語も目立った成果物もない状態でした。 そのため、入社前はうまくやっていけるのか漠然とした不安を感じることもありましたが、人事の方が親身に相談に乗ってくれたり、同期との助け合いもあり1人で抱え込まずに不安を解消していくことができました。 入社直後は、約2週間の全体研修があり、4月半ばからは1ヶ月半のエンジニア研修がありました。 エンジニア研修では、Webアプリケーション開発の基礎の講義を受講した後に個人開発演習を行いました。 Webアプリの個人開発では、要件定義・設計・実装・テスト仕様書の作成・テスト実施の一連の流れを初めて1人で行いました。 期間は2週間と短い上、1ヶ月間の講義で学んだことを振り返りながら頭をフル回転させる毎日で、なかなかハードな日々でした。 しかし、講師の方や同期のサポートのおかげで、Webアプリをなんとか形にすることができました。 配属後 5~6月 エンジニア研修が終わり、5月末からLHアプリチームに配属されました。 初めはどんな業務にアサインされるのかドキドキしていると、まさかのネイティブアプリ演習課題を与えられました。 課題に取り組み始めてから1週間後の1次レビュー会では、Swiftの文法や処理の流れの理解不足を指摘され作り直しの宣告…。 課題の発表会は1週間後でしたが、 要件を細かく分割して考えること 調べたり教えてもらった内容を再度自分で整理して理解すること ソースコード上には自分が理解できたコードだけを書くこと の3点を徹底して、なんとか課題の要件を満たすものを作り上げることができました。 実際の業務ではネイティブアプリを1から作る機会はほとんどないため、非常に有意義な時間となりました。 また、課題と並行してアプリチームのミーティングにも参加していたのですが、最初は苦労しました。 なぜなら、ビジネス用語や不動産業界用語、社内用語など、ネイティブアプリの知識に収まらないさまざまな用語が飛び交っていたからです。 そこで、私と一緒にアプリチームに配属された同期と協力してアプリチーム用の単語帳を作成し、先輩方に教えていただいたことや調べたことをまとめていきました。 この単語帳は今でも大切なお守りで、自分たちのチームに後から加わった方々の役にも立っているとの声も届いて嬉しい限りです! 7~9月 7月頭からは、ついにLHアプリの開発が始まりました。 コード数、ファイル数、アプリの機能数など、とにかく全てが想像以上に多い!!! これだけの規模のアプリを作れるようになるにはどんなスキルを身につけていく必要があるのか、iOSアプリ開発のスキルロードマップを確認すると9割5分が未知の世界…。 大学の研究時にSwiftでカメラアプリを作成していましたが、そこで得た知識だけでは「Swiftでアプリ開発ができます!」とは到底言えないことを痛感しました。 この時期は、主にUIに関する不具合対応を行なっていました。 不具合の原因の調査をしつつ、アプリがどのように動いているかを知るために、アーキテクチャやライフサイクルの勉強をしたり、Xcodeのデバッグ機能を活用しながら処理を追いかけたりしました(今でも必死に継続中)。 また、App Storeに申請してアプリをリリースする作業も経験しました。 自分が作ったものが世の中に公開され、住まい探しをしているあらゆる人に使ってもらえているという実感が持て、とても嬉しかったです。 10~12月 この頃から、UIの開発がメインの施策にもアサインされるようになりました。 LHアプリチームでは、1つの施策につき、エンジニアが1人、企画が1人、デザイナーが1人がアサインされることが多いです。 エンジニアの主な担当は、実装・テスト仕様書の作成・テスト実施ですが、仕様作成の段階から三職種で意見を出し合いながら1つのプロダクトを作り上げていきます。 困った時には、施策の担当者だけではなくアプリチーム全体で相談し合っています。 先輩方にサポートいただきながら、期日内に仕様を満たす機能を作りあげる経験を積むことができました。 マイページ画面の担当したUIの一部↓ 左:Before、右:After ひょこっと顔を出しているホームズくんがかわいい❤️ 左:Before、右:After LIFULL引越しへ遷移できるようにしたり、機能ごとの見た目を刷新しました また、アプリチームでは職種関係なくアイディアを出して施策化できる機会もあります。 12月には、自らアイディアを起案し、リリースまで実現しました。 具体的には、条件を設定する際の「路線・駅から探す」画面内などにリセットボタンを設置しました。 このアイディアは、実際に自分で物件を探していて、駅をひとつずつ選択解除していくことに使いづらさを感じていたため発案しました。 発案後にすぐにリリースまで実施でき、自分の住まい探しにも大活躍しました! 画面右上にリセットボタンを設置 一度に画面内すべての選択状態を解除できるようにしました 1〜3月 年明けからは、さらに業務の幅が広がりました。 アプリ内で利用しているSDKやFrameworkに触れる機会があったり、ABテストを行う施策を担当したり、不動産会社への問い合わせに関わる施策に取り組みました。 これまでの業務で経験を積んできたおかげで、一人でできることが増えてきました。 また、今までは実装のレビューをしてもらう側でしたが、少しずつ他の方の実装のレビューにも挑戦しています。 初めは何をどのように確認すれば良いかがわからず、必ず見るべきポイントや余計な影響が出ないように意識すべき点などを先輩方に教えていただきながら取り組んでいます。 確認するポイントがわかってきたことで、実装のセルフレビューの精度向上にもつながっています。 さらに、現在のLHアプリではUIのほとんどがStoryboardで作成されていますが、SwiftUIの導入検証に挑戦する機会もいただいています。 自分の伸ばしたいスキルや目指すキャリアをもとに、上司と相談しながら次に何の業務を担当するかが決まるので、仕事のモチベーションも上がります! その他 メインの業務以外にも、iOSアプリエンジニア内で設計に関する参考書の輪読会や同期のエンジニアとセキュリティ・ネットワークに関する勉強会を行いました。 業務だけでは理解が追いつけなかったことの理解が深まったり、学んだ知識を業務にすぐに役立てられたりしています。 まとめ 入社からの1年間をざっくりと振り返ってみました。 これからエンジニアとして働くことに不安を感じている方々は、少しは安心できたでしょうか? 開発経験はほとんどありませんでしたが、iOSアプリ開発の基礎を習得するところからLHアプリの実装ができるようになり、さらに施策を1人で担当できるまでに成長しました。 ネイティブアプリエンジニアとしての経験を着実に積んでいる今なら、「iOSアプリ作ってます」「Swift書いてます」と言えます! アプリ開発を含む様々なことを勉強する機会と挑戦をサポートしてくださったLHアプリチームの皆さんには、深く感謝しています。 新卒2年目のこれからは、これまでよりも難易度の高い業務を担当していきます! エンジニアとしてさらに成長し、LHアプリの成長に貢献できるよう精進して参ります。 最後に、LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは。エンジニアの北島です。普段は LIFULL HOME'S の売却査定領域 でエンジニアリングを担当しています。 今回は既存アプリケーションのパフォーマンス改善に、フロントエンドの観点から取り組んだ話をします。 経緯 弊社のサービスで プライスマップ という AI 査定による不動産価格を地図上で一気に見られるサービスがあります。 このサービスは 2015 年にローンチされ、当時は最新の技術を利用していたものの現在は老朽化が進んでいました。 私どもの部署では昨年このサービスの運用主幹となり、昨年度はバックエンドを弊社で運用しているコンテナオーケストレーション基盤 keel に載せ替え、インフラ改善を中心に取り組んで参りました。 今回は既存のアプリケーションのフロントエンドパフォーマンス改善を、Lighthouse を用いて行った話をしていこうと思います。 現状確認 このプライスマップにおいて、Google が検索エンジンにおけるサービスの評価指標としても採用している CoreWebVitals の各評価指標にも改善の余地がありました。 Google が無料で提供している Web サイトを分析・診断するための、Chrome 拡張機能 Lighthouse を使用し手元で診断してみたところ、 特にモバイルで低い評価でした。 引継ぎの時にサーバサイドのパフォーマンスチューニングを行っていたこともあり、サーバサイドレスポンスタイムに大きな問題はなく、フロントエンドの方に改善の余地がありました。 フロントエンドパフォーマンス改善に取り組んだ経験がなかったのですが、SEO 観点での指摘ということで事業的なインパクトも期待できるので、今回着手することに決めました。 具体的な対応内容 対応事項の整理 lighthouseスコア(改善前) Lighthouse のスコアを確認し、どれくらいの水準なのかを把握するために自社のほかのサービスと比べましたが、それらと比べても低い水準であることを確認しました。 経験の少ない自分としては情報は多い方がありがたく、自社のほかのサービスのスコアの方が上ということは自分にないノウハウが社内にあるはずだと考え、社内のエンジニアに help を出し情報を集めました。 その甲斐もあって、Lighthouse に直接指摘されている事項以外にも、パフォーマンス改善点を把握できました。 実際に対応した内容は以下の通りです。 text compression JavaScript の遅延読み込み css の@import 廃止 画像の Webp 化 text compression これが改善貢献度としては一番大きいものとなりました。 サーバからクライアントに静的ファイルを送信する際に圧縮した方が良いというものです。 静的なファイルなのできればアプリケーションから返すのではなく Contents Delivery Network を用いて実現したかったのですが。今回はアプリケーションから返す時に使用されるミドルウェアで gzip するという対応になりました。 一度用意すればあとはすべてのコンテンツが圧縮されて返されるので、それほど大がかりな対応なく効率的にパフォーマンスを改善できた点が良かったと思います。 JavaScript の遅延読み込み これは一番時間がかかった作業になります。 対応内容としては JavaScript の読み込みを遅延するというものです。 async/defer 属性を付与することによって対応すればよいのですが、遅延されていない既存のページをリグレッションなく遅延させるのは、一筋縄ではいきませんでした。 既存のページは html 読み込み時にインラインスクリプトでJavaScript も読み込まれているため、単純にJavaScriptファイルを遅延読み込みしてしまうと動作しないコードでした。 下記は該当のインラインスクリプトの一例です。 < script > namespace ( "PriceMap.constants.MAP_CONFIG" ) .zoom = 16 ; namespace ( "PriceMap.constants.MAP_CONFIG" ) .center = { lat: 35 . 4477777777778 , lng: 139 . 6425 , } ; </ script > lat と lng に実数を指定していますが、これらはテンプレートエンジンでは変数が指定されている箇所です。 テンプレートエンジンで変換する処理ですので、遅延読み込みすれば解決するような状態ではありませんでした。 まず最初に単に遅延読み込みを試しますが、js エラーが出ました。undefined のエラーだったのですが、まさに読み込み順の関係です。 テンプレートエンジン内に JavaScript が実装されていたので、こちらに関しても他ファイルに切り出したうえで遅延読み込みさせる必要がありました。 またテンプレートエンジン内に実装されていたコードはサーバサイドでの処理結果に応じて動的に JavaScript を作成する記述でした。 そのため、単にファイルとして切り出すのも難しい状況でした。 最終的には、3 段階で変更しました。 動的に変更になる、JavaScript 連携したいデータを data 属性で HTML に定義 インラインスクリプトを撤廃し、js ファイルに移築する JavaScript を遅延読み込み これらの変更はデータの渡り方や JavaScript の実行順が変更となる対応でしたので、テスト観点が多い作業となりました。 フロントエンドパフォーマンス改善の中で一番作業量が多かったかなと思います。 しかし作業の過程で既存の処理に関する理解が深まったので、ほかのリファクタリングも同時に行えたので、結果的には良い改善が行えたかなと考えています。 css の@import 廃止 これは社内の有識者からアドバイスいただいた項目で、css における@import が低速なので避けましょうということでした。 実際にこの記述はリポジトリ内には存在せず、アセットパイプラインを通して作成される記述のようでした。 結局 bootswatch-rails という gem が生成するものだと判明したのですが、ここまで見つけるのにかなり苦労しました。 生成される@import の行をソースコードに含む GitHub リポジトリのうち、プライスマップが使っているかもしれない gem をあたって見つけました。 原因が見つかった後はこの gem がどこでなぜ使われているのかを調査し、代替手段を模索していきました。 最終的にはこの gem を使用する必要はないということが分かり、使用しないように変更したうえでリグレッションテストを行うことで、無事脱却できました。 画像の Webp 化 静止画の配信方法に関しての指摘を、Webp による配信に変更することで解消しました。 サーバサイドで配信する前に png や JPEG などの画像を Webp に変換して配信するしくみを導入する方法もありますが、今回は直接元の画像を Webp に変更する方法を採りました。 変更には ffmpeg を使用し、リポジトリ内の特定の拡張子のファイルに対して一括で変換するスクリプトを使用しました。 #! /bin/sh array=`find src/app/assets/images/ -name *.png` for item in $array; do echo "ffmpeg -i ${item} ${item%.*}.webp" ffmpeg -i ${item} ${item%.*}.webp echo "rm ${item}" rm ${item} done この方法だとファイルの拡張子が変わるので、参照するコードに関しても置換する必要があります。 これは sed コマンドで一括置換を行い、コミットの際に 1 つ 1 つ差分を確認して進めていきました。 成果 最終的なパフォーマンス改善後のスコアはこのようになりました。 lighthouseスコア 少し分かりづらいですが、赤字の重要度の高い指摘について処理が短くなっており、改善していることがわかります。 一方指摘すべてを解消したわけではないので、改善の余地はまだまだありそうです。 良かったこと 指摘項目の解消を目指してリファクタリングするので実装方針を立てやすく、効果も見えやすいという点が非常に良かったです。 また指摘項目の解消を通してフロントエンドパフォーマンスの知識が身に着くので、自身の成長にもつながりました。 たいへんだったこと 既存の正常に動作しているシステムのリファクタリングですので、当たり前ですが既存のシステムの理解が重要でした。 中途半端な対応ですと既存システムがまったく動かなかったり、動くように対応しても細かい部分でリグレッションが起きたりなど、試行錯誤の連続でした。 その過程で既存のあまり詳細に意識していなかったところの構造理解が進んだり、細かい部分のサイト仕様を把握できたので学びの方が大きいのですが、それはそれとして作業としてはたいへんでした。 感想 lighthouse のパフォーマンス改善は、事業的な改善が見込め、エンジニアの成長にもつながると良いこと尽くめの印象で、ぜひ多くのエンジニアに経験してほしい体験だなと感じました。 フロントエンドエンジニアでなくとも、指摘の内容自体は広く一般的に分かりやすく説明されているものばかりですので、着手しやすいのではと思います。 まとめ 今回はフロントエンドのパフォーマンス改善に関して行ったことや、良かったこと、たいへんだったことを共有させていただきました。 もともと影響の多い text compression の指摘のみを解消すれば良い依頼だったのですが、指摘事項を確認するとほかにも改善できそうな点がいくつか見つかりました。 工数はかかりますが、いっそほかの指摘事項も対応できないか調査と対応をしても良いですか?と上長に確認を取ったところ、快く OK してもらえたのでこの作業が行えました。 対応しているときは仕様の決まっていないリファクタリングを行っている状態で、業務というよりは研究といった感触で楽しく進めることができました。 最終的にサイトのパフォーマンス評価も大幅に改善され企画サイドにも感謝されましたし、着手できてよかったなと考えています。 LIFULL ではこのようにともに成長していける仲間を募集しています。興味がわいた方はぜひともこちらのページもご覧ください。 hrmos.co hrmos.co
プロダクトエンジニアリング部の小林です。 2022年4月に新卒として入社し、アプリケーションエンジニアとしてバックエンドからフロントエンドまで幅広く開発を行っています。 この記事では、配属されたチームで取り組んだリファクタリングをどのように行ったか、その舞台裏について紹介します。 プロジェクトの技術選定と背景 技術選定 Clean Architecture x Next.js x TypeScript アプリケーション実行基盤: 内製ライブラリ「KEEL」 開発からリリースまで 理想の状態 リリース時の状態 リファクタ開始 リファクタの方針と準備 リファクタの手順 まとめ プロジェクトの技術選定と背景 技術選定 配属されたチームでは新規のtoC向けプロダクトの開発が始まっていました。 技術仕様は以下のとおりです。 アーキテクチャ: Clean Architecture フレームワーク: Next.js x TypeScript アプリケーション実行基盤: 内製ライブラリ「KEEL」 Clean Architecture x Next.js x TypeScript LIFULLでは3年前にリアーキテクティングプロジェクトが発足し、ソフトウェアアーキテクチャのベースにClean Architecture、言語にTypeScriptを採用し、新たなAPI(Backend For Frontend)を開発してきました。 新規開発でも基本的に同じ思想に基づいて設計を行います。 www.lifull.blog しかし今回はLIFULL HOME'Sの中心である物件情報を提供するシステムをリアーキテクティングする開発ではありません。 さらにモノレポで開発を行いたい事情があったため、Next.jsのAPI Routeで構築できるAPIをBackend For Frontendとして利用することにしました。 システム構成図 社内で先行してClean Architecture x TypeScriptでモノレポ開発をしているチームがあるため、多分に参考としています。 www.lifull.blog アプリケーション実行基盤: 内製ライブラリ「KEEL」 全社で利用している内製ライブラリ「KEEL」を利用しています。開発者がアプリケーション開発に集中できる素晴らしい基盤です。 詳細は下記の記事から御覧ください。 www.lifull.blog 開発からリリースまで チームではClean Architecture x TypeScriptなBFFの開発は初めてでした。 チャレンジングな開発としてアーキテクトグループによるレクチャーを受けながら動き出しており、配属から3ヵ月で最初のリリースが予定されていました。 私は開発経験こそあるものの、機械学習やゲーム開発がメインだったため、Web系の言語は入社してから触れています。 TypeScriptとClean Architecture、どちらも初めての勉強の毎日でしたが、エンドポイントの一部を開発する中で理解を深めることができました。 1つのエンドポイントを層と機能で分割することで開発タスクを分割できるのもClean Architectureの良いところですね。 さて、無事にリリースを迎え一安心かと思いきや… 私はリリース直前となったころ、大きな問題に気付きます。 「このプロダクト、Clean Architectureになっていない…!?」 理想の状態 理想の状態 Clean Architectureは層で役割を分け、依存関係を内向きにすることで外部に依存しない状態を理想とするものです。 Next.jsや外のAPIに依存するのは下の図で言うところの緑の層までにとどめ、ビジネスロジックは外部に依存しないように設計することで、APIやDB、フレームワークの仕様変更への対処が簡便になります。 なおかつドメイン知識を集約したクラスを作り、値オブジェクトとして利用することで入ってくる値をバリデーションする役割も担います。 リリース時の状態 リリース時の状態 一方、勉強しながら手を動かして実装していく中でアーキテクチャへの理解を深めることができ、その結果認識したプロダクトの実態は以下の通りです。 層ごとにファイルは分かれている ドメイン知識が集約されておらず、GatewayやUseCaseに分散している Next.jsの型に強く依存している (Next.jsのRequest型をGatewayまでトンネルしている) 社内APIに強く依存している (社内APIからのレスポンスをkeyとprimitiveな値をそのままにPresenterまでトンネルしている) つまり、ファイル数が多く複雑であるのに外部に強く依存したClean Architectureとは呼べないシステムになっていました。 簡単に言えば現状の社内APIを使うことを前提としたコードになっていて、いつの日か 社内APIが新しいものに置き換わったとき フロントを司るNext.jsが開発終了・破壊的変更をしたとき にこのプロダクトは7割方のコードを書き換える必要がある作りになっていました。 リファクタ開始 プロダクトの状態に気付いたものの、動作は正常であり、リリース予定日が目の前であったためそのままリリースすることになりました。 その前提で上長に状態と対策案を伝え、リファクタに工数を割くことにGOサインをもらいました。 リファクタの方針と準備 まずリファクタの方針と優先順位を定めました。 社内APIに対する依存を切る Next.jsとの依存を切る ドメインクラスを育成する基盤を作る その次に準備を行い、チームやアーキテクトグループの協力を仰ぎました。 アーキテクトグループとの相談 作業内容の切り分け、順序の設定、チケット化 チケットのレビュー (影響範囲を小さくしながら細かい粒度でアジャイル的に進めるため、確認は必須と考えました。) リファクタ実行 リファクタの手順 社内APIへの依存を切る (レスポンスを型変換する層を作る) APIからの戻り値に型をつけたInterfaceを作る RepositoryにUseCase層で使う値のInterfaceを作る APIから受け取った値をRepositoryに詰め直す処理を作る このとき、詰め替え元の方として1で作った型を使い、詰替え先の方として2で作成した型を使う Next.jsへの依存を切る ドメインを固める 以上の手順でリファクタを行いました。 私が学びながらの開発でアーキテクチャの理解が進んだからと言っても開発の経験は少ないので、まずはより良い状態を目指すためにアーキテクトグループの方にモブプログラミングをしてもらいました。 モブプログラミングの中でClean Architectureに頻出のTypeScriptの技法やリファクタ技法を学びました。 社内APIにアクセスするInteractorからGatewayまでの流れの一つをモブプログラミングとしてリファクタしたうえで、その学びを元に残りのコードを自らリファクタしました。 またリファクタと並行して ユニットテストの導入・API統合テストの導入 OpenAPI / Swaggerの実装 ESLintの設定の最適化 を行い、プロダクトの健全化を図りました。全体を通して開発者の心理的安全性がかなり高まったと感じています。 まとめ 今回、リファクタをすることで、Clean Architectureに関する知識を深めることができ、TypeScriptの型に対する理解が深まりました。 またリファクタで得た問題点と解決策や知見をチームに共有することで、並行していた開発が進んでいた新機能の品質も向上しました。 Clean Architectureに限らず、大規模なリファクタを行う際には 知見のあるスペシャリストに事前相談する チームに随時情報共有を行い、負債を作らないチームを目指す 自分の理解度が低い場合にはモブプログラミングも有効 ということを学べたことが最大の成果です。 今後も高品質で開発者体験の良いプロダクトを育てながら、ユーザーに価値を届ける基盤としていきたいと考えています。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
エンジニアの内藤です。LIFULL HOME'Sの売買領域を支えるエンジニアチームのマネジメントを担当しています。 弊社では「ネガポジミーティング」と称し、課題を出し合う「ネガティブミーティング」と課題の解決策を決める「ポジティブミーティング」をセットで実施しています。 前回「 ネガティブミーティング 」を実施し、今回はその対となる「ポジティブミーティング」を企画し実施したので、そちらを紹介したいと思います。 ネガポジミーティングとは ネガティブミーティングとは ポジティブミーティングとは ワークの流れ やってみた結果 まとめ ネガポジミーティングとは ネガティブミーティングとは ネガティブミーティングは課題だけを挙げるミーティングで解決策などは議論しない 課題を出すことだけに集中するため、普段だと言いづらい反対意見や代替意見も思い切って伝えることができる ポジティブミーティングとは ネガティブミーティングで出た課題の解決策を決めるミーティング 出た意見の良し悪しは評価せず、まずは提案自体に感謝する 実行する解決策を決めたら「誰が」「なにを」「いつまでに」を決める ワークの流れ 今回のワークでは前回と同じチームに分かれ、以下の流れに沿ってポジティブミーティングを実施しました。 ネガティブミーティングで出た課題から解決したい課題を選択する 解決案を出し尽くす 出た解決策をブラッシュアップして、具体的に「なにをするのか」決める 解決策やそこに至った過程を全体に共有 本来は「誰が」「いつまでに」も決めるべきなのですが、確保した時間では足りなそうでしたので解決策を決めるところまで実施することにしました。 ネガティブミーティングでは悲観的にならず笑顔で終えられるしくみとして、チーム内で最も共感された課題を川柳(または短歌)にまとめるような工夫をしました。 ポジティブミーティングではそのような配慮は不要だと考え、目的である課題解決に向けた実現性のある解決策が立案されることに注力することにしました。 具体的には自分たちで実行できる手段・実行するのに現実的なコストであり、すぐに動きだせる解決策が出せるように工夫をしました。 そこでロジックツリーを利用して課題の要素を分解しながら掘り下げ、その各要素に対する部分的な解決策を出すことで多くの解決案を出す方法で実施してみました。 やってみた結果 40分をワークの時間に充てましたが、ロジックツリーを利用して20ぐらいの要素に細分化するチームなどもあり解決策を出し尽くすには少し時間が足りなかったと思いました。 しかし、ブラッシュアップされた解決策はしっかりとエンジニア特有の視点で捉えられた実現性のある解決策となっていました。 例えば、「期間終了後のABテストが放置されがち」という課題に対して「AB実装時に本実装(AはrevertPR,Bの実装PR)を用意する」という解決策となりました。 「期間が空きすぎない方が実装が頭に残っているので本実装等の対応も早い」などエンジニアでないと気付き難い点も考慮した上での解決策の選択で、非常に有益なワークになったと感じました。 また、ロジックツリーによる要素の分解では、「本実装が計画に入っていない」「企画のモチベーション」など解決策が他者任せになりそうな要素もありましたが、期待通り自分たちで実行できる部分にフォーカスした解決策が選択されていました。 ロジックツリーの例 その他にも「俗人化しているプロダクトの開発効率が悪い」という課題に対して「知見共有会」「readmeを充実化」「できるところの自動化」などエンジニアらしい解決策が出ていました。 まとめ ロジックツリーを利用して課題を細分化することで、狙い通り自分たちで実行できる手段・実行するのに現実的なコストのすぐに動きだせる解決策を出せたワークになったと思います。 今回は解決策を決めるところまででしたが、「誰が」「いつまでに」も決めて実行していくことで課題を解決しより良い組織や職場環境にしていきたいと思います。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは。私はLIFULLで LIFULL HOME'S不動産査定 と ホームズマンション売却 の開発をしている新卒二年目の韓国人の開発者、ジョン ヨンソクです。入社当初に比べて、日本語によるコミュニケーションには慣れてきましたが、一方で成長が止まったのではないかと感じています。 そこで、これまでのやり方を振り返り、コミュニケーションに対する工夫や言語的な困難を克服するための方法を考え、さらなる成長を目指す話を共有したいと思います。 振り返り 基準を定める 捨てる要素 敬語 不安 重要な要素 相手を配慮 「結論 → 理由 → 事例」話法 課題 言語化能力の向上 日本語の専門用語を習得 まとめ PR 外国籍のお部屋探し 仲間募集 振り返り 日本語で仕事をするようになってから、ある程度の時間が経ち、徐々に慣れてきたと感じます。 年数が経過するにつれて、業務範囲が自然と広がり、より高度なコミュニケーション能力が求められる場面が増えてきました。また、今期からの状況の変化もあり、自身のコミュニケーション能力についてあらためて振り返ってみると、成長しているのかどうか疑問を感じました。 今までは、外国人としてコミュニケーション能力を上げるために、分からない専門用語とカタカナ語を暗記することに優先順位を置いてきました。入社当初は、この方法によってコミュニケーション能力が向上していることを感じられました。 しかし、このやり方のせいで私の中でコミュニケーションにおいて重要なことは完璧な日本語になっていました。チャットを送る際には、間違った日本語を使っていないかという心配で、正しい日本語を求めて調べることに時間を使うことが多かったです。結果的に円滑なコミュニケーションになっていないことに気付き、円滑なコミュニケーションになるための基準を定めました。 基準を定める 捨てる要素 敬語 敬語は、ある程度理解はしているつもりですが、正しい使い方に自信が持てないことが多くあります。 そのため、正確に区別して使用することは非常に難しく、文章を作るのに倍以上の時間がかかり、使用する際に不安を感じることがあります。 しかし、大切なのは円滑なコミュニケーションです。外国人という立場を活かし、少し間違っても良いという気持ちで、すばやく回答することを優先することにしました。 敬語は、現在持っている言語能力を十分に活用するために捨てる要素であり、この問題は今後解決すべき課題です。 不安 日本語を間違って使っているかもしれないという不安があります。この不安をなくすため、完璧な文章を作ろうとする傾向があります。チャットで何か文章を送信する際にも、何度も調べたり修正したりして時間がかかってしまうことがあります。 しかし、口頭で話すときは、不安を感じる暇もなく会話が流れます。口頭での会話のように、チャットでも不安を感じる暇を与えずに、いったん文章を送信することを意識することにしました。 一方で、内容がめちゃくちゃになることもありますが、それでも不安をなくすことが目的ですので、この問題も今後解決すべき課題です。 重要な要素 相手を配慮 捨てる要素が2つあるため、つい不適切な表現をしてしまうことがあります。そのため、お互いに気持ちよいコミュニケーションのためには、相手に配慮する心構えが最も重要だと考えます。 たとえば、コードレビューの際には、相手のコードを尊重し、ネガティブなコメントは避けるように心がけます。また、修正が必要な場合は、命令形ではなく提案形で伝えるようにしています。"いかがでしょうか"という表現をよく使います。 一方で、相手を配慮することが重要ですが、やり過ぎると不明瞭な表現になってしまうことがあります。明確な伝え方が必要な場合もありますので、過去の私の表現を振り返り、良い例を探して改善していく努力が欠かせません。 「結論 → 理由 → 事例」話法 この話法は、文書やプレゼンテーションなどで重要な内容を効果的に伝えるために役立ちます。 まず、結論を述べることで聞く人が主要な内容をすばやく理解でき、その後に理由や背景を説明することで聞く人がなぜその結論が導かれたのかを理解できます。 最後に、具体例を挙げて内容を具体化することで、聞く人がより明確に理解できます。 ただし、この方法が常に最適なわけではありません。たとえば、長い話をする場合には、順番に話す方が適しているかもしれません。また、状況によっては詳細が必要な場合もありますので、そのような場合には、より詳細を含めて話す方が適切かもしれません。 したがって、これらの方法は状況に応じて適切に使用する必要があります。しかし、通常の状況では、この方法がコミュニケーションに効果的であることが多いため、私もこの方法で話すことが多いです。 課題 コミュニケーションにおいて基準を定めて、実行しました。結果、チャットでのコミュニケーションは以前に比べて迷いも少なく、スムーズになったと感じています。 ただし、リアルタイムで反応が必要な会議などのコミュニケーションにおいては、理解が遅れたり、発言する機会を逸したりするなどの課題が残っています。これらは今後克服しなければならない課題であり、そのために2つの方向性を考えました。 言語化能力の向上 1つ目は言語化を練習することです。 私の意思を迅速に整理し、明確に伝える能力を身につけるためには、言語表現の練習が必要だと考えました。 私は個人ブログを開設し、韓国語で何でも書く練習を始めました。言語表現の練習が目的であれば、母国語であっても問題ないと思います。もちろん、慣れてくると日本語で作文することもできるようになるでしょう。 日本語の専門用語を習得 1年間で、技術的に足りない部分をAWSとLinux関連の勉強で補い、資格を取得しました。試験はすべて日本語で受けました。理由は、日本語の専門用語が理解できない時も多い一方、どのように勉強すれば良いか悩んでいたため、この機会に習得できると考えました。 業務に関連する勉強をする際には、日本語で並行して勉強することで、専門用語の習得につながると考えました。次に取り組むアクションとして、年に1冊の専門書籍を日本語で読んで文章で整理することを考えています。 まとめ 外国人である私が日本語によるコミュニケーションに慣れるため、これまでのやり方を振り返り、コミュニケーションへの工夫をしました。 具体的には、完璧な日本語にこだわらず、相手に配慮しながらも明確な表現をすることを心がけ、迅速な回答をすることを優先するようにしました。 私が定めた基準を実行するためには、自分が置かれている環境も重要だと考えます。LIFULLでは、率直で建設的なコミュニケーションを重視し、誰かが成長のための行動に対しては支援を惜しまない企業文化があります。 何かを実行する際に躊躇することなく、行動できる企業文化は 実際の業務 を通じて実感できました。そのため、本当は欠かせない要素たちを捨て、行動するなどの取り組みが試しやすかったと思います。 今後も、言語化能力の向上と日本語の専門用語の習得を目指し、今後克服すべき課題として取り組んでいくつもりです。 PR 外国籍のお部屋探し 一人で部屋を探す際、外国籍であることが理由で何度か断られた経験があります。外国籍などの普通でないバックグラウンドがあると、偏見を持たれることがあるのだと感じました。 そんな方々に向けて、さまざまなバックグラウンドの方に理解があり“ありのまま”住まい探しの相談ができる不動産会社検索サイト「LIFULL HOME'S FRIENDLY DOOR」を紹介します。 お部屋探しの際は、ぜひご利用ください。 actionforall.homes.co.jp 仲間募集 最後に、LIFULL では一緒に働く仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは。エンジニアの渡邉です。普段はLIFULL HOME'Sの売買領域のエンジニアチームにて開発を担当しています。好きなApache SolrのAPIはJSON Facet APIです。 今回はLIFULL HOME'Sの画像変換サーバの構成を見直し、アプリケーションの再構築並びに基盤刷新を行い、コスト面、性能面、運用面での改善を行った話をさせていただきます。 画像変換サーバの特性 開発における課題 課題1: 開発しづらいアプリケーション 課題2: インフラリソースの最適化の余地の向上 実際にやったこと 実際の画像変換サーバを読み解く コンテナベースに置き換える 実行基盤をEC2からKubernetesに変更する 負荷テストの実施 スケール戦略の変更 カナリアリリースの実施 成果 開発速度の向上 運用コストの軽減 レスポンス速度の向上 既存の構成・知識の集約 まとめ 画像変換サーバの特性 弊社ではLIFULL HOME'Sを始め、さまざまなアプリケーションから画像変換サーバを利用しています。 今回移行した画像変換サーバは、nginx用の動的画像モジュールの ngx_small_light を採用した自作のアプリケーションとなっています。 主な役割について簡単に説明すると、呼び出し元のアプリケーションが画像を取得する際に画像変換サーバは画像のサイズと画質を動的に変換し、アプリケーションに返却する役割を担っています。 常に数千RPSを受け取り、繁忙期にはさらに多くのリクエストが増えます。 開発における課題 もともと画像変換サーバを移行するきっかけになったのは私がLIFULL HOME'Sの画像の最適化を行いたいと思ったのがきっかけでした。 ユーザーに対して品質のよい画像を届けたり優れた体験のために読み込み速度を改善したいというモチベーションで改善を行いたいと思っていました。 LIFULL HOME'Sでは物件の内観や外観の画像を始め、ほとんどのページで画像を画像変換サーバから取得しています。 LIFULL HOME'Sのような不動産情報ポータルサイトにおける画像の重要性は非常に高く、画像配信を行うアプリケーションに機能改修を行うことでサイト全体の大幅なパワーアップを行うことができます。 しかしながらそれを実現するにあたりさまざまな課題を感じ、今のまま改修をするのは効率が良くないと思い、開発体験の向上を行う手段を講じることにしました。 開発体験の向上を行うにあたり、システム刷新をするかアプリケーションの新基盤刷新を行うかを天秤に取ったところ、画像変換サーバを利用するアプリケーションが多いことから影響範囲の広さと実装コストなどさまざまな要因から検討を重ねた結果、今回はアプリケーションの新基盤移行を行うことにしました。 課題1: 開発しづらいアプリケーション 既存の画像変換サーバは、アプリケーション開発者にとって改修しづらいアプリケーションになっていました。 その要因は大きく分けて以下の2点です。 運用をしているチームがインフラ開発チームであること アプリケーションの検証環境を気軽に用意できないこと 画像変換サーバは大量のインスタンスにより動作するアプリケーションであり、インフラチームの管轄として運用されていました。 そのためアプリケーション開発者には制限が多く、 検証サーバの作成やデプロイなどにも手間がかかり画像変換サーバを改修することが容易ではありませんでした。 また、開発にはインフラチームに検証環境を用意してもらう必要があり、すぐに検証を行うことができないことも開発速度を遅くしていました。 したがって、開発者が手元のマシンで気軽にアプリケーションを立ち上げるしくみを用意する必要がありました。 課題2: インフラリソースの最適化の余地の向上 画像変換サーバに対して圧縮率の変更やフォーマットのWebP化などの機能改修を行いたいモチベーションがあるのはアプリケーション開発者にあることが多いです。 また、インフラリソースなどの変更に対する権限もアプリケーションの特性を把握しやすいアプリケーション開発者が持つことによるメリットは多いと思います。 しかしながら、現状の組織運用体制ではアプリケーション開発者に検証と調整を気軽に行うことができませんでした。 したがって、アプリケーション開発者に自由にインフラリソースを変更する権限を与えられるようにすることが必要だと考えていました。 また、もともとの画像変換サーバはEC2インスタンスを非常に多く起動させるアプリケーションであることからコスト面での課題を抱えていました。 弊社のアプリケーションのほとんどが画像変換サーバを利用していることから、サーバがダウンしてしまうことに対するリスクは非常に大きいです。 そのため唐突なスパイクリクエストが来ても問題なくアプリケーションが動作できるだけの運用台数を用意する必要がありました。 大きめのEC2インスタンスを数十台で運用し、高コストでの運用が続いていました。 実際にやったこと 上記に挙げた課題を解決するためにやったことをセクションベースで記載していきます。 実際の画像変換サーバを読み解く 画像変換サーバを刷新することを決めたものの、その状況は芳しくないものでした。 というのも古いアプリケーションであり機能改修が少なかったこともあり、実際の運用やアプリケーションについての特性を正しく把握している人が社内に存在していませんでした。 したがって実際のデプロイフローやnginxの内容から何をしているアプリケーションなのかやデプロイに必要な構成要素を洗い出す必要がありました。 まずデプロイ内容やインフラ構成については、既存の画像変換サーバはリポジトリ管理等もされておらず、リリースに必要なものが各所に点在している状況でした。 ですので、当時デプロイ運用で利用されていたAnsible playbookとCloudFormationの内容を読み解き、デプロイに必要な要素を一つ一つ洗い出しつつまとめていきました。 またアプリケーションを理解している人もいない状況でした。 そこで、インフラチームに既存と同一のインスタンスを準備してもらった上で、nginxの内容を読み解きつつ必要なモジュールの確認や挙動の確認を進めました。 コンテナベースに置き換える 大まかな構成要素が洗い出されましたので、アプリケーションを構成し直しました。 もともと課題にあげた通り、開発者が気軽に検証環境を手元に用意できないという課題を解決するために、コンテナ化をすることにしました。 基本的なアプリケーションの構成要素やデプロイに必要なものはAnsible playbook等の内容に記載されていたので、それにしたがってコンテナ化を進めていきました。 そのタイミングで利用しているソフトウェアやBase Imageなども極力アップデートしています。 実行基盤をEC2からKubernetesに変更する 課題2に挙げた通り、アプリケーション開発者がインフラリソースを変更できるようにしたいという希望がありました。 それを実現する具体的なアプローチとして、実行基盤をKubernetesに移行することとしました。 弊社では全社的にインフラ環境をKEELという独自のKubernetesベースのアプリケーション実行基盤に移行していくことになっています。 KEELについてはこちらのエントリに記載されています。 https://www.lifull.blog/entry/2020/12/02/000000 もともとEC2で運用をされていたアプリケーションをKubernetes環境であるKEELに移行することは今回の課題であるアプリケーション開発者がインフラリソースを容易に調整をできることや検証環境を容易に作成できる点に対する明確かつ有効的なアプローチであることからKEELの実行基盤への移行を決定し、既存アプリケーションのコンテナ化を実施したうえで、KEEL環境へデプロイする形にしました。 負荷テストの実施 コンテナ化とKubernetes移行が完成したタイミングで負荷検証を実施しています。 既存の画像変換サーバが大量にリクエストされるアプリケーションであり、さまざまなアプリケーションから使われている都合上、アプリケーションの切り替えには無停止での入れ替えをする必要がありました。 したがって、既存のリクエストがさばけるように長期間にかけて、多種多様のリクエストを想定したシナリオを用意し安定してさばくことができるリソースの調整を行いました。 具体的には以下の手順にで負荷検証を実施しました。 1台のpodがさばける限界のリクエスト量を測定 一台あたりのスループットを確認 1.で求めた最大のリクエスト量の2倍を2台のpodでさばくことができるかを検証 podが増えたことで線形にスループットが増えることを検証 スケールアウトする閾値の検証 今回はcpuに負荷のかかるアプリケーションであったため、2.で実行したリクエスト量を安定してさばける台数とスケールアウトするタイミングを決定 実際に必要なpodの数を検証 3.でもとめたリクエスト量を安定してさばくことができるpodの台数から実際のリクエストを受け取る場合に必要な台数を求めて必要なpodの台数を決定 実際にプロダクトインしたケースを踏まえてsoakテスト(長時間のリクエストテスト)の実施 長時間リクエストを受け続けてもアプリケーションに問題がないことを確認 スケール戦略の変更 今回の移行時にスケールアアップ+スケールアウトの両軸で運用していた画像変換サーバをスケールアウトに寄せる形で運用しなおしました。 Kubernetes環境になったことで大きく変わったことがいくつかありますが、その一つがスケールアウトの時間が大幅に削減したことでした。 具体的な数字を挙げると、既存の画像変換サーバがスケールアウトするのには1分から2分必要だったところKubernetes環境化にすることで、15秒ほどにまで削減することが可能になりました。 それにより、スケールアウトに対する比重を非常に高く持つことができるようになりました。 したがって、サーバ1台に対してリソースの余裕を持たせる割合が少なくなり、サーバ1台あたりのリソースを削減できることができました。 また、既存の画像変換サーバは大きめのインスタンスをある程度準備している状況を常態化させる形にしていました。 したがって、朝方などのリクエストの少ない時間にリソースが余る状況になってしまっていたことも多々ありました。 そこで、一台あたりのリソースを極限に少なくし大量のサーバでさばく戦略に変えることで、リクエストの数に応じたリソース調整を行える形にすることでリソースに対する無駄を排除することにしました。 カナリアリリースの実施 今回の画像変換サーバは数多くのアプリケーションから利用されているアプリケーションであるため、無停止での移行をする必要があったので、それを行う手法としてカナリアリリースを実施しています。 約二週間の期間をかけて徐々に割合を上げていく形で切り替えていきました。 成果 上記に挙げたやったことを実施したことでいくつか成果につながることができましたので紹介します。 開発速度の向上 コンテナ化やKubernetes環境に移行したことで、自前で開発用のサーバを気軽に用意したり検証できるようになったので、今までに比べて格段に開発しやすくなりました。 そのわかりやすい例として、もともと私が基盤移行をする前に画像変換サーバに対して行いたかった改修の一つとして、画像の画質変更をするというものがありました。 内容としてはそこまで工数がかかるものではなかったのですが、既存の画像変換サーバに機能改修をすると他グループの作業にも依存するため検証環境の構築待ちなども発生しおおよそ2週間ほど時間を要してしまうことになるところ、検証まで含めて一日でリリースできるようになりました。 非常に検証が行いやすくなったことやアプリケーション開発者側にデプロイの権限が移ったことにより開発速度の圧倒的な向上を行うことができたのは機能開発に対するコストの兼ね合いからかなり大きな成果でした。 運用コストの軽減 nginxのチューニングやスケールアウト戦略に変更したことで非常にリソースを抑えることに成功しました。 リソースを縮小させた主な要因は以下のようなことが挙げられます。 pod一台あたりのリソースの縮小化 スケールアップ戦略からスケールアウト戦略への変更 一台あたりがさばけるリクエストの数を増加させる その結果、もともと大きめのEC2インスタンスを数十台で運用していたため高コストで運用していた画像変換サーバを約1/3のコストで運用することに成功しました。 レスポンス速度の向上 アプリケーションの構成を入れ替えの実施や、nginx-exporterを導入したうえでworker数の最適化を行うなどの改善をしたことにより、レスポンス速度にも大きな改善が見られました。 具体的な数値としては、平均レスポンスタイムは230msec → 110msecほどまでに短縮でき、アプリケーション全体のパフォーマンスを向上させることにつながっています。 既存の構成・知識の集約 GitHubリポジトリを今回新規に用意し、リリースに必要なものをすべて集約しました。 画像サーバはもともとリポジトリ管理されているアプリケーションではありませんでした。 したがって、アプリケーションの構成に必要なファイルやスクリプトが一ヵ所に集約されておらず、リリースに必要なものがすぐにはわからないという状態でした。 それを今回のタイミングでリポジトリ管理したことにより見るべき場所を単一化できたほか、機能改修のバージョニングを行うことができたので開発者目線で運用しやすいプロダクトになりました。 まとめ 今回はさまざまな要因から開発しづらいアプリケーションだった画像変換サーバを再構築並びに新規実行基盤に移し替えたお話をさせていただきました。 レガシーなシステムであったり、運用体制であったりと開発工数や調査工数が嵩んでしまうことというのはいたってめづらしい話しではないかと思います。 そのような場合にはたいへんだなーで終わらせず現状の課題を洗い出し、エンジニア的アプローチで改善をしてみることで、未来へつながるアプリケーションにできるかもしれません。 今回はその手法として基盤刷新をするというものでしたが、基盤刷新に限った話ではないと思いますので、さまざまなアプローチを視野に入れてアプリケーションの改善してはいかがでしょうか。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは!クリエイターの日運営委員の工藤です。 社内で熱海をフィールドとしたハッカソンを開催しました。 ハッカソンとは、ハックとマラソンを組み合わせた造語で1日から数日でエンジニアやデザイナー、プランナーでサービスのアイデアを考え開発するイベントです。 LIFULLは社会課題の解決を目指している企業であるため、実際に現地の課題を見に行ってサービスを考えるということでより課題に目を向けてサービスの開発ができるようになると考え開催しました。 今回LIFULLが運営しているLivingAnywhere Commons(LAC)が熱海に拠点があるため熱海をフィールドとしてハッカソンを行いました。テーマは「LAC熱海をさらに盛り上げるには」で、このLACの熱海拠点を盛り上げるサービスを各チームに分かれて考え開発しました。 livinganywherecommons.com ハッカソンの流れ 今回のハッカソンは1泊2日で行いました。 熱海に集合してそこからみんなでLAC熱海に移動し、熱海の現状や課題などを聞きました。 その後、チームに分かれて熱海の街のフィールドワークを行いました。実際に熱海の街を歩いたり、LAC熱海のコミュニティマネージャーに質問したり実際にある課題を調べ深堀りしていきました。 各チーム解決したい課題が見えてきたら徐々に開発に移っていきました。この写真のようなLAC熱海の宿泊施設やコワーキングスペースなどで各チームに分かれて開発しています。 成果発表 最後に2日間で開発したサービスの発表を行いました。 各チームが開発したものを簡単に紹介します。 まずはチーム「熱海の空」の「KYOU〜経由地〜」です。 こちらのチームは企画1名、エンジニア3名のチームです このサービスは、LAC周辺にいる利用者とお店の位置を表示することにより、LACユーザーと地元の方々を繋げることを目的としたものです。このアイデアは、LACで配布されている「まちあるきマップ」という地図を使って地元のお店を訪れる際、その地図をきっかけにコミュニケーションが生まれることからインスパイアされています。 続いてチーム「ムシメガネ」の「musubi」です。 こちらのチームは企画1名、デザイナー1名、エンジニア2名のチームです。 このサービスは、LAC熱海にチェックインする際にプロフィールと興味あること、話したいことを登録することで同じくLAC熱海に泊まってるユーザと話す約束ができるサービスです。これは、LAC熱海はコミュニケーションを大事にしているものの建物が3つあるためコミュニケーションが取りにくい課題に注目しました。 さいごに どちらのチームもエンジニアだけでなく、様々な職種でチームが構成されそれそれの得意分野をいかしてサービスの開発を行いました。 実際に課題のある現場を見て、生の声を聞くことでよりユーザのことを考えたサービスの開発ができたと思います。これは普段の業務でもとても重要なことなので今後の業務にもいかせたらと思います。 LIFULLでは今後もこのような社会課題などに向き合ったサービスの開発を続けていきます。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co