はじめまして、テクノロジー本部の布川です。そろそろ入社して一年が過ぎようとしています。 本記事ではLIFULL HOME'Sのページ高速化PJの一環として行われた、マイクロサービス間のhttp keepaliveを自前で有効化する取り組みについてお話しさせていただきます。 同じように高速化を行おうとしている方々の参考になればとても幸いです。 背景と目的 プラットフォームGと高速化について 2021年6月にGoogleがWebサイトの性能指標としてCore Web Vitalsを導入してから、サイトの読み込みや操作に対する応答の速度、レイアウトの安定性といった指標がSEOに影響を与えるようになりました。私の所属するプラットフォームGの高速化チームは、LIFULL HOME'Sの主要なページのパフォーマンスを監視しながら、主にサイトの読み込み速度の改善に取り組んでいます。 サイトの読み込み速度の改善方法は、大きくフロントエンドからのアプローチとバックエンドからのアプローチの二種類に分かれます。特に後者について挙げられる手段は、システムの構成や使われている言語やフレームワークなどのさまざまな条件に左右されるため多岐に渡りますが、どれも「できるだけ早くブラウザにレスポンスを返す」ことに繋がっています。 今回の話は、バックエンドからのアプローチに寄ったものになります。 LIFULLにおけるパフォーマンス改善に向けた取り組み 弊社の提供する不動産ポータルサイトであるLIFULL HOME'Sは、KEELと呼ばれるkubernetesベースのアプリケーション実行基盤の上で連携する複数のコンポーネントによって構成されています。システムを構成する要素が複数ある場合はボトルネックの特定が難しくなるため、横断的なパフォーマンス改善が難しくなってしまう側面がありますが、KEELがLIFULL HOME'Sに向けて提供しているログの横断検索機能やリクエストの分散トレーシング機能を活用することで、効率の良い改善活動を行うことができています。 www.lifull.blog KEELの上ではLIFULL HOME'Sに直接関係するサービスの他にもたくさんのアプリケーションが動いており、その一つ一つにパフォーマンス改善に関わる機能以外にも、自動デプロイフローやサービスメッシュ、セキュリティなど運用上必要になるさまざまなものが提供されています。 ボトルネックの発見 KEELでは、サービスメッシュとして導入しているistioが各マイクロサービスに付加するistio-proxyによって、サービスが通信時に必要とするトラフィックの管理などに関する機能を提供しています。しかしリクエストを直列に処理するモデルの関係上、一部のマイクロサービスではパフォーマンスの観点からistio-proxyの付与を無効にして、直接相手のサーバとやり取りしていることがあります。 LIFULL HOME'Sではフロントサーバの裏でAPIサーバやページ毎のリソースを集約するBFFが動いていますが、現状これらのサーバではistio-proxyが無効化されています。そのためこれらのサーバ間で通信を行う際は、リクエストのたびに接続を張り直したりリトライ処理が正しく行われていなかったりといった問題があり、特に都度接続を張り直すことに関してはサイトの読み込み速度の低下に繋がっていることが考えられました。そこで、httpクライアントの実装やサーバ側の設定を適切に行うことで、istio-proxyの無効化により失われていた機能のうちhttp keepaliveやリトライ機能を自前で有効化することにしました。 課題と対応 マイクロサービス間のhttp keepaliveを自前で有効化する(=リクエスト/レスポンスのConnectionヘッダにkeep-aliveを指定する)にあたり、解決すべき課題がいくつかありました。 内外のコンポーネントへの影響調査 コネクションプールの作成 スケーリング時の対応 その中でも主な課題について、それぞれの詳細と行った対応について説明していきます。 内外のコンポーネントへの影響調査 http keepaliveを有効化するとクライアント-サーバ間で一度張られたtcp接続は一定期間保持されるようになりますが、この時ある時間帯に張られる接続の数は減少するものの、あるタイミングで同時に張られる接続の数は増加します。そのため、サーバが同時管理する接続数が現状からどの程度増え、それが許容できる範囲なのか否かを調査しました。 その結果、APIサーバで一つのpodに対して同時に張られる可能性のある接続の数と比較して、それぞれのpodにいるnginxが管理できる接続数に十分余裕があることを確認しました。 また、APIサーバに直接接続してくる他のサービスのhttpクライアントが、APIサーバ側のhttp keepaliveが有効な場合は接続を使い回すようになるのか、その際接続が切れたら繋ぎ直すなどの必要な処理を正しく行えているのかといった他サービスの調査も念の為行いました。 実際、istio-proxyを無効にしてAPIサーバに直接アクセスしてくるマイクロサービスは他に2つほどあり、それぞれのhttpクライアントはnode(v14), python(v3.7)のものを使っていましたが、以下のように問題がないことを確認しました。 node(v14): デフォルトでkeepalive無効( 参考 ) python(v3.7): デフォルトでkeepalive有効、切断時のハンドリングは正しく行われている( 参考 ) コネクションプールの作成 先ほどの図のようにBFFにおいては一つのpodの中で複数のpassanger worker processが動いており、それぞれのworker processがフロントエンドサーバから受け取った一つのリクエストを処理しています。このworker processがスレッド並列でAPIサーバにリクエストを投げる際に張る接続を、http keepaliveによって一定期間保持して使い回そうとしています。 この時、あるworker processがAPIサーバに対して逐次的なリクエストしか行わないのであれば、worker process毎に保持する接続は一つで事足ります。しかし、worker processがAPIサーバに対してスレッド並列的にリクエストを投げる場合、worker process毎に複数の接続をスレッドセーフな状態で保持する必要があります。 これについては、worker process毎に一つのコネクションプールを保持し、リクエストを投げる際はプール内の使用可能な接続を使い回せるようにすることで解決しました。BFF上で動くアプリケーションはrubyによって記述されていますが、rubyの標準的なhttpクライアントであるnet/httpはコネクションプールの機構を持たないため、新しく Gem を導入して実装を行いました。 スケーリング時の対応 現在のkubernetesの運用では、事前に定義したHorizontal Autoscaling Policyに基づいて15秒毎にスケールするか否かの判断を行います。この時、APIサーバのスケールインによって接続先のホストがいなくなった場合やスケールアウトによって接続可能なホストが増えた場合のハンドリングを、BFF上のアプリケーションで正しく行う必要がありました。 スケールイン: APIサーバ側のpod数が減少するとBFF側で保持しているいくつかの接続では通信相手がいなくなるため、繋ぎ直しを行う必要があります。net/httpはあるtcp接続を使い回すにあたり自分もしくは相手からの終了処理によってソケットが閉じられていた際は次回使用時に自動的に再接続を行うため、この仕様とkubernetesのルーティング機能によってまだ生きているAPIサーバのpodと接続を張り直すことができました。 スケールアウト: APIサーバ側のpod数が増加しても、BFF側で新しく増えたpodに対して一部の接続を張り直すよう能動的に行動を起こすことはできません。また、kubernetesのデフォルトの通信機構ではkube-proxyによってラウンドロビンなリクエストの振り分けが行われているため、そのままでは新しく生成されたpodへの接続数が少なく偏ってしまいます。この問題についてはAPIサーバ側でkeepalive timeoutをスケール頻度である15秒に調整することで、BFFが定期的に接続を張り直せるようにしました。こうすることによって定期的な接続のバランシングが行われ、APIサーバの一つのpodあたりの接続数はほぼ均等に保たれます。 改善の様子 以上の取り組みによってistio-proxyが無効なマイクロサービス間のhttp keepaliveを有効化することができ、以下のように接続の張り直しがリクエストの度に行われなくなりました。 これによって、特に多くのリソースを要求するページにおけるサーバーレスポンスタイムが以下のように改善しました。 上のグラフは開発環境で各条件ごとに調査した結果ですが、本番環境でも同ページのリリース前日とリリース翌日の24時間のサーバーレスポンスタイムの平均を比較すると、34ms程の改善が見られました。程度に差はあれど裏側でリソースを要求するLIFULL HOME'Sの全ページが今回の施策の恩恵を受けることになります。 また、直接的なパフォーマンスの改善の他にも、active connection数の低減などといった効果が見られました。 まとめ マイクロサービス間のhttp keepaliveを自前で有効化する取り組みについて、接続を使い回すことに伴ういろいろな問題に対処しながら実装を行い、パフォーマンスの改善に繋げたお話でした。ここまで読んでいただきありがとうございました。 今回の施策によって生まれた改善は小さいように見えますが、このような小さな改善を積み重ねる過程でノウハウを蓄積させ、次の改善やパフォーマンスの劣化防止に繋げていくことが重要だと感じます。また、タスクの中ではkubernetesがいかに多くのことをやってくれているかや、インフラやミドルウェアに対する理解が高速化の文脈でも重要になってくることを感じました。 社内の様々な人に助けてもらいながらリリースに漕ぎ着けたこちらのタスクですが、今後は今回の経験と反省をもとに更なるパフォーマンス改善に取り組みSEOに強く良い体験のできるサイトに近付けて行ければと思います。 最後に、LIFULLでは共に成長できるような仲間を募っています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
フロントエンドエンジニアの嶌田です。株式会社 LIFULL でプロダクトのアクセシビリティ向上をミッションとして活動しています。 本日は、不動産・住宅情報の総合サービスである LIFULL HOME'S のスマートフォンサイト において、過去半年間で実施したアクセシビリティ向上施策をご紹介します。ご紹介する施策のうちいくつかは、内容を掘り下げて実装コードを交えて解説をしていきます。 それでは、早速アクセシビリティ向上のために実施した施策を見ていきましょう。 ボタンを正しくボタンにする 追加コンテンツを読み込む機能のフォーカス管理 チェックボックスに適切な名前を付ける カルーセルをアクセシブルにする スクリーンリーダーによる検索結果の件数の読み上げ そのほかの改善点 おわりに ボタンを正しくボタンにする ウェブサイトにおいて、ユーザーがアクションを実行するためにボタンが用いられます。ボタンは通常 button 要素を使って実装するのが望ましいですが、適切ではない要素(例えば a 要素、 span 要素、 p 要素など)を使って実装された箇所が数多くありました。これでは キーボード操作ができず、スクリーンリーダーにも「ボタン」であることが伝わりません 。そこで、私たちはこれらの要素を正しくボタンにし、キーボード操作が可能になるよう改善を行いました。 p 要素、 dt 要素、 a 要素、 span 要素など、様々な要素で実装されたボタンたち button 要素に差し替えるのが王道です。とはいえ、 button 要素がデフォルトで持っているスタイルのリセットや、要素セレクタに依存した JS の処理の存在を考慮すると、要素の書き換えによって予期しない不具合を呼び込んでしまうかもしれません。ノーリスクかつ最小工数を目指したかった私たちは、WAI-ARIA を使って役割を上書きすることにしました。 次のような HTML があったとします。 < p class = "load-more" > もっと見る </ p > p 要素はデフォルトで paragraph の役割を持っています 1 。ボタンではありません。ボタンであることを示すために、 role 属性を使って役割を上書きします。 < p class = "load-more" role = "button" > もっと見る </ p > 次に、キーボードで操作できるようにします。このボタンはマウスクリックで機能が実行されるようになっていますが、キーボードフォーカスが当たらないためキーボードのみで操作できません。 tabindex="0" を設定すると、通常のインタラクティブ要素( a 要素やフォーム関連要素など)のようにフォーカスを受取るようになります。 < p class = "load-more" role = "button" tabindex = "0" > もっと見る </ p > これで問題が解決したと思いきや、キーボード操作を試すと、元々の機能が実行されないことに気付きます。 button 要素や a 要素でないと、Enter キーや Space キーが押されたときに click イベントが発火しないことが原因です。JavaScript を使ってここの穴を埋めてあげましょう。コード例 2 は Stimulus です。 < p class = "load-more" role = "button" tabindex = "0" data -controller= "button" data - action = "keydown->button#keyboardClick" > もっと見る </ p > // button_controller.js import { Controller } from '@hotwired/stimulus' ; export default class extends Controller { keyboardClick( event ) { if ( [ 'Enter' , ' ' ] .includes( event .key)) { event .preventDefault(); event .stopPropagation(); this .element.click(); } } } 以上の対応により、p 要素がボタンとしての意味と振る舞いを持つようになりました。さらに、HTML の構造が変わらないため、スタイルの調整は不要であり、既存のイベントハンドラを修正する必要もありません。 追加コンテンツを読み込む機能のフォーカス管理 LIFULL HOME'S では、クリックにより追加コンテンツが表示される UI パターンがよく使われています。このパターンでは、ボタンをクリックするとボタンが消え、追加コンテンツが表示されます。このボタンが消える特徴は、ディスクロージャー 3 と呼ばれる UI パターンとはやや異なります。 ボタンをクリックするとボタンは消え、代わりに追加のコンテンツが表示される この部分にアクセシビリティ上の問題がありました。 ボタンを押すと同時にボタンが消え、同時にキーボードフォーカスが失われてしまう のです。フォーカスの位置がわからなくなり、キーボード操作を行うユーザーにとって不便な状況が生じます。この問題を解決するために、 ボタンをクリックしたときに表示されるコンテンツにフォーカスを当てる ように改善を行いました。 次の HTML コードを想定します。追加で読み込まれるコンテンツには hidden クラスが付いています。CSS で .hidden { display: none } が指定されていると想定してください。 < div > < a href = "..." > 敷金礼金0(ゼロ・なし)の物件 </ a > < a href = "..." > 新築・築浅物件 </ a > </ div > < button class = "load-more" type = "button" > もっと見る </ button > < div class = "hidden" > < a href = "..." > 二人暮らし物件 </ a > < a href = "..." > タワーマンション(高層マンション) </ a > </ div > 追加コンテンツの表示には hidden クラスを取り除き、ボタンを非表示にするには hidden クラスを付与します。 キーボード操作の場合、元々フォーカスがあったボタンが消えてしまいます。そこで、追加で表示されるコンテンツにフォーカスを移動してあげることで、キーボード操作のユーザーが操作箇所を見失わないようにします。 これらの観点を実装すると以下のようになります。まず HTML に、 aria-controls 属性を使って、ボタンの操作対象となる要素を関連付けます 4 。 < div > < a href = "..." > 敷金礼金0(ゼロ・なし)の物件 </ a > < a href = "..." > 新築・築浅物件 </ a > </ div > < button class = "load-more" type = "button" aria-controls = "next-content" data -controller= "inlay" data - action = "inlay#show" > もっと見る </ button > < div id = "next-content" class = "hidden" > < a href = "..." > 二人暮らし物件 </ a > < a href = "..." > タワーマンション(高層マンション) </ a > </ div > JavaScript のコードは次のようになります。ボタンをクリックすると show メソッドが呼ばれます。やや複雑ですが、 追加で表示される最初のタブ可能な要素にフォーカスを当てる という処理が読み取れるでしょうか? また、コード内で使用されている tabbable 関数は、パラメーターに渡された要素内の「タブ可能な」要素を列挙し、配列として返す npm パッケージです 5 。 // inlay_controller.js import { Controller } from '@hotwired/stimulus' ; import { tabbable } from 'tabbable' ; export default class extends Controller { show() { this .nextContent.classList.remove( 'hidden' ); this .element.classList.add( 'hidden' ); this .firstTabbableItem?.focus(); } get nextContent() { let control = this .element.getAttribute( 'aria-controls' ); return document .getElementById(control); } get firstTabbableItem() { return tabbable( this .nextContent) [ 0 ] ; } } チェックボックスに適切な名前を付ける 物件の一覧ページでは、物件を複数選び、まとめて不動産屋に問い合わせたり、お気に入りに追加したりできる機能が提供されています。そのために各物件にはチェックボックスが設けられています。しかし、このチェックボックスには適切な名前が設定されておらず、スクリーンリーダーで利用するときに「クリック可能 チェックボックス チェックなし」としか読み上げられず、 何のチェックボックスなのかわからない 状況が発生しています。 スクリーンリーダーでチェックボックスにフォーカスを当てると「クリック可能 チェックボックス チェックなし」と読み上げられる HTML を見ると、チェックボックスが単体で置かれているだけで、ラベルと紐づいていないことがわかります。 < div class = "room" > < input type = "checkbox" > < div class = "spec" > < p class = "photo noImage" > No Image </ p > < p class = "spec" > -/3LDK/120.00m² 8.5万円 管理費:12,000円 敷金1ヶ月/礼金1ヶ月 </ p > </ div > </ div > この問題を解決する難しさは、チェックボックスが建物全体ではなく各部屋を対象としているため、簡潔で明確なラベルが存在しないことです。適切な名前を設定するためには、適切な場所を探し出し、それを名前として使用する必要があります。 解決策として、 部屋の間取り・面積・賃料などを表すテキスト部分をラベルとして使用 しました。チェックボックスとラベルは離れた場所にあるため、 label 要素の for 属性を使うか、チェックボックスに aria-labelledby 属性をつけることで名前を指定します。今回は事情 6 があって、 aria-labelledby を用いました。 < div class = "room" > < input type = "checkbox" aria-labelledby = "room-0123" > < div class = "spec" > < p class = "photo noImage" > No Image </ p > < p id = "room-0123" class = "spec" > -/3LDK/120.00m² 8.5万円 管理費:12,000円 敷金1ヶ月/礼金1ヶ月 </ p > </ div > </ div > この対応により、スクリーンリーダーによる読み上げは「クリック可能 -/3LDK/120.00m² 8.5万円 管理費:12,000円 敷金 1ヶ月/ 礼金 1ヶ月 チェックボックス チェックなし」となりました。正直なところ、この読み上げ方で完全に不便なく利用できるかは疑問が残りますが、何もない状態よりは改善されているでしょう。 カルーセルをアクセシブルにする カルーセルはアクセシビリティ上の問題が起きやすい UI です。スライダーなどとも呼ばれたりします。LIFULL HOME'S のトップページにもドドンとカルーセルが置かれています。カルーセルに起因する問題はいろいろとありますが、中でも致命的な問題につながるのは「自動再生」です。一定時間ごとにスライドが切り替わる機能です。 3秒ごとにスライドが切り替わるカルーセル LIFULL HOME'S のカルーセルも自動再生つきのものでした。自動再生するカルーセルは WCAG の達成基準「 2.2.2 一時停止、停止、非表示 」に違反しており、 動きのあるコンテンツの動きを追うのが苦手な人や、切り替わるスピードに合わせて文字を読み切れない人は利用が難しいでしょう。動きに気を取られて注意が維持できなくなる人は、ページのほかの部分が利用できなくなることすらあり得ます。 代表的な解決策は「一時停止ボタンを設ける」というものです。自動再生はするものの、一時停止ボタンを押せば動きは止まるため、自分のペースでスライドを見ていけるようになります。 今回、私たちは「 自動再生せず、アクセスごとにスライドをランダムに並び替える 」というアプローチをとりました。自動再生を導入するモチベーションは、複数の優先度の高いコンテンツをユーザーに提示することです。ランダムに並び替えて表示することでも同様の結果は得られるだろうと考えました。 自動再生されなくなったカルーセル うれしかった副次的効果として、もともと後ろのほうに追いやられていたスライドのクリック率が向上しました。カルーセルのスライドは、最初に表示されるスライドの優先度が高いという「大まかな」傾向はある一方、後のほうのスライドの優先度が低いとは必ずしも言えません。スライドをランダムに並び替えると、これまで埋もれていたスライドがユーザーに届くようになる可能性もあり、ランダム化は意外と多くのユースケースで有効な手法かもしれないと思いました。 ちなみに、スライドを前後に送るボタンもコントラストを高くし、視認性が向上しています。 スクリーンリーダーによる検索結果の件数の読み上げ LIFULL HOME'S では、物件を探す際に検索条件を指定することができます。現在の条件で検索した場合のヒット件数が、画面下部にリアルタイムで表示されます。 検索条件を変更すると、該当物件の件数がリアルタイムに更新される スクリーンリーダーの利用者は、このような 操作と離れた場所で変化が起こったことに気づくことができません 。件数を知るためには、フォームを送信して物件一覧ページまで遷移するか、検索条件の最下部にあるボタンの横まで読み進めなければなりません。 これを解決するために、 該当物件件数の部分をライブリージョンとしてマークアップする ことで、件数の変化がスクリーンリーダーで読み上げられるようになります。 <!-- これまで:変化がスクリーンリーダーで読み上げられない --> < p class = "itemTxt" > 該当物件 < br > < span class = "num" > 1,366 </ span > 件 </ p > <!-- 修正後:変化がスクリーンリーダーで読み上げられる --> < p class = "itemTxt" role = "status" > 該当物件 < br > < span class = "num" > 1,366 </ span > 件 </ p > role="status" という属性が追加されています。この属性によって物件数の部分が「ライブリージョン」となり、内容の変化は逐次スクリーンリーダーによって読み上げられるようになります。類似の属性として role="alert" もあり、こちらはより緊急度の高いフィードバックのために用います 7 。 さて、件数の表示はもう少しだけ複雑な仕様がありました。検索条件が変更されたあと、件数の表示が即座に行われるわけではないという点です。設定された条件の該当物件数がいくつあるかサーバーに問い合わせを行いますが、その間は件数の表示が「…」に変わるのです。 この表示になっている間はスクリーンリーダーで読み上げさせないようにしたいです。そこで、読み込みが行われている間は aria-busy 属性を true に設定することで、読み込み中はテキスト読み上げが行われないようにしました。 < p class = "itemTxt" role = "status" aria-busy = "true" > 該当物件 < br > < span class = "num" > ... </ span > 件 </ p > 以上の対応で、スクリーンリーダーを使用しているユーザーも、該当物件数を確認しながら、自由に検索条件を調整できるようになりました。 そのほかの改善点 さらに、事前に実施していたテストによって明らかになった多くの問題点も修正しています。 名前の付いていないボタンに名前を設定する 名前がなかったボタンに適切な名前を設定しました。スクリーンリーダー利用者もボタンの機能を理解しやすくなりました。 モーダルダイアログのフォーカス管理、キーボード操作対応 キーボード操作だけでモーダルダイアログを利用できるようになりました。 ピンチアウトでズームできるようにする これまでスマートフォンサイトはビューポートの設定( user-scalable=no )によってピンチアウトでのズームが制限されていましたが、この制約を解除しました。 アイコンやテキストのコントラスト改善 一部のアイコンやテキストのコントラストが不足していたため、十分なコントラストが得られるように色を調整しました 8 。 おわりに 本記事では、ウェブアクセシビリティ向上に向けた取り組みとして、いくつかの改善策について解説しました。解説した内容は多くのウェブサイトやサービスで共通するものだと思います。この内容を参考に、ぜひあなたのプロダクトのアクセシビリティ改善に役立ててください。 また、キーボードや支援技術ユーザーの皆さんには是非サイトを使ってみていただき、感想をお聞かせください。ご意見をもとに、さらにアクセスしやすいウェブサイトにしていきたい気持ちです! ここ半年くらいをかけて、LIFULL HOME'S のスマートフォンサイトのアクセシビリティを改善してきました。デスクトップサイトは次の半年で改善を加えていく予定です。テスクトップサイトは特に歴史が長く、コアなファンも多いと聞きます。これからも、一人でも多くのユーザーが快適にサービスを利用できるように品質向上に努めてまいります。 お読みいただきありがとうございました。LIFULL では共に働く仲間を募集しています! hrmos.co hrmos.co ARIA in HTML 日本語訳 (momdo.github.io) ↩ HEY のプロダクションコードから拝借したものです。 参考 ↩ ソシオメディア | ディスクロージャー (sociomedia.co.jp) ↩ aria-controls 属性である必要は必ずしもありません。操作対象の要素を特定できる仕組みとして data 属性を用いてもいいでしょう。 ↩ tabbable - npm (npmjs.com) ↩ 部屋のスペック部分が ul 要素でマークアップされていたため、 label 要素で括ることができなかったことが理由です。 ↩ role="alert" は role="status" よりも高い緊急度を持つ領域のマークアップに向いています。 role="status" の領域での変化は適度なタイミングで読み上げられる一方、 role="alert" は即座に読み上げられることが期待されます。 ↩ LIFULL のブランドカラー #ED6103 は白色 #FFFFFF と組み合わせて用いられますが、この2色間のコントラストはテキストに対して不足(3.32:1)しています。社内的には APCA のコントラスト計算方式を推奨しており、問題ない(59.7)としています。 ↩
プロダクトエンジニアリング部の千葉です。 2022年に新卒で入社して、 売却査定領域 の開発に携わっています。 この記事では、2022年10月から取り組んでいる売却査定CRO(Conversion Rate Optimization)チームの施策について紹介していきます。 CROチーム取り組み紹介 ABテスト施策実施の背景 直面した壁 高速に回るPDCA よかったこと 大変だったこと ユーザーファーストGとの連携 エラーメッセージの見せ方改善の施策化 まとめ CROチーム取り組み紹介 売却査定CROチームでは主に集客用のランディングページ、マンションの売却を検討する人のための棟情報ページなどの最適化のためにABテストの施策を行っています。 2023年3月末までに目指すCVRを定め、その目標達成のために複数のABテスト施策を実施→A寄せまたはB寄せ→改善→新たなABテスト施策の実施といったPDCAを回しています。 ABテスト施策実施の背景 集客用のランディングページは事業インパクトが大きいのですが、直帰率・離脱率が高く改善幅も大きいという課題を抱えていました。そのため、これらのページでチャット式の入力フォームを導入するという施策を実施することになりました。 直面した壁 しかし、一部の媒体ではポリシー違反の懸念があることがわかりました。その結果、集客用の一部のランディングページにのみ、既存の入力フォームとは大きく変わったチャット式の入力フォームが導入されることになりました。 ほかのページでは 既存の入力フォームをより簡潔にしてボタン一つで次ページに遷移できるようにする ファーストビューの印象を変えるために背景色を調整する といったABテスト施策を実施することになりました。 高速に回るPDCA よかったこと 2022年10月からのチーム制の導入によりCRO施策に専念することになりました。 そのため、企画の方のABテスト範囲拡大・縮小や寄せの判断から技術側の開発→リリースまでの時間が短縮され、高速にPDCAを回すことができ、6ヶ月という短い期間の中でも多くの施策を打つことができました。 週1回の定例などでチームでの認識が取れていたことによって、テスト結果のジャッジから次のアクションへ高速に移ることができたことが大きな要因だと思います。 また、CRO施策に専念することにより培われたノウハウを活かすことで、速い開発スピードの中でも不具合をだすことなく進めることができました。 大変だったこと 高速にPDCAが回ることで多くの施策が打てる一方で、大変になるのはB本実装です。 ABテスト施策の実装時には既存のものをAとして新規にBのものが作られます。 ABテストを実施しAパターンに戻ることになった場合には、ABテスト実装分をリバート(差し戻し)すればよいのですが、Bパターンになることになった場合には ABテスト関連のために実装した設定ファイル、効果測定用の処理を全て撤去しなければいけない ABテスト用の設定ファイルでテスト対象を絞り込んでいた場合には、その設定を考慮してロジックを実装しなければいけない ABテストを実施した内容のうち、一部の内容だけを本実装する場合に、本実装の要不要の判断が難しい といった工数がかかってしまいます。 そのため、ABテストの実施の際には高速に開発を行うことができますが、B本実装になると工数がかかってしまい大変でした。 今後は、 ABテスト時にB本実装を見据えた設定ファイルに依存しすぎない実装 ブランチを分けてABテスト実装箇所のみをわかりやすくする などといった改善をしていきたいと考えています。 ユーザーファーストGとの連携 ユーザーファーストGの方に協力していただき、ABテストを実施しているページの課題点・懸念点などを洗い出していただきました。目的としては以下の2つです。 今後の修正対応の方向性を検討する際の判断材料にする ユーザビリティ観点での優位性/深刻度の高い箇所の把握 開発している中で見慣れてしまい見落としてしまっているような部分を、第三者目線からご指摘いただくことで、新たな改善点に気がつくことも多かったです。 エラーメッセージの見せ方改善の施策化 施策の一部として、私自身がかねてから改善したいと思っていた、エラーフォームメッセージの見せ方の改善を施策化することもできました。 所属しているウェブアクセシビリティ推進WGの活動を通して深めた知見から、入力フォームのエラーメッセージと背景の明度差が近く、エラーメッセージが見づらいことに気がつきました。そのことから、利用者にとってエラーの箇所の特定が困難になっている可能性があることを提示し、実際にエラーメッセージを見やすくする修正を行い、リリースすることができました。 エラーメッセージ(before) エラーメッセージ(after) まとめ 売却査定CRO(Conversion Rate Optimization)チームの取り組みについて紹介しました。 売却査定では2022年10月から始まったチーム制ですが、この短期間の中でも多くの施策を打つことができ、よかった点も改善点も含めていろいろな結果をだすことができました。 最後に、LIFULLではともに成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
テクノロジー本部の yoshikawa です。 普段の業務では全社データ基盤の開発や技術検証、ビジネスサイドのデータ活用支援を行っています。 本記事では、Google CloudのDataplex(主にData CatalogとData Lineage)を活用したデータマネジメント及びその支援に関する事例を紹介します。 また関連して、データ開発の質とスピードを高めるべくCloud Composerとdbt(dbt Core)を採用し、DWH(データウェアハウス)/DM(データマート)構築基盤を刷新する取り組みについても紹介します。 全社データ基盤の概観 全社データ基盤ができるまで、そしてこれから データマネジメントの必要性 データマネジメントの領域 データマネジメントをエンジニアとして支援する データスチュワードの発足 Dataplexの活用 データのカタログ化と用語集 データリネージの活用 DWH/DM構築基盤の刷新 まとめ 全社データ基盤の概観 LIFULLの全社データ基盤はBigQuery(Google Cloud)を中心としつつETL処理にStep Functions(AWS)などを利用し構成されています。 全社データ基盤には基幹サービスであるLIFULL HOME'S上のログデータや地理情報マスターデータなどのデータソース、それらを事業・用途別に集計・加工したDWH(データウェアハウス)/DM(データマート)など、様々な出自・用途のデータが集約されています。 記事執筆時点での規模は容量にして数百TB、テーブル数にして1万以上と大量のデータが蓄積されており、今もなおデータ量が増加しています。 そうした大量のデータはBI経由で各部署・プロジェクトの指標として定期的に参照されたり、施策の効果測定や検証などアドホック分析、機械学習などに活用されています。 全社データ基盤ができるまで、そしてこれから 数年前、今の全社データ基盤が構築される前はデータ活用に関して下記のような課題がありました。 クラウドサービスやDBにデータが散在(サイロ化)している セキュリティの観点からデータを抽出可能な人が限定されている データを使う人(アナリスト、企画、etc)と、データを抽出できる人(エンジニア)が別なので、手戻りが頻発しデータを使うまでの効率が悪い そうした課題を解決すべく今の全社データ基盤が構築され、BigQueryへのデータ集約やIAMによる権限管理によって課題は解決されてきました。 (全社データ基盤が構築されるまでの沿革は過去のブログからご覧になれます。) www.lifull.blog 利用実績の増加を遂げつつデータを蓄積し続けている全社データ基盤ですが、これからはデータを蓄積し誰でも利用できるようにするだけでなく、データの活用を通じ価値提供を促すことで、利益という形でも貢献できる基盤へと成長していくことが重要となります。 データマネジメントの必要性 現在、全社データ基盤が安定稼働している中でデータ活用に関して下記のような課題があります。 データを利活用するステークホルダーの増加・多様化、およびそれに伴う要望の複雑化・多様化(人の問題) データを生成する/参照するシステムの増加・複雑化、およびそれに伴う全体統制の難化(システムの問題) データ量の増加・管理の複雑化に伴うデータ品質維持の問題(データの問題) 全社データ基盤が構築される前と比べると業務プロセスの一部分としてデータを活用する際の問題だけではなく、データを扱う人や組織のリテラシーやガバナンス、中長期的にデータ活用を継続していく上での品質の維持や管理コストに関する課題が顕在化しています。 そのため、システムの一つとしてデータ基盤を開発し発展させるだけでなく、人・組織の問題へと越境することで上手くデータ活用をし続ける取り組み→データマネジメントが今の全社データ基盤で必要とされています。 データマネジメントの領域 データマネジメントには多様な領域が存在するとされています。 データマネジメントの知識体系をまとめた書籍として『DMBOK』が有名ですが、データガバナンスを中心とした「DAMAホイール図」と呼ばれる図にデータマネジメントに関する知識領域が定義されています。 (詳細は書籍かデータマネジメント協会 日本支部のホームページをご覧ください) www.dama-japan.org 全社データ基盤においても、データセキュリティやドキュメントとコンテンツ管理、メタデータ(カタログ化)をはじめとした領域でデータマネジメントの取り組みが行われています。 それらの取り組みについて以降で紹介していきます。 データマネジメントをエンジニアとして支援する データマネジメントはデータの生成や基盤に携わるデータエンジニアだけではなく、CDOやデータ基盤・統括に関連するマネージャー、データを活用するアナリストやデータサイエンティストなど多様なステークホルダーと連携し進めていきます。 筆者はデータマネジメントに必要とされる技術(Google Cloudのサービスが中心)についての検証や、ビジネス職(データスチュワード)への技術的な支援・提案といった形で携わっています。 データスチュワードの発足 先の課題として述べたようにステークホルダーの増加や多様化により、データエンジニアだけでデータに関する要望に応えることが難しくなりつつあります。 そこでLIFULLでは昨年末からデータ整備やデータ活用に関する相談窓口を担う役割としてデータスチュワードが発足しています。 Dataplexの活用 冒頭でお伝えしたように、全社データ基盤はGoogle CloudとAWS両方を利用して構成されています。 データ利用者にとって中心となるのはBigQueryであるため、データに関する情報(メタデータ、データ品質、セキュリティレベル、etc)はGoogle Cloudに集約するという流れになりつつあります。 データマネジメントの文脈において重要となるサービスはDataplexです。 cloud.google.com DataplexにはLake、Zone、Assetという論理的な概念を使い、各ストレージ(データレイク、DWH、DM)上のデータをドメイン別に一元的に管理できる機能をはじめ、カタログ機能(これまでのData Catalog)や、データリネージ機能など、データガバナンスおよびデータマネジメントの観点で有用な機能が存在しています。 多様な機能が存在していますが、中長期的なデータマネジメントに貢献するか、および現在のデータスチュワードのユースケースに貢献するかを念頭にいくつかの機能を利用しています。 データのカタログ化と用語集 昨年の夏からデータカタログを利用し始め、現在ではデータスチュワードによるカタログ化(=タグ付けやドキュメンテーション)が進行しています。 中長期的な目標として全てのデータに対してのカタログ化を視野に入れていますが、現在は新規作成されたデータや戦略上重要とされるデータを優先的にカタログ化しています。 カタログ化において注目している機能は用語集(Dataplex business glossaries)です。(記事執筆時点ではpreview版です) cloud.google.com 利用イメージは下記のGoogle Cloud Blogをご覧いただけると良いかと思います。 cloud.google.com 文字通り、事業/ビジネスに関する用語集を作成できる機能ですが、タグテンプレートのようにデータと関連づけを行うこともできます。 全社データ基盤では、Dataplex business glossariesが利用可能になる前からドメイン知識などのビジネス用語とデータとの関連づけを構想していました。 当時はタグテンプレートでの実現を予定していましたが、Dataplex business glossarieはタグテンプレートと比べ以下の点で利点があり、データ整備実施者とデータ利用者の両方にとってメリットがあると考えています。 データに対する用語の関連付けや、用語間の関連付けがGUI上で容易に行える=整備が楽 Data Catalogの検索結果上のスキーマタブで、内容を簡単に把握できる(下図を参照)=欲しい情報が見つけやすい カラムに関連付けられた用語(Business Terms).タグ(Column Tags)はクリックして内容を確認できる一方、用語なら内容が一目でわかる まだpreview段階の機能ではありますが、データソース(BigQuery)と近いところにデータの仕様(Dataplex business glossaries)を蓄積させ、データ利活用者およびデータスチュワードの認知負荷を軽減し、データ利活用の体験を向上させていければ理想的だと考えています。 データリネージの活用 筆者は昨年夏のデータカタログの利用開始と同時期にデータリネージの実現についても検証しています。 その結果として、Audit LogsとINFORMATION_SCHEMAから取得可能なテーブル定義とジョブ実行情報を元データとし、Streamlitとpyvisを用いて可視化するデータリネージアプリを実装しました。 詳細は過去のブログよりご覧いただけます。 www.lifull.blog データリネージアプリは半年程度運用し、その間に下記のようなユースケースが生まれました。 施策によるデータソース定義変更の影響範囲調査 不要データセット削除による影響範囲調査 そんな中、Google Cloudから公式のデータリネージ機能がアナウンスされ利用できるようになりました。(記事執筆時点ではpreview版の機能を含みます) cloud.google.com 機能の詳細は上記公式ドキュメントの通りですが、使用感としてはこれまでに生じた影響範囲というユースケースに対しては対応できると考えており、BigQueryのタブからもアクセスできることから、アドホックな分析でSQLを書きながら関連するデータがないか探索するといった用途にも適していそうです。 記事執筆時点では機能面の充足や費用面への影響を中心に検証を行っており、本番環境への適用に向けて準備をしています。 カスタマイズ性という観点では、自前で容易したデータリネージアプリが勝る場面もありそうですが、先のDataplex business glossariesとあわせ、データに関する情報をGoogle Cloudへと集約することでSSoT(Single Source of Truth)を構築できる良い機会と考えています。 DWH/DM構築基盤の刷新 こちらはデータマネジメントというよりエンジニアリング寄りの内容ですが、5年以上にわたって稼働しているDWH/DM構築基盤の刷新も進行しています。 現状のETL処理基盤はデータパイプライン構築にLuigi、ワークフロー実行環境にStep Functions(AWS)などを採用して構成されています。 現行の全社データ基盤の構成図 当時からデータエンジニアリングに関するツールや知見は進化し、開発速度や開発者体験を向上させる仕組みが整ってきたことから、現行の構成からワークフローの実行および管理の基盤としてCloud Composer、SQL開発およびデータモデリング用にdbt(dbt Core)を採用した基盤へのリプレイスの検証が開始しました。 cloud.google.com www.getdbt.com その他の採用技術の候補としてArgo Workflows(on GKE)やDataformも検討しましたが、 マネージドサービスの恩恵を受けながらも、開発者体験を上げつつデータ開発を効率化できるようComposerとdbtという組み合わせを採用しました。 記事執筆時点では一部処理での検証を実施している段階ですが、下記のようなメリットを感じています。 SQLとワークフロー実行設定が分離されるようになり、SQLにデータ・テーブルに関する仕様が集約されるようになった dbtがSQLの依存関係を解析してくれるため、SQL実行順序の制御が手軽になった スキーマ定義やワークフロー実行制御において手動実行していた開発フローが自動化された まとめ LIFULLの全社データ基盤ではGoogle Cloudを活用しデータマネジメントおよびその支援を進めています。 本記事ではその中でもデータカタログ(Dataplex business glossaries)を利用したカタログ化、データリネージ機能を採用した影響範囲調査の負荷軽減、開発者体験・開発速度の向上を目的とした既存ETL処理基盤の刷新についてお伝えしました。 本記事では紹介しきれなかったデータマネジメントに関連する取り組み(データ品質、データセキュリティなど)も多くあるため、後続のクリエイターズブログでお伝えできればと思います。 最後に、LIFULLではエンジニアを募集しています。カジュアル面談のご応募や現在募集中の職種については下記をご覧ください。 hrmos.co hrmos.co
初めまして! 2022年4月に入社しました、AI戦略室の岩﨑悠紀と申します。 普段の業務では主に機械学習モデルの性能改善を担当しています。 昨今ではChatGPTをはじめとした機械学習プロダクトが世に出始めており、 翻訳やチャットボットなどの自然言語処理、そして画像生成や物体検知などの画像処理の分野が注目を浴びています。 機械学習は応用範囲がとても広く、LIFULLでは間取りの3D画像の生成する「 LIFULL HOME'S 3D間取り 」や、対話型の物件検索システムである「 AIホームズくんBETA 」、一覧画面内の物件の並び替えを行う「AIおすすめ順」などにも利用しています。 その中でも、私は「AIおすすめ順」の機械学習モデル開発・改善に関わっており、この記事ではその取り組みについて紹介したいと思います。 AIおすすめ順 性能改善の事例 解決したかった課題 改善方法 まとめ AIおすすめ順 まずはAIおすすめ順について説明していきます。 AIおすすめ順は、LIFULL HOME'Sの一覧画面内の物件の並び順を機械学習を用いて最適化するプロジェクトで、ユーザーの方々が より魅力的に感じる物件を上位に表示する ことを目的としています。 具体的には、下の図のように機械学習モデルを用いて物件のスペックから「 おすすめスコア 」を算出し、そのスコアに沿って一覧画面内の物件を並び替えています。(図中央は"物件A"に対して機械学習モデルを用いた推論を行っている様子を表していますが、実際には図左側のすべての物件に対して推論を行います。) また、AIおすすめ順の機械学習モデル部分は「ランキング学習」と呼ばれる手法を用いて学習を行い、与えられた物件群の中で人気の物件をより上位に並べ替えるように最適化されています。 具体的には、下の図のようにユーザの方々から人気の物件により高い「おすすめスコア」を付与するように学習を行います。 私の業務はこのAIおすすめ順の機械学習モデルの性能改善で、ユーザーへ魅力的な物件をより高い精度で届けることにつながります。 では、実際にサービスに組み込まれた機械学習モデルの改善事例について紹介したいと思います。 性能改善の事例 解決したかった課題 初期のAIおすすめ順の機械学習モデルでは一年を通して同じ構造のモデルを使用しているため、「◯月はこういう物件がおすすめ!」というように時期によって並び順を変更することができませんでした。 しかし、時期によってユーザーの方々に人気な物件スペックは変化するため、より高い精度でおすすめする物件を届けるためには、 時期ごとに並び替えのロジックを変化させる 必要があります。 改善方法 AIおすすめ順のチームでは上記の課題を解決するために、機械学習モデルの学習時に「その物件がいつ掲載されていたか」という情報を同時に与える実験を行いました。 実験では木構造の機械学習モデルを使用したため、下の図のようにモデル内に分岐が作成されています。 そのため、学習時に掲載時期の情報を与えることにより、「◯月は〜〜の傾向があるから、△△というロジックを使う」というように、 モデル内部で自動的に「その時期に最適なロジック」に切り替える ことができます。 上記の改善を行った結果、オンライン検証(A/Bテスト)で既存のモデルよりも良い結果を得ることができました。 まとめ AIおすすめ順プロジェクトでは、こういった機械学習モデルの性能改善を日々行っています。 これからもユーザーの方々へより魅力的な物件を届けられるように、試行錯誤しながら頑張っていこうと思います。 最後に、LIFULLではともに成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
AI戦略室の神谷と申します。データサイエンティストとして、機械学習や数値モデルのアルゴリズム開発に従事しています。最近ではAIをはじめとしたさまざまなデータの活用やビジネスへの応用について興味を持っています。 私が所属するAI戦略室では、将来的な競合他社との差別化を見据えた「AI技術シーズの創出」と短中期的な事業貢献につながる「AI技術シーズの活用」の2本柱となるミッションを持っており、AIとビジネスの橋渡しをどのように行っていくかを日々議論・検討しています(参考: 社内でAI成果展示会を開催しました - LIFULL Creators Blog )。2022年ごろから組織内の特命チームとして、特にAI技術シーズの活用を社内に促進する「AI活用促進チーム」が構成され、日々の業務と並行して社内のAI活用促進を図る業務を担当することになりました。 今回はLIFULLにおけるAI技術の活用という観点から、AIの活用促進の重要性とAI活用促進チームの実際の取り組みについて紹介いたします。 キーワード: AI-Ready化とは AI活用促進の重要なキーワードとして、AI-Ready化というワードがあります。AI-Ready化とは、経団連が企業・個人・制度などあらゆるレイヤーに対するAI活用戦略の指針として定めたもので、「(社会・産業・企業が)AIを活用するための準備」が進んでいる状態を指します。 そもそもの技術背景としてAIによる識別・予測など一部の領域で人間を上回るような事例が増えており、AIシステムが新たなビジネスモデルの構築に欠かせないものとなっています。一方でAIを活用する企業や個人にもAIリテラシーの向上を促進する必要があり、日本がこれらの分野の産業競争に勝つために、経団連が2019年にAI活用戦略のためにガイドラインとして設定した指針がAI-Ready化ガイドラインです(参考: https://www.keidanren.or.jp/journal/times/2019/0221_02.html )。 AI-Ready化ガイドライン(出典: 日本経済団体連合会) AI-Readyはガイドラインで5段階のレベルに分けられており、それぞれの役職においての行動指針が記載されています。この指針に沿っていえば、レベル1の企業はそもそもAIの活用自体が議論のテーブルに上がっていない状態であり、逆にレベル5は全従業員がAIを活用しておりそれぞれのマーケット領域にAIの専門家がいる状態を表します。 まだ道半ばではありますが、LIFULLはこのガイドライン上でレベル3を達成しつつあります。実務へのAI活用を徹底する取り組み、社員へのAI教育をはじめ、AIへの投資が継続的にコミットメントされており、また独自のAI開発体制も保持しています。この投資を続けていき将来的に自然とAIやデータ活用がされる組織(会社)にしていくことで、事業課題解決の手段としてのAI活用が検討のテーブルに乗せられていけば、事業戦略の幅も徐々に広がっていくと考えられます。 AI活用促進チームのミッションと活動報告 以上のような背景におけるAI活用促進チームのミッションを紹介します。 AI活用促進チームのゴールは「 事業部とAI専門家のお互いの得意分野を活かした、具体的かつ良質なニーズや企画が量産される状態 」としています。そのゴールに対する打ち手として「人材育成・発掘」、「コンサルティング」に近い業務となりますが、具体的なミッションは大まかに以下の二つです。 AI活用のリテラシー・知見を全社に広める。 社内の事業ニーズに対する課題解決の手法として、AIの技術シーズを提案して導入につなげる。 チーム内のKGI(Key Goal Indicator)は「社内の事業ニーズに対するAI技術シーズの想定インパクト・導入確度の見積数」としています。AIを導入する企画段階においてどれくらいのインパクトが見込めるか、どれくらいの信頼度を持ってインパクトを出せるといえるかを見積もることで、AIの活用が事業課題解決の手段となるかの解像度が上がっていることを確認できる指標としています。 具体的な活動報告を以下に記述いたします。 AI学習Eラーニングサービスの受講促進 AI活用のリテラシー向上に取り組む上で、AIに関する基礎的な知識や活用事例を座学で学べるAI学習Eラーニングサービスを期間限定で社内に導入し、社員への受講を促進しました。 Eラーニングであれば業務の合間にでも耳で聴きながら受講することができることから、社員に対する学習負荷を最小限に抑えつつAIリテラシーの向上が図れることで、初学者の方でも取り組みやすいのではないかと考えました。ただし強制的に受講必須とするのは、本当に受講意欲のある社員に行き渡る前にサービスが終了してしまう恐れがあったため、受講希望者を優先する体制を整備しました。また、カリキュラムはメンバーの職種によってカスタマイズし、企画・エンジニア・バックオフィスなどそれぞれの職種に適した知見に関する講座を受講必須にする運用を実施いたしました。 この運用を約1年間弱行った結果、 運営側を除く総受講者数は87名、基礎カリキュラム100%達成者は29名 という結果でした。今回のEラーニング受講による効果を以下のように考察しています。 全社的なAIリテラシーの向上とAI導入の意欲向上に役立った。 各部署のAIに対する温度感を可視化することによって、今後のAI戦略室との関係性構築のベースとなるデータを取得することができた。 受講者が次の受講者を紹介してさらに受講希望者が増えていくような受講者ネットワークが構築され、全社的に広く告知ができた。 この活動を通して、後述するAI活用を目指す社内ニーズの発掘につながることになります。 社内の事業ニーズのヒアリング AIを活用するという議論の前に、そもそも解決すべき事業課題を深掘りしなければなりません。解決すべき課題は何か、現状のプロジェクト進捗はどのような状況か、見るべきKPI(Key Performance Indicator)は何か、その課題にAIを活用するとした際の利点は何か、AIを活用するとなれば達成すべき精度はどのくらいだと見積もれるか、などを深掘りすることによって、AIを導入した際の想定インパクトや導入確度を見積もることが可能になります。 現在AI活用促進チームでは、各部署の組織長に対して事業ニーズの深堀ヒアリングを行っています。いわゆる「コンサルティング」になるのですが、深掘りの過程で自部署が持っているAI技術シーズと事業ニーズのマッチングを検討します。ニーズにおける課題をAI技術シーズによって解決できそうかどうかを、簡易的なデモやプロトタイプを用意して実際に触ってもらうことで一緒に議論・検討します。 ただし、一方的に我々が持っている技術を売り込みに行ってもなかなか理解されないことが多いため、事業部と同じ指標で導入による想定インパクトを概算しておかなければなりません。この場合の指標は基本的に短期的な指標、例えば売上やCVRが挙げられますが、過去にAIの導入事例がないケースでABテストなどの実績もない場合、インパクトの概算が非常に難しいというのが一つの悩みポイントになっています。このようなプロセスの過渡期においては、どうしてもお互いの見ている数値が異なっているケースが多いため、直接のコミュニケーションによって目指すべきゴールをすり合わせていくことが重要となります。 まとめと今後に向けて AI戦略室ではLIFULLのAI-Ready化に向けてAI技術の活用を社内で促進していく「AI活用促進チーム」が結成され、「事業部とAI専門家のお互いの得意分野を活かした、具体的かつ良質なニーズや企画が量産される状態」をゴールとして打ち手を検討しています。それらの打ち手として、「1. AI活用のリテラシー・知見を全社に広める」「2. 社内の事業ニーズに対する課題解決の手法として、AIの技術シーズを提案する」の2点を重視し、日々の業務と並行して担当しています。 この記事を書いている現在、AI界隈では GPT-4 の自然言語処理モデルが世間を賑わせています。この波に乗り遅れないように、技術の革進を全社に広めていきたいと思います。 また、LIFULLでは共に成長できるメンバーを募集しています。この記事を読んでいただいた方は、ぜひこちらのページもご覧ください。 hrmos.co hrmos.co
検索エンジンチームにいながら外部公開APIのメンテナンスもしている加藤宏脩です。 この記事では、毎日大量に書き込まれ膨れ上がったMySQLのテーブルを、 テーブルローテーションさせることで不要なデータを継続的かつ安全に削除する処理の実装をしたのでそれについてお話したいと思います。 利用している技術 Amazon RDS for MySQL Engine version: 5.7.41 Amazon ElastiCache for Redis Engine version: 6.2.6 起きていた問題 LIFULLのとあるサービスは、アプリケーションとMySQL、DBの結果をキャッシュするRedisがあるというよくみる一般的なアーキテクチャで運用しています。 このMySQLのテーブルは毎日100万件以上のレコードが追加されていく状態になっており、 総レコード数は6億件を超え、容量は2TBを超えていました。 またMySQLの仕様もあり、不要になったレコードを簡単に削除することはできなくなっていました。 そのため、DBの空き容量が少なくなるたびにストレージを追加する運用を数年続けていました。 このままでは永遠にデータが増え続けてしまうので、不要な数億のレコードを削除して、今後も増え続けないようにする必要がありました。 問題解決を阻む課題 書き込みが継続しているテーブルへのレコード削除は不安定 MySQLのデフォルトのストレージエンジンであるInnoDBは、DELETE文を実行しても物理的な削除を行わずフラグメンテーションしてしまう仕様となっています。 サービスをとめずに解決するためには、 DELETE コマンドで不要なデータを削除したあとに下記のように、 ALTER TABLE コマンドを実行することでテーブルを再構築する必要があります。 ALTER TABLE tbl_name ENGINE=INNODB MySQLのドキュメント: https://dev.mysql.com/doc/refman/5.7/en/innodb-file-defragmenting.html 検証環境で上記コマンドを実行したところ、以下のことがわかりました。 初回のALTER TABLEの実行時間は12時間を超える 初回のALTER TABLEはテーブルをコピーするため容量が2倍の4TB以上必要になる InnoDB テーブルの online DDL 操作中に使用される一時ログファイルのサイズの上限設定値以上に書き込むと、 それまでのデータがすべて失われる。(innodb-online-alter-log-max-size) このアプリケーションのデータ書き込み量は不安定で、突然今までの2倍以上書き込まれる可能性がありました。 そのためサービスで利用しているテーブルのデータを削除して容量確保することは危険であり、 定期的に実行できるものではないことがわかりました。 DBごと移行することは、データの同期にラグができてしまい切り替え時に不整合が起きてしまう 上述の通り、運用しているDBに手を加えて解決することはできなかったため、新しく空のDBまたはテーブルを作り、 移行する処理を考えましたが、こちらにも課題がありました。 考えていた処理は以下の通り。 利用中のテーブルから不要なデータをDELETE文で削除する(この時点ではフラグメンテーションされているため空き容量は確保されていない)。 その後、新しくDBもしくはテーブルを作り、データの同期をする。 データの同期が終わり次第2で作ったDBもしくはテーブルに書き込みの向き先を切り替える。 このDBは絶えず激しく書き込みが行われているため、3の実行時に データの同期が完全に終わることがなく、DBの向き先を切り替えると 不整合が起きてしまうためできないことがわかりました。 書き込みを制限することは困難 本来、書き込みは一日数十万件にもなるようなものではなく、意図した設計とは違う使われ方をしていました。 アプリケーション側で書き込み数上限を設定することも考えました。 しかし、アプリケーションの利用者が社外にいるため 突然制限するわけにも行かず、 半年〜1年以上システムの改修対応をする期間を待つ必要があったため、書き込み制限は行いませんでした。 この問題に時間はかけられない データ量が多いため、レコード数の取得をするだけでも長時間かかります。 システムの状況を把握し検証をすると、数日かかってしまう状況でした。 本プロジェクト自体、チームのメイン業務ではなく、早くメイン業務に合流する必要があるため 時間をかけずにできるだけ早めに解決する必要がありました。 解決策 解決策は、 4つのテーブルを用意し3ヵ月ごとにローテーションし、 向き先のテーブルにデータが見つからなければ、前の時期のテーブルを探すように実装することでした。 こうすることで古いテーブルに書き込んで新しいテーブルにない状態でも不整合が起きなくなります。 さらに、2期間前のテーブルには完全にアクセスされなくなるため、 TRUNCATE するバッチ処理を実装するようにしました。 4つのテーブルの内訳は、下記の通りです。 現在の時期の向き先。読み書きするテーブル 1つ前の向き先。現在の時期の向き先にデータがない場合に呼ばれるテーブル 2つ前の向き先。読み書きされないため安全にデータを削除できるテーブル 次回のローテーションの向き先。 TRUNCATE 前にローテーションして新しいデータを書き込んでしまうのを防ぐためにある空のテーブル 結果、最小限の工数でサービスもやめず、DBの不要なデータを定期的に削除できるようになりました。 問題解決をするうえで着目したポイント レコードの有効期限は最長でも1ヵ月なのでそれ以降のデータはすべて削除できる データは一本釣りしかないので、向き先のテーブルにデータがなければ前の時期のテーブルから探しやすい 検索結果はRedisにてキャッシュするため、 同じレコードを取るために何度も向き先のテーブルと前の時期のテーブルにクエリが走ることは起きない 解決策の実装 アプリケーションの変更: 現在の向き先テーブルにデータが見つからなければ、古いテーブルを探すようにする DBの変更: 同じテーブルを数個用意する バッチの実装: 2期間前のテーブルを TRUNCATE する 他に考えていた手段 一本釣りのようなクエリが多くデータの賞味期限が短いことなどから、 MySQLのようなRDBの利用は妥当じゃないので、ほかのデータストアの利用を検討していました。 前段に検索結果をキャッシュするRedisがいるので、 Redisを永続的なデータストアとしても使えるAmazon MemoryDB for Redisを検討していました。 今回は工数の関係で解決策に記載している方法をとりましたが、 まだ諦めていないのでAmazon MemoryDB for Redisに変えてみたいと思っています。 感想 6億レコードを超えたMySQLのテーブルを継続的かつ安全に削除する処理の実装をした話でした。 読んでいただきありがとうございました。 フラグメンテーションの問題を回避しつつ、 サービスをとめることなく 完全にアクセスされないデータを削除できるようになりました。 このような泥臭い作業は軽視されがちですが、 積み重ねることでシステム運用の負担を減らせて、開発者が本来の力を発揮しやすくなるのだと考えています。 ひいては LIFULLのビジョン実現につながるのだと思います。 最後に、 このような効率化をしたいまたは得意なエンジニアの方々、 LIFULL では一緒に働く仲間を募集しています。この記事を読んで LIFULL に興味ができた方は求人情報も御覧ください。 hrmos.co hrmos.co
こんにちは、LIFULL社内アワード運営チーム「クリエイティブアワード委員会」のチバです! 今回は、「未来につながるクリエイティビティの創出」をビジョンに掲げ社内アワード運営などを行う社内組織、「クリエイティブアワード委員会」の活動紹介です💪❤️🔥 クリエイティブアワード委員会とは? 私たちのミッション プロジェクトアワード2022の取り組み グランプリを選び出す必要性は果たしてあるのか? 目標の成果指標を考える Zoomの参加者エクスポート機能で計れるもの Googleフォームを投票フォームにして計れるもの プロジェクトアワード2022の結果 デザイナーの伊藤さんの取材記事 クリエイティブアワード委員会とは? 部署関係なく、有志メンバーで構成されている会社公認のワーキンググループです。 社内の表彰制度「プロジェクトアワード」の運営を主な活動にしています。 「プロジェクトアワード」とは、 「日々の業務成果」を社内にお披露目して多くのプロジェクトにスポットライトを当てる取り組みです。 在籍メンバーは通常業務が他にあり、私自身も、普段は LIFULL HOME'SのLINE・メールのリテンションマーケティング をしています。 私たちのミッション 主管に人事本部がついており、私たちは以下のミッションを与えられています。 LIFULLの創り出すアウトプットの品質ベンチマークを高めることを狙いとして、仲間の取り組みに称賛する機会を生み出す 上記を活動目標に落とし込むと、下記の3つになります。 アウトカムを生み出すまでのプロセスが社内に知見共有として拡散される機会をつくる 他チームの取り組みを知る"面"を増やして、部署を越えた協働のきっかけをもたらす 社員が社員に「期待」や「感謝」を「称賛」として伝えられる場を用意する 上記の目標をどのように施策に置き換えて取り組まれたのか、 プロジェクトアワード2022 開催の様子とともにご紹介します💁 プロジェクトアワード2022の取り組み プロジェクトアワードは社内で毎年開催されるイベントです。 「日々の業務成果」を社内にお披露目し、最も称えられたプロジェクトはグランプリを受賞してオリジナルグッズを手にすることができます。 グランプリ選抜は、社員全員が投票で選ぶことになっており、これは、2006年にボトムアップではじまった頃から変わらない方針です。 2006年 ものづくりの素晴らしい仕事を評価する、トップセールスと対をなす存在として誕生してから、2022年 「プロジェクトアワード」一次審査に審査員制度を導入、社員投票は二次審査実施へ変更した グランプリを選び出す必要性は果たしてあるのか? そもそも、どのプロジェクトも素晴らしい取り組みであるのにグランプリを選び出さなくてもよいのではないか、とリプレースをかけようとした時期もありました。 しかし、プロジェクトアワードを開催すると、社内中の優れたプロジェクトがわっさわっさ集まってきます。 「知らなかった、こんな利点があったのか」 「この取り組みは知ってたけど、うちのプロダクトには関係ないと思ってた、そんなことないじゃん」 「これの話はもっと聞きたい・・・!担当者とつながるぞっ」 普段から社内各所のプロジェクトや施策情報はあらゆるチャネルで共有されています。 LIFULLでは、毎月の全社総会、本部長主催のウェビナー、Slackや掲示板に投稿される社内報などで知ることはできます。 しかし、スポットライトが当たることで注目が集まりやすくなりました。 ピックアップするだけで、知の拠点ができあがる のです。 ならば、ピックアップしなければ。私たちがやることだ、そう思えたので活動を継続することになりました。 目標の成果指標を考える 上述した3つの目標、それぞれには以下のような成功指標を与えました。 これらの成果指標は、それぞれ下記の方法で計測しました。 Zoomの参加者エクスポート機能で計れるもの 最終プレゼン審査会参加人数 継続視聴時間 参加者所属部署比率 Googleフォームを投票フォームにして計れるもの 投票総数 ユニーク投票数 投票コメント数 ここまでいったら成功、が明確になったことで、これらの指標を押し上げることを意識した活動ができるようにしました。 過去トラッキングできている投票総数、ユニーク投票数、投票コメント数の3つを過去2年と比較すると、やはり例年よりも成長を実感できます。 見える指標が出来上がることで、グンッと自律的に動けるようにもなったので、チームメンバーが自らタスクを取りにいき進めやすくなりました。 プロジェクトアワード2022の結果 プロジェクトアワード2022では、社内デザイナーによって製作されたオリジナルグッズが景品となりました。 デザイナーの伊藤さんの取材記事 note.com グッズ化アイディアも、並行開催したアイディアアワードで一般社員から公募したものです👏 電源タップもヘッドホンもUSBハブも入るゆとりの16インチサイズ ウィンクしてるホームズくんでディスプレイも常にきれいにできちゃうぞ! フリーアドレスになったオフィス移動もこれで安心安全👷 実際に受取った社員も喜んでくれて、運営チーム一同安心しています。 note.com これからも仲間の取り組みに称賛する機会を生み出せるよう努めてまいります💪 次の活躍にご期待ください!
こんにちは! LIFULLエンジニアの吉永です。 普段はLIFULL HOME'SのtoC向けのCRMチームにてエンジニアリングマネージャをやっています。 本日はクリーンアーキテクチャで構築したプロダクトが初版リリースから2年経過した現在、どうなっているか?について紹介したいと思います。 これから新規プロダクトにクリーンアーキテクチャを採用しようとしている方々の参考になれば幸いです。 アジェンダ クリーンアーキテクチャで構築したプロダクトの概要 クリーンアーキテクチャを採用して得られたメリットやデメリットについて 2年経過してみて今どうなの? まとめ クリーンアーキテクチャで構築したプロダクトの概要 今回のブログで紹介する我々のプロダクトですが、オムニチャネル戦略を推進する為の各種機能を提供するAPIサーバーとなり、下記のような機能を持っています。 LINE/Mailでその日の新着物件情報をユーザーが希望した検索条件に応じて通知する機能 LINE/Mailでユーザーが問合せた物件に近い条件の物件をレコメンドする機能 LINE公式アカウントでユーザーとトークルームでインタラクティブなやりとりを行い、シナリオ配信やレコメンド物件配信を行う機能 各種サービスとSalesforce社のService CloudやMarketing CloudとのAPI通信を仲介する機能 言語はGoを採用 これらの機能を構成するにあたりクリーンアーキテクチャを採用しており、有名な下記の図にならった形で各レイヤーを構成しています。 出典:The Clean Code Blog https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html なお、このプロダクトにクリーンアーキテクチャを採用した当時はチーム内でクリーンアーキテクチャ及びGoの実装を経験したメンバーはおらず、書籍やWeb、社内で先行採用されていたプロダクトの実装を参考に見様見真似で実装しました。 開発初期は実装もレビューもキャッチアップに時間がかかっており、新しい言語、新しいアーキテクチャによる実装で非常に苦労したことを覚えています。 クリーンアーキテクチャを採用して得られたメリットやデメリットについて どんなアーキテクチャにもメリット・デメリットはありますが、クリーンアーキテクチャにおいても同様で、実際に採用してみて個人的に感じたことを紹介します。 メリット 各レイヤーの責務をアーキテクチャで示してくれているのと、各レイヤーはインターフェースに依存するように設計するので自ずと各レイヤーが疎結合になり、ユニットテストを行いやすい。 ユニットテストを行いやすいので、テストコードを実装するのがあまり面倒にならず、結果的に変更に対する品質の担保をしやすい為、安心して変更できる。 レイヤー間の独立性が高いので、インタフェースさえ設計してしまえば、各レイヤーの実装を別々の担当で並行に作業を進めやすい。 クラス設計に迷ったときにクリーンアーキテクチャという指針があるので、チーム内での議論があまり散らばらず収束しやすい。 指針は示してくれているが、かと言ってガチガチに固まっているアーキテクチャではないので、ある程度は自分たちの開発に特化している形に実装してしまっても、大枠のレイヤー構成や依存ルールさえ守れていれば拡張性と保守性を高く保つことができる。 レイヤーが疎結合になることで、各レイヤーの実装のシンプルさが保たれるので、ソースレビューしやすい。 デメリット 小さな機能追加でもアーキテクチャ内の登場人物が多いので、クラス数が増え、煩雑になりやすい。 基本的にインターフェースに依存するので、IDEでメソッドの「定義へ移動する」際に、たいていのIDEでは定義だとインタフェースに飛んでしまい、メソッドの実態へ飛ぶには「実装へ移動する」を選択する必要があるので、コード内の移動に少しだけストレスを感じることがある。※それはあなたのIDE環境の問題でしょうというご意見もありそうですが エンティティレイヤーは「ビジネスルールをカプセル化したメソッドのあるオブジェクトやデータ構造と関数の集合」を実装するレイヤーなので、DBの1レコード分のデータをマッピングするだけで、アクセッサー以外のメソッドを持たない単純なオブジェクトと複雑なビジネスロジックを実装したオブジェクトが混在することもあり、適切にパッケージなどで分けないと煩雑になりやすい。 2年経過してみて今どうなの? ステップ数 初版リリース後、様々な機能を追加していったので、リポジトリ内のステップ数は2年間で約3倍になりました。 テストカバレッジ 初版リリース時は自動テストコードのカバレッジは80%程度でしたが、初版リリース後3ヶ月ほどかけて98%まで上昇させ、その後現在に至るまでこの水準を維持しています。 これは、新規追加ファイルのテストコードもなるべくカバレッジは100%を目指そうという方針で開発を進めてきたので、チームの皆で意識して取り組んでいる結果、維持できていると思います。 レビュー 新しくエンドポイントを実装する際はまずクラス図をクリーンアーキテクチャの各レイヤー図にあてはめて作成し、各レイヤー間のインタフェース仕様と追加・変更するクラスを設計レビューしています。 この時点で大枠の設計を固めており、以降の実装フェーズでは基本的にどのレイヤーから実装しても良いようになっている為、実装者が手を付けやすい個所から実装することが出来ています。 メリットでもあげましたが、レビュアーからすると各レイヤーが疎結合でシンプルに実装されている為、レビューはしやすいと思います。 まだ本格的には運用できていないのですが、直近ではPRの変更行数をなるべく抑え、レビューしやすい粒度にPRを分割しようという取り組みもあり、以前だと新規エンドポイント実装時はコントローラー層からドメイン層まで一気に実装してからまとめてレビュー依頼をしていましたが、今後はコントローラーとコントローラーのテストコード、テストコード内で利用するユースケースのモックで1PR、それが終わったらユースケースとユースケースのテストコード、テストコード内で利用するリポジトリやドメインのモックで1PRというようにある程度レイヤーで区切ってのレビューを試してみて、レビュー負荷の軽減ができそうかを検証していこうと思っています。 ※もともとクリーンアーキテクチャで実装されたソースはレビューしやすいと感じていたので、1PRで確認する対象が減ると見逃しも減り、対象へより集中してレビューできると思うので、PRサイズの削減を検討しています 改修のしやすさ 改修はしやすいです。 理由としてはメリットでも挙げた、単体テストを実装しやすい為、既存のコード変更があまり怖くなくなっていることと、各レイヤーが独立している為、例えば新規でエンドポイントを追加する際にも、既存のリポジトリやユースケースのインタラクターを再利用しやすく、コントローラーだけを新規で作成すれば良いといった場面もあり(もちろんその逆もありますが)、インタフェースに依存するようにプログラミングしておくことのメリットを最大限享受できていると思っています。 また、言語も静的型付けであるGoを採用していることもあり、各レイヤー間のインタフェース変更も型が一致しなくなってコンパイルエラーになってくれるので、改修漏れが発生しないことは良いことだと思います。 直近だと、分散トレーシング対応をする為に、コントローラーからエクスターナルまでContextオブジェクトを伝搬させる為のリファクタリングを進めているのですが、コンパイルエラー除去と単体テストの修正を行えば安心してリリースできる状態であることが、開発者がストレスなく開発できていて、開発者体験向上にも一役買ってくれていると実感しています。 余談になりますが、LIFULLではKEELというプラットフォームで分散トレーシングする為の基盤を提供してくれていて、アプリケーションレイヤーで少しだけコードをいじるだけ簡単に複数サービスのログを統合して閲覧できるようになっているので、下記の記事も良かったら見てください。 www.lifull.blog qiita.com まとめ クリーンアーキテクチャを採用したプロダクトが2年経過してみて今どうか?について紹介しました。 正直ネガティブな感想はあまりなく、今後数年に渡って改修を続けていってもあまりゆがまずに済むのではないか?と思っています。 もちろん、初期構想時と比べると、いくつかパッケージ分けが適切じゃないかもとか細かい個所で修正したい個所はあるものの、大枠でみていくとクリーンアーキテクチャで実装したからこそカオスにならずにある程度秩序が保たれていると思います。 また、クリーンアーキテクチャで実装することで、 SOLID原則 に自然と準拠していき、今まで理解があやふやだったSOLID原則が少し理解が深まったと思うので、そういった面でも一度触れてみることは良いと思います。 これからクリーンアーキテクチャを採用してプロダクトを開発しようとしている方々へ少しでも参考になれば幸いです。 最後に、LIFULLでは共に成長できるような仲間を募っています。 よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
LIFULLでフロントエンドエンジニアをしている齋藤です。 2008年入社なので15年目ぐらいの古株です。今は LIFULL HOME'S の賃貸部門でフロントエンド開発をしています。 いきなりですが、みなさんはアイコンをどう実装していますか? アイコンフォント、スプライト、一つ一つ切り出す。フォーマットもSVG、PNG、Fontなどなど。 実装方法はいろいろありますし、サイト規模や運用体制などでどれが扱いやすいかは変わってきますよね。 たかがアイコン、されどアイコン。 利用頻度が高く、大きさや色が微妙に違ったりすることも多くてこれまでいろいろと試行錯誤してきました。 そこで今回はLIFULL HOME'Sでどんなアイコン実装が行われてきたかを振り返ってみたいと思います。 これまでの歩み。 都度一つ一つ切り出しての対応 スプライト画像での対応 SVG symbolを利用した対応 data-url化したSVGをimgタグのsrcに埋める対応 番外編:SASSでSVGをdata-url化してbackground-imageで読み込む 終わりに これまでの歩み。 都度一つ一つ切り出しての対応 15年ぐらい前、このころはデザインが上がってきたら一つ一つ切り出して対応していました。 共通化はしていても色が違う、大きさが違うとなれば都度切り出して作成が必要でした。 すでにあるこの大きさじゃダメなのなんて思いながら渋々切り出して対応していたものでした。 スプライト画像での対応 11年ぐらい前、リニューアルを機に新たに設計していく中でアイコンはスプライト画像化を選択しました。 ui-icon という汎用classを作り、アイコンの指定にはもう一つ ui-icon-hoge のように一意のclassを作って対応しました。 すでに作られた画像であれば <span class="ui-icon ui-icon-hoge"/> という感じでHTMLを書けば良かったので格段に楽になりました。 大きさも文字サイズ連動するように作っていたのでフォントと同じように扱えたのも良かった点でした。 また、画像置換の手法を入れていたので空要素としてだけでなくテキストを含めることもできたので、アクセシブルな作りでもありました。 難点は色が違うアイコンだけはスプライト画像を修正して追加しなくてはいけなかったことでした。 // CSS .ui-icon { display : inline-block ; width : 1em ; height : 1em ; line-height : 1 ; background : url( 'sprite_icon.png' ) no-repeat 0 0 / 1em 54em ; text-indent : 1em ; overflow : hidden ; white-space : nowrap ; } .ui-icon-history { background-position : 0 0 } .ui-icon-favorite { background-position : 0 -1em } SVG symbolを利用した対応 7年ぐらい前、このころになるとブラウザのSVG対応も安定してきたのでSVGを活用し始めました。 具体的には、 <body> 直下に利用するSVGのsymbolを置き、利用する箇所でSVG useして利用するというものでした。 SVGなので大きさだけでなく色に関しても可変性があり、ここでついに色・サイズ違いの再作成という呪縛から解き放たれました。 利用する場合にはSymbol IDと色、大きさを指定するだけのtwig macroを準備したのでそれを利用するだけとお手軽になりました。 inline SVGだからこそできるCSSからの色指定も重宝しましたね。 今現在もこれが現役で利用されています。 // Twig 内での利用方法 {{ svg.use('history', '32', '#000', 'title') }} // Twig macro {% macro use(id, size, fill, title) %} {% if title %} {% set aria %}role="img"{% endset %} {% else %} {% set aria %}aria-hidden="true"{% endset %} {% endif %} <svg width="{{ size }}" height="{{ size }}" {{ aria }} focusable="false"> {% if title %} <title>{{ title }}</title> {% endif %} <use xlink:href="#{{ id }}" aria-hidden="true" {{- fill -}}></use> </svg> {% endmacro %} とはいえ課題も出てきました。 運用としては必要なsymbolのみを必要なページで埋めるということをしてきました。 ところが、ページも増え、長年の運用による複雑さも増してきた中でどこのページにどのsymbolが必要なのかが把握できなくなってきたのです。 symbolを置く場所と利用する場所の距離が離れているのが一つの原因でした。 利用しているコンポーネントが読み込まれたらsymbolを埋め込む等ができたら良かったのですが、それもそう簡単ではないので別の方法を準備することにしました。 data-url化したSVGをimgタグのsrcに埋める対応 そして今。上記課題を解決しつつ、作り上げた資産(SVG)も有効活用できる方法として、SVGをdata-urlに変換してimgタグのsrcに埋めるmacroを新たに作りました。 これによりsymbolの読み込みが不要になり、上で挙げた課題解決への一助となる狙いです。 課題解決への一助と書いたのは、既存のsymbol利用をやめるわけではなく併用するからです。 当初は既存macroを書き換えてしまおうと考えていたのですが、symbol利用で書いた通りCSSからの色指定は重宝しているのでこれをなくすことはせず、併用という道を取りました。 // Twig macro {% macro img(id, size, color, alt) %} {% import _self as svg -%} {% set replaceMap = { '<': '%3C', '>': '%3E', '#': '%23', '{': '%7B', '}': '%7D', ' ': '%20', '"': "'", } -%} {% set src = 'data:image/svg+xml,' ~ svg.svg(id, size, color)|replace(replaceMap) %} <img src="{{ src }}" width="{{ size }}" height="{{ size }}" alt="{{ alt }}"> {% endmacro %} {% macro svg(id, size, fill) %} <svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}"> <def> {% include 'Bundle:Assets:svg/icon/_' ~ id ~ '.svg.twig' %}{# svg symbol file #} </def> <use href="#{{ id }}" {{- fill|raw -}}></use> </svg> {% endmacro %} 番外編:SASSでSVGをdata-url化してbackground-imageで読み込む 実はこのdata-url化という手法は以前からSassで利用していました。 Twig同様にSVGコードを渡したらdata-urlに変換して返す関数を作って実現しています。 SVGコードをencodeして data:image/svg+xml で返す。 TwigでもSassでもやっていることは一緒ですね。 // Sass @function svgUrlEncode( $svg ) { $replaceMap : ( '<' : '%3C' , '>' : '%3E' , '#' : '%23' , '{' : '%7B' , '}' : '%7D' , ' ' : '%20' , "'" : '"' , ); @each $s , $r in $replaceMap { $encode : string.str-replace( $s , $r , $svg , true); } @return $encode ; } @function svgDataUri( $svg ) { $svg : svgUrlEncode( $svg ); @return url( 'data:image/svg+xml;charset=utf-8, #{ $svg } ' ); } 終わりに いかがでしたでしょうか? やり方はほかにもいろいろありますが、言語やブラウザの対応状況等のタイミング、使える時間というのもあってこういった変遷をたどってきました。 技術の進歩やその時の課題・状況に合わせ最適解を探す。 今後も課題解決をしながらサイト機能や開発効率などの改善に取り組んでいきたいと思います。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
プロダクトエンジニアリング部の海老澤です。 普段は LIFULL HOME'S の賃貸部門のフロントエンド開発をしています。 近年、LIFULL の開発部門では「開発生産性」という言葉が取り沙汰されるようになりました。 LIFULL HOME'Sの主要リポジトリは10年以上運用され続け、今も多くの開発者によって日々改修され続けています。 長い年月の中で小さな設計ミスも積み重なって大きくなり、ちょっとした実装でも入念な調査が必要となり開発生産低下の要因となっていました。 また10年以上前に採用したフレームワークで稼働しているため、今ではドキュメントを探すのも一苦労という具合です。 そこで主要開発部門では「自部門の機能はマイクロサービスへと切り離し、各々で面倒を見る」という方針になりました。 賃貸部門も同様に機能の切り離しを始め、先日「物件詳細ページ」のマイクロサービス化を行いました。 今回はマイクロサービス化にあたり新基盤の技術選定をしたので、実際触ってみた感想などを交えながら紹介していければと思います。 画面のビフォーアフター マイクロサービス化と同時にUIのリニューアルも検討、開発中です。 旧UI 新UI ※キャプチャは開発中の画面です。 旧基盤ではダイナミックサービングでPC/Tab用・SP用のレイアウトを切り替えていましたが、新基盤ではレスポンシブデザインへと変更しました。 デバイス間のデザインルール・トンマナの差分をなくすこと、開発生産性を上げ改善スピードを上げるのが狙いです。 新基盤の全体構成 今回は Web アプリケーション部分のみ刷新を行いました。API は全社で使っている既存のものを使っています。 新基盤の構成図 ここからは技術選定についての詳細を書いていきます。 バックエンド バックエンド 技術選定のモチベーション 社内で標準採用されている言語・技術であること これは組織の課題でもあるのですが、LIFULL はエンジニアの部署異動がそこそこあります。 社内では技術集約の観点で標準技術が定められてるのでそれに従いました。 技術選定で「挑戦」しないこと LIFULL HOME'S の賃貸部門は LIFULL の中でも中核を担う組織で、ほかの開発チームに比べて規模が大きいです。 それゆえに人員の入れ替えも顕著で、さまざまな技術スタックを持った人がいます。 そういった中で目新しいフレームワークを用いるとドキュメントや知見不足に悩むことがあります。 有識者がチームに必ずいるのであれば良いのですが、チーム編成の都合上難しい場合もあります。 そうなってしまった場合、開発はつまずきがちになりますしコードの品質も落ちることが過去の経験則から容易に想像できました。 このような事態を避けるために我々は技術選定で「挑戦」するのをやめ、学習コストの低いフレームワークを採用することを選びました。 採用技術 上記のモチベーションを踏まえ、バックエンドは以下を採用しています。 アーキテクチャ: Clean Architecture フレームワーク: Express x TypeScript アプリケーション実行基盤: 内製ライブラリ「KEEL」 アーキテクチャ: Clean Architecture LIFULL のバックエンド刷新プロジェクトで採用されたアーキテクチャです。 それが社内に広まっていて知見も溜まっているため、今回 Clean Architecture を採用しました。 www.lifull.blog 情報を表示するだけの Web アプリケーションで Clean Architecture は冗長ではないかという意見もありましたが、レイヤごとの責務・規約がはっきりしており制約が厳しいことから開発者によるブレが少なくなることが見込めます。 実際業務で触ってみたときも「どこに何を書けばいいのか」というのが分かりやすく、設計しやすいのは魅力だと感じました。(実装の「詳細」「抽象化」まわりの理解はちょっと大変でしたが……😇) フレームワーク: Express x TypeScript 言わずと知れたバックエンドフレームワークです。 日本語ドキュメントや記事、ライブラリも充実しているため学習コストを低く抑えることができます。 型の恩恵は受けたかったため TypeScript も導入しています。 TypeScript は社内でも導入事例が多くすんなり決まりました。 今の所は複雑な型推論は必要としておらず、素直にコードを追えば型がわかるため安心感があります。 バックエンドとフロントエンドで言語を共通にできるのも良い点だと感じました。 アプリケーション実行基盤: 内製ライブラリ「KEEL」 実行基盤は全社で利用している内製ライブラリ「KEEL」を活用しています。 デプロイやログ吐き出しなど日々の運用タスクを吸収してくれるすばらしい基盤で、 KEEL のおかげでアプリケーション開発に集中できています。 当ブログでもよく記事があがっていますので詳細は割愛しますが、ぜひご覧ください。 www.lifull.blog フロントエンド フロントエンド 技術選定のモチベーション メンテナンスコストが軽いこと まず第一に、LIFULL HOME'S は複雑な操作やインタラクションを必要としないポータルサイトです。 クライアントが入稿した情報の表示がメインで、ユーザーの操作によって画面が大きく変わるということがほとんどありません。 そういったサイトを開発していくうえで React や Vue を用いるのは冗長だと考えました。 実際アップデートの頻度も高くメンテナンスコストが嵩みますし、ビルドプロセスも必須です。 今後さらにイケているフロントエンド技術が出てきた際に引き剥がしにくいという欠点もあります。 前身のメインリポジトリは10年以上稼働していますが、 ソースコードの規模が膨大すぎること、依存関係が大きいことからアップデートが非常に困難になっています。 今回の新基盤は何年稼働するか分かりませんが、少なくとも5年以上は開発されるでしょう。 そうなった時にメンテナンスコストが軽いというのは大きなメリットとなります。 開発者のスキルに左右されないこと バックエンドの技術選定でも触れましたが、賃貸開発部門は規模が大きく人員の入れ替えも顕著です。 常に複数のチームが稼働しており、同じページを並行開発していることもあります。 HTML/CSS/JavaScript に明るくないコーダーやバックエンドエンジニアがフロントを触ることも多々あります。 そういった際に学習コストの高い React や Vue だと逆に開発の遅れを招いたりレビューコストが高まるという懸念もありました。 そのため、なるべく平易で学習コストの低い技術を採用しています。 後述する Tailwind CSS や Stimulus は比較的最近のフレームワークですが、ドキュメントが簡潔かつできることも限られているため1日あれば手に馴染む技術です。 フロントエンド 採用技術 これらのモチベーションを踏まえ、フロントエンドは以下を採用しました。 HTML: Preact x TypeScript で HTML を生成し Express でサーバサイドレンダリング CSS: Tailwind CSS JavaScript: Stimulus HTML: Preact x TypeScript Preact x TypeScript で Express から素の HTML を返却しています。 Preact の選定理由としては軽量であること、型が使えることです。 我々は HTML をレンダリングしたいだけなので React のような重いライブラリは冗長でした。 とはいえ型情報は付与したかったので、 tsx 形式が使える Preact を採用しています。 クライアントサイドの振る舞いは前述の Stimulus を利用するので Preact はテンプレートエンジンとしてのみ利用しています。 これは別のテンプレートエンジンに差し替えたくなったときに載せ替えを容易にするためでもあります。 CSS: Tailwind CSS Tailwind CSS はユーティリティファーストな CSS フレームワークで、非常に小さい単位の CSS クラスを HTML に直接書いて組み合わせることでスタイリングをします。 従来の課題として、HTML と CSS の概念的距離が遠いという問題がありました。 普通にコーディングを行おうとすると HTML に適当なクラス名を振って別途 CSS でそのクラスに応じたスタイルを書き、HTML 側でその CSS を読み込むという手間が発生します。 Sass の登場で CSS を書くのはいくらか楽になりましたが、概念的距離の圧縮まではいきませんでした。 Vue のように SFC でやるという手もありますが、それでもクラスの命名という一番面倒で退屈な作業は残ってしまいます。 Tailwind CSS はすでに用意されているクラスを組み合わせて使うので命名という概念がなく、 HTML にガンガン書いていけるので概念的距離をゼロにできます。 また、クラスの単位が非常に小さいのでカスタマイズ性も高く、デザインの再現も容易です。 HTML を見るだけである程度見た目が想起できるというのも魅力的ですね。 頻出スタイル(ex: ボタンデザイン)は @layer component へ切り出して使っていますが、ごく少数ですし1ファイルにまとめているので何個も CSS ファイルを開くこともありません。 体感ですが CSS を書いていたころよりも倍以上のスピードでコーディングできるようになったと感じています。 素 CSS/Sass を書くときは少し憂鬱になります。 JavaScript: Stimulus 前述したように、LIFULL HOME'S は物件情報を載せるポータルサイトでありユーザーの複雑な操作を必要としません。 そのため js で与えるべき振る舞いも少なく、再利用できる動きがとても多いです。 そういった振る舞いを共通化する際、React や Vue でコンポーネント化して再利用するというのが考えられます。 しかし表示箇所によって少しデザインが変わるだけで HTML や CSS クラスの分岐が発生してコンポーネントが肥大化していくという問題があります。 Slot や Mixin を使いこなせばそんなことにはならないかもしれませんが、大規模な開発組織でそれらの統率をとっていくのは非常に困難です。 そこで我々は 37signals 謹製の JavaScript ライブラリ Stimulus を採用しました。 ドキュメント にもあるように、Stimulus はちょっとした振る舞いを HTML に与えるライブラリです。 HTML に data-controller とそれに付随するデータ、アクションを書くことでイベントを貼り付けることができます。 Stimulus の旨味は Controller を複数アタッチできること、DOM 構造に縛られないこと、イベントアタッチの処理が不要なこと です。 再利用性の高い小さな Controller を作っておけば HTML がどんな構造・デザインであろうとアタッチすれば動きますし、複数組み合わせると複雑な動きも作れます。 また、HTML と js の結合で一番面倒なのが addEventListener の処理かと思いますが、 Stimulus は MutationObserver で DOM を監視しており HTML が画面に出現するだけで 自動で該当の Controller のイベントがアタッチされます。 これは非常に強力で、サーバサイドから Stimulus Controller が設定された HTML を返却すれば Ajax で画面を一部更新しようと勝手にイベントがアタッチされるため js の読み込み忘れで画面が壊れた〜という心配もありません。 同様に DOM が消えれば removeEventListener されるのも安心ポイントです。 今回の新基盤ではオートローダーの仕組みも導入しているためページごとに読み込む js を bundle する必要もありません。たいへん快適です。 弊社エンジニアの Qiita 記事でも紹介しております。 qiita.com 再利用性の高い Stimulus Controller が充実してくると HTML に既存 Controller を書いていくだけで振る舞いの実装が終了するため、js を書くことがほぼなくなります。 実際、新基盤が稼働してから数ヵ月機能追加・画面改修をしていましたが、ほとんど js は書いていません。 コードは書くほど負債になると言いますが、きちんと設計すればするほど資産になっていくのも Stimulus の魅力だと感じます。 過去の記事でも話題にしていますので、ぜひこちらもご覧ください。 www.lifull.blog テスティング テスティング 技術選定のモチベーション テスティングに関してはそこまで大きい課題感はなかったのですが、 UnitTest を速く実行できること、E2E テストを書きやすくするのは意識しました。 テスティング 採用技術 UnitTest: Vitest E2E: Playwright UnitTest: Vitest Vitest は Vite の設定をそのまま使えるテスティングライブラリです。 今まで弊社の TypeScript 環境では Jest を採用していて最初はそれに倣っていましたが、 開発中に Vitest がよいぞとなり乗り換えをしました。 書き味は Jest とほぼ同様ですが実行速度が桁違いで、爆速で UnitTest を回せます。また、config も非常にシンプルで設定がとても楽ちんです。 Vite を使っているわけではないので全恩恵を授かっているわけではないのですが、実行速度が速いというだけで爆アドだと思います。 乗り換え自体もとても簡単にできたので、Jest を使っている方はぜひ Vitest もご検討ください。 E2E: Playwright Playwright は Microsoft 謹製の E2E ライブラリです。 TypeScript に標準対応しているのが魅力的で、難しいセットアップをせずとも導入が可能です。 公式ドキュメント も充実しており、「こういうことがやりたいな〜」というのはたいてい Guide にそろっているので安心感があります。 主に旧基盤と新基盤の情報の表示差分確認や package bump 時のビジュアルリグレッションで活用していて、テスト工数の削減に役立っています。 まとめ まだ新基盤が稼働して数ヵ月しか経っておりませんが、旧基盤と比べて圧倒的に開発しやすく生産性が高まっているのを感じています。 実際稼働から何名か新規メンバーが入りましたが、そこまで苦労せず開発できているようです。 LIFULL HOME'S はまだまだ成長していくプロダクトです。 今回の基盤刷新をきっかけにより良い価値をユーザーに届けてまいります💪 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
エンジニアの加藤です。LIFULL HOME'Sの注文住宅領域を支えるエンジニアチームのマネジメントを担当しています。 LIFULLでは定期的に エンゲージメントサーベイ を実施し、組織の状況把握を行っています。 しかし、エンゲージメントサーベイは全職種共通のフォーマットとなるため、「開発のしやすさ」や「プロジェクト進行」などエンジニア特有の課題までは特定しづらく、さらに深掘った分析が重要です。 こうしたエンジニア特有の課題発見や課題の深掘りを効率的に行うため、「ネガティブミーティング」を企画し実施したので、そちらを紹介したいと思います。 ネガティブミーティングとは 弊社では課題を出し合う話し合いを「ネガティブミーティング」と称して実施しています。 ここでは解決策などは議論せず、ひたすら「コト」に向けて率直な課題を挙げることに集中します。 また、ネガティブミーティングで出た課題の打ち手は別途「ポジティブミーティング」と呼ばれる話し合いで議論し、課題解決の実行まで落とし込みます。 我々はこれら2つを組み合わせて「ネガポジミーティング」と称し、セットで実施しています。 ネガティブミーティングの特徴 ネガティブミーティングは、課題だけを挙げるミーティング 課題を出すことだけに集中するため、普段だと言いづらい反対意見や代替意見も思い切って伝えることができる ネガティブな内容でも、意見を出すことを楽しむ ポジティブミーティングの特徴 ネガティブミーティングで出た課題の解決策を決めるミーティング 実行する解決策を決めたら「誰が」「なにを」「いつまでに」を決める ワークの流れ 今回のワークでは3, 4名ずつのチームに分かれ、以下の流れに沿ってネガティブミーティングを実施しました。 誰かが「ここがつらいよ〇〇」を決める(例:ここがつらいよ目標設定) みんなで「その心は〜」をひたすら出す(例:その心は、エンジニアの成果の定量化がキツい) 意見を出し切ったら1に戻る 今回のようなワークではより多くの観点で課題を出し合うことと、チームメンバー全員が話し合いに参加することが重要です。 そのため、1のテーマ設定は一人ずつ持ち回りで行うよう工夫しました。 また、ネガティブミーティングでは出てきた課題に対し悲観的になるのではなく、むしろ課題が浮き彫りになったことを前向きにとらえることも重要です。 最後は笑顔で終えられるしくみとして、チーム内で最も共感された課題を川柳(または短歌)にまとめ、全体へ共有してもらうこととしました。 やってみた結果 今回は約30分のワークにて実施しましたが、50を超える課題を発散したチームもあり想像以上の結果が得られました。 大きく分けると「開発プロセス」、「プロジェクト進行」、「技術的負債」、「プロダクト」、「目標・評価・キャリアパス」についての課題が多く挙がり、狙いであったエンジニア特有の課題発見と深掘りが実施できたと感じています。 また、どのチームも楽しみながら課題を出し合えていたように見受けられ、この後実施する課題解決にも前向きに取り組んでいける印象を抱きました。 組織課題のようなネガティブな意見は心理的安全性が担保された環境でなければなかなか言いづらいものです。 ネガティブミーティングのフレームでは課題を出し合うことだけに集中することが前提となるため、そういった環境づくりに効果的であると感じます。 まとめ 今回はネガティブミーティングを通じて組織課題を発散する取り組みについての紹介でした。 今後はポジティブミーティングを開催し、今回出てきた課題への打ち手を考え、解決に導くための取り組みも実行してきたいと思います。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは!LIFULLのエンジニアで、Ltech運営チームの河西です!今回は 2023年2月21日(火)に開催した『Ltech#23 LIFULLにおけるSalesforce活用事例について語ります』についてレポートします。 Ltechとは 株式会社LIFULL主催の、技術(エンジニアリング・テクノロジー)をテーマにしたイベントの総称です。 特定の技術に偏らず、様々な技術をピックアップしていきます。 Session1 Salesforceのシステム構成と最近の開発施策の共有 www.docswell.com このセッションでは、LIFULLにおけるSalesforceの利活用による目指す世界、そのために直近で行った施策について紹介いただきました。 最初に、Salesforceの利活用によって実現される世界として、「オムニチャネルの実現」を掲げ 一箇所にデータを集めて、オムニチャネルを実現。 LIFULLのエンドユーザ様へ最高のユーザ体験をご提供したい。 そのために、LIFULL HOME'S等LIFULLの各サービスや社内システムを繋ぎ、CRM(Customer Relationship Management)を実現する。という世界観を語っていただきました。 続いて、様々な具体的な取り組み事例として PIS(Personal Information System)というお問い合わせ情報を一元管理するためのAPIを開発 ログイン画面でユーザ登録した、ログインユーザ認証情報を格納・管理するための仕組みを構築 Salesforce Marketing Cloudと連携し、エンドユーザに対して、物件案内のメールを配信する仕組み 不動産会社様向けLIFULL HOME'S PROサイトで閲覧できるレポート機能 商談・注文情報など各種情報や売上金額が確認できる社内システム Pardotを利用した不動産会社様向けツールの利用 などなど、様々な事例をご紹介いただきました。 今回は時間の都合上、全てを細かく紹介できませんでしたが、まさしくCRMを実現するために、様々な取り組みをされている印象でした! Session2 Salesforce Field Service Lightningによる不動産相談窓口サイトの開発立上げ www.docswell.com このセッションでは、Field Service Lightningを利用して、不動産相談窓口サイトの立ち上げを行った話について紹介いただきました。 LIFULL HOME'S住まいの窓口の相談予約ページでは、高いNPS(Net Promoter Score)を実現するために、手動による管理が多くの工数を占めており、運用負荷が高い状態でした。 その状況を打開するために、Field Service Lightning(以下FSL)に着目し、 テクニカルナレッジは公式のわずかなものだけ 国内導入事例が皆無の新製品での挑戦 という状況下のなか、如何にして改善を行ったのかをご紹介いただきました。 当日の発表を聞きに来ていただいた方からも、FSLについてのQAが多く、 ・Field Service Lightning は使ったことがないのですが、導入にあたってハマったポイントや苦労した点はありますか? ・Field Service Lightningを選んだ理由はなんですか ・Field Service Lightningはチャット機能等、ほかにもいくつかありそうですが予約受付以外ではどのような機能をつかっていますか? といった様に、実例が少ない中、実際に導入・運用をされた経験を聞けた貴重なセッションでした! Session3 SalesforceのデータをもとにTableauで月ごとの売上金額を表示 www.docswell.com このセッションでは、最初のセッションで紹介されていた「商談・注文情報など各種情報や売上金額が確認できる社内システム」についての具体的なお話について紹介いただきました。 LIFULLにおける商談の流れの中で、それぞれ商品情報の持ち方が異なるため、月ごとの売上がいくらなのか集計しずらいといった課題がありました。 こちらの課題を解決するために、 SalesforceのApexバッチやApexトリガで売上金額を分割 BigQueryに連携 BigQueryに連携したデータをTableauで可視化 といった手法を用いて解決した事例を紹介いただきました。 QAでは、以下の様な質問もしていただき、 ・Salesforceの標準レポートやダッシュボードの機能とTableau?の機能に差はありますか? 回答として、「Salesforceで補えない部分を他のサービスを組み合わせて解決していく」といった、実務でないと聞けない貴重なお話を聞くことができました! まとめ 今回はLIFULLにおけるSalesforce活用事例としてSalesforceに関する話を3名のエンジニアに発表いただきました。この他にもLIFULL HOME'Sではメンバーが随時 LIFULL Creators Blog にて情報を発信しています。 www.lifull.blog Ltechでは、LIFULLのエンジニアが中心になって皆様の技術欲を満たすよう実例を交えた勉強会を開催しています。今後も Ltech を積極的に開催していきますので、ぜひ気になった方は、connpass で LIFULL のメンバー登録をよろしくお願いします! lifull.connpass.com また、LIFULLでは、数多くの職種の仲間を募集しています。 よろしければこちらのページもご覧ください。 【エンジニア】募集求人一覧 | 株式会社LIFULL 【エンジニア】カジュアル面談 | 株式会社LIFULL
プロダクトエンジニアリング部の二宮です。 我々のプロダクトエンジニアリング部では「強い個人・最高のチームになることで価値創造を加速させ続ける」というビジョンを掲げています。そして、その「強い個人」を目指して、週に数時間程度、普段できないチャレンジングな技術の探索など、ある程度自由に時間を使うことが推奨されています。 その一つのやり方として、最近は社内で技術書の輪読会をすることが流行ってます(以前、LIFULLクリエイターズブログにも「 "INSPIRED"の輪読会を通してふりかえるプロダクト開発 」という記事も共有されています)。 今回は、2つのチーム合同で『 システム運用アンチパターン 』を読み終わり、その輪読会がなかなか好感触だったので紹介します。ある程度ベテランのエンジニアが持つ知識を身につけるとともに、本の内容に触発された議論も同時に行えたと思っています。 輪読会とは まず、輪読会とはなにかに説明します。 weblio辞書 から引用します。 人々が集まって、同じ教科書などの本を読み、その内容について意見を交わすことを意味する語。 事前に決められた担当者が、本の内容を訳したりまとめたりしてから、他の参加者が理解できるように発表する形式がとられることも多い。 ちょっと話を先取りすると、個人で勉強するより「質問ができる」「その場で自分たちの文脈での議論ができる」などのメリットが感じられました。 輪読会の実施方法 輪読会には様々なやり方があるのですが、私たちが実際にどのように実施したのか紹介します。これ以外の方法を幅広く知りたい方は「 輪読会のすゝめ。全8回の開催で学んだ失敗パターンと成功のコツ 」などの記事が参考になると思います。 参加者 担当プロダクトが別の2つのチームが合同で行いました。人数は6人で、全員がソフトウェア開発がメインのエンジニアです。 本の選定 候補に挙がったのは次の3つの本です。 システム運用アンチパターン Googleのソフトウェアエンジニアリング 仕事ではじめる機械学習 「主管システムに関わらず共通しているシステム運用の実務に関して学べそうな内容で、比較的短期間で読み終わりそう」という理由で『システム運用アンチパターン』を選びました。 重厚な承認プロセス、可視化されていない運用、プロセスの最後でのみ行われるソフトウェアテスト、ノイズだらけのアラート、インシデントから学習しない習慣、時間外のデプロイ、情報のため込みなどを取り上げ、ソフトウェアシステムの開発運用が滞るチームや組織に共通してみられる陥りがちな状況や犯しがちな間違いをアンチパターンとして紹介します。そして管理職やマネージャでなく、エンジニアが実行し、繰り返すことで改善できる具体的な行動を解説します。 本の内容もけっこう面白く、後述する「過去の失敗談をフラットに共有できた」「開発文化について考える機会となった」みたいな感触は、この本のテーマのおかげが大きかったんじゃないかと思います。 実施方法 「各章の担当者を決め、毎週輪読会の時間を取り、1章ずつ担当者が内容をまとめて発表する」というオーソドックスな方法で行いました。輪読会本編は前半で発表を聞きながら質問やコメントを表に書いていき、後半でそれを元に話をする形式です。 第3章の「盲目状態での運用」で会話した跡の表 このように、「社内の文脈に置き換えるとどうなのか?」とか「実は本の内容と近い失敗をして後悔してるんだ」とか話が広がりました。 輪読会をやりながら、工夫して変えた点は2点あります。自分たちにとって輪読会ははじめての経験だったため、途中で一度振り返りの時間を用意していました。 輪読会の時間を15分に設定していたが、めちゃめちゃ駆け足になってしまったため30分に延長した まとめ方は完全に自由だったが、「どこまでまとめればよいかが分からなくて大変じゃない?」「自分の経験も絡めた話もあったほうが面白そうだ」みたいな声が上がり、簡単な推奨フォーマットを用意した フォーマットといっても、要約や章のポイントを箇条書きし、関連するエピソードや自分なりの解釈 をコメントするという簡単なものです。 実施した実感 輪読会の後に振り返りでは、次のようなメリットが感じられたという意見が出ました。 本の内容に触発され、ベテランの過去の経験を聞くことができた。過去の失敗談をフラットに共有できた 他の人に説明する必要があるので、担当した章の内容を、分かったつもりにならずに理解できた 社内の開発文化について自分の立場から貢献できることはないか考える機会となった 共通認識ができて、その場で現状改善のアイデアの議論ができた 会話が弾んだ。単純に楽しかった 特にこの本の内容はネガティブさとポジティブさのバランスが絶妙だったと思います。例えば「 第10章 ブレントだけが知っている 」には次のような話がありました。 意識しないとキーパーソン(『 The Phoenix Project 』の登場人物のブレント)に知識が集まる 情報を積極的に共有すればいい?→公開するだけでは興味を持てるわけではない ひどいと誰も全体を見なくなる 私自身もそうだったことがあるように、「せっかく情報共有のドキュメントを書いたのにみんなが興味を持ってくれない」「組織文化がドキュメントの重要度を理解してない」という、自分自身のモチベーションを下げ、冷静な議論を妨げてしまう形で悩んでしまうことも多いと思います。 ただ、そのような失敗例を共有するだけでなく、「実はドキュメントだけでなく別のコミュニケーションの方法もあったんじゃないか」とか「情報共有を習慣づけするためにこういう方法もあったんじゃないか」という紹介があり、そこでポジティブで建設的な話に自然に繋がったように思います。 おそらくDevOpsやアジャイル手法の本は、特にエンジニア経験の違いに関わらず発展的な議論ができ、同様の感触が得られるものも多いんじゃないかと思ってます。特にベテランから若手へと、数々の失敗で学んだ暗黙知を共有するのにも役立つかもしれません。 そして次の輪読会へ 今回の輪読会では、きちんと本を最後まで読み終わり、2チーム合同の輪読会の形では解散ということになりました。 次に、自分たちのチームでは『 仕事ではじめる機械学習 』を読み始めています。自分たちはBigQueryにあるデータを扱うことが多く、機械学習も絡めたシステムを作れれば、より有用な機能を実装するチャンスを広げられると考えているためです。今度は本の内容に合わせて「全員が軽く事前に読んでいて、それぞれが気づいた点や質問したい点をまとめて発表する」という別のやり方で工夫しています。 仕事ではじめる機械学習の輪読会の様子 こちらも、また機会があればブログで報告します。 最後に 最後に、募集求人やカジュアル面談のページを紹介します。一緒に成長しながら、前向きにチーム文化を作っていける方に来ていただけると嬉しいです🙇♂️ hrmos.co hrmos.co
こんにちは!LIFULLプロダクトエンジニアリング部の 鄭 在淳(ジョン・ジェスン) です。2022年に新卒で入社して、主に 不動産アーカイブ や 住まいインデックス の開発・運用を担当しています。 今年、新卒2年目のエンジニアとなり、ますます幅広い分野の業務に取り組んでいます。そして、自分が担当するタスクをより効率良くこなすためには、 個人の「情報力」を成長させることが非常に重要 だと感じています。 「情報力」とは、エンジニアが最新技術や良いコードの書き方、アーキテクチャ設計などの 情報を習得(Input) し、これらの情報を 実際の業務で活用(Output) する一連の流れと定義します。つまり、エンジニアたちの情報との向き合い方を意味します。 「情報力」を向上させるためには、自己学習などを通してエンジニア自身が一人で頑張れば、十分成長できるかもしれません。一人ではなく、 多くの人が集まって一緒にすれば、より大きな価値を出すこと ができます。 LIFULLではエンジニアの「情報力」向上のため、様々な取り組みを行っているので、今回の記事でいくつかの活動を紹介します。 目次 目次 LIFULLの活動紹介 Qiita Blog LIFULL Creators Blog LIFULL Developer Channel エンジニアいつでも相談 エンジニア向けの社外・社内イベント まとめ LIFULLの活動紹介 Qiita Blog 主にLIFULLのエンジニアたちが Qiita organizations にアカウントを登録して 誰でも自由に作成できる技術ブログ です。技術的な内容以外にも、プロジェクト運用・効率的なツールの使い方・開発環揃えなど様々なカテゴリーの記事を投稿しております。 Qiita Advent Calendarに参加し、LIFULLは全てのカテゴリーの中で7位となるほど大盛況でした。その中でも、 @pal4de が投稿した2件の正規表現式記事は、合計で500件を超えるいいねを獲得しました。正規表現でお困りの方は是非ご覧になってください。 たった4文字でコード検索の精度がブチあがる正規表現 シンプル図解: 正規表現の (?= ) とか (?! ) とか (?<= ) とか (?<! ) とか LIFULL Creators Blog この記事が掲載されているHatena blogです。LIFULLのビジョン実現につながる価値提供への取り組みを発信しております。 LIFULLのコーポレートメッセージ LIFULLのビジョン Qiitaとは異なり、エンジニアではなく「LIFULLのものつくり」に取り組んでいる社員なら、職種と関係なく記事を投稿できます。主にLIFULLのサービスを発展させるため、取り組んでいる内容が投稿されていて実際のプロダクトを事例としているので、 記事を通してLIFULLの文化や雰囲気などを知ること ができるのが特徴です。 例えば、LIFULLへ入社を考えている方が気になる「リモートワーク化での働き方やコミュニケーションの取り方」や リモートワーク化でも大切にするオフラインコミュニケーション リモートワーク時代におけるサークル活動の取り組み 「LIFULLのプロダクトどのようなシステムで運用されているのか?」等の入社前に知ることができない情報を記事を通して確認することができます。 LIFULLの全社アプリケーション実行基盤 KEEL について LIFULLのプロダクトの可観測性の向上について LIFULL Developer Channel 自分自身も運営メンバーとして参加している YouTubeチャンネル です。 LIFULLのエンジニアたちがYouTubeを通して、QiitaやCreators Blog 等のようにテキストに加え動画といった より多様な形式での情報の発信ができる環境を創る ために運営メンバーとして参加しています。 昨年9月頃に 「情報セキュリティ対策を行う意義」 という最初の映像を投稿させていただきました。3月上旬頃に2本目のLIFULLエンジニアのキーボードを紹介する動画を投稿することを目指しています。 LIFULLの文化や雰囲気をよりリアルで伝えていきたいと思いますのでよろしくお願いします。 エンジニアいつでも相談 1年ほど前にエンジニアの二宮が投稿したブログでも紹介させていただいた、GitHub Discussionsを使った社内向けのQ&Aフォーラムです。エンジニアが 誰でも気軽に技術相談やプロダクト仕様に関する質問など行えるようにすること を目的として作られたのが「エンジニアいつでも相談」です。 GitHub Discussionsで社内のQ&Aフォーラムを開設する 例えば、誰かが「設計の方針を決定することで〇〇が気になります。」、「実装で〇〇を迷っています。」、「〇〇に関して知見がある方がいらっしゃったら、教えていただけますか?」のような 質問を投げると、LIFULLエンジニアの皆が回答 してくれます。 このように相談しやすい環境ができているので、いつでも心理的安全性を保ちながら働くことができます。複数の人がDiscussions内で議論して解決策を探っていく様子がOSS活動にも似ていると感じており、課題に対して皆が協力して一緒に解決するという開発文化が社内で作られています。 エンジニア向けの社外・社内イベント 「カイゼン・ジャーニー」という書籍でも紹介されたことがありますが、 チームから会社へ越境して社内改善の場を作る のはエンジニアリングで非常に重要なところです。それを実現するため、LIFULLではLtech、LIFULL Tech Hub、ハンガーフライト、フリートーク勉強会等のエンジニア向けの多様な社外・社内イベントを開催しています。 LIFULL主催の技術勉強会 Ltech 『#21 LIFULL HOME’Sを支える検索技術』開催レポート (社外向け) 社内テックカンファレンスLIFULL Tech Hubを開催しました (社内向け) 毎月、技術的な話からチームのプロジェクト管理方法など様々なテーマでイベントが開催されており、自部署で担当している業務範囲以外の分野の技術・知識も得ることができます。経歴や担当業務に限らず、希望すれば誰でも手を挙げて発表できるので、小さな改善でも全社的に拡大していくことができます。私も入社して2回程度自分が改善のため取り組んだことを発表したことがあります。 まとめ 今回紹介した活動以外にもLIFULLでは エンジニアの情報力を成長させることができる様々な活動 をしています。こうした活動が「エンジニアとしてみんなを幸せにしたい」という想いの実現につながり、LIFULLの社是である「利他主義」とも繋がっていると思います。 LIFULLのエンジニア組織は エンジニアとして経営をリードする ことで活躍するというスローガンを掲げています。短期的に技術の幅を広げるだけでなく、技術を手段として中長期的に社会課題解決に貢献していくことを目指しています。 LIFULLではコーポレートメッセージである「あらゆるLIFEを、FULLに。」の実現を目指して、国籍、年齢に関係なく共に働いていただける仲間を募集しています。興味がある方は以下のページをご覧ください。 hrmos.co hrmos.co
エンジニアの松尾です。LIFULL HOME'S の売買領域を支えるエンジニアチームのマネジメントを担当しています。 私が所属する組織ではLIFULL HOME'Sからより良い価値を提供していくために、エンジニアの「業務効率化」と「コミュニケーション活性化」が課題となっています。今回はこれらを効率良く進めるために取り組んだ内容を紹介します。 組織の課題 業務効率化 コミュニケーション活性化 両立のための取り組み 開催の準備 コンペ当日の流れ その後の取り組み まとめ 組織の課題 業務効率化 プロダクトエンジニアリング部では、ユーザーへの価値をより早くより良く提供するために、常に業務プロセスの効率化を検討しています。 KPIマネジメント を元に、どのような作業がボトルネックになっているかを適宜洗い出しながら着実に進めていきます。 ボトルネックの分析から「データベースからのデータ取得業務(SQL作成)に割いている時間が長そう」という結果が見えており、削減を検討していました。 コミュニケーション活性化 LIFULLでは部署やチームの結束を高めるために「総会」という形での場作りを行っており、プロダクトエンジニアリング部2ユニットでも月に一度のユニット総会を開催しています。 リモートワーク中心での業務では直接会話する機会も少なくなっているため、グループを超えたコミュニケーションの活性化は組織の課題となっています。 両立のための取り組み そこで、(「業務効率化」につながる)SQLを作成するコンペを、(「コミュニケーション活性化」のために)ユニット総会の場で行うことにしました。 チームに分かれて、制限時間内に完成したSQLの数で競ってもらうことにします。 開催の準備 過去にあったデータ抽出の依頼やほかの職種のメンバーへのヒアリングを元に、「あれば今後の業務が楽になりそうなSQL」をイメージして作問しました。問題として成果物に期待される条件と、カラム名を与えるようにしています。 問題のサンプル 最終的に30問程度の問題を用意しましたが、すべての回答を用意するのは難しく(むしろ用意すると開催の意味がなくなるため)、正解は成果物の内容から軽いチェックで判断することにしました。※成果物のレビューについては後述します。 コンペ当日の流れ 開催が12月後半だったこともあり、グループを紅組と白組の2チームに分けて競ってもらう形式で実施しました。 参加者は人数が多かったため、チーム内でMeetでの部屋の分割やSlackのハドルを活用した分担を行いながら進めてくれていたようです。社歴でバランスを取ってペアを決めたり、「新築マンション関連は詳しいので任せてください」というような強みでチームをリードしたり、各チームで効率を考えながら取り組めていました。 開催の成果として多くのSQLができあがり、隣の部署のメンバーとの会話の機会も作れたことで、当初の目的は達成できたと感じています。 その後の取り組み さまざまなデータを取得できるようにはなりましたが、「なぜこのテーブルから取得するのか」「なぜこの条件を絞るのか」という疑問が解消しきれていない箇所もあります。 そのため週に一度有志のメンバーが集まり、完成したSQLを一つずつ紐解きながらLIFULL HOME'Sの知識を蓄積していく勉強会を行っています。この会はレビューも兼ねており、これにより正確にデータを抽出できるしくみが整ってきています。 まとめ SQL作成のコンペを通して、「業務効率化」と「コミュニケーション活性化」の両立に取り組んだ事例について紹介しました。今後もより良くユーザーへの価値提供を行っていくために、無駄を省き本質に集中できるしくみを作っていきたいと思います。 最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。 hrmos.co hrmos.co
こんにちは。エンジニアの中島です。 現在はアクセシビリティ推進グループ(以下推進グループ)に在籍しています。 以前同組織の紹介記事をいくつかあげましたが、その通り弊社は自社の運営するサービスをアクセシブルにするため日々奮闘しています。 www.lifull.blog www.lifull.blog 以前の記事ではどういったマインドで同組織ができたか、どのように推進しているかについて話ました。 今回は、そういった活動の中でいくつか技術的な副産物が生まれたのでその話をしようと思います。 キーボード操作編 CSSの概念距離 さいごに キーボード操作編 アクセシビリティ対応にあたって、基本的なやることの一つにUIをキーボード操作可能にするという作業があります。 自社のサービスにもキーボード操作不能ないくつかのUIの存在を認識しており、それらを実際に直していくということをしています。 修正時、場合によってはJavaScriptを記述する必要があるのですが、それにあたって我々の採用しているStimulusというライブラリに若干の不満がありました。(概ね気に入っています) それはHTML側からみて宣言的に記述することを良しとする同ライブラリの指針に対してキーボードイベントのハンドラを宣言的に記述することが難しいというところにあります。 Stimulus3.1まではキーボードイベントにフックして何らかの処理を記述する場合、以下のように書きます。 < button type = "button" role = "tab" data - action = "keydown->tab#moveLeft keydowon->tab#moveRight" > タブ1 </ button > // tab_controller.js ... moveLeft(evt) { if (evt.key !== 'ArrowLeft' ) { return ; } // タブを一個左に切り替える } moveRight(evt) { if (evt.key !== 'ArrowRight' ) { return ; } // タブを一個右に切り替える } keydownが発生した時にmoveLeftとmoveRightが動くというところまではHTML側から理解できますが、この実装では、"どのキー"を押した時に動作するふるまいなのかまで説明されておらず、読み手の関心はJavaScriptの実装にまで到達してしまいます。 これに不満を感じていたのでStimulus自身に本件のIssueとPRをなげて3.2で取り込んでもらうに至りました。 github.com stimulus.hotwired.dev これによりStimulusでも今までより宣言的に記述できるようになり、UIのふるまいに対する関心をHTMLに封じ込めることができるようになりました。 < button type = "button" role = "tab" data - action = "keydown.left->tab#moveLeft keydowon.right->tab#moveRight" > タブ1 </ button > CSSの概念距離 UIの修正は時にはHTML, JavaScriptだけでは完結せず、CSSの修正が必要なケースもあります。 ただ、長年運用され続けている巨大なサービスにおいては、思いもよらぬ依存と向き合わなければならないこともあります。 AというUIを直そうとしたら、それがBというCSSに依存していて、さらにそのBはCという別のページのよく似たUIにも適応されていて...といった厄介な依存です。 少しの深さの依存なら把握できても、とても深い依存になるとなかなか一筋縄ではいきません。 これはHTMLとCSSの概念距離が開いているが故に起きる問題です。 world.hey.com qiita.com 両者の距離が開くことで片方がまた別の何かとの依存を起こしやすくなっているのです。 恥ずかしながらいくつかの実装にあたってこの依存起因で表示崩れを起こしてしまい、緊急対応を行いました。 そうしたことをしているうちに、継続的に直していくには(これはアクセシビリティ対応だけにとどまらず)この概念距離をなくしていかないとニッチもサッチもいかないなと感じました。 そこでこの概念距離をなくしてHTMLとCSSがぴったり一対一で対応するようにTailwind CSS(UtilityCSSのライブラリ)を導入しました。 tailwindcss.com こぼれ話のこぼれ話ですが、導入にあたり、サイト内でベースとなるfont-sizeが100%だったり62.5%だったりと揺れていた問題が浮き彫りになりました。 その問題はテンプレートを再起的にパースして62.5%の適用されたテンプレートで参照されているCSS、そうじゃないCSSを分類し、CSSパーサでtransformをかけて単位を合わせるといったことをして乗り越えました。 Tailwind CSSはスタンドアロンでも動作するようになっており、わざわざ利用者側がPostCSSを導入しなくても、内包しているPostCSSを利用して動作させることができます。 メンテナンスコストの面から考えて、なるべくパーサ系ライブラリとの依存を小さくしたいと考え、このスタンドアロンな挙動にのっかりたいと考えました。 しかし、リリースにあたって、キャッシュバスティングの機構も同時に必要だったので、Tailwind CSSが内包しているPostCSSにキャッシュバスティングの手製プラグインを適応させたところうまく動作しないことがわかりました。 キャッシュバスティングとは ブラウザがキャッシュされた古いファイルを参照し続けないようにするための機構 (パスやクエリにファイルのダイジェスト値を入れたりするケースが多い) ソースを読んでみたところ、どうやらTailwind CSSは内部でPostCSSを利用しているものの、そこで最終的に生成されるファイル名情報に関しては破棄して動くようになっているようでした。 そこで、Tailwind CSSに対して内包するPostCSSで生成されたファイル名情報を尊重する実装をPRで提案したところ、数日のうちに取り込んでいただけました。 github.com こちらはTailwind CSS 3.2.5で搭載される予定になっています。 さいごに 何か作業するにあたっていくつかの困難と向き合い、そちらに作業がスライドしていくyak shavingはエンジニアの宿命なのかもしれません。 いろんな作業を通してスパイラル的によりよい改善が行えるとよいなと思います。 最後までお読みいただきありがとうございました。LIFULL では共に働く仲間を募集しています! hrmos.co hrmos.co
AI戦略室の嶋村です。私はAI戦略室のエンジニアマネージャを担っておりますが、 イノベーションマネジメント委員会(IM委員会) という委員会活動にも参加しています。今回は、IM委員会で取り組んでおります 新規技術の蓄積を促進するための活動 について、投稿したいと思います。 弊社LIFULLは社是で「 利他主義 」を掲げている 社会課題解決型企業 であり、コーポレートメッセージで表明している「 あらゆるLIFEを、FULLに。 」の実現を目指しています。その実現に向けて、どのような社会課題を解決していくのか、弊社の取り組みを「 LIFULLアジェンダ 」として公開しています。複雑な社会課題を解決していくためには、社会課題が生まれる社会構造や原因を深く理解することはもちろんのこと、 解決に必要な技術を適用 していく必要があると考えています。必要な技術は一朝一夕で獲得できるものではないため、 継続的に新たな技術を獲得・蓄積 していき、イノベーション創出につなげていく必要があると考えています。 イノベーションマネジメント委員会「技術蓄積チーム」とは イノベーションマネジメント委員会(IM委員会)は「 イノベーション創出サイクルを加速させるエンジン 」の役割を担っており、小さな改善のみならず 非連続的な大きな革新・革進 をもたらすイノベーションを創出する文化の醸成に取り組んでいます。 そのIM委員会では幾つかのチームが連携をして イノベーションマネジメント体制の構築 を進めており、私は新規技術の蓄積を進めるための「 技術蓄積チーム 」を担当しております。この「技術蓄積チーム」は、 「社会課題や事業課題の解決につながる新規技術を獲得・蓄積・共有する文化を醸成する」 ことを目的として、新規技術の蓄積を促進するための活動に取り組んでいます。 多くの社員が新規技術を実際に触ってみて獲得し、それを事業で使える形で蓄積し、他の社員がその技術を利用できるように共有していく、という流れを作っていきたいです。 Social Innovation Forumで注目された技術 弊社では、社員一人ひとりが社会課題について向き合い、 課題解決に向けたアイデアを技術と掛け合わせて発想するための知的交配の場 となる「 SOCIAL INNOVATION FORUM 」( SIF )を開催しています。 技術蓄積チームは、このSIFの中で社員のアイディエーションを促進するために、 社内外の新規技術をまとめた「テクタスシート」 と呼ぶシートを作成して社内公開しました。どのような技術なのか、 その技術で何ができるのか 、を平易にまとめており、非エンジニアでも理解できるように仕上げました。 SIFのアイディエーションで、社員が課題解決するために活用する技術を選ぶワークがあるのですが、その際に選ばれた技術がどのようなものか簡単にですが紹介します。 前々回で 主に注目が集まっていた技術 は下記の通りです。 AI技術 レコメンド技術 これらの技術に注目が集まるのは AI戦略室の一員としては嬉しい結果 ですが、"AI"や"レコメンド"という広い言葉に 過度な期待が集まっている表れ でもあり、何でもできる魔法のツールとして考えられていないかと複雑な心境もありました。 一方で前回のSIFでは前々回と異なり下記の 新たな技術に注目 が集まりました。 Web3技術 XR技術 ヘルステック技術 Web3技術 や XR技術 については、メタバース・DAO(Decentralized Autonomous Organization)・NFT(Non Fungible Token)といった 新しい概念への期待 が高まっていると感じます。仮想現実が当たり前のようになると、物理的な制約に縛られることなく、 いつでもどこでも誰とでもつながることができる世の中 になるかもしれません。 ヘルステック技術 については、私自身もコロナ禍で在宅勤務が中心となった後、運動不足・体力不足に陥り、ヘルステックへの興味が強くなりました。私事で恐縮ですが、現在はウェアラブルデバイスを身に着けて運動量を計測し、1日の目標消費カロリーを決めて、それを超えるようにウォーキングを継続しています。健康第一という言葉があるくらい、健康は重要なテーマであるため、 健康維持は大きな社会課題の一つ だと感じています。 IM技術蓄積レポート もう一つの取り組みとして、 社会課題や事業課題に関連する新規技術 について、IM技術蓄積チームが 週1回の頻度で社内発信 する「 IM技術蓄積レポート 」を始めています。上で述べたSIFというイベントの時のみならず、普段から新規技術に触れていただき、技術蓄積を促進するねらいです。 なお、ここで記載した「 新規技術 」とは、 世の中でも新しめの先進的な技術 世の中では既に活用されている技術だがLIFULLやLIFULL HOME'Sにはまだ取り入れられていない技術 の両方を指しています。 週1回の頻度で新たな投稿ネタを探すのは大変でもありますが、IM技術蓄積チームのメンバが交代で、 社会課題×新規技術 、 事業課題×新規技術 、 他社の最新技術情報 などなど、バラエティに富んだ発信をしています。また、継続性を重視するために毎回のレポートでは短めでも発信することを優先しています。「 継続は力なり 」とも言いますので、発信を絶やさずに興味を惹くレポートを増やしていきたいと思い、2022年12月上旬時点では Vol.11まで発行 しています。 これまで私は「SIFで注目が集まった技術の紹介」・「新規技術に関する情報源の紹介」・「サッカーW杯のVAR(Video Assistant Referee)を支える技術に関する考察」というテーマで発信をしてきました。私は社会課題解決への興味が強いため、今後は社会課題に対してどのような技術が使われているのか新規技術活用の事例を中心に紹介していく予定です。 IM技術蓄積レポート おわりに 今回は イノベーションマネジメント委員会 の 技術蓄積チーム での取り組みを紹介しました。 社員の一人ひとりが新規技術に目を向けて技術を獲得・蓄積・共有し、その技術を社会課題や事業課題に適用することで課題解決をする、という イノベーション創出の仕組み化 に向けて革進を続けていきたいと思います。 最後になりますが、LIFULLでは共に成長できるような仲間を募っております。AI戦略室では データサイエンティスト や MLOpsエンジニア を募集中です。 カジュアル面談もありますのでご興味ある方は是非ご応募ください! hrmos.co hrmos.co
LIFULL札幌開発拠点で働くエンジニアの村田です。 札幌では本格的に雪が降り始め、寒さが非常に厳しい時期になってきました。 本エントリーは、 LIFULL Advent Calendar 2022 、12月22日の記事であり、LIFULLのプロダクトの可観測性を向上させた話をさせていただきます。 背景 LIFULLではマイクロサービスプラットフォームとして内製の「KEEL」というPaaSを用意し、それを利用してアプリケーションを開発することで開発者の生産性の向上を図ってきました。 KEELについての詳しい話は以下を参考にしてください。 www.lifull.blog ログ基盤もKEELが提供する機能の一つで、当時はAWSのフルマネージドサービスであるCloudWatch Logsを利用していました。 特にCloudWatch Logs Insightの体験が良く、これを利用してアプリケーションのログを柔軟に検索することができていました。 しかし、当時のログ基盤周りでは以下の問題点がありました。 ログはCloudWatch Logs、メトリクスはGrafanaで閲覧と、ツールが2つに分かれて利用している状況だった マイクロサービス毎のログに共通IDが割り振られておらず、特定のリクエストに関連するログの調査コストが高かった CloudWatch Logsにかかる費用が増大しつつあった これらの状況を鑑みて、KEELチームはGrafana Lokiを利用し、より利便性の高いログ基盤を再構築する決断に至りました。 Grafana LokiとはGrafana Labが提供するスケーラブルで高可用性なログ集約システムです。 集約されたログはLogQLと呼ばれるクエリで柔軟に検索・集計することが可能であり、ストレージはS3などを利用して費用面でも安く抑えることができます。 grafana.com CloudWatch Logsとの比較検証 いきなりGrafana Lokiに移行しても、開発者体験が失われてしまっては意味がありません。 KEELチームでは、Grafana Lokiによるログ検索体験がCloudWatch Logsを利用していた時と変わらないか、それ以上のものを提供できるか検証する必要がありました。 まず最初のステップとして、従来のログ基盤に加え、検証対象であるGrafana Lokiにもログを書き込み、KEELチームでその体験を検証することにしました。 たとえばCloudWatch Logs Insightの以下のようなクエリは fields @timestamp, @message, @logStream | filter structural_log.status >= 499 | sort @timestamp desc | limit 20 Grafana Lokiでは、LogQLを用いて以下のように同様の表現をすることができます。 {grouping="kubernetes.ns.svc"} | json | structural_log_status >= 499 CloudWatch Logs Insightでできる以下のような集計については stats count(*) by @logStream | filter structural_log.status >= 499 検索したログから、アドホックスタティクスアイコンをクリックすることで簡単な集計結果を得ることができますし 以下のようなクエリで代替する結果を得ることができます。 sum( rate( {grouping="kubernetes.ns.svc"} | json | structural_log_status >= 499 [1m] ) ) by (metadata_pod_name) 上記のようにCloudWatch Logs Insightによるログ検索は、LogQLでほぼ代替することができ、これらの体験はGrafana Lokiでもほとんど損なわれることはないと判断しました。 Chrome ExtentionによるGrafana Lokiの体験向上 CloudWatch Logs Insightでは、fieldに@logStreamを指定することで、該当ログ付近のログを表示するリンクが生成されます。 Grafana Logにも対象ログの前後のログを表示する機能はありますが、少々使い勝手が悪く利用しづらい状態でした。 そこでKEELチームはChrome Extentionを独自に開発して、Grafana LokiのHTMLを書き換え、同等のリンクを作成することに成功しました。 具体的には、Grafana Lokiに表示されるログのtimestampをクリッカブルにして、その前後の時間帯をクエリに加えたログの検索結果に遷移できるようにしました。こうすることでCloudWatch Logs Insightの@logStreamと同じ体験を実現することができました。 このChrome Extentionはこれだけの利用にとどまらず、後述するTraceIDを利用したログ検索への導線を作成したり、エラーログに書かれるスタックトレースからGitHub上の該当コードに飛べるようにするなど、色々応用することができました。 TraceIdの埋め込み さらなる体験向上を目指し、KEELチームが次に行ったのは、LIFULL HOME'Sへのリクエストに共通のID(TraceId)を割り振り、後続のマイクロサービスにそれを伝播させることでした。 LIFULL HOME'Sは複数のマイクロサービスで構築されており、それぞれのマイクロサービス毎にロググループができていたため、マイクロサービスを横断した関連ログの検索が非常に手間のかかる作業でした。 特定のリクエストに関連したログ全てにTraceIdを付与することで、複数のサービスに散らばったログを横断で絞り込むことができるようになります。 KEELでは、以前からログに共通IDを付与する運用を開発者に推奨してきましたが、LIFULLの主要サービス含めあまり徹底されていませんでした。結果、ログの検索に手間がかかることに繋がっており、これは従来のログ基盤自体の問題というよりは、ログの運用方法の問題といった方が正しいです。 今回のログ基盤刷新を機に、この辺りの運用もしっかりと整備することにしました。 OpenTelemetryを導入することで、比較的容易にTraceIdの伝播を行うことができます。 opentelemetry.io アクセスログだけでなくエラーログにも可能な限りTraceId等を埋め込み、特定のリクエストと紐付けられるようにしました。 対象となるマイクロサービスの数は少なくはなかったものの、各部署の開発者と協力し作業を進めることで、ほぼ主要なサービスへのTraceIdの導入が終わり、IDによる関連ログを横断検索する体験を提供できるようになりました。 ここでも前述したChrome Extentionを利用し、ログに記載されているTraceIdをクリッカブルにして、TraceIdのリンクを辿るだけで容易にログの絞り込みが行えるようにしました。 リクエストのトレーシング TraceIdを導入した副産物として、リクエストの分散トレーシングも可能になりました。 以下は、とあるリクエストをトレースした結果になります。ツールはGrafana Tempoを利用しています。 特定のリクエストに対して、裏ではどのサービスが何回呼び出されてそれぞれどの程度時間がかかっているかが視覚的にわかるようになりました。 リクエストのトレーシングができるようになったことで、ボトルネックとなっているサービスが特定しやすくなり、サイトのパフォーマンスチューニングが捗るようになります。 実際、サイトの高速化をはかるPJのメンバーがこの機能を利用することでパフォーマンス改善に役に立ててくれていました。 ログ基盤の切り替えの障壁を下げるために ある程度ログ基盤の並行運用期間を経て検証を重ね、十分に代替できると判断したタイミングで、CloudWatch Logsへのログの転送をストップし、ログ基盤を切り替えました。 CloudWatch Logsから新しいログ基盤に切り替わることで多少なりとも開発者に学習の負担がかかるので、そのコストを最小限にするためにKEELチームではチュートリアル動画を用意しました 1-2分程度の動画の中で、ログ基盤の検索のチュートリアルを行うことで、すぐに開発者がGrafana Lokiを使いこなせるイメージがわくようにしたのです。 この工夫もあってか、特に大きな混乱もなく開発者は新しいログ基盤を利用してもらえることができています。 加えて、ログ基盤を切り替えたことにより従来のログ基盤にかかっていた費用の約70%を削減することができました。 CloudWatch Logs関連費用のうち、その大半を占めるのがPutLogEventsであり、今回のログ基盤の移行でこのあたりの費用がすべてカットできたのが大きな要因です。 これは年間で換算するとかなり大きなコストカットになり、LIFULLのビジネスにも大きなインパクトを与えることになりました。 まとめ CloudWatch Logsによる従来のログ基盤をGrafana Lokiに移行した話を紹介させていただきました。 移行後は、Observabilityを構成する要素である、メトリクス、ログ、トレーシングの全ての情報がGrafanaで参照できるようになりました。 さらにGrafana LokiとChrome Extentionを組み合わせることで、CloudWatch Logsと同等、それ以上の体験を開発者に提供できるようになり、LIFULLのプロダクトの可観測性が向上しました。 フルマネージドサービスに比べるとGrafana Lokiを自前で運用する必要があるため、その分の運用コストは増加したものの、上記の体験に加え金額面での大きなコストカットメリットもあり、ログ基盤の移行は大きな価値があったと思っています。 今回お話しさせていただいたログ基盤だけでなく、KEELチームの様々な取り組みを別エントリーで紹介させていただいていますので、ご興味のある方は以下のエントリー一覧をご覧ください。 www.lifull.blog 最後に告知です。LIFULLでは、「あらゆるLIFEを、FULLに。」に実現を目指して共に働いていただける仲間を募集しています。 カジュアル面談という形で、まずは気軽に情報交換、ということも可能ですので、ご興味がある方は以下のページをご覧ください。 hrmos.co hrmos.co
フロントエンドエンジニアの嶌田です。アクセシビリティ推進グループに所属し、社内のプロダクトのアクセシビリティを高めるために日々奮闘しています。 LIFULL HOME'S は不動産・住宅情報の総合サービスです。住宅や住み替えに関する多くの情報を取り扱っており、サービス全体の規模はかなり大きいといえます。 レスポンシブデザインに対応したヘッダ・フッタの制作については以前に公開した記事で取り上げました。2022年5月から10月にかけて行われた今回のプロジェクトは、このヘッダ・フッタを LIFULL HOME'S サービス全体に展開することで、四散しているヘッダ・フッタを統合・刷新する ことを目的としたものです。 www.lifull.blog ヘッダ・フッタの統合・刷新により、サービス全体のアクセシビリティが向上し、キーボードやスクリーンリーダーのユーザーにとって利用しやすくなりました 。改善の内容をいくつか取り上げて解説します。また、追いかけているアクセシビリティスコアが向上し、コードレベルの内部品質も高まりました。 よくなったところ 1. レスポンシブデザインへの対応 2. キーボード操作・スクリーンリーダー対応 3. スキップリンク機能の搭載 4. ランドマークによるジャンプが可能に 効果測定 たいへんだったところ 根気の A/B テスト SEO リスクとの闘い 膨大な作業量 刷新を終えて よくなったところ 1. レスポンシブデザインへの対応 旧デザインと新デザインの比較。旧デザインは構成が何パターンもあり、デスクトップとモバイルでは体験も異なっていた。 現時点では、LIFULL HOME'S の多くのページはデスクトップとモバイル向けに別々の HTML を出力しています。ヘッダとフッタも別々のものが使われており、ナビゲーションの項目や使い勝手はデバイスやサービスごとに異なっていました。 新しいヘッダ・フッタはレスポンシブデザインを前提にデザイン・実装されたため、一貫性のあるユーザー体験が提供されるようになりました。 2. キーボード操作・スクリーンリーダー対応 これまでのヘッダ・フッタは一部の機能がキーボードやスクリーンリーダーだけでは利用できないことがありました。 旧デザインのヘッダをキーボード操作している様子。閉じられているはずのハンバーガーメニューの中身にフォーカスがあたり、フォーカスが見えなくなっている。 非表示のコンテンツにフォーカスが当たってしまう クリックできるボタンにフォーカスが当たらない メニューを開いたときにフォーカスがどこに行ったか分からなくなる ページ下部の「ページトップへ戻る」ボタンを選択したときにフォーカスがページ下部に残り続けてしまう これらの問題は新しいヘッダ・フッタで解消され、キーボードやスクリーンリーダーでも不自由なく操作できるようになりました。 3. スキップリンク機能の搭載 スキップリンク機能 新しいヘッダにスキップリンク機能を実装しました。スキップリンクは初期状態では隠されていて、ページを開いたあとキーボードの Tab キーを1回押すと表示されます。キーボードやスクリーンリーダーの利用者はページを開いた後、ナビゲーションを順番にたどってメインの領域にたどり着く必要があります。スキップリンクがあることでその手間を大きく省略でき、利用しやすさが向上します。 4. ランドマークによるジャンプが可能に スクリーンリーダー NVDA のランドマークを一覧する機能のスクリーンショット。ヘッダ・メイン・フッタの領域が検出されている。 ランドマークとは、ページの主要な領域を特定の役割として明示したものです。新しいヘッダ・フッタにランドマークが付与されています。これにより、スクリーンリーダーのジャンプ機能を使うと、ランドマークとして示された領域までショートカットしてアクセスできるようになりました。 効果測定 私が所属する組織はアクセシビリティ推進グループといい、LIFULL のプロダクトのアクセシビリティを高めることをミッションとしています。取り組みの効果を測るため、部署で独自に定めた採点基準を用いて、プロダクトのアクセシビリティスコアを定期的に計測しています。 スコアリングは Lighthouse による自動テストに加え、自動化できない項目を手動で検査した結果を用いて算出しています。スコアリングの詳細については中島による記事を参照してください。 www.lifull.blog ヘッダ・フッタを統合・刷新した結果、サービス全体のアクセシビリティスコアが底上げされ、平均スコアが向上しました。 サイト 導入前スコア 導入後スコア スコア増減 モバイルサイト 68.3 81.4 +13.1 デスクトップサイト 72.6 79.7 +7.1 あくまでも効果を定量化するための指標ですので、スコア向上の割合が使い勝手の向上の割合とは必ずしも一致しない点には注意が必要です。 たいへんだったところ 根気の A/B テスト 入居希望者からの問合せ数の上下は弊社のビジネスにおける重大な関心事項です。ヘッダ・フッタの改修は影響範囲が非常に大きいため、もし問合せ数が下がった場合の売り上げへのインパクトもまた大きいものになります。 リスクを避けるため、A/B テストを通じて問合せ数への影響を慎重に判断することになりました。A/B テストは必然的にコード上に条件分岐を増やすものであり、テストの項目数も膨れ上がるため、特に今回のような影響範囲の広い改修では極めて根気のいる作業になります。にもかかわらず、担当だった中島は苦しみながらも完遂してくれました。 A/B テストの結果は「特に良くも悪くもなっていない」でした。忘れずに A/B テストの後片付けをして正式リリースにこぎつけました。 SEO リスクとの闘い 問合せ数と同じくらい重要視されているのが検索エンジンでの表示順位です。LIFULL HOME'S には多くのユーザーが検索エンジンから訪れるため、平均でひとつ表示順位が下がるだけでも訪問者数には多大な影響があるのです。 ページ全体の文書構造に変化があったため、今回の改修にはリスクがあるとみなされていました。これまでは原則として、ページあたりひとつの h1 要素を持っていました。新しいヘッダではロゴが h1 要素でマークアップされたため、ページあたり 2 つの h1 要素を持つことになります。h1 要素は検索ロボットによるページの評価に影響を及ぼすと考えられているため、リスクだと考えられました。 ハンバーガーメニューの中身が構造化されており見出し要素を使いたかったために、ロゴを h1 にする必要があったのですが、潜在的な影響の大きさを考えると慎重になる必要がありました。SEO の専門知識のある方に意見を伺い、問題なさそうだというお墨付きをもらったうえで、リリース後は1ヵ月ほど検索結果を監視し順位の大幅な変動がないことを確認しました。 膨大な作業量 これまでのヘッダは長年の運用の結果、表示パターンが増殖し一元管理とは程遠い状態でした。表示パターンを洗い出し、新しくどのパターンに当てはめるかを判断し、必要に応じて該当の事業部との調整を根気強く積み重ねる必要がありました。ここは企画の鈴木が途中心折れそうになりながらも完遂してくださりました。感謝しかありません。 コード上の変更箇所もおびただしい量にのぼりました。特に、フッタに並ぶ多数のリンクは一部、サービスによってはリンク項目とリンク先が変動するという地味に HP を削ってくる要件がありました。目視によるレビュー、手動によるテストをやりつくすには限界があったため、アドホックな自動テストスクリプトを書き、変更前後の HTML を比較することで切り抜けました。 刷新を終えて 今回展開された新しいヘッダ・フッタはもともとサービス全体に展開する予定はありませんでした。部署としてプロダクトのアクセシビリティを向上していくモチベーションがあり、タイミングよくサービスのユーザー認証基盤が新しくなったことで推進力を得て、ヘッダ・フッタを展開することになりました。 全体に手を入れるならば今こそと、ついでにほかのアクセシビリティ改善やコード上の負債も改善できました。結果としてユーザーのみなさまには、これまでよりアクセシブルで一貫性のあるユーザー体験を提供できるようになったと考えています。 置き換え作業は一筋縄ではいきませんでした。調整コスト・実装コストがかさんだ要因の一つに、長年引き継いできた SEO 由来の要件から脱却できなかったことが挙げられそうです。これまでサービスに投入されてきた SEO 施策群は、数年経た後にその効果が振り返られることはなく、リスクを恐れて取り下げられることもありませんでした。SEO 要件は、積み重なってアンタッチャブル化してしまわないように、少しずつ効果を再検証しながら固着を引きはがしていく地道な作業が必要なのだと感じました。 今回のリプレイスで私(嶌田)はコードレビューとしてのみ参加し、調整や実装の大半は企画の鈴木、エンジニアの中島によって行われました。💗をこめて実装したヘッダ・フッタを、会社の目玉サービスに展開してくださったお二人には感謝してもしきれません。 お読みいただきありがとうございました。LIFULL では共に働く仲間を募集しています! hrmos.co hrmos.co