Cake.jpとテスト駆動開発

こんにちは、Cake.jp技術部でエンジニアをしている山内です。

この記事では、Cake.jpのエンジニアがどのような場面でテスト駆動開発(以下、TDD)を取り入れているのかについて話します。

※今回はTDDがどのようなものかは割愛させていただきます。

導入の背景

前提として、3年ほど前はテストコードがあまりない状況でした。

そこから、チームとしてレガシーコードに立ち向かうために、テストコードを書くということを推し進めていきました。

中にはTDDを実践しているメンバーもおり、週2で行なっているモブプロの中でTDDが行われ、さらに小話会(好きに技術の話をする時間)で共有されたりということを経て、社内でもTDDが広がっていきました。

個人的には、書籍などでもよく言われる「テストを書きやすいコードを書くことで、設計が良くなる」というのも感じていますし、TDDがうまくはまった時はゲーム感覚で実装できるという楽しさもあり、積極的にTDDを取り入れています。

※弊社のモブプロについて: 「コンテキストの共通化」を加速させるために1年以上モブプロを続けた結果

どのような場面で採用するか

TDDを取り入れているとは言いましたが、全ての開発で取り入れているわけではありません。

傾向としては以下のようになります。

複雑度が高い 複雑度が低い
新規開発 ×
追加開発 ×
不具合修正 ×

複雑度が低いケースでは、TDDは行わない(テストを書かないという意味ではないです)ケースが多いです。

そういった場合は、今までやってきたことに似た実装であるケースが多いので、ササっと書いてしまっています。

複雑度が高いケースについて、それぞれ補足していきます。

新規開発 × 複雑度が高い

新規開発で複雑度が高いので、TDDチャンスです。

特に補足も必要ないと思います。

追加開発 × 複雑度が高い

ここを△としていますが、大きく分けて二つのパターンがあります。

  • ファットコントローラーで機能追加が辛い
  • それ以外

分類がアレな感じはありますが、、、

1番目のケースはそもそもテストを書くのが辛いパターンです。

弊社のサービスはコードベースだと10年選手で、当時から膨れ続けているようなコントローラーも存在します。

丁寧に剥がしていけば良いという話ではあるんですが、機能追加の頻度が高くない場合が多いので、そこまでリファクタ工数を取れないのが現状です。

不具合修正 × 複雑度が高い

こちらも△としていますが、多くの場合は不具合用のテストケースを先に追加して、それを通すための実装をしています。

テストファーストで不具合修正をしているものの、「レッド/グリーン/リファクタ」というthe TDDな開発ではないので、△としました。

成果

一言で言うと、不具合が減りました。

ただし、TDD一本で不具合が減ったというわけではありません。

僕が入社した当初(2022/08)は今と比較して、不具合が多く発生していました。

不具合が出るたびに振り返り→改善を繰り返していきまして、今では不具合が激減しました。

不具合が減った理由の一つに、TDDがあると思っています。

さいごに

Cake.jpでは、TDDに限らず良いと思ったものは積極的に取り入れていっています。

また、輪読会などで得た知識を積極的に活かしていくこともしています。

学んだことを活かすことに肯定的、かつ、積極的な良いチームだと感じています。

引き続きインプットとアウトプットを繰り返して、より良いサービスを目指していきたいと思います。

IntersectionObserver APIを使った速度改善について

こんにちは、Cake.jp技術部の福井です。

はじめに

ウェブページの速度改善を行う中で、IntersectionObserver APIを利用しました。この記事では、どのようにこのAPIを活用してパフォーマンスを改善したかについて詳しく解説します。

改善前の状態と背景

前提

Cake.jpの商品ページは縦長のスクロールが必要なサイトです。デザインを変更することによる、DOMサイズの縮小を行わない方針です。

速度の問題点

商品ページを表示する速度に当初はFCP(First Contentful Paint)が8.8秒かかっており、ユーザー体験に悪影響を与えていました。特に、ページの初期描画時に大きいサイズのDOMを一度に読み込むことで、表示が遅れてしまいました。

FCPとは?ページのコンテンツが最初に描画されるまでの時間を測る指標です。

▼詳しくはこちら: FCPの説明

web.dev

技術スタック

  • 使用技術: JavaScript
  • パフォーマンス問題の具体例: 初期ロード時に要素が表示されるまでに時間がかかる、ボタンを押しても反応しない

