LIFF の原則を提唱します。

2024/09/13に公開

こんにちは。やまゆです。

「Lazy Implementation, Fast Feedback の原則」、通称「LIFF の原則」をここに提唱します。 (もうどこかで出ていた名前だったらすみません)

これは直訳の通り、 「実装は遅延させよ。フィードバックは早くせよ。」 という意味を持っています。どういうことか説明していきます。

(主語が大きいですが)エンジニアは、コードを書くのが好きな人が多いです。自分の求めるモノを、コードを使って表現するパフォーマーでもあります。

しかし、現実として 「誰かによって書かれたコード」 には様々な重しがかかります。

そのコードが動作する場所がある限り、保守され続けなければなりません。動かなくなったら直さなければなりません。バージョンアップも必要かもしれません。「この実装は不要になったため削除する」という決定がされるまで、 そのコードは常に利益を生み出す源泉であり、保守費用を持つ負債 です。

実行が遅いだけならまだましです。要件が満たされていれば問題にならない可能性もあります。しかし、要件を満たさなくなったり、コード自体に問題があり動作時にエラーを発生させてしまう場合は、修正するコストが必要です。

「なら、出来るだけ実装しなければよいのでは?」 という至極当然な考えがこの原則の根底にあります。

また、既に要件を満たすコードが存在するのであれば、それを再利用した方が良いでしょう。それが OSS であれば、問題が発生しない限りは負債を自分で持つことなく要件を満たし続けることが出来ます。問題が発生したら、 issue で報告して誰かに直してもらったり、自分で PR を出して修正することも可能です。 OSS でなくても、例えば社内でこれまで 5 年間保守され続け、また動作し続けてきた実績のあるコードの方が、新しく実装するコードよりも信頼性が高いと言うことが出来ると思います。 5 年間の動作実績を上回るコードを書くことは非常に難しい です。

DRY 原則、同じことを繰り返さないようにしよう、という原則は聞いたことがあると思います。 LIFF の原則はこの DRY 原則をリスペクトしています。車輪の再発明をすることに意味がある場合もありますが、多くの場合バランスとして負債の方が大きくなりがちです(要出典)。

ということで、要件を満たすために「自分が実装を行う」ことを出来るだけ避け、代替案がないか考え、自分の手を動かすことを一番最後にしよう、という考えが Lazy Implementation です。


次に、 Fast Feedback について説明します。

どんなに調査を行っても、現段階で与えられた要件を満たす実装が存在しないかもしれません。そして、現実としてそういった場面は非常に多く訪れます。何故なら、 既に実装が存在するのあれば、新しく作っても相対的に価値が低い からです。 Google 検索で世界中の Website を検索出来るのに、今から全世界で公開されている Website をスクレイピングして、データベースに保存し、そこから全文検索出来るシステムを作る必要があるでしょうか。今であれば生成 AI にデータを処理してもらいさらに付加価値を生むことが出来るかもしれませんが、全く同じものを提供しても意味がありません。

エンジニアは、誰かが考えた 「これまで世界に存在していなかった、または存在していても洗練されていなかった、新しい価値を提供するためのシステムを作る」 職業です。新しい価値を提供するためには、新しい実装が必要になることが往々にしてあります。そして、たくさんのエンジニアが新しい価値を創造するために今日もコードを書いているわけです。つまり、 いくら実装を最後にしようとしても、最終的に何かしら実装することになる のが真実です。

では、実装するとなった際に、どうすれば付加価値(≒利益)を最大化出来るでしょうか?そこで第一に考えたいものが Fast Feedback の原則です。

フィードバック、ここでは、 「書いたコードが要件を満たすかどうか、実際に動作させた結果」 と定義します。

例えば、 Web フロントエンドであれば、「ボタンが存在し、クリックすることで〇〇ページに遷移する」といった要件があるとして、実際にそれに対してコードを書き、 Web ページを表示して、クリックしたら〇〇ページに遷移することが確認できた段階で、フィードバックが得られた、と出来ます。

この例には改善点があります。最も大きいのは、 「要件を満たしているか、実際に表示して、目で確認し、操作を行わなければ分からない」 部分です。

