何をしている人? こんにちは! 2017年に入社して5年目になります、志茂です! 2年半営業職を経験した後、未経験でwebアプリケーションエンジニアにジョブチェンジしました。 今回は未経験で異動するまで道のりとLIFULLでのキャリアの選択制度について、お話したいと思います。 プログラミングを始めたきっかけ まずは入社前の私ですが、私立文系と全くプログラミングと縁のない大学生活を送っていました。 就活を始めてインターンで出会ったLIFULLの社員の人柄にひかれそのまま入社しました。 初めてプログラムを書いたきっかけは、日々のルーティン業務を効率化できないかと考え、 Google Apps ScriptとChatworkAPIを使って、ルーティンタスクを毎朝通知するというものでした。 自分で作ったものが実際に動いて、ちょっと便利になっていくという体験をしていくうちに、 なんとなく「プログラミングを勉強したい」から、「エンジニアとしてユーザーに少しでも便利になるものを作ってみたい」という気持ちに変わっていました。 そう考えるようになってから、本格的に勉強を始めました。 勉強の方法は人それぞれだと思いますが、自分の場合は周りの環境から変化させるために、G's ACADEMY(ジーズアカデミー)さんというプログラミングスクールにお世話になりました。 転職ありきではなく、基礎から学んで、自分でサービスを一から作れるようになりたい方にとてもおすすめです! gsacademy.jp 背中を押してくれた直属の上司 仕事で何かを作るなら、思い入れと実現したい目的を共有できるサービスに携わりたいと考えていました。 そのため転職は考えず、社内で異動できないかと考え、動き始めました。 LIFULLではジョブローテーションを行っておらず、半期に一回社員一人ひとりのキャリアビジョンを本人と上司が共有し、 それに基づいて目標設定や人員配置を行う 「キャリア選択制度」 があり、 人事異動や職種変更、新規プロジェクトへの参加等の希望を随時申請することができます。 ただこれまでLIFULLの中で営業からエンジニアへの職種変更の事例は前例がなかったので、 ダメ元で 当時の上司に異動希望の旨と理由を相談したところ、 「エンジニアのこと詳しくないけど、やりたいことがあるなら応援するよ!!なかったら事例を作ればいいから、いろんな部署の人に相談してみよう!」 と背中を押してもらい、職種変更のために必要な関係者たちとの面談調整というタスクまで設定してもらいました。 そこから、エンジニアになるためのインプットはしっかり継続しながら、並行して関係部署との面談で「なぜエンジニアになりたいのか」についての想いを伝え続けた結果、 半年後にWebアプリケーションエンジニアにジョブチェンジすることができました。 会社からは、スキル面で言えば新卒のエンジニア内定者の水準に少し劣るレベルだったのですが、私自身の意志と熱意を信じてもらい、 初めは辛いけど、最後は気合いでがんばれ!のスタンスで送り出していただきました。 うまくいかない配属当初 配属でスタートから高速でキャッチアップして、成果を出す!!と意気込んでいたのですが、そう簡単ではありませんでした。 会社には最大限の配慮をしてもらい、自身が営業の時に携わっていた領域商材をそのまま開発できるようにしてもらい、 タスクの難易度も段階を経て、成長できる環境を与えてもらっていました。 それでも基本的な技術の理解が足りておらず、他のメンバーが数時間で終わる作業が何日もかかってしまう状況で、 周りは経験豊富なメンバーばかり、スキルのない自分は何も成果や価値が生み出せていないと、とても申し訳ない気持ちと焦りでいっぱいになりました。 そんな時に直属の上司が、 「プログラミングのスキルで足りないところは別のポータブルスキルでカバーして、周りの人にどんどん聞いて解決しよう!一つ一つ落ち着いて考えれば、必ずできるから」 と持ち歩いているスケッチブックで、構造やデータの流れなどを随時図示してわかりやすく説明してくれました。 そこから、自分に今できる最大限のことをしようと考えるようになり、 目の前のことに集中して、一つ一つ取り組むことを意識することができました。 いましてること 現在は「 住まいインデックス 」というエンドユーザーに、住まいにまつわる情報を提供して、自身にピッタリの住まいの探しの条件を決めることができるサービスを開発しています。 インフラ構築からサーバーサイド、フロントエンドもすべて5人いるチーム全員で担っています。 比較的新しいプロダクトになりますので、新機能を作っていくために、社内でもスキルの高いメンバーが集められています。 業務の中で一緒に働く経験豊富なメンバーたちから吸収できることがたくさんあり、日々新たなことを学んでいる感覚があります。 またスクラム開発を導入しているため、チームで議論する場が多くあることも学びが多くなっているポイントになっていると思います。 先輩達の思考や課題を解決する上での視点や観点を知ることができるので、毎日が成長の機会だと思って仕事をしています。 最近ではAWSのソリューションアーキテクトの資格を取得したこともあり、インフラの改修や運用を任せていただくことが多く、 自分の身につけたスキルがすぐに業務に活かせることがとても楽しいです。 これからやっていきたいこと 社内で初めて営業からエンジニアにジョブチェンジした前例を作ることが出来たので、 そのロールモデルになれるようビジネスサイドの経験や個性も活かしながらエンジニアとして成長をしていきたいと考えています。 まだ抽象的ではあるのですが、中長期的には技術の力とビジネスの力を組み合わせてサービスの成長を牽引し、 ユーザーやその先にある社会の課題を解決できるエンジニアになっていきたいなと思っています。 最後に LIFULLでは一緒に働く仲間を募集しています。 ゼロからやりたいこと、本人の行動と熱意を信じてをやらせてくれる環境があります。 私の記事を読んで、少しでもLIFULLに興味を持っていただけたら幸いです。
こんにちは。検索エンジンチームの加藤 宏脩です。 先日、検索エンジンチームでLIFULLが利用しているSolrのバージョンを7.xから8.xにバージョンアップしました。 今回のSolrバージョンアップから自社で制作した性能テスト、回帰テストツールを導入したおかげか 大きい障害はなく無事にリリースできました。 リリース後は検索精度、パフォーマンスも向上しておりほっとしているところです。 8.xへの移行時はいくつか問題がありましたが、中でもbqパラメータのNegative Boostの廃止対応がたいへんでした。 Negative Boostの廃止対応が必要なことはバージョンアップ時のテスト中に気付き、簡単な対応だと思っていたのですが 並び順を維持させようとすると速度劣化が激しくなったりと対応がかなり難航するということがありました。 この記事では、Solr 8.xへのバージョンアップ作業の障害となったNegative Boostの廃止をどのように 対応したのか話をさせていただければと思います。 bqとは Solrのパラメータの一つで、条件にマッチしたデータを重みづけして優先順位を上げたり下げたりする機能です。 bqについてのドキュメント https://solr.apache.org/guide/8_8/the-dismax-query-parser.html#bq-boost-query-parameter Negative Boostとは ここでいうNegative Boostとは条件にマッチしたらマイナス方向の重み付けをすることを指しています。 例1 # hogeの値が5のデータを-100の重み付けをする bq=hoge:5^-100 Negative Boost廃止についてのチケット https://issues.apache.org/jira/browse/LUCENE-7996 どう変更したか LIFULLでの利用例 擬似的なスキーマ name type multiValued indexed description multiValuesParam1 pint true true multiValues型のパラメータ1つ目 multiValuesParam2 pint true true multiValues型のパラメータ2つ目 boolParams1 pint false true bool型のパラメータ1つめ。0がtrue、1がfalse boolParams2 pint false true bool型のパラメータ1つめ。0がtrue、1がfalse 例2のように multiValuesParam[1 or 2] に一致して、 boolParams[1 or 2] がtrueだったときにマイナスの重み付けをしていました。 例2 n=複数個の数字 bq=((multiValuesParam1:(n) AND boolParams1: 1) OR (multiValuesParam2:(n) AND boolParams2: 1))^=-100 (multiValuesParam1:(n) AND boolParams1: 1) の条件を1とし、 (multiValuesParam2:(n) AND boolParams2: 1) の条件を2とした場合 優先順位は、以下のようになります。 (1と2の条件どちらも一致しない、 1と2のどちらかの条件に一致しない) > 1と2のどちらの条件も一致する 変更案 結論から言うと、現行の重み付けに一致させる案は本番環境に耐えうる速度にはならなかったので3番目の案を採用して少し優先順位を変更するようにしました。 1. 条件の反転 例2の条件に当てはまらないデータを重み付けをするようにしました。 クエリは例3のように n 以外のデータに絞り込みつつ、 それだけではパラメータが空だったときの条件が抜けてしまうため例4のような条件を加えるようにしました。 例3 multiValuesParam1:[* TO (n - 1)] OR multiValuesParam1:[(n + 1) TO *] 例4 *:* AND -multiValuesParam1:[* TO *] 結果は、優先順位は一致するが速度が非常に遅くなってしまいました。 原因はおそらく検索する範囲が広くなりすぎてしまったことによるところが大きいと思います。 またNOT条件はキャッシュができないので、二回目以降のアクセスも速度が遅くなってしまうという問題がありました。 2. bfで代用 次に例2の条件をbfで代用するように試してみました。 bfパラメータはスコアが0以下にならなければマイナスの重み付けが可能です。 全件に100の重み付けをしたあとに、例2の条件をbfで実装するようにしました。 bfには where in 的な便利な構文はないので例5のような実装をつなげるようにしました。 例5 or(eq(multiValuesParam1, n), eq(multiValuesParam1, m)...etc) 結果は、優先順位は一致するが multiValuesParam[1 or 2] に指定する値が多くなると速度が遅くなってしまいました。 また、Solrには指定できる句の数に上限があるため、指定する値が多すぎるとエラーが変えるようになってしまう問題がありました。 3. NOTで計算する量を絞って計算にかかるコストを減らす(できるだけ理想に近い順番を編みだす) 反転のところでも記述したようにbqだけで解決しようとするとNOTの使用は避けられないので、 NOTで計算する量を限界まで減らすようにしました。 方針としては、例2の条件に当てはまらないデータをプラスの重みづけしていくようにしました。 実装は簡単に説明すると、例6のように前段で弾けるものは先にtrueを返すようにして NOTの条件に到達するときは対になる multiValuesParam[1 or 2] が一致かつ boolParams[1 or 2] が1のデータだけが残っているようにします。 例6 ( ( (multiValuesParam1:(n) AND boolParams1: 0) OR (multiValuesParam2:(n) AND boolParams2: 0) ...etc ) OR ([ここでできるだけ計算量を減らす] AND -(multiValuesParam1:n OR multiValuesParam2:n)) )^=100 結果、優先順の前後は同じで中間部が多少入れ替わるというようになりましたが、速度は元の実装と変わらないくらいになりました。 このやり方だと、NOTの前の条件を詰め込める環境であればさらに速度を早くできそうです。 まとめ 今回は、Solr versionの8.xへのバージョンアップに伴うnegative boostの廃止の対応をしました。 私自身Solrに投げらているクエリを大きく変えるというのはチームにジョインしてから初めてで良い経験になりました。 複雑な条件でNegative Boostを使用している方の助けになれば幸いです。(少数だとは思いますが。。) 検索エンジンチームでは今回のような、バージョンアップ作業をブロックしてしまうような問題をいち早く発見して解決できるようにするため、 Solrの新バージョンのリリースを検知し回帰テスト、性能テストを自動実行するしくみの導入を進めています。 また運用の自動化などで作った時間で、感動を届ける検索を実現することに注力しています。 カジュアル面談もやっていますので、一緒に「 感動を届ける検索エンジンを実現する 」、ひいてはLIFULLが目指している「 あらゆるLIFEを、FULLに。 」することに興味がある方はぜひお話しましょう! ここまで読んでいただきありがとうございました。 hrmos.co
は ろーはろー!チバです。 LIFULL HOME'Sのユーザー向けメール配信・LINE配信などのCRMシステム を担う部署に務めています。 今年2月にリリースした「 LINEで新着物件通知を受け取る」機能の担当者です。 www.homes.co.jp PdM/PjMのスキル発揮に触れながら、 「LINE新着物件通知」とは? プロジェクトの立ち上げ プロジェクトの実行 プロジェクトの終結 PdM/PjMのスキル発揮とキャリア形成 についてスライドにまとめたので、どうぞ、ご笑覧ください💁 ㊗ LINE新着物件通知 リリース!! PJ進行に沿って話す、 PjM/PdMとして やったこと - slideshare ㊗ LINE新着物件通知 リリース!! PJ進行に沿って話す、 PjM/PdMとして やったこと from LIFULL Co., Ltd. www.slideshare.net エンジニアが書いた同じ施策の記事 www.lifull.blog 最後に 今後もユーザーの住み替えに役立つ情報を、LINEやメールに限らずにオムニチャネルで提供して参ります。 記事・スライドの感想をぜひぜひ、Twitterでツイートしてください 。 エゴサしている私が小躍りしてRTします🙌 また、現在 一緒にCRMシステムを爆進させる仲間を熱烈募集中 です! 同じ部門で働けるポジションの求人の最新情報は採用ページご確認ください💁 hrmos.co \株式会社LIFULLでぼくと握手!/
はじめに みなさんこんにちは。 品質改善推進ユニットQAグループでQAエンジニアをしている飯泉です。 今回はチームで行なっている 「本番障害からテストのヒントを抽出して活用する」 ための活動について紹介したいと思います。 本番障害からソフトウェアテストのヒントに活かす「シンプルチャーターエレメント」 本番障害レポートからソフトウェアテストのヒントを抽出した観点集を「シンプルチャーターエレメント」と呼んでいます。 本番障害レポートを分析してどのような観点でテストをすれば障害を防ぐことが出来るのかまとめて開発やテストで活用をしています。 本番障害レポートとは LIFULLでは本番環境でバグが発見された場合に本番障害レポートを作成してバグの解消状況の管理と再発防止策の検討を行っています。 本番障害レポートは以下のようなにまとめております。 障害の影響範囲 障害の概要 障害が発生したサービス・利用者 原因 対策内容 再発防止策 シンプルチャーターエレメントはどんな内容か 本番障害の概要、原因、発生手順、このシンプルチャーターエレメントから発見を期待できる欠陥、テスト観点などを記載します。 参考にしたものは 「Explore It!」 「Explore It!: Reduce Risk and Increase Confidence with Exploratory Testing 」 は探索的テストの技術書です。 この中にある「A Simple Charter Template(シンプルチャーターテンプレート)」という探索的テストに使えるシンプルなヒントの作り方から発想を得ています。 シンプルチャーターエレメントの内容は、バグ発見へのインスピレーションが得られるようにするため以下を考慮して作成し、レビューを通してから全社のものづくりチームへ共有する形で運用しています。 抽象化の粒度 ソフトウェア起因でかつ汎用性のある情報にするため、プロダクト依存の情報は排除する イメージとしては、フリーズドライの食品のように、お湯(プロダクト情報)をかけるとテストのヒントが複数出てくるにまとめる 本番障害などの情報を元に 探索的テスト時のテスト観点となるようにまとめる 「(どの) 対象となる画面・機能 で、(どんな) 前提条件・操作 の時、(どんな) 期待値 か」 その他ポイント シンプルチャーターエレメントの内容がバグを再現できる粒度になっていること 再現する手順が汎用的かつ再現性のある手順であること 活用方法 開発で活用する 設計時の考慮漏れのチェックやソースレビューの観点に使えます。 過去に発生した障害を参考に作られているので同じバグを作り込んでいないかチェックをする時に活用できます。 テスト分析やテスト設計で活用する 「バグを発見するため」のテスト観点の洗い出しや、テスト設計時にフォールト指向で考える際に参考になります。 バグ発見のインスピレーションを得られるような内容にまとめているので、どのようにするとバグが発生するか広く考えるためのヒントとして使えます。 探索的テストで活用する 元々探索的テストのために作られていたので、シンプルチャーターと探索的テストとの相性が非常に良いです。 探索的テストは複雑な条件・タイミングの操作を実行することに向いているので、 本番障害が発生した手順を再現したり、そこから派生した操作するといったことが行いやすく、バグ発見に繋がりやすいです。 最後に 失敗を恐れず失敗に寄り添う文化がLIFULLには根付いているため、 今回紹介したように失敗を糧に本番障害を減らすための活動ができるのだと実感しています。 自分がエンジニア時代にこの活動に出会えていたらもっと救われたのかもしれないと思っています。 (私はミスが多いエンジニアで失敗しないように必死でテストしていたら、いつの間にかQAエンジニアになっていました。) 失敗は恐ろしいことですが、失敗に向き合い寄り添えば未来への救いとなる知識に変わると私は信じています。 この記事で失敗を糧に少しでも次の成功へ繋がるヒントとなれば幸いです。
出典: オムニチャネルサービスの実施 単元 | Salesforce Trailhead いつもお世話になっております。 プロダクトエンジニアリンググループの孫です。 LIFULL HOME'SにおけるSalesforceとLINEの連携について紹介したいと思います。 背景 LINEのLIFULL公式アカウントを使い、簡単な対応はBotで対応し、それ以外はLINEのWebhook API機能を利用し、直接 LIFULL HOME'S 住まいの窓口 とやり取りができるチャット機能を、Salesforceで管理・運用する施策がありました。 ※ 現状このサービスはクローズされています。クローズされた背景については後日、別の記事として記したいと思います。 Salesforce Service Cloud 設定 外部からのチャット情報を受け取り、Salesforce内で対応させるためにはSalesforceのオムニチャネルの有効化が必要となります。 オムニチャネル LiveAgentにて送信されたチャット要求をSalesforce内で待機中のエージェントに連携させるルーティング機能です。エージェントの作業量やステータス、スキルなどを参照し、自動的に作業を振り当てます。スーパーアドバイザーを設定し、全体のチャット対応状況を監視・管理することも可能です。 設定 オムニチャネルの有効化 受信した作業をエージェントに転送するまで保持するキューの作成 ルーティング設定とプレゼンス設定の作成。この 2 つは連動してエージェントのワークロードを制御し、キュー内の作業に優先度を設定します。 作業要求を引き受け可能なユーザの選択 新しいキューを通じて受信されるケースのエージェント業務量と作業項目サイズの設定 細かい設定内容は以下のSalesforceドキュメントにまとまっているのでここでは省略します。 help.salesforce.com Webhook側の開発 全体図 LINE×Salesforceシーケンス図 上の図は構成を簡単に表現したものとなります。仕様によってケースや取引先のオブジェクトを作成・更新する処理を追加することも可能です。 LINEから連携されるUserIDとFollowEvent、TextEventなどを基にWebhookからSalesforceのLive Agent REST APIを叩き、ユーザ情報と相談内容などをSalesforceを更新する仕組みです。 このSalesforceのLive Agent REST APIを利用するとLINE以外でもFacebookやWebチャットなどを利用しSalesforceにデータをためることができます。 LINE → Salesforce developer.salesforce.com Live Agent セッションを作成する 新しい Live Agent セッションを作成するには、SessionId 要求をコールする必要があります。 チャット訪問者のセッションを作成する Live Agent REST API を使用して、チャット訪問者のセッションを作成または再確立するには、特定の要求を実行する必要があります。 チャット活動を監視する Live Agent 要求では、チャットセッション中に特定の活動がいつ発生したのかを指定します。 LINEから送信されたメッセージをSalesforceに連携する処理となります。 Salesforce → LINE developers.line.biz エージェントからのメッセージをLINEに送る チャット監視中のChatMessageからレスポンスから受け取ったエージェントのメッセージを送信する。 結果 LINEからのチャットをSalesforceのオムニチャネルから受け取り、Salesforceのオムニチャネルから送信されたメッセージがLINEから確認できます。 最後に 記事にまとめているのは基本的な正常パターンの流れとなります。実際の実装では多様なメッセージパターン(画像やスタンプなど)の扱いやエージェントのステータスによる処理、細かい仕様への対応への注意が必要ですが、Live AgentはLINE以外でもWebや他のスマホアプリなど色んな所から顧客問合せをSalesforceでまとめて対応できるメリットがある良い機能だと思いますので誰かの参考になれればと思います。
こんにちは!テクノロジー本部基盤開発ユニット改善推進グループ所属の王です。 基盤開発ユニットは常にLIFULLの各種サービスが依存する基盤システムの構築と改善のために、いろいろな取り組みをしています。 www.lifull.blog www.lifull.blog www.lifull.blog 今回は技術負債の解消の一つである、DB移行プロジェクトの詳細について紹介します。 DB移行プロジェクトとは? 現在LIFULL HOME'Sの各種サービスが依存している中心的なデータベースをOracle DatabaseからPostgreSQLに置き換えることを推進しているプロジェクトです。 背景の詳細は省略しますが、現状のDB運用体制を続けると会社の事業発展のボトルネックとなることが予想されているので、運用コストの削減、開発効率およびパフォーマンス改善の面から移行の必要が出てきています。 プロジェクトの概要 次はこのプロジェクトの進め方について、我々が取り組んでいることの紹介をさせていただきます。 DB移行手順 今回のプロジェクトの目的、アプリケーションが使っているDBをOracle DatabaseからPostgreSQLに変更するには、以下移行手順の元で進めています。 スキーマオブジェクトとアプリケーションの依存関係の洗い出し Oracle Databaseに依存するアプリケーションからSQL文を抽出して分析する、アプリケーションとデータベースのスキーマオブジェクトの対応関係を以下のようにまとめます。 Application SQL Schema Object SQL Type app1 sql1 table1 select app1 sql1 table2 select app1 sql2 table1 insert 以上の調査により、各アプリケーションがOracle Databaseの使用状況を明確にして、アプリケーションDB移行に必要な修正および移行の優先順位を決めます。 ソースデータベースとターゲットデータベースのデータ同期を確保する アプリケーションへの影響を最小限にするために、ソースデータベース(移行元)とターゲットデータベース(移行先)のデータの一致性を確保することが必要です。テーブルの移行を例として考えると、以下の条件を満足する必要があります。 テーブル構造の一致性 テーブルの定義がソース・ターゲットデータベースで正しく対応する必要があります。たとえば、カラム名、カラムのデータ型、インデックス、制約などのものはできるだけソースデータベースの仕様を再現することによりアプリケーションへの影響を最小化します。 テーブルデータの一致性 対応するテーブルが常に同じデータを持っていることを確保すること。 テーブルに対するトランザクションの一致性 ソースデータベースでテーブルへのデータ更新が、できるだけ遅延少なくターゲットデータベースの対応するテーブルに反映されること。 以上のデータベースのデータ同期を確保した上で、移行作業を始めます。 参照系SQLを先に移行する まずは各アプリケーションからOracle Databaseを参照するSQL文(SELECT文)の参照先をPostgreSQLに変更します。 後述するしくみによってソース・ターゲットデータベースのデータは同期されるため、許容できる範囲のタイムラグはありますがデータの不整合は発生しません。 参照系の後に更新系SQLを移行する 参照系のSQLをすべて移行した後に、更新系SQL文(UPDATE、INSERT、DELETE…)の参照先のDBを少しずつターゲットデータベースに変更します。 全体図 移行手順の全体図は以下の通りです 移行前 参照系移行 更新移行 DB移行に採用する技術 移行の全体図を一見するとそこまで複雑ではないと思う方がいると思います。ですが、Oralce Databaseを参照するアプリケーション数および参照するデータベーススキーマオブジェクト数が多いため、各アプリケーションとスキーマオブジェクトの依存関係が複雑になります。ゆえに、システム全体のDB移行の難易度が上がっています。 移行に必要な工数を削減するために、AWSから提供されている以下のツールを利用しています。 SCT (Schema Conversion Tool) AWS Schema Conversion Tool は、ソースデータベーススキーマ、およびビュー、ストアドプロシージャ、関数といったデータベスコードオブジェクトの大部分を自動的にターゲットデータベース互換フォーマットへと変換することにより、異種データベース間の移行を計画的なものにします。 上記AWS SCT の 公式ドキュメント の紹介の通り、異種データベース関のスキーマオブジェクト変換に使用されています。現在はこのサポートツールを利用して、Oracle Databaseのスキーマオブジェクトを正しい型でPostgreSQLに移行しています。LIFULLではデータベース内に複雑なDDLを持っているスキーマオブジェクトが比較的多いので、この自動変換ツールはプロジェクトを進める上で欠かせません。 下記はSCTの使用画面です。 GUIでソースデータベースのスキーマオブジェクトを指定すると、ターゲットデータベースに通用するDDLが自動出力されます。 AWS DMS (AWS Database Migration Service) AWS Data Migration Service(AWS DMS)はAWSが提供するDB間のデータ移行をサポートするサービスです。サービスの処理プロセスは以下の図のように示されています。 DMSプロセス 図が示すように、DMSはデータレプリケーション機能を持つAWSクラウドサービスです。ソース・ターゲットデータベースを指定すると、2つのデータベース間のデータ同期が実現できます。 詳しい説明はAWS DMSの 公式ドキュメント を参照してください 我々のDB移行プロジェクトでは、ソースデータベースとデータベースのデータ同期を実現するために、DMSを使用しています。ソース・ターゲットデータベースのデータ同期はアプリケーションの参照DBを切り替えるために必要なことですので、DMSは我々のDB移行プロジェクト内で重要な役割を果たしています。 以上2つの外部サービスを利用してDB移行の推進をしています。AWSが提供する ハンズオン を見ていただければより詳細を理解できるので、興味がある方は参照してください。 プロジェクトの体制 プロジェクトの体制についても少し紹介させていただきます。 DB移行プロジェクトは私が所属する基盤開発ユニット改善推進グループと基盤運用ユニット基盤グループが共同作業しています。 改善推進グループは主に必要なアプリケーションの改修に注力していてます。そして基盤グループはDB移行に使用される本番環境インフラ(DBおよびAWS DMSの関連リソース)の運用、検証および改善に注力しています。 そのほか、アプリケーションの改修には各アプリケーションの主管部署の協力も不可欠ですので、全社に渡り影響範囲が大きいプロジェクトとも言えます。 プロジェクトが完了するまでの課題 DB移行プロジェクトはLIFULLの技術負債を解消する重要な施策の一つであり、目的を達成するために我々は日々取り組んでいます。しかし、プロジェクトの完了までにまだ解決しないといけない課題がいくつもあります。 安定的なデータ同期のためにDMSのチューニングが必要 プロジェクトを進める上で、処理するデータ量が増えると同時にデータ同期の遅延が大きくなることまたはデータ同期の一時的な停止などの予想外の問題が発生しました。安定的なデータ同期を確保するため、AWSからのサポートしていただきながらインスタンスのサイジングやパラメータの調整、設定変更などDMS側のチューニング施策に取り組んでいます。 データ同期によるデータベースへの負荷が上昇する DMSへの負荷だけではなく、データ同期によるデータベースへの負荷が上昇することも無視できません。ソース・ターゲットデータベースを正常稼働させながらプロジェクトを進めるための施策も今いくつか進めています。 SQLの調査が不十分 移行が必要なSQL文への調査がまだまだ不十分で、移行の抜け漏れがアプリケーションの障害を起こす可能性があります。ですので、抜け漏れ検知のためのデータベースへのアクセス監視の強化などの施策を今取り組んでいます。 上記の課題はLIFULLのDB移行プロジェクトにとって大きなリスクであり、解決するためにプロジェクトメンバーが日々取り組んでいます。 以上DB移行プロジェクトの紹介となります、ここまで読んでいただきありがとうございます。参考になれば幸いです。 まだまだプロジェクトの完了は先が長いですが、LIFULLの技術負債をなくすには必要不可欠の一歩ですから、これから頑張って取り組んでいきたいと思います。今後プロジェクトの進捗が何かありましたら、また記事を出させていただきます。
いつもお世話になっております。検索エンジンチームの秀野です。 試験的な取り組みとして、社内通貨LIFULL COINをSlack上で送り合うピアボーナスの仕組みを作ったので、その紹介をします。 検索エンジンの話は1つもでてきません。 LIFULL COINとは LIFULL COINはトップダウンな評価でなく、お互いをフラットに評価できるプラットフォームとして作られました。 参考にした評価システムとして、日本古来のお天道様や、欧米だとアダム・スミスの公平な観察者、あとアニメのPSYCHO-PASSに出てくるシビュラ・システムなどがあります。 こういった仕組みを通して、会社の社是である「利他主義」ですとか、ガイドラインの可視化・推進を目標としていました。 また、交換する/価値を測る/貯めるといった通貨と同じ特徴を持っています。 そのため、LIFULL COIN Wallet というWebアプリケーションを使って、銀行口座のようにコインを管理できます。 基本機能として、下記のような機能があります。 残高の確認 送金と取引履歴 ベーシックインカム(週1) 投げ銭 LIFULL COIN Wallet Slackを使ったピアボーナス 始めの課題感 2020年、コロナ禍によってエンジニアにとっては待望の在宅勤務が始まったわけですけれども、コミュニケーション上の課題というものがご多分に漏れず弊社でもでてきました。 特に新規事業部のような小さいチームが複数集まっている部署で課題感があるようでした。 周りが何をやっているか分からないですとか、人事部のアンケートでも気持ちが不安定になっている人がちらほらいるようで、社会適合者の人たちは大変だなーと思っていました。 そんなこともあり、ピアボーナスとしてLIFULL COINを送り合い、状況の共有やいいね👍ができる仕組みを用意することになりました。 ピアボーナスの仕組み Slack のリアクションに連動してコインが付与される、というよくある仕組みです。 ピアボーナスの仕組み リアクションされた人、ここではペンギンにコインが付与され、リアクションした人、というかロボットにも少額が付与されます。 リアクションした人からリアクションされた人にコインが送られるのではなく、お天道様(LIFULL COIN Bot)から2人にコインが降ってくるイメージです。 「お天道様は見ている」のです。 将来的には、何かしら社員のアクティビティに反応して自動的にコインを付与したりしたかったです。 例えば、リファクタリングやPRに応じて開発者に付与されたり、本番環境のAPIのリクエスト数に応じて担当部署に付与されたり。 ブロックチェーンの送金処理 LIFULL COINは、内部でブロックチェーンを利用しています。 ブロックチェーンでは、通貨を扱う仕様が 標準化 されており、秘密鍵と公開鍵を使った送金フローの検証機能が元々備わっています。 (LIFULL COINでは Ethereum を元に開発された Quorum を使っています) ここで簡単にその送金の仕組みを説明します。 トランザクション これは、お天道様からペンギンに対してコインが送られている図です。 送金のトランザクション ブロックチェーンにはアカウントアドレスというものがあり、これが銀行の口座番号のような役割りを果たしています。 このアカウントアドレスは、公開鍵のハッシュから生成されます。 公開鍵は秘密鍵から生成されるものなので、アドレスは秘密鍵からユニークに生成される値ということになります(ECDSAを前提にしています)。 実際に送金する際は、どのアドレスから、どのアドレスに、いくら送る、というトランザクションと呼ばれるデータを作成してブロックチェーンに送信します。 お天道様の秘密鍵でトランザクションに署名することで、初めて送金が可能になります。 ブロックチェーンはトランザクションの署名を検証し、問題なければブロックに追加します。 これでペンギンに対しての送金が完了したことになります。 スマートコントラクト ブロックチェーンには、スマートコントラクトと呼ばれるプログラムをデプロイできる仕組みがあります。 スマートコントラクトは自身のアドレスを持っていて、トランザクションをトリガーになんらかの処理を行えるイミュータブルなプログラムです。 LIFULL COIN Botのコアになるプログラムは、このスマートコントラクトとして実装されていて、 コインの発行 口座の管理 コイン付与の実行 などの機能を担っています。 スマートコントラクト この2つのプログラムは実際に使っているコードで、LifullCoinコントラクトとOtentoコントラクトです。 更新系の関数は秘密鍵で署名しないと呼び出せないようにしたり出来るので、セキュアな実装が可能です。 ただ、不変ゆえ基本的にはバグがあっても直せないので恐ろしすぎます。 出来上がり ピアボーナスの処理の流れ ブロックチェーンの仕組みを踏まえて、こちらがピアボーナスの全体の構成と流れになります。 全体といいつつ実際は色々作ったのですが、それはまた別のお話… コインの流れを可視化するAthenaでの集計の仕組み 社員ADとCognitoのOpen ID Connect連携 社内のあらゆるID同士を相互変換するID Masquerade ブロックチェーン上のデータのバックアップ/リストア 余談ですが、draw.ioで図を書くとみんな同じ感じになるので手書き風にしてみました。 ピアボーナスの流れ 右側のブロックチェーンの青い四角がさきほど説明したスマートコントラクトです。 事前準備 事前にLifullCoinコントラクトからOtentoコントラクトに対して、お天道様の口座のコインを自由に送金する許可を与えておく必要があります(手順0)。 許可を与える処理には、お天道様の口座を扱う秘密鍵で署名する必要があります。 口座には1億コインあり、Otentoコントラクトには1億コイン全てを送金できる許可を与えてあります。 リアクション〜コイン付与と通知 リアクションがあった時は、左上のSlackアプリからフローが開始されます Slackサーバーからリアクション追加イベントがLIFULL COIN Bot(Slack bot)に送られてきます LIFULL COIN BotはID変換テーブルを参照して、SlackのチームIDとユーザーIDをブロックチェーンのアドレスに変換します コイン付与額と付与先のアドレスを含めたトランザクションを作成し、お天道様用の秘密鍵で署名します ブロックチェーン上のOtentoコントラクトに署名済みトランザクションを送信します Otentoコントラクトは公開鍵でトランザクションの検証を行い、対象者のアドレスにコインを送金します OtentoコントラクトからLIFULL COIN Botに送金結果を返します( PoW でなくRaftのためほぼリアルタイムで送金が完了します) LIFULL COIN Botはリアクションした人/された人に着金通知を送信します リアクションした人/された人のSlackアプリのメッセージタブに着金通知が表示されます UIなど アプリのホームタブ&メッセージタブ ↓Slackアプリのホームタブでは簡易的に残高が確認できます。 前述の着金通知メッセージです。付与額はある程度ランダムです。↑ 未登録者勧誘メッセージ&プロフィール登録モーダル ↓アドレス未登録でリアクションされた場合、登録を促すメッセージが届きます。 登録用のモーダルです。Slack上で登録できるようになっています。↑ 衝撃的な幕切れ 開発前、ピアボーナスを試す予定の部署ではSlackを利用していませんでした。 そこで開発中にSlackを導入し、普段使いしている状態でピアボーナスを導入するという流れでした。 開発が終わり導入できるようになった頃、Slackのワークスペースを作ったという話を聞かされました。 そう、誰も…Slackを導入していなかったのです。 これを「 傍観者効果 」といいます。 役割分担をしたからといって、必ずしも実行されるわけではないということを学びました。 どんな些細なことでも、必ず進捗確認をしていきましょう。
こんにちは、 アプリケーションエンジニアとして働いてます。キムと申します。 今日はこの最近経験したことの中で、アプリケーションを開発する途中や、リリース後にコードベースを管理する時重要なことの一つ「品質管理」について経験したことを共有したくて記事を準備しました。 背景 去年は新しく配属されたPJでアプリケーションの基盤から作るチャンスを頂きました。 当時、個人的にLintを投入したい思いがあったので、Go言語で最もよく使われていたgolangciというツールの基本的な部分を使えるように設定を行いました。 個人的に Lintを入れたかった理由は、自分の経験の中でコーディングルールが甘い状態が長く続けると、古いコードを読んだり、複数人のメンバーが同じ作業を行う、又はコードレビューをする等、他人のコードを読む時、人によってバラバラの書き方が混在し、場合によっては基本的なタイポイシューがあったりする指摘でレビューが長くなったりする不便がありました。 そこで、Lintを投入してたチームで経験した、Lintが存在する時のメリットを今回参加してるチームメンバーたちにも経験してもらいたかった思いでした。 すごく極端てきなイメージですが、十分にありえるシチュエーションです。 PJはどんどん進んで、最初より多い人数が入っていただきました。みんなどんどん新しい機能を開発し続けましたが、Lintがあったので、コードベースは基本的なルールを守り状態で管理できたと思います。 この時、部内では新しくCode Climateていうツールを利用し、コードのクオリティチェックを行い、より高いクオリティを目指しましょうていう話が出始め、私達のチームもこのツールを投入することを決めました。 これを適用しながら感じたことや、Golangiciと連携してもっと有効活用できる方法やカスタムルール作成方法などを共有したいと思います。 コード品質について アプリケーションは機能が増えるたびコードベースは大きくなります。 レポジトリに対して、コード品質管理はなぜ必要だと思いますか? サービスがリリースされた後、時間の経過とともに機能はどんどん増えるし、コードベースはどんどん複雑になっていきます。この時コードベースの管理状態をひと目に判断する方法として、コード品質管理を行っていると思います。 アプリケーションは他の商品とは異なって実態が目に見えない商品であり、ということで人の目には見えないところで問題が起きる可能性が存在します。 目に見えるエラーは原因把握も簡単にできるし対応も簡単ですが、目に見えないところから発生したエラーは原因を把握することも大変複雑で、対応するにはより高いコストがかかる可能性も高いです。 この問題は、チーム単位で作業するときにより頻繁に発生します。 同じ機能を作るとしても人によってコードの書き方や、考え方はそれぞれです。そのためコードレビューを行ったり、コードを作成する前にダイアグラムやシークエンス図などを準備して設計レビューをしたりするプロセスが発生してきました。 Active DiagramやSequence Diagramなどでお互い認識合わせや、効率的な処理を探してきました。 このプロセスの中、どうすれば少しでもレビューを簡単にできるのか、こういった機能を十分に活用して投入することで、コードはより統一感を持つことになり、時間が過ぎてもある程度ルールで守られ、読む人に安定感を渡します。 Lintチェックでは簡単な英語スペルチェックから、メソッド名の作成方法、変数の書き方をCamelCaseかsnake_caseにするかなどのチェック、1ファイルあたりの行数制限など様々なチェックを行っています。レポジトリに対して、コード品質管理はなぜ必要だと思いますか? サービスがリリースされた後、時間の経過とともに機能はどんどん増えるし、コードベースはどんどん複雑になっていきます。この時コードベースの管理状態をひと目に判断する方法として、コード品質管理を行っていると思います。 Lintチェックでは簡単な英語スペルチェックから、メソッド名の作成方法、変数の書き方をCamelCaseかsnake_caseにするかなどのチェック、1ファイルあたりの行数制限など様々なチェックを行っています。 簡単に言うと人によっていろんな差があります。 ## camel case CamelCase := "" ## snake case snake_case := "" if () { // 処理 } if () { // 処理 } 「こんなことまで気にするの?」と言われるかもしれませんが、こういった細かいところから問題は大きくなっていきます。 今回紹介しようとするレポジトリ管理サービスも同じです。関連サービスには独自、又は有名アルゴリズムによってコードのメンテンナンス状態や、テストカバレッジ状況を見やすく可視化してデータを提供することで該当サービスがどんな状態で管理されてるのかを一目に把握できるように提供してくれます。 上記でも話したように、アプリケーションは実態が目に見えない商品なので、このように可視化してくれるのはすごく役に立ちます。利用者には、該当アプリの信頼性を、関係者には今アプリにはどんな暫定的な問題があり、どんな改善が必要なのかの判断の軸になることもあります。 このように、ウェブ上でレポジトリの状態をひと目に見れます。 こういった機能を十分活用し、導入することで、コードはより統一感を持つことができ、時間が過ぎてもある程度ルールに守られ、後々読む人にも安定感を渡します。 もちろん、色々設定が必要とか、今すぐ始まるには面倒なことが多い等、今までのやり方と違うことで大変かもしれませんが、将来を考えたら今時間を使って投入することが確実にメリットがあると思っています。 準備 Code Climateの利用準備 Repository と、Code Climateへの連動(ここではGithub を利用します。) レポートをアップロードするための手段準備(ここではGithuib Actions を利用します。) golangci 公式ドキュメント Golangを利用して開発してるなら、最も注目するべきツールです。 Go言語が提供してる基本Lintはもちろん、世の中に名前が知られてる各種3rdパーティーモジュールも追加で設定を行うことで利用できる環境を提供してくれます。 Local環境はもちろん、Github ActionsなどCI/CDを利用する場合、自動チェックを行うことができます。 また、下で話しする Code Climateとも似てるチェック項目が準備されて、これを利用することでCode Climate側でチェックされる前に先に対応することも可能です。 チェック項目のカスタマイズについて PJのRootに設定ファイルを準備します。 Code-Climateのチェックと同じチェックがあるので、先に行うとCodeClimate側で指摘されないので、安心です。 nestif や、 gocognit の場合コードの複雑さを表す部分です。 CodeClimate側でも言語によって自動でチェックしているので、内容をある程度把握しておいたほうが良いと思います。 ## golangci.yml ## code-climate側と同じチェック linters-settings : nestif : # min-complexity : 4 gocognit : # minimal code complexity to report, 30 by default (but we recommend 10-20) min-complexity : 20 gofmt : # simplify code: gofmt with `-s` option, true by default simplify : true nakedret : # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 max-func-lines : 100 issues : # Excluding configuration per-path, per-linter, per-text and per-source # 該当チェックでは、*_test.goファイルは除外する。意味 exclude-rules : - path : _test\.go linters : - errcheck - gocognit - nestif - gofmt ## 設定使うLinterを表示 linters : enable : - nestif - gocognit - gofmt - nakedret fast : false このように、どこで、どんな問題があるのかを教えてくれます。 Code Climate 公式ドキュメント Github 以外にも様々なレポジトリ管理サービス(gitlab,gitbucket等)と連動することで各種項目のチェックが可能し、ひと目に見れるUI/UXを提供してます。 メンテナンススコアー テストカバレッジスコアー コードスメール(設定値を超えてるもの) コード重複(似てるコードが存在してる) などの項目を簡単に確認できて、該当項目を連動されてるレポジトリ上にイシュー化されることもでき、Pull Requestを作成する時コードレビューにも使える機能もあります。 レポジトリと連動されてるときにウェブページ以外に、コードベースにも設定ファイルを追加して、より細かい設定が可能になる。 設定のカスタマイズについて CodeClimateの場合、 ウェブページでの操作 である程度コントロールができますが、コードで設定することも可能です。 golangciと同じくPJのRootに、設定ファイルを配置することで設定可能になります。 ※一緒に使えるPluginも多数あるので自分た ちのレポジトリで必要なものを選択して入れましょう。 version : "2" # required to adjust maintainability checks checks : # パラメータの数 argument-count : config : threshold : 4 # ロジックの複雑さ complex-logic : config : threshold : 15 # 1ファイルの最大行数 file-lines : config : threshold : 500 # メソッドの複雑さ method-complexity : config : threshold : 15 # 1ファイルあたりメソッド数 method-count : config : threshold : 20 # 1メソッドあたりの行数 method-lines : config : threshold : 50 # nest制限数 nested-control-flow : config : threshold : 4 # 1メソッドあたりRetrunの制限 return-statements : config : threshold : 4 similar-code : config : threshold : # language-specific defaults. an override will affect all languages. identical-code : config : threshold : # language-specific defaults. an override will affect all languages. exclude_patterns : - 除外したいファイルパターン CodeClimateのトップ画面を見ると トップページ、各種スコアーが一目で見れます。 このようにサマリーが見えます。 詳細設定を利用して連動されてるコードレポジトリ(Github,Gitlab,Gitbucketなど)にコメントをつけることの可能です。(レビュー機能です。) 全体の流れ テスト結果レポート転送について こちらのようにテスト完了した結果をCodeClimateに転送して、現Repositoryの状態を更新します。 全体図 上記案内した各設定が全部準備できたら、このような流れの運用が可能になります。 Github Actionsの利用方法によって、golangci-lintや、UNITテストのエラーをチャットワークや、Slackなどに送ることで、自分がPUSHしたコードがどんな問題があるのかを早めに把握でき、必要最小限の統一感を持つコードとして保存できます。 そして、最後にテスト結果をCodeClimate側に送ることでレポジトリの状態を管理することも可能になります。 自分が作成したこの例は、自分が配属されてるチームで使ってる方法の一つであり、これを見てより良い案があれば、チャレンジしてみてください。 最後に いかがでしょうか? 使える機能を考えると以外に簡単に設定できますね? 項目の設定はチームメンバーと一緒にどれぐらいの数字を設定するのかをディスカッションすることでみんなと、認識合わせもできるし、コード作成時Lintチェックで怒られながらどんどん設定したコーディングルールが身につけられて行けると思います。 もちろん設定した数値は、絶対的なものではないので後で修正しながらチームの状態に合わせて行くことも可能です。 私も今まで、簡単なLintチェックなどは自動化した経験はありますが、ここまで全部自動化してコードベースを管理するのは初めてだったのでかなりいい経験だと思っています。 余談ですが、badgeをREADMEに入れることで、レポジトリページに接続したらレポジトリのメンテナンススコアや、テストカバレッジスコアがすぐ目に見えることで、より具体的なチームの目標や、モチベーションにもつながると思います。 このようなBadgeをREADMEに配置することで、簡単に確認できます。 今までコード品質管理などについて特に興味がなかったとしても、この記事がきっかけでご自分のレポジトリにも品質管理ツールと導入を検討していただければいいかなと思います。 長い記事読んでいただきありがとうございます。
こんにちは。検索エンジンチームの宮崎です。 皆さんご存じの通り、LIFULL HOME'Sのメイン機能は 物件の検索 です。 LIFULL HOME'Sでは、 検索機能の大部分 を全文検索エンジンSolrで賄っています。 以下のような機能を検索エンジンで実現しています。 こだわり条件検索(ガスコンロ3口、2階以上、など詳細な条件での検索) 駅・エリアでの絞り込み 地図検索 タグによる物件検索 検索結果の件数 並び順 建物や戸ごとのグルーピング これらの機能を実現している検索エンジンは、 アプリケーション実行基盤やDBに並んでサービス継続のために必要な重要コンポーネント です。 しかし検索エンジンはその特性上、ステートフルなソフトウェアです。 HDFSやその他ストレージと組み合わせることでステートレスにすることもできるかもしれませんが、多くの場合ステートフルなアプリケーションとして運用していることが多いと思います。 今回はステートフルなアプリケーションである検索エンジンを一部スポットインスタンス化することで ランニングコストを削減 したので、構成や進め方について紹介しようと思います。 🔎 全文検索エンジンをスポットインスタンス化??? 全文検索エンジンとは そもそも全文検索エンジンとは何でしょうか? Wikipediaにはこうあります。 全文検索とは、コンピュータにおいて、複数の文書(ファイル)から特定の文字列を検索すること。 「ファイル名検索」や「単一ファイル内の文字列検索」と異なり、 「複数文書にまたがって、文書に含まれる全文を対象とした検索」という意味で使用される。 もともとは多数の文書の中から特定の文字を含む文書を検索するために作られたもののようです。 LIFULL HOME'Sでは文書の代わりに物件情報を検索エンジンに入れて物件の検索を実現しています。 スポットインスタンスとは 次にスポットインスタンスとは何でしょうか? これは、AWS(Amazon Web Services)において、条件付きでマシンを安く使用できるしくみのことです。 その条件というのが、「スポットで起動されたインスタンスはAWS側の都合により、事前の予告なく※1停止されることがある」というものです。 ※: インスタンス停止の2分前に通知されます。 これは、スポットインスタンスのしくみによりそのような条件になっています。 スポットインスタンスは、AWSのクラウド内で使用されていないEC2を安く使えるというしくみです。 誰にも使われてないのはもったいないから、安くてもよいので使ってもらおうということですね。 ステートフルなソフトウェアとスポットインスタンス スポットインスタンスはその特徴から、 公式の説明「ステートレス、耐障害性、または柔軟性を備えたさまざまなアプリケーションでご利用いただけます」にある通り、 ステートレスなアプリケーションで主に利用されます。 任意のタイミングでインスタンスを停止できることと、ステートレスであることは相性がよいからです。 ステートレスであれば、インスタンスを停止したい場合はそのままインスタンスを停止すればよいのです。 (実際には停止時に行いたい処理があることのほうがほとんどだとは思います) さて、検索エンジンはステートフルなソフトウェアでした。 しかしタイトルの通り、 ステートフルでも安くしたい! のです。 そんな検索エンジンをスポットインスタンス化してコスト削減した構成が以下です。 🔗 検索エンジンの構成 検索エンジンは以下のような構成になっています。 Solrクラスタの構成図 一部省略していますが、概略としてはこんな感じです。 Lambdaは、Solrのリーダーにデータを書き込みます。 実際に検索用のクエリを受け取るインスタンスはAutoScalingGroupで管理しています。 AutoScalingGroupを使用しているので、MixedInstancesPolicyを設定することでスポットインスタンスを適用できます。 🤔 工夫点 インスタンス起動時に、systemdでクラスタに自動で参加するようにしている インスタンス停止時に、systemdでクラスタから自動で抜けるようにしている スポットインスタンスの終了通知をEventBridgeで受け取って、ALBから自動で切り離すようにしている 書き込みと読み込みでエンドポイントを分けている クラスタはデプロイ時に全インデックスを再構築できる マスタ/スレーブ構成にすることで参照用Solrはリードオンリーに動作する 「クラスタはデプロイ時に全インデックスを再構築できる」、「マスタ/スレーブ構成にすることで参照用Solrはリードオンリーに動作する」の 2点のおかげで、ステートレスなSolrを実現しています。 ステートレスなSolrを実現するとAutoScalingGroupで管理できるようになり、負荷増に耐えたり柔軟性が上がり、スポットインスタンスで動作させることができています。 またマスタ/スレーブのような構成にすることで、検索クエリの負荷や書き込みの負荷をお互い影響させないようにしています。 📖 結果 EC2インスタンスの支払いの内訳 コストエクスプローラーの数値が見えない状態で切り取ったものです。 オレンジの部分がスポットでかかっている金額です。緑色のオンデマンドの金額がかなり小さくなっているのがわかると思います。 スポットインスタンスが、だいたい正規の値段の1/3程度だったので概算で、1日あたり約20%強のコスト削減を実現しました。 別のプロジェクトで行っていた SavingsPlansによるコスト削減 と含めると、 ほぼオンデマンドで動いているインスタンスがいない状態です。 社員の数が少なければボーナスで焼き肉を食べに行けるくらいにはなったでしょう。 最後に Solrの特徴とAWSをうまく組み合わせることで、耐障害性を確保しつつ、コスト削減を実現しました。 ステートフルなアプリケーションの中の、ステートレスな部分のみスポットインスタンスを適用したという話でした。 もともとイミュータブルに作っていたことや、更新と検索でエンドポイントを分けた構成にしていたことで、 簡単にスポットインスタンスによるコスト削減を実現できました。 アーキテクチャは大事ですね。 カジュアル面談もやっていますので、一緒に「 感動を届ける検索エンジンを実現する 」、ひいてはLIFULLが目指している「 あらゆるLIFEを、FULLに。 」することに興味がある方はぜひお話しましょう!
LIFULLで売却査定サイトの開発をしています、北島です。 このたびTestCafeというE2Eテストを、awsのリソースを使ってクラウド移行しましたので、簡単に振り返りたいと思います。 前提 売却査定のサービスは本番を含めて4つの環境が用意されています。 prod環境(本番) pool環境(開発環境) dev環境(開発環境) unit環境(開発者各々の環境) これらのうちunit環境以外の3環境に関して、デプロイをトリガーにE2Eテストを行うような仕組みを実現しました。 構成図 CodeBuild上でTestCafeを実行することで実現を試みました。 テストに関する構成図を下に示します。 構成図 CodeDeploy(既存)のデプロイ成功時にSNSメッセージを発行し、LambdaFunctionからCodeBuildを実行します。 CodeBuildでのテスト実行終了時に、テスト結果を含むSNSメッセージを発行し、notificationで実行結果を通知します。 構成図内の"env"は、実際にはdev/pool/prodと環境ごとに別々のリソースに分かれています。 awsリソースのメインとなるのはtestcafe_kickerのリポジトリで、テストに関するawsリソース全般に関して扱っています。 実際に使用されるテストコードに関してはcodebuild-testcafeのリポジトリで管理しています。 testcafe_kickerリポジトリ このリポジトリの役割は、CodeBuildを実行するlambdaを管理することです。 今回使用した serverless というフレームワークでは、 CloudFormation テンプレートを使用することで、awsリソースをデプロイすることができます。 lambdaを管理するリポジトリながら、CodeBuildプロジェクトやsnsトピック、及びそれらの間の通知ルールの一括管理が可能となっています。 CodeBuild projectについて メインとなるCodeBuild projectの実装について書いていきます。 serverless.yml templates: # anchor用template定義用 dev_topicName: &testcafe_dev_topic_name testcafe_kicker_dev pool_topicName: &testcafe_pool_topic_name testcafe_kicker_pool prod_topicName: &testcafe_prod_topic_name testcafe_kicker_prod project_name: &testcafe_project_name CodeBuild-TestCafe build_project_properties: &testcafe_build_project_properties Artifacts: Type: NO_ARTIFACTS BadgeEnabled: true BuildBatchConfig: ServiceRole: !GetAtt TestcafeBuildRole.Arn ServiceRole: !GetAtt TestcafeBuildRole.Arn VpcConfig: VpcId: vpc-****** Subnets: - subnet-****** SecurityGroupIds: - sg-****** Source: Auth: Type: OAUTH Location: https://github.com/{#testcafeリポジトリのurl} GitCloneDepth: 1 Type: GITHUB SourceVersion: refs/heads/master ~中略~ Resources: TestcafeBuildProjectDev: DependsOn: - TestcafeBuildPolicies Type: AWS::CodeBuild::Project Properties: <<: *testcafe_build_project_properties Name: !Join - '-' - - *testcafe_project_name - Dev Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 Type: LINUX_CONTAINER PrivilegedMode: true EnvironmentVariables: - Name: TESTCAFE_DOMAIN Type: PLAINTEXT Value: https://www-test-~~(テスト対象のドメイン) - Name: ECR_REGION Type: PLAINTEXT Value: !Ref AWS::Region - Name: ECR_URI_LATEST Type: PLAINTEXT Value: !Join - '' - - !Ref AWS::AccountId - '.dkr.ecr.' - !Ref AWS::Region - '.amazonaws.com/' - !Ref TestcafeEcrRepository - ':latest' ロールやセキュリティグループなどをしっかりと定義する必要があります。 またgithub上のテストコードをcloneして実行するので、リポジトリのurlも設定します。 privateリポジトリの場合、認証情報もあらかじめawsコンソール上で登録しておき、cloneに成功するようにしておく必要があります。 テスト実施時のコマンドは、このプロジェクト自体に定義するほか、cloneしたリポジトリ内で定義されたものを使うようにも設定できます。 実際のテスト実施時のコマンドはテストコード毎に異なってくるので、このリポジトリではなくTestCafeリポジトリで定義されたものを使うようにしています。 buildspecに書けないテスト対象のドメインなどは、プロジェクトの環境変数としてテンプレートに定義しておきます。 TestCafeリポジトリ このリポジトリの役割は、TestCafeのテストコードを管理することです。 ローカル実行できるようなテストコードを作成しています。 これをDocker内でも実行できるように調整したうえで、CodeBuild上で行いたいコマンドをbuildspec.ymlに記述することで、CodeBuild プロジェクトでも実行できるようになります。 buildspec.ymlを見ていきます。 version: 0.2 batch: build-list: - identifier: pc_page env: variables: UA: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/604.1' testcase: "'chromium:headless' tests/page/" - identifier: pc_inquire env: variables: UA: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/604.1' testcase: "'chromium:headless' tests/pc/inquire" OVERWRITE_COOKIE: 'abtest_AB=a;' - identifier: sp_page env: variables: UA: 'Mozilla/5.0(iPad;CPUOS11_0likeMacOSX)AppleWebKit/604.1.34(KHTML,likeGecko)Version/11.0Mobile/15A5341fSafari/604.1' testcase: "'chromium:headless:emulation:device=iphoneX' tests/page/" - identifier: sp_inquire env: variables: UA: 'Mozilla/5.0(iPad;CPUOS11_0likeMacOSX)AppleWebKit/604.1.34(KHTML,likeGecko)Version/11.0Mobile/15A5341fSafari/604.1' testcase: "'chromium:headless:emulation:device=iphoneX' tests/sp/inquire/" - identifier: bot_page env: variables: UA: 'Googlebot/2.1 (+http://www.google.com/bot.html)' testcase: "'chromium:headless:emulation:userAgent=Googlebot/2.1(+http://www.google.com/bot.html)' tests/page/" phases: install: commands: - yum update -y - yum install -y bind-utils pre_build: commands: - aws ecr get-login --no-include-email --region ${ECR_REGION} - docker pull ${ECR_URI_LATEST} || true # cache利用目的のpullなのでエラーでもビルドを続ける - docker build --cache-from ${ECR_URI_LATEST} --tag ${ECR_URI_LATEST} . build: commands: - export POOL_IP=`dig +short (pool環境のCDN) | tail -n1` - docker run --env TESTCAFE_DOMAIN --env UA --env OVERWRITE_COOKIE --add-host (poolのドメイン):${POOL_IP} ${ECR_URI_LATEST} /usr/bin/npx testcafe ${testcase} --disable-multiple-windows -q post_build: commands: - aws ecr get-login-password --region ${ECR_REGION} | docker login --username AWS --password-stdin ${ECR_URI_LATEST} - docker push ${ECR_URI_LATEST} 大きく二つ、batchとphaseから構成されています。 phase phaseでは、ビルド内で実際に行いたいテストコマンドを書いていきます。 installやpre_buildなどの詳細なphaseの名前はあらかじめawsによって用意されているので、より細かく分けたい場合はどんなものが使えるのかを調べる必要があります。 ここに書くコマンドはどのビルドでも使用されるので、なるべく簡潔に汎用的に書くことを心掛けました。 docker imageはaws ecrを使用してキャッシュするようになっています。 どこかのphaseで一つのコマンドが正常終了しなかった時点でそのビルドは停止してしまいます。 複数テストコマンドを記述しても途中で終わってしまっては後のテストが実施されないので、テストを行う(正常終了しない可能性のある)コマンドは、一つのビルドにつき1つが望ましいと考えました。 一つのプロジェクトで複数のテストを行いたかったので、今回はバッチビルドによって実現しています。 batch バッチビルドでは、一つのプロジェクトから複数のビルドを作成することが出来ます。 batchという項目ではそれぞれのビルドをどのように、どのような設定で作成するかを定義しています。 build-list は、リストの形で作成するということを意味しており、他にbuild-graphやbuild-matrixという方法が用意されています。 build-listは配列で定義しており、各項目が一つのビルドの設定になっています。なので今回の設定では5つのビルドが作成されるわけです。 identifierはユニークな文字列で設定し、各ビルドの名前になります。わかりやすい文字列が良いでしょう。 envの中ではビルドごとに変えたいパラメータを設定でき、variables内では環境変数を設定できます。 ここでは以下の3つを環境変数として定義しています。 テストを行うUA テストコード(testcase) 特に指定したいcookie(OVERWRITE_COOKIE) これらを設定することで、色々な条件でテストを行うことが出来るようになりました。 検討事項 今回はbuild-listを使用しましたが、build-graphとbuild-matrixの使用も検討しました。 それぞれの性質について、簡単にご紹介します。 build-matrix 最初に検討したのは build-matrix で、これはmatrixとあるように、色々な変数に関して、全組み合わせでビルドを作成します。 例えば、今回は3つの環境変数を使っていますが、 UA2種 testcase3種 OVERWRITE_COOKIE2種 の様に設定した場合、build-matrixでは2*3*2の全組み合わせ、つまり全12個のビルドを作成します。 これはいろいろな環境でビルドを行いたい場合に非常に便利で、当初はこちらを使用していました。 デメリットとしては、例外パターンを作れないことです。全組み合わせに近い11のビルドだけ作成、が出来ない点がデメリットだと思います。 今回私が実現したかったテストは、UAによって行いたいテストケースが異なっており、特にbotではステータスチェックのみを行いたいテストでした。 matrixでは不要なビルドが出来てしまい、matrixの恩恵を受けられないと感じました build-graph build-graph は各ビルドの依存関係を作ることが出来、このビルドが終わったら次のこのビルドを作成、のような設定が作れます。 これも便利な機能で、より詳細にテストを計画することが出来ます。 デメリットは並列実行よりも実行時間が長くなるという点で、より早く行いたい場合はlistやmatrixの方が良いですね。 今回はテスト間に依存関係が無かったので、時間を優先してlistとしました。 しかし特に本番以外の環境に対してテストを行う場合、並列実行数によってはサーバー負荷が問題になってきます。 もしもビルドが増えてきて並列実行が難しくなった場合は、開発用環境のスペックを上げる選択肢だけでなく、build-graphに変更して負荷を減らす選択肢も考えたいと思います。 苦労したこと ネットワーク関連の設定や検証に苦労しました。 CodeBuild プロジェクト自体は開発用の環境に構築したのですが、poolやprod環境は別のawsアカウントを使用しているため、vpcが異なります。 pool環境は通常売却査定のVPC外からアクセスするため、通常の方法では疎通ができませんでした。 最終的に今回は、TestCafeの項のyamlにもあるように、自VPCからでもアクセス可能であったCDNのIPを動的に取得し、それをdockerコマンドのadd-hostオプションとして渡して実行する方法で解決しました。 まとめ 今回は、デプロイ後にE2Eテストを行う工程をCodeBuildを使いクラウド移行しました。 ローカルで行っていたテストが自動化できたので、開発者の時短や、アプリケーションの品質担保に貢献できたと思います。 今後もローカルで行っているフローをクラウド移行していければ良いなと考えています。 ここまで読んでいただき、ありがとうございました。
こんにちは。プロダクトエンジニアリング部の渡邉です。 今回は先日私が所属するプロダクトエンジニアリング部にてオンラインで実施でき、チーム形成とエンジニアが楽しむことができるチームビルディングを開催しましたので、そちらの内容について紹介させていただきたいと思います。 チームビルディングとは チームビルディングとは、組織を単なる「グループ(人の集まり)」で終わらせずに、成果を上げる『チーム』に組成するための一連の手法です。 新しいチームを組成する際には、チームのビジョンや戦略を共有し、所属メンバー一人一人がそれらを自分のものにしなければなりません。 LIFULLでは、期初に各グループに対してチームビルディング予算が割り当てられ、メンバーが自分達で考えたさまざまなチームビルディングが行われています。 そして今回は私たちプロダクトエンジニアリング部2ユニット(※以下ユニット)で行われたチームビルディングについて紹介します。 参加者の構成 LIFULL HOME'S事業本部では現在職種別組織の体制を取っており、私たちのユニットは全員がエンジニアで構成されています。 またユニットの中で3つのグループに分かれており、普段は異なるサービスを開発しています。 今回はその3つのグループが集まり、フロントエンド・バックエンド・マネジメント層など、あらゆる領域を専門とするエンジニアたちでチームビルディングを行いました。 目的 チームビルディングで達成したいゴール 今回チームビルディングを行うにあたって達成したい目的がありました。 それは隣のグループとのコミュニケーションの活性化です。 もともとエンジニアではありながら別サービスに対してコミットをしてきたグループが一つの集団となったこともあり、 お互いのことを知る機会が少ないことが課題でした。 チームビルディングのフレームワークであるタックマンモデルにおいて、チーム形成のプロセスには5段階あると言われています。 現在のユニットはその最初の段階の「形成期」にあてはまるので、お互いに意見をぶつけ合うことができる「混乱期」にステージを進めることをゴールとしました。 ビジョンを交えて実施したい 目的に記載した内容を元にチームビルディングを行うということだけであればみんなで一体感を感じることができるようなアクティビティであったり、 比較的心理的ハードルの低い内容を元に議論を行うといった方法でチームビルディングを行うことも可能ですが、 今回は『エンジニアらしさ』というものにフォーカスを当ててコンテンツを準備しました。 なぜエンジニアらしさにこだわったのか 一つは普段のチームビルディングが企画やデザイナーなどの職種を横断したものであることが多かったのに対し、今回の参加者はエンジニアのみでした。 もう一つはチームのビジョンによるものです。 私たちのチームは”プロダクトエンジニアリング部”という部署であり、プロダクト開発を行うエンジニア組織です。 その部署の達成したいビジョンは"強い個人・最高のチームになることで、価値創造を加速させ続ける"というものになります。 一人一人が強い個人を達成することで最高のチームが形成され価値創造は加速するということを意味しているのですが、 強い個人を達成する一つの指標に"技術力の向上"というものも含まれています。 ですので、今回のチームビルディングでは、ただチームの風通りをよくしたいというだけでなく強い個人の達成というものも意識して強化したいということも含めて『エンジニアらしさ』にこだわって実施することにしました。 どんな内容にしたのか 今回私たちが上述した目的を達成するために選んだ手法は 自作のエンジニア謎解き を開催するというものでした。 どんな風に実施したのか 形式はGitHubにエンジニアらしい分野ごとのクイズを用意し、解答をスプレッドシート上で解答する方式を取りました。 GitHubに用意した問題 クイズはチームビルディング運営メンバーで分担して考え、LIFULLらしさを活かした問題など、さまざまな問題を用意しました。 エンジニアなら答えられて当然!?な問題や 暗号を解読する問題 今回の問題のために準備されたデータベースから答えを導く、エンジニアの腕がなるような問題もあります。 中には、アルゴリズムを考えるような問題も!( PKU JudgeOnline『Expedition』 改題 ) という具合に、知らないと解けないような問題から頭を使って解く問題、専門性を活かした問題と非常に多岐に渡る領域から問題を作成しました。 問題を作成するにあたって工夫した点 エンジニアそれぞれで得意な領域が違うので、まったく手が付かないことがないように各領域で難易度を考えながら用意しました。 参加者に公開せずに各問題で難易度に応じて配点を設けて、「これは難しいから配点が高いのではないか」「まずは簡単そうなやつから解いて着実に稼ごう」などゲーム性を持たせて少しでも参加者のみんなが楽しめるように運営メンバーで考えました。 当日の様子 実際に解き始めると各チーム取り組み方が違いました。全問題を一つずつみんなで解答していてくチームもあれば、得意分野ごとに各自別れて解答していくチームもありました。 実際に問題に取り組んでいる時の様子です。基本的にzoomで画面共有しながら解いていました。 みんなで解いているチームは会話量も多かったですが、各自で分かれて解いていたチームは会話量も少なくなりがちでした。 このようなクイズでも、ちゃんと最初に作戦を練るチームがやはり強かったです。見積もることはどんな場面でも大事ですね。 振り返り チームビルディング終了後に参加者へのアンケートを実施したところ、以下のようなコメントが集まりました。 「問題の難易度がバラけていたのでとっつきやすさ/やりがいの両面がありつつ、戦略も考えられる内容だったので良かったです」 「エンジニア歴の浅い私でも解けるようなスプレッドシートやGitの問題など、幅広い問題が考えられていて楽しかったです!」 これらをもとに運営メンバーで振り返ったところ、良かったところと改善できるところが見えてきました。 良かったところ オンラインならではのコンテンツ オンラインでの実施にあたって、全身を使うアクティビティや共通の道具を使ったゲームなどは難しく、 今までのチームビルディングとは違ったやり方を試みる必要がありました。 オンラインでのチームビルディングを成功させるには、ビデオコミュニケーションのために全員の手元にあるPCを十分に活用する必要がありますが、ITエンジニアにPCを持たせたらまさに水を得た魚・鬼に金棒・虎に翼です。 GitHubなどの普段から用いているサービスや、それぞれが得意とする技術を使用することで、スムーズな実施ができました。 置いてけぼりがいない、全員が楽しめる設計 エンジニアとしての基礎知識から、フロントエンジニアが活躍できる問題、論理的思考力で勝負できる問題、ひらめきが重要になる問題、データベース等の知識が問われる問題など専門性の高い問題まで幅広く問題を用意しました。 その結果、問題の取り組みやすさとやりがいが両立され、最後まで全員が楽しめました。 今まで業務で関わらなかったチームメンバーのかっこいい一面を見ることができ、お互いの強みの理解へとつながっているようです。 改善できるところ 運営メンバーと参加者とのコミュニケーション不足 運営メンバーは作問をした立場ですので、必然的に謎解きへの参加はできなくなります。 そのため、今回のチームビルディングを通して運営メンバーと参加者のコミュニケーションはあまり十分ではなかったように感じられました。 事前に 何回か、チームの出した答えが正しいかを聞ける 何回か、他チームの解答状況を確認できる といったような、『クイズ$ミリオネア』の"ライフライン"さながらのルールをうまく作っておくことで、運営メンバーと参加者のコミュニケーションも活性化したかもしれません。 企画者が参加者と同じ立場でゲームに参加できないというケースはよくありますが、どうやってコミュニケーションロスをカバーするかを事前に考えておく必要があると感じました。 チームによるコミュニケーションの差 普段の業務にも通じる部分ですが、チームでたくさんの問題を解くという性質上、チーム内で各々の強みを把握することで有利に進めることができます。 競技として戦略の組み立てがうまいチームが勝つことは望ましいのですが、お互い初対面の場合もあるチームビルディングで戦略的なコミュニケーションをとることが容易でないことは明らかです。 そこで、 お互いがどんなスキルを持っているか どのような方針で問題に取り組むか どのように報告しあうか といった観点を事前に提示したうえで、作戦会議をする時間を設けることで、より円滑なコミュニケーションができたかもしれません。 チームビルディングに限らず言えることですが、明確な話題や流れの設定があると、即席のチームでも短い時間に密度の高いコミュニケーションをできると考えられます。 まとめ エンジニアとしての矜持のもとに個々の持つ強みを発揮し、協力して壁を打ち破るという今回のエンジニア謎解き。 「コードで語れ 頭を使って 謎を解け」と言えるような今回のチームビルディングでは、チームで協力して謎に挑戦することで、お互いの理解と技術的な気付きを得ることができました。 風通しの良さとエンジニアとしての能力を高めてもらうためのいいコンテンツとなったと思います。 完全オンラインでのチームビルディングの一例として、参考になれば幸いです。
こんにちは。テクノロジー本部のyoshikawaです。好きなLinux DistributionはManjaro Linuxです。 今回はレガシー化が進むLIFULLのメインサービスの開発効率の向上とコードベースの健全性の確保をすべく、Clean Architectureを採用しバックエンドを刷新している取り組みについて紹介させていただきます。 なお、Clean Architecture自体の説明および解説は本記事では行いません。 背景:歴史あるバックエンドの刷新 アプローチ:新たなアーキテクチャと共創 採用したアーキテクチャ・技術 Clean Architectureを採用した理由 TypeScriptを採用した理由 LoopBackを採用した理由 Clean Architectureの実践 レイヤー分け:例の図と新BFFアーキテクチャのレイヤーとのマッピング レイヤー内・レイヤー間:独自の規約を導入する 規約違反の検知を自動化する コンポーネントレベルでの規約:物理的なリポジトリ分割 組織構造に追従したリポジトリ分割 Clean Architectureを実践した所感 開発効率の向上とコードベースの健全性の確保は達成できたか? BFFにClean Architectureの規約は複雑すぎないか? 規約の遵守と開発効率の最適化 ドメインモデリングが不足していないか? Clean Architectureを採用したのは正しいかった? 今後導入したいこと 実装効率を向上させるために おわりに:銀の弾丸はない 背景:歴史あるバックエンドの刷新 LIFULLのメインサービスであるLIFULL HOME'Sのバックエンドはその大部分がSymfony(PHP)ベースのモノリスと、ともにSinatra(Ruby)ベースのBFFとAPIサーバーの3層構造で構成されています。 このうち、Symfonyベースのモノリスは9年以上開発されており歴史のあるアプリケーションになっています。 長年の間多くのエンジニアによる開発が行われ、レガシー特有の問題が発生しています。 例えば、以下のような問題が発生しています。 ビジネスロジックの複雑化、およびテンプレートエンジン(Twig)内のロジックとの混在化・密結合化 APIおよびシステム内部のドキュメントが充実しておらず、I/Oがわかりにくい 異なるユースケース・開発組織(アクター)が利用しているなど、責務の不明瞭なモジュールが偏在している このような問題の結果、開発効率の観点で以下の課題が生じています。 一度の変更が予期せぬ影響を与え得るため、新機能追加のための調査・設計・実装に時間がかかる 一度の変更による影響範囲が広く、機能改修のための影響範囲分析に時間がかかる 品質維持のための工数効率が悪い、バージョンアップがしづらい こうした課題を解決すべく、バックエンドを刷新するプロジェクトが発足しました。 アプローチ:新たなアーキテクチャと共創 LIFULL HOME'Sの大部分のバックエンドは先述のSymfonyベースのモノリスに加え、ともにSinatra(Ruby)ベースの既存BFFとAPIサーバーの3層構造で構成されています。 この既存BFFにリファクタリングを行い、モノリスからビジネスロジックを移植することで開発効率の向上と健全性の確保を行うアプローチも考えられました。 検証の結果、モノリス上のビジネスロジックの移植対象をこの既存BFFではなく新しいBackend For Frontend(以降、 新BFF と呼びます)に移植する、というアプローチによってバックエンドの複雑度を下げ開発効率を向上と健全性の確保を行うことになりました。 いわゆるストラングラーパターン(Strangler Fig Application)のアプローチに相当します。 martinfowler.com 現在は筆者を含めた数人のバックエンド刷新プロジェクトのチームメンバーが主体となって実装を進めていますが、今後は LIFULL HOME'Sに関わる多くのエンジニア と共に移植作業を行い、 共創 して刷新を遂行していく予定です。 採用したアーキテクチャ・技術 新BFFの技術スタック; Clean Architectueベース、言語はTypeScriptを採用 新BFFのアーキテクチャは「 Clean Architecture 」、言語は「 TypeScript 」、フレームワークは「 LoopBack 」を採用しています。 blog.cleancoder.com loopback.io この3つの技術が選定された理由を紹介していきます。 Clean Architectureを採用した理由 採用した理由は複数あります。いくつか列挙すると、 著名かつ制約の厳しいアーキテクチャであり、実装の方言が生まれにくい。そのため多数のエンジニアが開発しても共通認識が持ちやすく。アーキテクチャの遵守が期待される DDDのパターン(レイヤードアーキテクチャ)の実装表現の一つであり、自己文書化をはじめとしたDDDの恩恵を受けることができる 実装の「詳細」を「抽象」に依存させることで、フレームワークやライブラリとの依存を減らし、バージョンアップを行いやすくすることが可能 物理的・概念的なレイヤー間の責務を明確にしやすく、 オニオンアーキテクチャやヘキサゴナルアーキテクチャと比較すると書籍などの学習資料が充実している などが挙げられます。 決め手となっていることは、最初に挙げたように 「アーキテクチャレベルで明確な共通言語があること」 です。 新BFFは長年に渡って多数のエンジニアによって開発されることが見込まれるため、健全性を保ったコンポーネントにするためにはアーキテクチャの共通言語を用意することは重要な観点でした。 一方で、Clean Architectureは学習コストが高く、その性質上Dto(Data Transfer Object)やInterfaceの実装量が増えたり冗長な実装が増えたりするというデメリットも存在し、開発効率向上には貢献しない可能性も考えられました。 この辺りの対処については後述します。 TypeScriptを採用した理由 言語のその他候補にはGolangやKotlinがありましたが、以下の理由からTypeScriptが採用されました。 漸進型付き/静的型付き言語により、これまでの開発環境にはなかった以下の強力な恩恵が受けられること データ構造が明確になることで、設計、実装およびIDEに頼ったリファクタリングのコストが下がる 型付けによって、APIドキュメンテーションの自動化が可能になること 多くのエンジニアが実装する(共創する)以上、学習コストの低い言語が望ましいこと フロントエンドと言語を一致させることで、学習コストの発生確率を下げて実装可能なエンジニアを増やせるといったシナジーが期待できること 決め手になっていることは「 型による恩恵と学習コストの低さ 」です。 これまでのLIFULL HOME'Sのバックエンドの言語はPHPとRubyであったこともあり、型付き言語の導入は開発効率向上に大きく貢献することが見込まれました。 また、Clean Architectureを採用している時点で一定の学習コストを計上しているため、JavaScriptのスーパーセットであるTypeScriptを採用することで学習コストを下げることは重要な観点でした。 LoopBackを採用した理由 LTSバージョンの期間、学習コスト、OpenAPIとの親和性、そしてClean Architectureとの親和性といった観点を考慮した上でLoopbackが選ばれました。特筆すべき点は以下です。 OpenAPIのドキュメントが容易にホスティングできる(swagger)など、OpenAPIのドキュメンテーションのための機構が整っていること アノテーションだけで(View)ModelからJSON Schemaへの変換が容易にできること DI(Dependency Injection)が備わっていること 2つ目に挙げたJSON Schemaへの変換を取り入れた場合、UI層のViewModelがフレームワークに依存することを許してしまうことになるので、厳密にはClean Architectureの規約違反になります。 ただ、アノテーションのみの軽微な依存であるため許容するという判断になりました。 Clean Architectureの実践 Clean Architectureは設計の原則を提供しているものの、抽象度が高くそのままでは実装の自由度は高いままです。 具体的な新BFFでのレイヤー分け(=ディレクトリ構成)と、レイヤー内・レイヤー間の実装規約について紹介します。 レイヤー分け:例の図と新BFFアーキテクチャのレイヤーとのマッピング The Clean Code Blog( https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html )より引用 こちらはRobert C.Martin氏が提唱したClean Architectureの図です。Clean Architectureを調べると一度は目にする有名な同心円ですね。 一方、以下が新BFFのアーキテクチャです。図中の色はClean Architectureの同心円の色と対応させてあります。 新BFFとClean Architectureのマッピング; 一部は独自規約を導入して最適化している おおむね同心円の通りに各レイヤーと依存の方向を定義しています。 大まかなディレクトリ構成は以下のようになっています。 . ├── adapters // Interface Adapters │ ├── gateways // 外部DB等、内外のデータ形式変換レイヤー │ │ ├── datasources // Cacheや, Backend API接続用 │ │ └── impl // Data Access Interfaceの実装 │ └── ui // Controller, View, Presenter ├── application // UseCase/Application Business Rules │ ├── repositories // Data Access Interface │ └── usecases // Controllerと一対一に対応するUseCase ├── domain // Entity. Value Object, Domain Serviceも含まれる └── framework // フレームワークを拡張したもの レイヤー内・レイヤー間:独自の規約を導入する レイヤー間のBoundaryはどう実装するのか、フレームワークとの結合はどのように表現するのかなどレイヤー内・レイヤー間での規約も独自に導入しています。 その規約の中からいくつかの特徴的なものを列挙すると、 RepositoryのInterface(Data Access Interface)をapplicationレイヤーに配置する 各レイヤーの境界を渡るデータ通信ではDto(Data Transfer Object)を利用する DtoはplainなTypeScriptのinterface UseCaseとControllerは原則一対一にする(アクターが混在する汎用的なUseCaseは避ける) これを実現するために、Controllerを細分化する ViewやControllerでOpenAPISpecification用の機構を利用し、フレームワークへの依存を一部許容している UseCaseではInputPortのみ実装するなど、冗長と思われるInput/OutputBoundaryは実装しない といった点が挙げられます。Clean Architectureに準拠し守るべき原則は守りつつ、実装コストを下げるためにも適宜設計を変更しています。 規約違反の検知を自動化する Clean Architectureの重要な規約の一つに、上位の方針が詳細に依存してはならない(=同心円の図における内側のレイヤーは外側レイヤーに依存してはならない)という規約があります。 この規約を守るようために紳士協定的にチェックリストを作成したり、人力のレビューで規約違反を防止のではなく、dependency-cruiserというライブラリを利用してCIに組み入れることで規約違反を自動で検知しています。 github.com コンポーネントレベルでの規約:物理的なリポジトリ分割 新BFFは単一のGitリポジトリから構成されるような巨大なコンポーネントではありません。 以下の図のように、単一リポジトリ内でnamespaceを物理的にGitリポジトリを分割しmicroservice的に分割統治することで、一度に開発する開発者を限定することで開発効率の向上を図っています。 それぞれのGitリポジトリで前項までに紹介したClean Architectureベースのアーキテクチャが採用されています。 Gitリポジトリ分割図; 開発組織ごとに分けている 組織構造に追従したリポジトリ分割 Gitリポジトリは、新BFFが取り扱うドメインの違いによって分割しています。ドメインには賃貸、流通、分譲などの マーケット固有 のものと、横断的な関心がありマーケットで分断することが難しい マーケット非固有 のものがあります。 LIFULLでは取り扱うドメイン=マーケットをベースにして開発組織が構成されています。 つまりマーケット=組織構造に基づきGitリポジトリ=コンポーネントを分割することは、書籍『Clean Architecture』で述べられているようなコンウェイの法則の体現でもありますね。 前項までに述べたような、一つのコンポーネント内でClean Architectureを実践することによりモジュールレベルでの開発効率の向上と健全性の確保を図ることに加え、Gitリポジトリを物理分割することでそれぞれの組織が独立して開発可能になるようにコンポーネントレベルでも開発効率の向上と健全性の確保を図っています。 Clean Architectureを実践した所感 約1年前からバックエンド刷新プロジェクトが発足し、筆者は昨年5月末にプロジェクトにジョイン、そして昨年の8月ごろから新BFFの実装・設計に携わってきました。 それから今日までに得られたClean Architecture的な知見を書いていきます。 開発効率の向上とコードベースの健全性の確保は達成できたか? これまでのモノリスと比較すると、設計、調査のためのコストは減少しており開発効率は向上していると思います。 これはClean Architectureを採用したからというより、PHPから型付き言語であるTypeScriptに移行したことにより、内部データ構造が明確になったこと、IDE(VSCode)を活用したリファクタリングがによる恩恵が大きいと感じています。 Clean Architectureを採用した影響についてですが、習熟度が低いうちは規約が複雑でレイヤーの責務がわかりにくく思えてしまいどのレイヤーにどの処理を書くべきか判断を誤ることもあり、かえって実装時間やレビュー時間の増加につながることもあります。 しかし、習熟度が高まれば解決可能な問題であるのでClean Architecture自体の性質に問題があるというよりは、それを継承している新BFFのアーキテクチャの啓蒙を進める必要があると考えています。 ただ単にモノリスからClean Architectureへの書き換えを行うだけでなく、実装可能なエンジニアを増やすことあるいはレビュー可能なエンジニアを育てることも当初の目的を達成するには必須と考えています。 BFFにClean Architectureの規約は複雑すぎないか? 前述した通り、少なくともClean Architectureを熟知したバックエンド刷新プロジェクトチームが制御できないような複雑さではないです。 設計・実装に悩んだ時はClean ArchitectureのルールとSOLID原則に立ち返って考えれば良いという根拠(=共通言語)が常にあるのは大きいと考えています。 レビューの根拠としての役割も大きいです。 とはいえ、現状の新BFFはクライアントからのクエリをバックエンドAPIに送信し、その結果にビジネスロジックを適用して返却するという参照系の処理が中心です。 そのため、単一のクライアントから単一のバックエンドAPIを呼び出すという処理を実現したい場合はPort、Adapter系をはじめとした抽象度の高い概念は、実現したい要件に対して実装の抽象度が高すぎるように思えてしまうなど、要件によっては規約が厳しいと思われるケースも存在します。 規約の遵守と開発効率の最適化 Clean Architectureを採用している以上、新BFFにおいてコードベースの健全性の確保のために規約を遵守すればするほど開発効率が落ち、開発効率を重視するほど健全性が落ちるというトレードオフと常に隣り合わせです。 冒頭で「共創」というアプローチをとっている、と述べた通り現在の数人のプロジェクトメンバーだけが新BFFの設計・実装を担うのではなく、LIFULL HOME'Sに関わる多くのエンジニアが新BFFの設計・実装を担う予定です。 将来的には累計で100人以上のエンジニアが開発に参加すると予想されるので、現状では冗長に思える実装があってもアーキテクチャを健全に保つ先行投資として規約の遵守を重視しています。 とはいえ、プロジェクト外のエンジニアの方から設計・実装面でのご相談・提案が発生していることもあり、規約の改善であったりアーキテクチャ自体を啓蒙し浸透させていく必要性も存在しています。 まとめると、 バックエンド刷新のプロジェクトメンバーだけが遵守・理解可能な複雑すぎるアーキテクチャ・規約になること あるいは、 実装の選択肢が多数考えられるような緩すぎるアーキテクチャ・規約になること これらの両極端な結果になることで、Clean Architectureの設計原則が守られないアーキテクチャになることは避けたいところです。 ドメインモデリングが不足していないか? Clean ArchitectureはDDDにおけるレイヤードアーキテクチャの実践パターンの一つであり、(Clean Architectureの)Entityを実装するにあたってはドメイン知識が整理されていることが必要となります。 しかしLIFULL HOME'Sのドメイン知識は整理されているとは言いづらく、ドメイン知識がドキュメントや実装に散在しているという状態です。 そのため、数値系のValue Objectを実装するにあたって、取りうる値の範囲を調べるためにDB仕様書や別のコンポーネントの実装を見なければならないというような事態が往々にして起こります。 そうした事態を防ぐためにもアーキテクチャのパターンとしてClean Architectureを採用する(いわゆる軽量DDDに陥る)だけでなく、DDDにおける戦略的設計を通じてドメインモデルを定義し集約し正しくDDDを実践していくことが重要だと認識しています。 現在は、DDDの思想の通りに改めてユビキタス言語を策定し実装各所に散らばったドメイン知識を集約・充実化するの取り組みが進行中です。 Clean Architectureを採用したのは正しいかった? バックエンド刷新プロジェクトが発足してから1年が経過し、本番で稼働している新BFFも増えてきており新BFFのアーキテクチャも徐々に成熟してきました。 Clean Architectureを採用したのは正しいかった?という問いがあるとすれば、 開発者が増えた数年後に答えがわかる という回答になると考えています。 バックエンド刷新プロジェクトのメンバーのアーキテクチャへの習熟度は高いですが、将来プロジェクト外のエンジニアが実装するようになった時にこそ目的が達成できたかわかるるためですね。 その時に正しかったと言えるように現在鋭意開発を行っています。 今後導入したいこと これまでに新BFFへと移植・リリースしてきたビジネスロジックはすべて参照系の処理でした。DBへのWrite処理が発生するような更新系処理の実装はありません。 そもそもLIFULL HOME'Sのほとんどの処理が参照系の処理であるためです。 単にソフトウェアエンジニアとしての興味もありますが、更新系の処理DDDにおける集約やトランザクションなどの観点でのプラクティスも確立していきたいところです。 例えば、現状はApplication層のみにあるData Access Interface(Repository)に対し、CQRS取り入れて参照系Data Access Interface(Query)と更新系Data Access Interface(Command)を作成することでDomainを洗練させていくといったアプローチがあるかもしれません。 実装効率を向上させるために Clean Architectureの特性およびGitリポジトリを分割したことが起因して、新BFF全体で見ると冗長な実装が増えてしまっている箇所もあります。 例えば 大量のData Transfer Objectを作成する必要がある Dependency Injectionなどの定型実装が頻発する 同じようなValue Objectを複数Gitリポジトリ作成する必要がある などが挙げられます。 前者2つはScaffolding Toolの作成・導入で解決できると想定しており、最後の1つはPackage(Github Packages)化することで解決できると想定しています。 しかし、どちらの解決策もある程度実装上のプラクティスが確立されることを前提としています。 Clean Architecture自体が抽象度の高いものでありそれゆえに実装の選択肢が多く、新BFFでの実装プラクティスも完全には確立できていません。 プロジェクト外のエンジニアの方々と円滑に共創していくためにも実装効率を向上させる取り組みは継続的に行っていきたいところです。 おわりに:銀の弾丸はない 開発効率の向上と健全性の確保を目指したバックエンド刷新プロジェクトはまだまだ進行中です。 Clean Architectureのわかりやすい解説や実装例を紹介した記事などの情報は存在しますが、実在するサービスで採用した事例や開発者の経験についてはあまり存在せず、投稿すれば有益な情報になるのではと思いこの記事を書かせていただきました。 銀の弾丸はない 、という言葉はアーキテクチャ選定にも当てはまると思います。そのため、この記事をそのまま流用できるようなケースはあまり存在しないと思います。 今回紹介させていただいた中から転用可能なエッセンスを抽出して、技術的負債の解消や何年も続くことを見越した新規サービスの技術選定の際に役立てていただけると幸いです。
はじめまして テクノロジー本部 基盤運用ユニット 基盤グループの久保田です。 より良いサービスを提供していくために必要なことは色々あり、また答えがあるものではないと思っていますが どういったアプローチを行うにせよ、それを検討していくためにはまずは 「自分たちが置かれている状況を把握すること」 が必要と考えています。 そこで以前、自社サービスであるLIFULL HOME'Sを検証のためサイト運営で利用しているツール以外で計測し可視化したときの話をしようと思います。 なにを使って計測するか Googleが提供しているLighthosueを使って計測することにしました。 理由としては SpeedIndex、SEO、Accessibilityが計測できる。 お金が掛からなければ良かった。 各メトリクスは内容やツールなど色々ご意見があると思いますが 今回は一定の基準で比較検討すること 複数のツールを使わなくても良いことからLighthouseを使うことにしました。 SpeedIndex:ページ内のコンテンツが視覚的に認識できるようになるまでの時間 SEO:Search Engine Optimization(検索エンジン最適化)の略称で、このスコアが高いとGoogleなどの検索エンジンでキーワードが検索された際、上位に表示されやすくなる。 Accssibility:誰でも使用することが出来るかという点をスコアとして表したもの なにを使って可視化するか DatadogにLighthouseで計測したメトリクスを収集し可視化することにしました。 理由としては 使ってみたかった 当時、社内でDatadogを使っていこうという流れがあった 技術的な理由などは特になかったです。 自分の必要と考えていたことは 「自分たちが置かれている状況を把握すること」 なのでこだわりはありませんでした。 なにを計測するか 自社サービスであるLIFULL HOME'S ついでにトップページや物件一覧ページなどをカテゴライズして今回の計測用のサイトマップを作成しました。 どう可視化されたのか このような感じでメトリクスごとに時系列でページ別に比較することが出来るようになりました。 カテゴライズした時の分類をメトリクス送信時に付与しダッシュボードで絞り込めることも出来ます。 HOME'S Quality Metrics Board グラフ内の赤帯について LIFULL HOME'Sに対しリリースが行われたタイミングをGithubから取得しグラフに表示するようにしています。 これでリリースによりLIFULL HOME'Sに対して変化が起きた際、気付くことが出来るようにしました。 可視化して得られたもの 自分たちのサービスがどの様な位置にいるのか確認することが出来た。 PDCAを回していくための道具を手に入れることが出来た。
こんにちは。Ltech運営チームの井上です。 今回は、2021年3月2日(火)に開催した『Ltech#14 「LIFULL HOME'S」のフロントエンドについて語り尽くします!』についてレポートします。 事前に共有させていただいていたウェビナーのURLに誤りがあり入室できないというトラブルもありましたが、参加者の方の温かいフォローもあり、最終的には120名を超える方にご参加いただき会は大盛況で閉会することができました。ご参加いただいた皆様本当にありがとうございました! lifull.connpass.com Ltechとは Ltech(エルテック)とは、LIFULLがお送りする、技術欲をFULLにするイベントです。特定の技術に偏らず、様々な技術の話を展開しています。 「LIFULL HOME'S」のフロントエンドについて語り尽くしました 今回の Ltech のテーマはLIFULL HOME'Sのフロントエンドについてです! 日本最大級の不動産・住宅情報サイト「LIFULL HOME'S(ライフル ホームズ)」の大部分は約10年前に開発を開始したアプリケーション上で構成されています。10年前から現在まで、周知の通り フロントエンド技術の進歩の勢いは速く、当初は素晴らしかった設計や技術スタックも今の技術情勢に照らし合わせるとレガシー化している現状がありました 。 そこで今回登壇したメンバーを中心に、開発を担当するフロントエンドエンジニアが、 数々の技術負債解消や、フロントエンドアーキテクチャの刷新、アクセシビリティ改善などの課題に対して、「いかに速く」「ユーザーに最高のユーザー体験をもたらすにはどうしたら良いか」という観点から行なった(そして、現在も行なっている)取り組みについて発表しました 。 新しい検索体験とデザインシステム 新しい検索体験とデザインシステム from LIFULL Co., Ltd. www.slideshare.net 最初は、 LIFULL HOME'S の新しい検索体験を提供するサービスの開発で採用したデザインシステムの話です 。 LIFULL HOME'S では昨年末にリリースした「 叶えたい条件から探す 」機能があります。 この新機能の開発にあたって、従来の巨大なアプリケーション基盤上に追加で開発するのではなく、Nuxt.js (TypeScript)で新規のアプリケーションを構築しました。この新サービスを開発する上で新規のデザインシステムを設計しています。 デザインシステムは非常にポピュラーである Atomic Design をベースに、デザイン仕様に合わせて少しカスタムした設計になっています。 基本的な Atomic Design のコンポーネント分類 カスタムした Atomic Design のコンポーネント分類 template は排除し、代わりに routes という分類を用意しています。 routes はページ依存の organisms であり、ビジネスロジック的制約が強く汎用化が難しいと判断したものがこの routes コンポーネントとして実装されています。この分類をしたことで、依存関係やデグレを意識せずに安心して開発できるようになりました。 また Atomic Design を採用したことのメリットは、 Storybook でのコンポーネントカタログの運用やコンポーネントの unit test の書きやすさがあることです 。初回実装だけではなく、追加改修時にコンポーネントの流用やテストを書くことでデグレの検知ができるため、これらのメリットをより実感しているとのことでした。 改善したいところとしては、 atoms / molecules / organisms の粒度の線引きが難しく、 molecules を小さく設計し過ぎたところ molecules / organisms を改修するときの利用先ごとの若干のデザインの差異の吸収 とのことでした。 発表者の海老澤はこの「叶えたい条件から探す」機能でのコンポーネント設計以外にも、 LIFULL HOME'Sの大部分を構成するアプリケーションの開発効率向上のために、Sass の Mixin や共通変数を制定したり、スタイルガイドを開発環境に立ち上げたりといった活動もしています 。過去に海老澤がこの件について執筆したブログ記事もありますのでぜひご覧ください。 www.lifull.blog LIFULL HOME'S におけるフロントエンド開発環境の刷新 LIFULL HOME'S における フロントエンド開発環境の刷新 from LIFULL Co., Ltd. www.slideshare.net 次は、冒頭に紹介した 約10年の歴史をもつメインのアプリケーション開発環境の刷新について の相馬からの発表です。 LIFULL HOME'SのほとんどはPHP(Symfony、Twig)の構成のアプリケーションで開発しています。 刷新前まではCSSとJSの bundle / minify はランタイムにPHPで実行しており、初回リクエスト時にはレスポンスの大幅遅延が避けられない致命的な問題を抱えていました 。 この問題をBabel、Rollupなどで事前のビルドプロセスを構築することで解決しました。これらについては相馬が執筆したブログ記事がありますので合わせてぜひご覧ください。 www.lifull.blog www.lifull.blog 刷新後の開発環境では、開発者がモダンな JavaScript でコードを書くことができるようになり、サポートブラウザに準じた Vendor Prefix が自動付与されるようになり、そして初回リクエストのランタイムでの bundle/minify 処理が無くなったことでユーザー体験の向上も図ることができました。 しかし、Rollup を導入したことで新たに見えてきた課題もありました。アプリケーションが巨大で、bundle 対象のファイルが多すぎる為、開発時の warmup に時間がかかりすぎてしまう問題です。(開発モードでビルドすると完了までに5分以上かかってしまう...) これについては ESBuild を使ったランタイムでの高速ビルドをするという方法で、現在改善を試みている最中ですので、詳細についてはリリース後に相馬が執筆する記事をお待ちください 。 ウェブアクセシビリティ推進活動はじめました ウェブアクセシビリティ推進活動はじめました from LIFULL Co., Ltd. www.slideshare.net LIFULL HOME'S だけに限らず、LIFULL におけるアクセシビリティ推進活動の旗振りをしている嶌田からの発表です。 サービスを開発していく中でビジネスやデザインの都合や開発者の知識不足などを理由に、アクセシビリティが十分ではない状態でリリースをしてしまうことが少なくありませんでした 。この由々しき現状変えるために数々の取り組みを行なっています。 まずは、 現状の社内のアクセシビリティへの取り組み状況の把握でした 。社内のナレッジベースを検索し、アクセシビリティに過去に関わっていた社員を調査し、ランチに誘って話を聞く+推進活動を行なうチームに誘うということを行いました。 次に、 推進活動を行なうチームを結成しました 。目標を別にするチームを2つ結成しました。 アクセシビリティの全社的な普及を目標にするウェブアクセシビリティ推進ワーキンググループ リリースフローの中に組み込むアクセシビリティのチェックリストの作成 アクセシビリティ観点でレビューし、該当サービス開発担当者に改善提案をする社内サービスを開始 主体的に普及活動までではないが、アクセシビリティについてゆるくキャッチアップしておきたいくらいの社員でも参加できるアクセシビリティ研究会 スクリーンリーダーの使い方ハンズオン開催 他社のアクセシビリティ・ガイドラインの輪読会 このように目標と活動内容が異なる2つのチームを用意したことで、推進活動に参加する心理的障壁を下げることにも成功しました。 これからの取り組みとしては、 アクセシビリティ・ガイドラインの整備と運用 アクセシビリティ専門職を創設 新卒研修などでアクセシビリティについて学ぶ時間をつくる など、数々の打ち手を並行して進めています。これらについても皆さんに後日ご報告できることがあると思います。 最後に、アクセシビリティ推進活動を通じて大切だと感じたことについても言及がありました。 書籍「FEARLESS CHANGE アイデアを組織に広めるための48のパターン」にアクセシビリティ推進活動が順調に進んでいる理由があった 情熱を持ち、絶やさない 社内に顔が利く人とつながる ひとりではなく、仲間を集める これらのことは、アクセシビリティ推進活動に限らず、新しい文化を組織に根付かせるためには必要なことですね。 LIFULL は「あらゆるLIFEを、FULLに。」というコーポレートメッセージを掲げています。あらゆる人々にアクセシブルな LIFULL HOME'S を目指してこれからも改善活動を続けていきます! 大きめレガシープロジェクトのフロント行く末 大きめレガシープロジェクトのフロント行く末 from LIFULL Co., Ltd. www.slideshare.net 最後は、 LIFULL HOME'S のフロントエンドの現状の課題と未来を見据えた技術選定について 中島からの発表でした。 HTML と JS の概念的距離の圧縮を主軸に、React / Vue のような Virtual DOM を取り扱うフレームワークにリプレイスしていくのではなく、Stimulus を採用した設計にシフトしていく構想 について語りました。 当日の発表資料に加えて、弊社クリエイターズブログにて関連記事を執筆していますので合わせてご覧ください。LIFULL HOME'S のフロントエンドの歩んでいく方向が見える記事になっています。 www.lifull.blog どんなに素晴らしい設計もイケてる技術も時間が経てば過去のものになってしまいます。より少ないコードでより多くの素晴らしい体験を届けられるエンジニアリングをしていきたいと強く決意させられる発表でした。 最後に 今回のLtech#14では、LIFULL HOME'Sを支えるフロントエンドエンジニアの取り組みについてフォーカスを当てました。 Ltechでは、LIFULLエンジニアが中心となって皆様の技術欲を満たすよう実例を交えた勉強会を開催しています。今後も Ltech を積極的に開催していきますので、ぜひ気になった方は、connpass で LIFULL のメンバー登録をよろしくお願いします! lifull.connpass.com
LIFULLの中島です。 近頃、LIFULL HOME'Sのフロントエンド(ここではJavaScriptのみを焦点とします)もようやく進む道を見出し、そろそろ設計方針を一新しようと試みています。 今回はそれについて話したいと思います。 現在の私たちの課題感 私たちの管理する多くのレガシーコードはDOM操作ライブラリとしてjQueryを、UI設計の格子としてBackbone.Viewのような設計方式を導入しています。 (もちろんそうでないマイクロサービスも多くありますが) 具体的なコード例を示すことこんな感じになります let Slider = Backbone.View( { events: { '.next click' : 'next' , '.prev click' : 'prev' } , next() { this .$(...).css( { left: '111px' } ); } , ... } ); let photoSlider = new Slider( { el: '#photo_slider1' } ); View間の連携を取りたい時はBackbone.Eventsをglobalに放出するpubsub実装パターン( like USA today )のようなものを用意し、コミュニケーションをとるように実装しています。 ファイル分割の単位が明確化され、またUIの振る舞いが統一的に規格化(@events)され、コードの追い易さは野良コードに比べると随分マシな状態になりました。 しかしながら、10年も運用していると(実際のところもっと早くから苦しんでいるが)、これらのコードに存在する、いくつかの腐りうる隙が目につくようになります。 DOM探索の害悪 数年コードを運用してわかることはDOM探索という行為は運用上、基本的にはコードを汚くする主要因であるということです。 セレクタでの要素探索は壊れやすく、探索した要素はNodeListやHTMLCollectionといった紛らわしい要素となり、それらのnormalizeに我々はまたコードを一つ書かなくてはなりません。 またせっかくView単位でファイル分割しても親ViewはDOM探索によって簡単に子Viewの要素にアクセスできてしまい(その逆もしかり)、Viewの境界線が曖昧になってしまいます。 その結果我々のコードは子Viewの責務を兼ねた再利用のきかない大きな親Viewが多く誕生し、その再利用性の低さから、「非常によく似た、しかし少し違う」コードが増え、無駄にプロジェクトを肥大化させることになりました。 これはbundlerやtranspilerのビルド時間を無駄に長引かせ、結果として開発効率を大きく落とす結果となります。 動的に追加されるコンテンツに対する振る舞いのアタッチ 動的(XHR等による)に追加されるコンテンツに振る舞いをアタッチする際に、そのDOMをelとしてViewをインスタンス化せねばなりません。 これはViewのインスタンス化を一元管理することが困難になることを意味します。 コードの統一性や、そのインスタンスがどのように扱われるかに注意を払わねばいけなくなるのはリードコストを増大させ、これまた開発効率を落とすことにつながりました。 グローバルイベント(pubsub)に依存した実装 グローバルイベントの採用は一定の成功を収めましたが、Viewの唯一のコミュニケーション手段としてしまったのは失敗でした。 左右間のViewの連携においては、グローバルイベントを用いたコミュニケーションは効果的ですが、親子間のコミュニケーションにはしばしば課題を伴います。 A1- |-B1 |-B2 |-B3 A2- |-B4 |-B5 |-B6 B2が自身の親のA1にだけ情報を伝えたい時、グローバルイベントをなげてしまっては、A2を除きA1だけが呼応するという実装を伴う必要がでてくるからです。 素直にCustomEventを投げ、バブルアップさせるべきでした。 結果としてこれもコード量を無意味に増やすことにつながりました。 見えてきた次の設計に必要な観点 これまでの反省を踏まえると次の設計では以下の点にこだわる必要があります。 DOM"探索"の排除 DOMの振る舞いの自動アタッチ (Bubble up eventで)親子間でコミュニケーションがとれる 3つ目は普通のことであるとして、前者の二つを兼ね備えるものはあるのでしょうか かのDHHはこの観点を「HTMLとJavaScriptの概念的距離」と表現しており、圧縮すべきだと主張しています。 HTMLとJavaScriptが離れたところにあるがゆえに探索は必要であり、探索した要素にイベントを自主的に割り当てる必要があると言えます。 もしHTMLとJavaScriptの境界がもっとぼやけていて、JavaScriptからDOMに変数やプロパティへのアクセスのようにアクセスでき、HTMLからJavaScriptの振る舞いを呼び出せればこの辺の複雑性はなくなると言えます。 モダンライブラリにおける「HTMLとJavaScriptの概念的距離」 あまりこういう言い方で流行りのライブラリを表現することはありませんが、かなりのシェアを集めているReact/Vueもこの概念的距離の圧縮によって成功を収めているライブラリに見えます。 new Vue( { el: '#app' , template: ` <button type= "button" @click= "notify" >click me</button> `, methods: { notify() { alert ( 'click button!' ); } } } ) このコードはDOM(button)に振る舞いを与えるものですがDOM探索は行われていません。 VueやReactはJavaScript側でHTMLを生成することで探索という工程を排することに成功しています。 もちろん他にもテンプレート側からみて振る舞いが宣言的であったり、データバインディング機構があったりと魅力的な点は多くありますが、私の観点ではここがもっとも重要に思います。 これらのモダンライブラリを採用するのか? 答えはNoです。 もちろん、新規でマイクロサービスを作ったり、もつべき状態がすこぶる多いのであれば採用を考えたかもしれません。(事実そういうマイクロサービスもあります) しかしながらLIFULL HOME'S本体のサイトはいくつか様子が違います。 多少の検索UIが状態を持つとはいえ、基本的には物件情報を取り扱うドキュメントサイトです。 そこでは振る舞いよりも文章の重要度が高く、且つ、これまで積み上げてきたSEO地位に対してのリスクは非常にシビアに評価する必要があります。 これらのモダンライブラリはJavaScript側からHTMLを生成することにより概念的距離を縮めたが、それゆえにHTMLの生成がJavaScriptによって"後から"生成されることとなり、クローラビリティやプログレッシブエンハンスメントの観点においていくつかの懸念を残します。 SSRも元々そこに存在しなかった問題に対する対処であり、害虫を狩るために猛獣を飼いならす必要がある状況のように感じます。 設定やビルドといった新しい複雑性を極力伴わず、HTMLはそこにあり、その上でHTMLとJavaScriptの概念的距離を圧縮するアプローチこそが我々の望んでいるものなのです。 採用した概念圧縮の方法 我々はBasecamp製の Stimulus に命を預けることにしました。 (もしかすると1年後にはそれとturboを組み合わせたhotwireに命を預けると言ってるかもしれません) Stimulusはなんなのか これはRails7にデフォルトで導入されるHotwireに組み込まれているライブラリなのでそのうち大きく広まるかもしれません。 React/Vue同様にHTMLとJavaScriptの概念的距離の圧縮に成功したライブラリと言えますが、大きく違う点はそれ自身がHTMLを生成しないところにあります。 MutationObserver でDOMの変更を監視し、変更されたDOMに振る舞いが必要であることがわかれば自動的に必要な振る舞いをアタッチするように動きます。 対象となるDOMにどのような振る舞いが必要か、その要素内のどの要素に参照が必要か、それをDOM自身に記述することでアタッチやDOMの参照を自動化するのです。 しかもビルドレスでなんならCDNをimportするだけで動きます。 テンプレートレイヤを侵すことはないため、既存のコードを式年遷宮する必要もありません。 具体的にコードを書きましょう。 ` <div data-controller= "counter" data-counter-num-value= "0" > <p data-counter-target= "view" >0</p> <button type= "button" data-action= "click->counter#increment" >+1</button> <button type= "button" data-action= "click->counter#decrement" >-1</button> </div> ` import { Application, Controller } from 'https://cdn.skypack.dev/stimulus' ; let app = Application.start(); // MutationObserverでDOM全体の監視を始める app.register( 'counter' , class extends Controller { static targets = [ 'view' ] ; static values = { num: Number } ; increment() { this .numValue++; } decrement() { this .numValue--; } numValueChanged() { this .viewTarget.textContent = this .numValue; } } ) codepen HTMLに注目してみましょう。 Stimulusはいくつかのdata属性を利用して動きます。 data-controllerが属性が付与された要素がmutation observerに引っかかれば、即時にその名前でregisterされたcontrollerをその要素に対して適応します。 data-action属性が付与された対象の要素で発生するイベントに対し、値部分に書かれた振る舞いが自動でアタッチされます。 さらにdata-[controller]-target属性が付与された要素がJavaScript側からthis.[controller]Targetとしてアクセスすることができるようになっています。(これは実際にはstimulusによってstatic targetsを元に自動で作られるgetter関数です) (残るdata-[controller]-xxx-value="{value}"はstimulus2で実装された、データ変更コールバックを伴う状態管理機能です) これらの機構によりDOM参照はプロパティアクセスで実現され、振る舞いのアタッチは自動でされる世界線が実現することになります。 HTMLに振る舞いと状態を記述しておけば勝手に振る舞いがアタッチされるので、XHR等で動的に追加されるHTMLも、ただ挿入するだけでよくなります。 我々は常にサーバからはHTMLを返せばいいのです。 それはとてもシンプルに感じます、 HTMLの組み立てロジックをサーバサイドに集約できるのはレガシーシステム観点で考えるととても痛みのない方法です。 もしSPAを実現するとなった時、Turbolinksはもう一度息を吹き返すかもしれません。 おそらくBasecampはそういう意図でTurbolinksに投資を続け、この度 turbo をリリースしたのでしょう 必然的にクライアントサイドで大きなデータ操作をするシーンは減ることになり、型やスキーマの必然性が下がることになります。 (これはもしかすると将来ビルドレスを推し進めた先のtranspilerと決別するシーンで役に立つかもしれません) さらにStimulusは興味深いことにコントローラを一つの要素に複数つけることを許容し、各コントローラを単一責任にせよと示すのです。 よくある機能を単一責任なcontrollerとして切り出す時の例をあげていきましょう disclosure disclosure(パカパカ)の機能 適切なaria-expand付与等 removal 要素削除機能 modal dialog roleやlabel系ariaの設定等 content-loader contentのxhrロード機能 combobox サジェストの機能 候補の表示と適切なrole,labelの設定など xhr-form form条件を元にxhrしコンテンツを部分リフレッシュする etc... 弊社のようなドキュメントメインのサイトだと、この辺が用意されていて再利用性が高いコントローラとして設計されていればわざわざページごとのロジックを精査する必要もないのかもしれません。 ただパズルのようにHTMLにcontrollerやactionを付与していけばそれだけでサイトの振る舞いが完成するのは私たちの次の理想です。 我々はStimulusを導入し、プリミティブなコントローラを揃え、コード量を1/20にすることを次の目標にしていきたいと思っています。
LIFULLでのアジャイル開発について LIFULLのプロダクトエンジニアリング部の野澤です。エンジニアリングマネージャーをやっています。LIFULLには2017年に中途入社しましたが、以前からアジャイル開発に興味があり、昨年スクラムマスターの資格を取得しました。LIFULLでもアジャイル開発がだいぶ普及してきていますが、本日はそんなLIFULLでのアジャイル開発について書きたいと思います。 LIFULL HOME'Sでのアジャイル開発 私自身、社内でのアジャイル開発の普及活動をしているのですが、その活動の一環として昨年の8月にLIFULL HOME'Sの開発に携わる部署に社内アンケートを実施してみました。 それによると、 LIFULL HOME'Sの開発に関わるエンジニア・デザイナー・サービス企画のスタッフのうち 7割がアジャイル経験者 半分のチームでアジャイル開発手法が採用されている アジャイル開発を採用した理由として、「生産性が上がるから」と答えた人は約7割。「チームの結束力やモチベーションが向上するから」と答えた人は6割 ということが分かりました。LIFULL HOME'S事業においてはアジャイル開発手法はかなりスタンダードになっていることが分かります。 アジャイル経験者である「1回以上アジャイル開発のPJに従事したことがある」、「1年以上アジャイル開発の経験がある」、「3年以上アジャイル開発の経験がある」を合計すると約7割になる 約半分のチームがアジャイルを採用しています さらに細かく見ていくと以下のようなことも分かりました。 ウォーターフォールを採用しているチームも含めて 全チームの8割で朝会や振り返り会が実施されている アジャイル開発が主体のチームでもガントチャートも併用しているケースがある ウォーターフォールを採用しているチームでもアジャイルのプラクティスを採用しているケースがある このようなことからも、開発手法はあくまで手段であり、目的やプロジェクトの特性に応じて開発手法を柔軟に組み合わせて使っているのが特徴と言えそうです。 教え合う文化 LIFULLでは 「利他主義」 を社是としており、お互いを助け合うことで、自分だけでなく周囲の人たちみんながHappyになっていくことを大事にする考え方が深く浸透しています。 それを体現している一つの取り組みが「LIFULL大学」です。LIFULL大学は個人のスキルアップ・キャリアアップのために、誰でもある分野の専門家として講義を開くことができ、社員の誰もが自由に参加できるしくみです。LIFULL大学で開講される講義は「ゼミ」と呼ばれ、ゼミには一定の教育研修予算が割り当てられています(2021年2月現在)。 私も長年アジャイル開発に取り組み、スクラムマスターの資格も取ったということで、「アジャイルゼミ」を開講しました。約4日間に渡ってアジャイル開発についての講義やワークショップ、社内メンバーによるパネルディスカッションを行いました。参加者は延べ154名で、参加者の9割の方に満足していただけました。 以前にもこうした勉強会は各所で実施されていて、従業員が自由に勉強会を開催・参加できるようなカルチャーがLIFULLにはあります。 「バッチサイズが大きいと何がいけないのか」など、アジャイル開発の基本的な概念についてワークショップも交えながら考えてもらいました サークル活動 また、LIFULLでは今年からサークル制度が導入されました。コロナ禍によってface to faceでのコミュニケーション機会が減っていくなか、今まで以上に社員どうしの交流を深めるのが目的で、やはり一定の予算が割り当てられています。スポーツや趣味のサークルもあれば、仕事に役立つようなことをテーマにしているサークルもあり、今では約70ものサークルが存在しています。 以前からLIFULLにはアジャイル開発に関わるコミュニティがありましたが、これを機にサークルになりました。私も参加させてもらっているのですが、月に一回、オンライン飲み会を開催して、LIFULLのいろんな部署のメンバーが集まってそれぞれのチームの悩みや成功事例を共有しています。またスクラムやアジャイルに関連するイベントがあったら参加者から情報を共有してもらったり、アジャイルに関する本が出るとその本について議論したりしています。 このようにLIFULLでは部署間の垣根が低く、業務以外で他部署の社員と交流する機会が頻繁にあることがLIFULLの魅力の一つかもしれません。 今後とまとめ このようにLIFULL HOME'Sでは時代や目的に合わせて開発手法を柔軟に取り入れて開発を進めています。最近ではウェブ業界でも「プロダクトマネジメント」が注目されていますが、社内でもプロダクトマネジメントについて議論する機会が増えてきました(詳細は弊社の花多山の 記事 をご覧ください)。 また大規模スクラムに挑戦しようとしている部署があったり、社内の開発プロセスのノウハウを共有する自主的な活動があったり、 開発生産性や技術的負債を可視化し、改善していくプロジェクト があったりと、各所で貪欲に業務改善が行われています。 昨年、 スクラムガイドがアップデート され、アジャイル開発宣言も 20周年 を迎えました。 アジャイル開発はまだまだ発展していくと思います。LIFULLでもより良いプロダクト開発をして、より多くの人々が心からの安心と喜びを得られるようにするためにも、自分たちなりに開発プロセスを改善させていければと思います。 アジャイル開発に挑戦したいと感じた方、ぜひLIFULLで一緒に働いてみませんか? hrmos.co hrmos.co
どうも エンジニアの「市場価値」を向上する をキーワードに活動している @サム です。今回は LIFULL HOME'S におけるLINEを活用した施策「 LINEで新着物件通知を受け取る 」を紹介したいと思います。 なぜやるのか 不動産は年末から3月末にかけて住み替えシーズンのため、毎日のように新しい物件が LIFULL HOME'S に公開され、選べる物件の数も増えています。 この時期は住まいを探す人も増え、この記事を読んでくれているあなたも同じ経験があったのではないでしょうか。 物件を探す方法はいくつもありますが、基本はエンドユーザが自ら LIFULL HOME'S などの検索サービスを利用する、いわゆる「能動的」なものになります。 住まいに希望する条件は変わらなくても、検索しないと新しい物件に関する情報は手に入りません。 そこで LIFULL HOME'S では新着物件をお届けする「 新着物件お知らせメール 」という機能があります。しかしこの機能を使うにはメールアドレスの登録が必要になるため、Webサイトでの登録が必要だったりといくつかの手順をおこなう必要があります。 とても便利な機能ですが、利用するまでに必要な煩わしさやメールアドレスを登録しなければならない敷居の高さがネックとなっております。 そこで LINEを活用することで次の利点があります 。 簡単に登録できる 使い慣れたLINEを利用できる スマートフォンに最適化されたデザイン LINEで新着物件通知を受け取るとは その名前のとおり、 毎日決まった時間に希望に沿った物件をLINEでお知らせしてくれる機能 になります。 LIFULL HOME'SのWebサイトを使って物件を探した際に、希望に沿った物件がその日に見つからなかったとき、次の考えに繋がることが多いと思います。 諦める 希望の条件を緩めて改めて検索する せっかく理想とする暮らしに沿った検索条件なのに、その日に 諦めてしまったり条件を緩和すると満足度は下がって しまいます。 しかしいつ理想とする物件が見つかるか分からないのに探し続けるのはそれなりの労力がかかります。 そこで本機能である「LINEで新着物件通知を受け取る」を使うことで、あなたの 希望する条件にマッチする物件が見つかれば「自動的にLINEに通知してくれる」 大変便利な機能になっております。 登録方法 1. 物件一覧ページの「LINEで受取るボタン」をタップ! 2. LINEで「LIFULL HOME'S」のアカウントを友だち追加! 3. トーク画面に通知が届いたら登録完了! どのような仕組みになっているのか この機能を実現するためにいくつかのアプリケーションを横断しています。 この機能の中心にあるのが「 EOS ( Elastic Omni-channel Service )」と呼んでいるオムニチャネル用のAPIアプリケーションです。 EOSはオムニチャネル戦略を実現するために作られたアプリケーション で、今回はLINEの機能にフォーカスしていますが、いずれはメールやSMS、CRMツールとの連携など、様々なサービスに成長していくものです。 LINEで新着物件通知を受け取るは、エンドユーザのお気に入りの検索条件を圧縮文字列にすることで、複雑になりがちな検索条件を共通で使えるように設計されました。 この設計にすることで、ログインが不要になり住み替えシーズンに間に合うようサービス提供するまでのコストも削減できました。 最後に 今後も LIFULL HOME'S はLINEを活用し、エンドユーザにより便利な機能をお届けしていきます。 もちろんオムニチャネル戦略としてLINEだけではなくSMSやメールなど様々なチャネルを利用し、エンドユーザ1人1人に沿ったサービスを開発していきます。 サービスを開発することでエンドユーザが理想の住まいに出会えるよう手助けできればと思います。 告知 LIFULL では Ltech という LIFULLエンジニアが中心となってエンジニアの技術欲を満たすような実例を交えた勉強会 を開催しています。 コロナ禍でもZOOMを使ったオンラインで開催するなど、今後もLtechを積極的に開催していきますのでぜひ気になった方は、connpassでメンバー登録をよろしくお願いします! lifull.connpass.com twitter.com
プロダクトエンジニアリング部の佐藤です。 今回はLIFULLの開発において実際に使われている技術スタックの一例としてLIFULL HOME’S 引越し手続きを紹介いたします。 LIFULL HOME'S 引越し手続きとは Nuxt.js TypeScript Context Nuxt Community 認証 Nuxt 3に向けて まとめ LIFULL HOME'S 引越し手続きとは 住み替えの際、各事業者(電気・ガス・水道)の住所変更手続きを一括で申請できるサービスです。 主なシステム構成はこちらです。 Nuxt.js GAE(Google App Engine) Firebase 今回はNuxt.jsについてどのように活用しているか見ていきます。 Nuxt.js LIFULLでは新規でのフロントエンドの開発においてNuxt.js(Vue)での開発事例が増えてきています。 serverMiddlwareプロパティにおいて各APIのBFFとしてexpressを使うこともあります。 nuxtjs.org serverMiddleware プロパティ - NuxtJS TypeScript TypeScriptを採用しています。 yarn dev での開発時に素早くフィードバックを受けられて便利です。 www.typescriptlang.org Vue 3以前のプロジェクトではクラススタイルでNuxt Property Decoratorを使い、TypeScriptでVueファイルやVuexを記述しています。 https://github.com/nuxt-community/nuxt-property-decorator Context Nuxt.jsのContextではこちらの図を参照することが多いです。 ja.nuxtjs.org Contextからstoreやredirectなどの便利なメソッドやパラメータを利用できます。 コンテキスト - NuxtJS Nuxt Community Nuxt Communityのpackageを積極的に使い、エコシステムの恩恵を受けています。 github.com 主に以下のpackageを採用しています。GTMやサイトマップなどの車輪の再発明をなくし、機能の作り込みに集中できると実感しています。 @nuxtjs/device @nuxtjs/gtm @nuxtjs/sitemap @nuxtjs/pwa 最新のNuxt.jsを使い始めれば、@nuxt/componentsや@nuxtjs/dotenvなども取り込まれており、Nuxt.jsのDX(Developer Experience)が良くなり続けていることを実感できます。 認証 認証周りでは以下のようなnpm packageを活用しています。 express-session openid-client passport 関連してOAuth 2.0やOpenID Connectについて知る機会もあります。 https://tools.ietf.org/html/rfc6749 https://openid.net/connect/ Cookieの管理ではSameSite属性を意識しています。 HTTP Cookie の使用 - HTTP | MDN Nuxt 3に向けて これからの開発では @nuxtjs/composition-apiを使用してvue 3のcomposition-apiでの開発を進めています。 github.com Composition APIのドキュメントを参照しながら、新しい記法にも慣れていきたいですね。 Composition API | Vue.js まとめ 今回は実際にWebサービスを開発していて、どんな技術を意識しているのか見てきました。 LIFULLのWebエンジニアは日々技術のキャッチアップを行い、UXやDXの高いアプリケーションを開発することで、エンドユーザーや開発者などすべてのステークホルダーに取って良いWebアプリケーションの構築を進めています。 カジュアル面談もやっていますので、プロダクトエンジニアリング部のビジョンである「 強い個人・最高のチームになることで、価値創造を加速させ続ける 」や、LIFULLが目指している「 あらゆるLIFEを、FULLに。 」に興味がある方は是非お話しましょう! hrmos.co
輪読会のテーマと題材 なぜ輪読会か 学習効率を高める工夫 3つのパート 1枚プレゼンテーション 振り返り(気付きと疑問点) 簡単なクイズ まとめ iOSアプリ開発チームの池田です。 iOSアプリチームでは週1回1時間という時間をとって定期的に輪読会を開催しています。こちらの輪読会の内容と、学びの効率を高めるために工夫していることについてご紹介できればと思います。 輪読会のテーマと題材 今回のチーム内での輪読会のテーマは「通信関連の基礎固めと一貫した知識の習得」です。 iOSアプリ開発の施策を進めている中でネイティブアプリのフロント側での開発が多くなっており、インフラ関連の知識に触れる機会が実務でかなり少なくなっていました。 こういった機会が少ないことでメンバーの中でのインフラ関連の知識について、なんとなく理解しているけど固まっていない、知識が断片化しているといった状況が生まれていました。 この状況を改善するためにインフラ関連の知識、その中でも土台となる通信関連の知識をつけようということで上記のテーマが採用されています。 また、このテーマに合う題材として「ネットワークはなぜ繋がるのか 第2版」を選んでいます。「ネットワークはなぜ繋がるのか 第2版」では通信に関して、ハードウェアの部分から、ソフトウェアの部分まで幅広く扱っており、基礎固め、一貫した知識の習得に合う題材です。 ネットワークはなぜつながるのか 第2版 知っておきたいTCP/IP、LAN、光ファイバの基礎知識 作者: 戸根 勤 発売日: 2007/04/12 メディア: 単行本(ソフトカバー) なぜ輪読会か 基礎固め、一貫した知識の習得という目的を考えると本を利用した学習が最適だと考えチーム内で話し合った結果、輪読会形式での開催を決定しています。 何かを題材にして実際に手を動かす、本以外の題材での学習なども検討していますが、実際に手を動かすことは動くものができることでなんとなく理解してしまうことが懸念され、本以外での学習は情報の断片化が懸念されます。 そういった懸念がなく、本を利用した輪読会形式がテーマには合っており採用になっています。 学習効率を高める工夫 学習効率を高める工夫として、復習を短サイクルで多くできるようにしています。その他としてただインプットするだけでなくアウトプットに繋げる、制約を作ることによって練度を高めるといったことを取り入れています。 それらを実現するために3つのパートで輪読会を構成しています。これらの中身について説明していきます。 3つのパート 1回の輪読会を構成している3つのパートは以下になっています。 1枚プレゼンテーション 振り返り(気付きと疑問点) 簡単なクイズ 1枚プレゼンテーション 1枚プレゼンテーションでは個々人で輪読会開催日までに対象になっている章を読んできて、その内容を何かしらの形式でA4用紙1枚にまとめてきます。 まとめ方は個々人の自由になっており、図示や手書きでまとめるメンバーもいればテキストベースで要点をまとめるメンバーもいます。 こうしてまとめたサマリを各々輪読会開催日にプレゼンします。 1枚のサマリにまとめることでただ読むだけではなく、考えてアウトプットすることに繋がります。また、制約として1枚にコンパクトにまとめなければいけないことで、何度も考え要点を絞る必要が出てくるため、自分の中での復習と情報の整理に繋がり、練度が上がってきます。 更にそれをプレゼンする必要があるので、人に説明できる形にするという部分でも練度が上がっていきます。 チーム内には3人のメンバーがおり三者三様のプレゼンをするため、自分がまとめている情報からの差異も生まれるためプレゼンを聞くことも復習に繋がります。 振り返り(気付きと疑問点) 振り返りの中では、各々がサマリをまとめる中で感じた気付きや疑問点の抽出を行いこれも輪読会内で発表します。 この振り返りの中では、断片化していた知識が繋がったことに対する気付きや、理解しきれなかった疑問点などが発表されます。 理解しきれなかった疑問点については会の中で議論され、疑問解消と腹落ちに繋がります。 気付きや疑問点を持ったメンバー以外も、自分になかった視点で改めて学んだ情報を見ることになるので、より深い理解に繋がります。 簡単なクイズ 簡単なクイズパートでは、前回の輪読会で学んだ範囲について独断と偏見による復習クイズが出題されます。 ここでは単純な復習もあれば、本の中身の情報だけではないより発展的な部分まで触れたものについても出題されます。 例えばDNSの説明がされている章では、「AWSが提供しているDNSサービスはなんでしょう?」のようなクイズです。 本の中ではクラウドサービスの情報は触れられていないため、本の内容から広げて知識習得に繋がるようこういったクイズ出題をしています。 このパートでは1週間で抜けかけている情報を再度復習することによる知識の血肉化、知識をただ得るだけではなくそこから更に広げるという部分に繋がってきます。 まとめ iOSアプリ開発チームで進めている輪読会で採用している学習効率を高める工夫、それを実践するパートについて紹介させて頂きました。 クイズパートをやる中で、パッと出てこないけどこんな話あったなという内容があったり、覚えている内容が本来の回答と微妙にずれていたり、1週間という期間でも復習が大切なことが痛感されました。 今後の輪読会やその他スキル向上に繋がる施策の中でも復習をうまく重ねられるような仕組みを取り入れながら、同じ時間の中でも学習効率を高められるように工夫して進めていければと思います。 輪読会や、スキル強化に繋がる取り組みをされる中で何かの参考になれば幸いです。
こんにちは。エンジニアの加藤です。 普段はLIFULL HOME'Sの注文住宅領域にてエンジニアグループのマネジメントを担当しております。 マネジメントに携わり3年目となりますが、エンジニア組織の成果を定量的に測る難しさを常に感じておりました。 そのような中、今期より全社的にKPIマネジメントが導入され、その考え方を元に自身の担当するエンジニアグループとして目指すべき指標が明確化されたため、今回はその内容を紹介したいと思います。 KPIマネジメントについて 弊社では中尾隆一郎さんが提唱するKPIマネジメント( 最高の結果を出すKPIマネジメント )を取り入れており、そちらには以下の4つのメインキャラクタ( 4MC )が定義されています。 Goal 最終的に目指すべき状態。 KGI Key Goal Indicator = 重要目標達成指標 最終的に期末に到達したい最も重要な目標数値。 CSF Critical Success Factor = 重要成功要因 最重要プロセス = 事業成功の鍵。 KGIを達成するためにいくつかあるプロセスの中から、最も重要なプロセス。 KPI Key Performance Indicator = 重要業績評価指標 KGIの先行指標であり、CSFの数値目標。 最も重要なプロセスであるCSFをどの程度実施すれば、期末にKGIを達成できるかを表した数値。 KPIマネジメントでは上記4MCの関係性を理解したうえで適切に設定し、運用・計測・改善することが重要となります。 グループ目標としての4MC KPIマネジメントを構成する4MCに対し、私たちのグループでは以下のように設定致しました。 Goal 開発生産性の向上 KGI 開発生産力 = 価値創出数(= Pull Requestマージ数) / 開発に関わるコスト(= 人件費 + 業務委託費 + システム利用料) CSF 1タスクにかかる開発速度の向上 KPI Pull Requestマージ数 これらの4MCを選定したプロセスを紹介していきたいと思います。 Goal・KGIの選定 まずGoalとKGIについてですが、弊社では各組織単位においてそれぞれ4MCを設定し、KPIマネジメントの運用を行っております。 そのため、私がマネジメントするグループの上記組織であるユニット・部においても同様に4MCの設定がなされております。 そこで、ユニット・部を含めた組織全体として同じ方向性を持ち意識を揃える意図として、最終的な目標であるGoalとKGIは上位組織を踏襲することと致しました。 ただし、目標を達成する上での課題や状況、最適なプロセスは組織ごとに異なると考えられるため、Goalに対しての手段(CSF、KPI)はグループとして検討を行います。 CSFの選定 中尾さんの提唱するKPIマネジメントではCSFを選定する上でのポイントは以下のように言われております。 現場でコントロールでき、努力で変化するプロセス(季節要因、外的要因などは排除) 実行していればGoalに繋がるプロセス 複数候補の中から一番弱いプロセス これらを踏まえCSFを選定するにあたり、まずはKGIを構成する要素を分解し以下の2つをCSFの候補として比較致しました。 価値創出数 開発に関わるコスト 開発に関わるコストは人件費やシステム利用料などであり、ある程度固定費としてかかるため、価値創出数に対し現場の努力により変動する幅の狭い要素であると判断しました。 そのため、この時点で開発に関わるコストはCSFの候補から除外し、 価値創出数を増加 させるプロセスを深堀りすることと致しました。 KGIの要素である価値創出数はPull Requestマージ数に置き換えているため、Pull Requestマージ数の増減に影響する要素を以下のように分解しました。 実開発時間(= 総業務時間 - 運用時間 - ミーティング時間 - 社内行事時間) / 1タスクあたりの開発時間 * 1タスクあたりのPull Request数 これらの要素に対し分析を行い、最終的なCSFの決定を行います。 総業務時間 総業務時間を増加することで価値創出数が増加 各メンバーの業務時間を単純に増やすことで総業務時間を増加できる ただし業務時間の増加 = 残業時間の増加となるため、 本質的でなくCSFには適さない 運用時間 運用時間を削減することで価値創出数が増加 運用業務の削減、効率化、自動化することで運用時間の短縮を図ることができる 体感として運用業務が発生することで意識が分散し、実時間以上に生産性の妨げになっている可能性がある しかし、日次採算システム上、全体に占める割合は約5% 改善すべきポイントではあるが、 最重要プロセスとはいい難い ミーティング時間 ミーティング時間(以降MTG)を削減することで価値創出数が増加 不要なMTGの廃止、必要なMTGのみへの参加、MTGの効率化などにより削減可能 ただし日次採算システム上、全体に占めるMTGの割合は13%ほど 在宅勤務により必要なコミュニケーションも存在するとの考え方もあるため、 一番弱いプロセスとしては考えづらい 社内行事時間 社内行事時間を削減することで価値創出数が増加 社内行事時間は全社的に設けられているイベントに対しての時間のためグループとしてコントロールしづらい そのため極めて定数に近い要素となるため、 CSFには適さない 1タスクあたりの開発時間 1タスクあたりの開発時間を削減することで価値創出数が増加 開発タスクに着手してから完了となるまでのリードタイムを削減する 実行するためには開発プロセスの改善やシステム基盤の整備(リファクタ、リプレイス、仕組み化、標準化など)など様々なアプローチが考えられる エンジニアに裁量があり、主体となって実行する部分であるためコントロールがしやすい 現状システムの複雑性や技術的負債が開発生産性を妨げている大きな要因となっている これらにより考えられるアプローチを実行したうえで開発時間を短縮することが、 最も重要なプロセスであると考える 1タスクあたりのPull Request数 1タスクあたりのPull Request数を増加することで価値創出数が増加 レビュアーの1回のレビューにかかる負担を減らすなど開発生産性の向上に対しての一定の効果が表れる可能性がある ただ1タスクあたりのPull Request数を上げることだけを最重要プロセスとして捉えると、小手先での調整でも可能となり、本質的な取り組みから離れてしまう懸念がある そのため、ここでは CSFの対象としては除外する 上記より、最終的に私たちのグループでは「 1タスクにかかる開発速度の向上 」が最重要プロセスであると捉え、CSFとして選定しました。 また、弊社では以前より日次採算システムを全社的に導入しており、一人ひとりが各業務ごとにどれだけ時間を費やしているかを計測しております。 体感では運用業務に多くの時間を費やしている印象もありましたが、実際に計測数値を確認してみると割合としては少ないと分かったことから、事実を元に分析をする上でKPIマネジメントと日次採算システムの組み合わせの有効性の高さを感じました。 KPIの選定 CSF同様、KPIにも選定する上でのポイントがあり、以下のように言われております。 シンプルで分かりやすい指標 CSFとの整合性が取れている指標 現場でコントロール可能な指標 即座に計測・入手できる指標 CSFは1タスクにかかる開発速度の向上であるため、それを計測する指標としては1タスクあたりの開発時間がKPIとして考えられます。 しかし、現状では各開発タスク毎にどれだけの時間がかかっているかを正確に計測する仕組みがなく、「即座に計測・入手できる指標」であるという部分にマッチしません。 また、計測に際しての運用も複雑になる懸念があるため、現状KPIとしては適さないと判断し、別の指標を検討致しました。 そこで、1タスクにかかる開発速度の向上を計測する指標を選定するため、改めて価値創出数を算出する要素を確認します。 価値創出数(Pull Requestマージ数) = 総業務時間 / 1タスクあたりの開発時間 * 1タスクあたりのPull Request数 ここで各要素に着目し、それぞれ定数と変数に分類しました。( 青:定数 、 赤:変数 ) 定数と変数に分類したことにより、1タスクあたりの開発時間の変化に比例して、Pull Requestマージ数が増加すると捉えることができると思います。 そのため、Pull Requestマージ数がKPI候補となり得るのではないかと考え、KPI選定のポイントと照らし合わせても以下の理由で最適であると考えました。 シンプルな値で分かりやすい 上記の式によりCSFとの整合性が取れている (開発時間の短縮を)現場の努力で改善可能 ダッシュボードより既に容易に計測可能 上記より、最終的に私たちのグループでは「 Pull Requestマージ数 」をKPIとして選定しました。 ただし、このKPIには注意点も存在します。 総業務時間と1タスクあたりのPull Request数が定数であることが前提であるため、この2つの要素が現状から大きく変化する場合、正しく成果が計測できないこととなります。 そのため、定期的に上記2つの数値は確認をし、変化が大きい場合はKPIを見直すことをルールとして定め運用します。 KGIとKPIの目標値について KPIマネジメント導入の初年度ということもあり、今期に関しては現状からの105%成長で設定しました。 2020/10〜2020/12をサンプリング期間として定め、こちらの期間の3ヵ月の平均値をベースに105%成長した数値を目標値として設定し、2021/1〜2021/9までの9ヵ月間の平均値にて計測します。 目標値を平均値としている理由には、繁忙期やプロジェクトのフェーズにより数値のばらつきも発生すると考えられるため、平均して一定のパフォーマンスが発揮されているかを測る意図があります。 まとめ 以上が私の担当するエンジニアグループがKPIマネジメントの考え方を元に目標指標と定量的な目標値を導いた内容となりますが、まだスタートラインに立った状態であると思います。 今後、設定した目標に対し実行・計測・改善を繰り返し、より生産性の高いエンジニア組織へと成長していきたいと思います。 今回の記事を通じ、私と同じようにエンジニア組織の定量的な成果計測に悩む方の一つのヒントになれば幸いです。