改善のアプローチ

私はFCPのスコアが悪い理由として、DOMサイズが大きすぎることに着目しました。DOMサイズ削減のため、ファーストビューに関係ないDOMを非同期で読み込む方法を検討しました。 非同期で読み込む方法として、ある特定の地点に到達したらイベントが発火するIntersection Observer APIを採用し、ユーザの動作によってファーストビューに関係ないDOMを取得するかどうかを決定することとしました。

IntersectionObserver APIとは

概要

IntersectionObserver APIは、要素がビューポートや祖先要素と交差する状態を非同期で監視するためのインターフェイスです。これにより、ユーザーが要素に到達するタイミングを検知し、適切なアクションを実行できます。

▼詳しい説明はこちら: MDN Web Docs - IntersectionObserver developer.mozilla.org

IntersectionObserver APIを導入できるかどうかの3つのステップ

  1. ファーストビュー外に多くの要素があることを確認する

    • 理由: ファーストビューにある要素をIntersectionObserver APIで遅延読み込みしても、恩恵をあまり受けることができないため。ファーストビュー外の要素による効果がより大きい。
  2. ファーストビュー外の要素を非表示にしたときに表示速度を向上できることを確認する

    • 理由: DOMを描画しないことで初期描画にかかる時間を軽減できるかどうかを検証するため。非表示の要素が後からロードされることで、ページの初期表示が迅速になることを確認。
  3. 全てのブラウザに対応しているオプションが利用可能であることを確認する
    • 理由: 一部のブラウザで対応していない場合、表示したい要素が正しく表示されない可能性があるため。全ブラウザ対応のオプションが利用可能であれば、互換性の問題を回避し、全ユーザーに一貫した体験を提供できる。

「IntersectionObserver API」を用いて改善することができると分かったら、次にどのように改善していくかを以下の3つのステップに整理しました。

  1. どこまで情報を非同期で表示するのかを決める
  2. 1で決めた箇所で、ソースを分割し、コンポーネントを別のエンドポイントで呼び出せるようにする
  3. ユーザーのスクロール位置に応じて、必要なコンポーネントを遅延ロードする

今回は、3番目の「IntersectionObserver API」を使用したアプローチについて詳しく見ていきます。

IntersectionObserverの適用とその効果

基本的なコード例

以下は、IntersectionObserver APIの基本的な使い方の例です。

    // IntersectionObserverのインスタンスを作成
    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                // 要素がビューポートに入った時の処理
                entry.target.classList.add('visible');
            }
        });
    });
    // 監視する要素を指定
    const elements = document.querySelectorAll('.lazy-load');
    elements.forEach(element => {
        observer.observe(element);
    });

設定のカスタマイズ

  • root: 監視するビューポートの要素(省略するとデフォルトでビューポート全体)
  • rootMargin: 監視する領域のマージン
  • threshold: 要素が交差する割合(0から1の値)

具体的な実装方法

ユーザーが特定のスクロール位置に到達したときに、必要なコンポーネントを遅延ロードすることで、初期描画の速度を改善しました。これにより、ページの表示速度が当初の8.8秒から3.8秒に短縮されました。

測定方法

計測には、PageSpeed Insightsの「低速4G」設定を使用。以下のツールで測定を行いました。

  • ツール: Pagespeed Insights、Lighthouse
  • 改善前と改善後の比較: FCPの指標を使用

PageSpeed Insightsとは? モバイル端末やパソコン向けのページの実際のパフォーマンスに関するレポートと、そうしたページの改善方法を確認できます。

▼詳しくはこちら:PageSpeed Insightsの説明 developers.google.com

注意点と考慮事項

  • パフォーマンスのトレードオフ: Intersection Observerを使うことで、初期ロード時に一部のリソースの遅延が発生する可能性があるため、適切に調整する必要があります。

  • ブラウザサポート: Intersection Observer APIのサポート状況について確認することを推奨します。一部のオプションではSafariFirefoxなどのブラウザでのサポートが限られています。

まとめ

IntersectionObserver APIを活用することで、ページのパフォーマンスを大幅に改善することができました。今後は、さらに異なるシナリオでの応用や、他のパフォーマンス改善手法との組み合わせを検討していきたいと考えています。