人間が確認する、または操作する、という行為は非常にコストが高いです。何故なら、 デジタルデータの実際の保守コストはほとんどが人件費 だからです。つまり、人が介入すればするほど、費用が膨らみ、結果として利益が減ってしまいます。

現代であれば「E2E テスト」と呼ばれる、この確認行為を自動化する仕組みが存在します。

example.spec.ts
import { test, expect } from '@playwright/test';

test('get started link', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  await page.getByRole('link', { name: 'Get started' }).click();

  await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});

これは Playwright と呼ばれる E2E テストフレームワークのサンプルコードです。

このテストコードを書き、 npx playwright test コマンドを実行することで、人が確認していた「ボタンがある」「クリックすると遷移する」といった要件を、自動的に確認出来るようになりました。また、このテストコードがあることで、後々誰かがコードを修正した結果要件を満たさなくなる(=テストに失敗する)リグレッションも検知出来るようになりました。一石二鳥ですね。

しかし、「テストコード」も実装の 1 つです。つまり、負債ともいえます。しかし、リリースを行うたびに誰かが「このボタンがある!」「クリック出来る!」「遷移出来る!」と指さし確認し、シートにチェックマークを入れるよりは、安価ではないでしょうか。

また、 E2E テストは万能ではありません。例えば Playwright を使った場合は、実際にブラウザをバックグラウンドで動かして試験するため、テスト自体に時間がかかります。先ほどのようにボタンのクリックと遷移を確認するだけであれば数秒ですむかもしれませんが、そのようなテストが 100 個あればテスト 1 回につき数分の時間を消費してしまいます。テストは出来るだけ個々の確認内容を少なくした方が効率的です。 1 行修正するたびに全く違うページのテストを実行する必要はありません。

そこでようやく出てくるのが Fast Feedback の原則です。

これはエンジニアの考え方というよりも、そういう 仕組み作り をしようという話です。 1 行の修正ごとに 5 分全体テストを待つより、明らかに該当する 1 テストだけを実行して 5 秒で結果が返ってきた方が作業効率が良いのではないでしょうか。または、 5 秒と言わず書いた直後に結果が返ってきた方が良いと思いませんか?なんなら、 コードを書いている最中にも横で結果が表示されていた 方が高速ではないでしょうか?

これが Fast Feedback です。つまり、 実装の検証を出来るだけ早く行おう 、ということです。

例えば、テストコードが全くないプロダクトで、開発環境も存在せず、実装を直接本番サーバー上で行う環境があるかもしれません。実装が要件を満たすか確認するためには、全てのコードをビルドし、本番環境で実際に目で見て確認することになります。これは時間がかかるうえ、要件を満たしていなかった場合本番にも影響を与えてしまうためリスクが高いです。これは今回の原則に反する Slow Feedback な環境と呼ぶことが出来るでしょう。

テストコードがないことは仕方ない場合もあります。しかし、この状況よりも改善出来ることはあるように見えます。例えば、本番と同じような形で動作するローカル環境が作れればどうでしょうか?

最近使われている webpackVite といったフロントエンドビルドツールは、 Hot Module Replacement(HMR) 機能を備えている場合があります。これは、実装コードを保存した際に差分コンパイルしてすぐにブラウザ上で反映してくれる便利機能です。

ローカル環境でこの機能が動作するようになれば、ビルドしてデプロイしなくても、ファイルを保存するだけで手元で実際の結果を確認することが出来るようになります。人間が目で確認する分のコストはありますが、それまでよりも圧倒的にフィードバックが早くなり、本番環境にエラーを持ち込みづらくするリスク回避の側面もあります。テストコードがなくてもフィードバックを得やすい環境になったと言えるでしょう。

特に見た目に関するテストは書くのが難しいです。「青いボタン」という要件があった時、 #0000FF の背景のボタンで本当に良いのか、実は #4444FF の方が要件をより正しく満たしていた、という場合や、マウスホバー時に背景色を少し明るくした方が良かった、と要件自体が実装中に増えることもあるかもしれません。これらもフィードバックが早くなったことにより、 高速にイテレーションを回せる ようになっています。

もちろん、実装して要件が完全に固まったのであれば、その要件を満たすテストコードを記載した方が今後に向けた負債を小さくすることが出来ます。

これは Web フロントエンドだけでなく、どの実装でも同じことが言えます。特に数十人が同時に開発していたり、数年保守され続けてきた大きなプロジェクトでは、コンパイルだけで数十分かかってしまうかもしれません。 1 行変えるたびに数十分待つのは耐えきれませんよね。

PHP で作られたページを作っているとして、その結果を確認するのに最もシンプルなのは F5 でページをリロードすること です。 PHP は明示的なコンパイルをする必要がないので、ファイルを保存して再度読み込むだけで反映されます。

しかし、例えばフォームの送信の確認をしたい、 JSON のデータを送ったらこういう JSON のデータが返ってきて欲しいといった、複雑な要件がある場合はこの方法が使えません。そこで使われるのが、実装をパーツ(≒ PHP でいえばクラス)ごとに分離して、パーツごとに要件を満たすテストを書く 「ユニットテスト」 です。

これはほとんどのプログラミング言語でサポートされている方法で、最も普及しているといえます。分離の仕方などは話がそれてしまうのでここでは省略しますが、ユニットテストも素早いフィードバックを得られる手法の 1 つです。

例えば PHP では vendor/bin/phpunit コマンドを実行することでユニットテストが実行されます。しかし、それだけでは全てのテストを実行するため、大きなプロダクトでは時間がかかることが想定されます。そのため、実際に実装とユニットテストを書く際は vendor/bin/phpunit tests/Unit/FizzBuzzTest.php のようにテストする対象を絞ることでイテレーションを早くできるでしょう。

ここで注目したいのは、これで最速の Fast Feedback になったと満足しないことです。

指定したテストだけを実行して結果を得るためには、

  1. 実装したコードを保存する
  2. ユニットテストコードを保存する
  3. コマンドを実行する
  4. 結果を目で確認する

この 4 ステップが必要です。もっと短く出来るのではないでしょうか。

https://pavolkutaj.medium.com/python-pytest-ptw-watch-mode-and-setting-up-unit-testing-for-python-8be927800c37

例えばこちらの記事では、 Python のテストに ptw というモジュールを使っています。このプロセスを立ち上げておけば、コードを保存したタイミングでユニットテストが自動で実行できます。つまり、コマンド実行の 1 ステップ分短縮することが出来ますね。

また、 IDE である Visual Studio には Live Unit Testing と呼ばれる機能があります。この機能を有効にしていれば、ユニットテストを自動的に実行するだけでなく、エディタに 「この行に対するテストが通過したこと」 がリアルタイムに可視化されます。そうすることで、テスト結果の文字列を読んで確認する必要がなくなり、さらに 1 ステップ短縮出来ます。ついでにカバレッジにより 「テストが不足していないか」 も同時に確認出来るので、これは素晴らしい機能だと思います。

この機能は Visual Studio で C# を使った時のみ利用できるため、ここまでのサポートを得られる環境を他のエディタや言語で用意出来ないこともあるのがもどかしいのですが、出来るだけ近づける努力をするのが大事です。

Python や TypeScript/JavaScript では、 vscode を使って似たような環境を作ることが可能です。このような環境を用意することで、副次的に テスト駆動開発(TDD) を自然と実現出来るようになるでしょう。このようにして、イテレーションを可能な限り高速に行う考え方が Fast Feedback です。


ということで、持論である Lazy Implementation, Fast Feedback の原則を紹介してみました。共感出来たら❤をもらえると嬉しいです。皆さんも Implementation に対して Lazy になり、 Feedback に対して高速化する仕組み作りを心がけるようにしてみましょう!

※ PHP で Live Unit Testing のような仕組みを実現するには、 PHP Tools for Visual Studio Code を使ったり、 PHPUnit Test Explorer を使うことでかなり近づけることが可能です。是非皆さんの環境を最適化するプラグインを探してみましょう。
※ Zenn のこの記事は vscode を使って執筆していますが、 npx zenn preview というコマンドで Hot Module Replacement を利用してプレビュー表示を確認することが出来ます。これも Fast Feedback ですね。

GitHubで編集を提案

Discussion