Cake.jpのバージョンアップについて語った、PHPカンファレンス福岡2024

Cake.jpでバックエンドエンジニアをしている山内です。

2024/06/22(土)、PHPカンファレンス福岡2024に参加してきました。

弊社からは代表の高橋、私の2名で参加しました。

今回は、登壇もしてきたので、当日の発表内容や参加してみての感想を紹介します。

登壇

Cake.jpのバージョンアップの歴史を語ってきました。

https://fortee.jp/phpcon-fukuoka-2024/proposal/83f31e89-1286-4d6a-b015-c46382a6b14e

オンラインでの登壇は経験がありましたが、カンファレンス登壇は初めてでした。

緊張のせいで客席の方を見る余裕は一切なく、ひたすら手元のPC(カンペ)と映し出されるスクリーンを見ていました(笑)

発表内容

こちらが登壇した際の資料です。

https://speakerdeck.com/kechiiin/ban-nian-kaketephp5-dot-6karaphp7-dot-4madebaziyonatupusitaku-lao-togong-fu-phpkanhuarensufu-gang-2024

登壇内容を一部紹介すると、

テスト環境がコンテナ化されていなかったので、それをやった話や(P.9)

バージョンを0.1ずつ進めていった話や(P.18)

バージョンアップの際に対応したものの話や(P.46)

動作確認で工夫した話など(P.67)

こういった話をしてきました

参加の感想

登壇の感想

前述したように非常に緊張をしていました。

発表自体は概ね練習通り進めることができたのでよかったです。

多くの方の前で話す機会はそう多くはないので、良い経験になりました。

 

聞いてくださった方々から、感想(フィードバック)もいただけたので一部ご紹介させていただきます。

 

バージョンアップって本当に大変なことなんだなぁ…と思いました。色々な工夫を伺えてよかったです。ありがとうございました。

 

バージョンアップ大変ですがビックバンリリースをしないように進めててすごいと感じました。

バージョンアップ前後の画面htmlの比較も良いですね。

面白い発表でした

 

フィードバックはありがたい限りです。

 

機会があれば、またカンファレンスでの登壇をしたいと感じたので、今後もカンファレンスにはプロポーザルを出して行こうと思っています。

全体の感想

トークは、その他コンテンツも盛りだくさんだったため、半分くらいしか聞けませんでした汗

トーク以外の時間は主に、スポンサーブース巡りや、カンファレンスで知り合った方との交流、Ask the Speaker(トーク後の質疑応答)などに参加させていただきました。

トークを聞いて学べるというのも良い点ですが、懇親会や(非公式ですが)前夜祭などに参加して、他社のエンジニアと関われるというのもカンファレンスの醍醐味だと思っています。

今回はPR TIMESさん主催の「PHPカンファレンス福岡2024・前日Meetup」、Fusicさん主催の「PHP Conference Fukuoka After Hack!!」にも参加して、交流を楽しんできました。

トータルで非常に有意義なカンファレンスだったと感じました。

最後に

PHPカンファレンス福岡2024スタッフの皆さん、ありがとうございました。

初参加でしたが非常に楽しかったです。

Cake.jpの話

今回は個人的に参加したカンファレンスでしたが、Cake.jpでは過去、PHPerKaigi2022, PHPerKaigi2023, PHPerKaigi2024にエンジニア一同参加しています。後日、感想を言い合う会をしたりもして、学びの共有やモチベーションアップを行っています。

また、Cake.jpでは定期的にLT会をしていますので、ご参加お待ちしております!

https://cakejp.connpass.com/

「コンテキストの共通化」を加速させるために1年以上モブプロを続けた結果

こんにちは、Cake.jp技術部の中村です。

Cake.jp技術部では、週に2回モブプログラミング(以下、モブプロ)を実施しています。導入してから1年以上経過して、導入してよかったこと、課題について紹介させていただきます。

1. モブプロとは

Cake.jpでは、1つの画面を3人以上のエンジニアで共有し、コーディングを行うことをモブプロとしています。一般的なモブプロと異なり、コーディングを進めていく「ドライバー」とそれを見守り、意見をいう「メンバー」に分かれて行っています。(一般的なモブプログラミングでは、「ナビゲーター」の指示の元「ドライバー」がコーディングを行っていくと思いますが、Cake.jpでは「ナビゲーター」不在で行っています。)

全エンジニアがフルリモートで業務を行っているので、Slackのハドルミーティングを使って画面を共有しています。(ハドルミーティングの画面の拡大/縮小機能、画面共有への手書き機能を駆使して行っています。)

参加者は固定せず、グループ表を作り毎回同じ組み合わせにならないように実施しています。その週に各自が割り当てられたタスクや、設計の議論、プランニングにのらなかった軽微な不具合を修正する場だったりと、そのときどきによって内容を変えながら行っています。

2. Cake.jpでなぜ取り入れたのか

Cake.jpでは大半のメンバーがフルリモートで活動しています。モブプログラミングをとりいれるにあたり、当時抱えていた3つの課題の解決を目標として導入しました。

 

(1) 技術的な内容を相談する場が不足

開始当初は同期のコミュニケーションが朝会しかなく、朝会が終わると非同期でそれぞれが作業するというスタンスを取っていました。朝会にはエンジニア以外のメンバーも参加していたことや、そもそもの開催時間が短いことから、コードについての相談の場がGitHubのPRでのやり取りでしか生まれ辛い状況でした。


(2) 技術の共有

チームにはCake.jpの在籍年数やエンジニアになってからの年数がまちまちのメンバーがいます。コードレビューの場では納期の観点から細かい部分についての指摘がついつい後回しになってしまったり、指摘をもらってもコードに反映ができないということがよくあり、FBサイクルがうまく働かないときがありました。

(3) プロダクトの仕様の共有

当時のチームは、3人のチームで大きめのプロジェクトが2本走っており、どちらかのプロジェクトを専任している状況でした。そういう状況下において、プロジェクトをまたいだレビューが頻繁に行われていました。そのため、以下2点の課題を感じていました。

・PRでコードはわかるけど、仕様→コードの関連性がわかりづらい。

・ドキュメントに残っていないことがある。

Cake.jpでは開発メンバーの役割として、運用業務も含まれています。障害発生時やお問い合わせへの対応をスムーズにできるようにするため、システム全体やプロジェクトの仕様を把握しておくことが重要でした。

 

1人1回ドライバーを担当できるように週3回からスタートさせていきました。もともとモブプロとは別でコードリーディング会というものを行っており、プロダクトコードを1つの画面で複数人で同時に読むという文化があったため、スムーズに導入することができたと感じています。

モブプロを通して、IDEの使い方の習熟度が上がったり、プロジェクトで導入した新しい技術(React)についてエンジニアの知見が広がったり、設計思想の共有(DDDを意識した設計に取り組んでいる)ができたりしていることを実感しました。

組織変更に伴いチームが合併した際に、モブプロはチームの取り組みからエンジニア全体の取り組みとして活用しています。

3. 現状出ている効果と課題

2であげた3点の課題は、一定達成されたと感じています。

(1) 朝会以外でもコミュニケーションを取ることができ、他エンジニアとの接触頻度が上がった。

 

(2) 技術面では、ツールの使い方を習熟したり、単体テストを書けるようになったりと効果がありました。週に一度行われているチームの振り返りでは、モブプロを行ったことにより、よりよいコーディングを行うことができた。などの意見も出ています。
自身が習得した新たな技術のよい実験場として利用されることがあり、ほかメンバーの学びにもつながりました。

 

(3) プロダクトの仕様共有では、仕様に精通しているメンバーと進めることでタスクの理解がより進んだ例がありました。(未然に不具合を防げているのがかなり大きいです。)

一方で会を続けていくうちにわかった課題として、

(1) 自分が持っているタスクの進行状況によって、モブプロへのタスクの持ち込みができず、ドライバーになれないエンジニアがいた。

 

(2) 持ち込むタスクによっては、ただ作業を見守るだけのモブプロが発生した。

 

これらの課題を解消するために、軽微な不具合修正タスクをストックして、持ち込みタスクがないメンバーでもドライバーができるように体制を整えました。

不具合修正のタスクはただ直すだけではなく、「生まれた背景」「原因」「修正方法」の議論を行っております。議論を通して、「コミュニケーション」・「技術の共有」・「仕様の共有」ができており、単なる不具合修正以上の効果が生まれていると感じています。

今後も継続して活動を続け、エンジニア全員で学習を続けていきたいと思っています。