この文書について
この文書は、2025年2月1日に私 @debiru_R によって記述されました。私は2000年頃より HTML, CSS を研究し始め、クールな URL の心得、Webの誕生とブラウザの歴史(PDF - 21MB)、HTML 文書の在り方、ブラウザ実装状況について考察・調査してきました。2013年頃から MDN Web Docs(旧 Mozilla Developer Network)の日本語訳コントリビューターも務めており、MDN Web Docs や Wikipedia といった各種 Web コンテンツの HTML 関連の記事の整備に従事しています。
Web サイト・Web アプリケーション開発においては、様々な開発手法やフレームワークが考案されてきた歴史的な経緯があります。その中で、開発アプローチのベストプラクティスやアンチパターン等の分類も登場し、よりモダンでエレガントな開発スタイルが提唱されています。
しかしながら、一般的に開発時の常識とも認識されている「ソースコードの圧縮」について警鐘を鳴らしたいと思います。
定義 - ソースコードの圧縮とは
ソースコードとは、本来「Code」のうち「Source」であるものを指します。Source でない Code としては実行コード「Executive Code」があります。これは、コンパイルが必要であるようなプログラムにおいて、コンパイル前のコードとコンパイル後のコードを区別するための呼称です。
しかしながら、コンパイルを不要とする、あるいは暗黙的に行うようなプログラムや実装が登場してきました。それによりソースコードと実行コードの区別が曖昧になっている分野もあり、Web フロントエンド領域もその一つといえるのかもしれません。HTML, CSS はそもそもプログラムではないですが、ソースコードはあるわけです。そこで、この文書では、コードの総称を指して「ソースコード」と呼ぶことにします。つまり、ビルド後のコードのこともソースコードと呼ぶことにします。
この文書では、特に Web アプリケーション開発におけるフロントエンド領域のソースコードに注目しています。それはつまり、HTML, CSS, JavaScript のことです。
HTML, CSS, JavaScript の実装をする際には、「入力ソースコード (SRC - source code)」を記述します。それを場合によっては何らかの変換処理を行って「出力ソースコード(DST - source code)」にします。Web サーバーは「出力ソースコード」を HTTP レスポンスとして、エンドユーザーの HTTP クライアント(ブラウザ)に届けます。入力ソースコードから出力ソースコードに変換する手順として、入力 HTML ファイルを手動でビルドして(圧縮などをして)出力ソースコードを用意する場合もあれば、PHP などのプリプロセッサで入力ソースコードを記述し、出力ソースコードは Web サーバーが暗黙的に用意する場合もあります。
SRC(Source の略)は、出所といった意味の英単語です。DST(Destination の略)は、行き先といった意味の英単語です。しばしばソースコードやディレクトリ名として "src" や "dst (dest)" という名称が使われることがあります。一方で、後者に対して最近では dist という単語を使うフレームワークが存在します。dist(Distribution の略)は、配布といった意味の英単語です。Grunt, Vite, Astro, Nuxt などが dist というディレクトリ名を使っている例として挙げられますが、個人的には dist というディレクトリ名は違和感があります。src に対応するものは dest(あるいは3文字で揃えるなら dst)であって、dist ではないからです。Grunt のページでは dest: 'dist/'
のように記述されており、滑稽さも感じられます。個人的には src / dst という単語の組を使うことが多いですが、dist というディレクトリ名を使うフレームワークがあることを覚えておいてください。
HTML, CSS, JavaScript の出力ソースコードを用意する際に、「コード圧縮 (Minify / Compress)」をすることがあります。
(function() { // BMI を計算して出力する const bmi = (weight, height) => weight / (height / 100) ** 2; console.log(bmi(60, 170)); })();');
(function(){const f=(a,b)=>a/(b/100)**2;console.log(f(60,170))})();');
改行や空白文字など、空白類文字を取り除くだけの場合もあれば、コメントを削除したり変数名や関数名などのトークンを短縮したり、呼び出されていない不要コードを削除したりする場合もあります。いずれにしても、ソースコードの実行可能性には影響を与えずにテキストレベルでコードを圧縮することをここでは意図しています。ここでは圧縮時の処理内容は特に区別せずに「圧縮 (Minify / Compress)」と呼ぶことにします。
人々がコードを圧縮する理由
Web アプリケーション開発者が出力ソースコードを圧縮する理由として、次のようなものが考えられます。
- コードを軽量化して、HTTP レスポンスの転送量を減らしたい
- コードを圧縮・難読化して、簡単にコードを読めなくすることでセキュアにしたい
- SPA フレームワークやビルドシステムが標準で圧縮機能を備えているので使っている
- 圧縮することが当たり前だと思っている
ここで、圧縮とは趣旨が異なる「難読化 (Obfuscation)」について触れておきましょう。難読化とは主に JavaScript に対して、そのコードの可読性を著しく低下させることでリバースエンジニアリングやコード内容を解読される可能性を軽減するというものです。これはセキュリティに関して根本的解決にはなりませんが保険的対策にはなります。
セキュリティにおける「根本的解決」と「保険的対策」という考え方については「安全なウェブサイトの作り方 - IPA」を参照ください。
それぞれの理由の妥当性
ソースコードを圧縮する理由として、前述のものは妥当なのでしょうか。一つひとつ見ていきましょう。
HTTP レスポンスの転送量を減らしたい
結論から述べると、これを理由にソースコードを圧縮するのは止めたほうがよいでしょう。なぜなら、コードの可読性を失うというデメリットが大きいからです。
転送量の削減は Web サイト開発者の仕事ではない
転送量を削減したければ、HTTP レスポンスを返す時点で Web サーバーが gzip 等のバイナリレベルでの可逆圧縮をすればよいのです。ブラウザには gzip 等の圧縮フォーマットを処理する機能が備わっています。
例えば Apache Web サーバーでは mod_deflate や mod_filter といったモジュールがあり、デフォルトで特定のファイル形式に対して gzip 圧縮を施すための設定が有効化されています。
<IfModule mod_filter.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/wasm AddOutputFilterByType DEFLATE application/xml </IfModule>');
この機能によって、Apache を利用した Web サーバーであれば次のように転送量が削減されます。
HTML ファイルに注目すると、この例のファイルの本文は 4682 バイトあります。それを gzip 圧縮すると 1736 バイトになります。転送量には本文だけでなく HTTP レスポンスヘッダ分が上乗せされますが、それでも gzip 圧縮した方は転送量が 2060 バイト程度になっています。一方、未圧縮の場合は 4950 バイト程度になっています。
バイナリレベルで圧縮すれば、30% 〜 60% 程度の容量削減が期待できます。しかし、テキストレベルで空白類文字を削除する程度だと 10% も圧縮できないことが多く、その後に gzip 圧縮をするのであれば、最終的な圧縮率はテキストレベルの圧縮をしない場合とほとんど変わりません。テキストレベルの圧縮を施すと可読性だけ失われる結果になります。
圧縮ではなくアルゴリズムを改善せよ
JavaScript であれば、空白類文字だけでなく変数名や関数名の短縮や、不要コードの削除が行われるから圧縮率は大きくなるので圧縮することによる転送量の削減効果が高い、と考えている方もいるかもしれません。
しかしそれは、本当に gzip 圧縮後の圧縮率に寄与するのでしょうか。不要コードがあるのであれば、それを削除するなり Tree Shaking するなりすべきであって、ソースコード圧縮によるコード削減というアプローチが適切だとは思いません。空白類文字(改行やスペース)、変数名・関数名の短縮は効果があると思っている方は、その短縮が転送量削減にどれだけ寄与するか把握されているでしょうか。
jQuery 3.7.1 を例にすると、未圧縮の場合は 285 kB 程度、Minify 版は 87 kB 程度、そして未圧縮 + gzip の場合は 84 kB 程度、Minify + gzip の場合は 30 kB 程度でした。jQuery のようなライブラリファイルは、それ単体で使われることがあり gzip のようなサーバーサイドでの転送量圧縮が期待できない場合もあるので、ソースコード圧縮することには意味があると思います。しかし、Web サイトを開発して公開するような場合、その公開 Web サーバー上で gzip 圧縮を有効にするという選択肢を採れないケースはどれほどあるのでしょうか。
JavaScript ファイルの容量を削減したければ、Minify / Compress に頼るよりも、アルゴリズムを改善した方が効果的です。ロジックが冗長になっていたり、使う想定のない拡張パラメータが含まれているようなコードであれば、ロジックやアルゴリズムを見直しましょう。ファイル容量にそこまでこだわるのであれば、コードゴルフやショートコーディングのテクニックを適用すべきです。それでも可読性を失ってまで、実質的にあまり意味のないソースコード圧縮をするのですか?改行を削除することに何の意味があるのですか?
ソースコードの圧縮が有効なケース
直前のセクションでも少し触れましたが、いわゆるライブラリを公開する場合にはソースコード圧縮をしても良いでしょう。jQuery の例で示したように、未圧縮であれば 285 kB、gzip 圧縮しても 84 kB だったところ、ソースコード圧縮をすることで 87 kB、gzip まで組み合わせれば 30 kB まで軽量化できました。
数百キロバイトの JavaScript を数十キロバイトまで容量を削減したい意図があれば、自作した Web アプリケーション向けの JavaScript ファイルを圧縮してもよいでしょう。ただし、その圧縮に本当に効果があるのかというのは常に考えたほうが良いかもしれません。未圧縮の jQuery であっても高々 300 kB です。あなたの Web サイトで使っている画像データの総容量はどのくらいですか?可読性を失ってまでテキストデータを圧縮しても、転送速度の向上に寄与できるバイト数は画像1枚にも満たないかもしれません。本当にそのソースコード圧縮は必要ですか?
ソースコード圧縮を行う場合、特に JavaScript であれば code.min.js
のようなファイル名で圧縮コードを提供し、同時に code.js
のようなファイル名で未圧縮版を提供するようにしましょう。可読性のあるコードの参照可能性を提供することは重要です。ライブラリファイルであれば、これを実施しているところがほとんどであるはずです。
難読化をしたい
難読化をしたいケースは確かにあるかもしれません。しかし、なぜ難読化したいのか明確に答えられますか?答えられる人は、目的に応じて難読化を伴うソースコード圧縮を実施すればよいでしょう。
フレームワークやビルドシステムに搭載されている
フレームワークやビルドシステムの開発者さん、空白類文字を取り除くようなソースコード圧縮をデフォルトで有効にするのやめてもらっていいですか?
圧縮することが当たり前だと思っている
ソースコード圧縮はデメリットしかありません。今すぐソースコード圧縮をやめましょう。ソースコード圧縮が有効なケースもありますが、ほとんどの場合はデメリットが上回ります。
HTML, CSS, JavaScript の可読性を失うことは、ソースコードを参照したいエンドユーザーの期待を裏切ります。ブラウザから HTML ソースコードを参照したときに、改行が取り除かれていると読む気が起きなくなります。HTML がどのような設計になっているのか、どのような属性、class 名が使われているのか、何のフレームワークでそのサイトが作られているのか、など様々な理由でそれを知りたいユーザーがソースコードを参照しようとします。もちろん HTML に限りません。CSS, JavaScript がどのように作られているのか、それを参考にしたい場合にソースコードの可読性が低いとユーザーは困ってしまいます。
Web サイトを公開するということは、同時に技術的なノウハウを公開することにもなるのです。その恩恵を受けられるようにするためにも、ソースコードを圧縮することの悪影響について考えてみてください。
BeautifulCode という概念
BeautifulCode という概念があります。BeautifulCode とは、Web ブラウザが取得・参照できる HTTP リソース(HTML, CSS, JavaScript など)のソースコードが人にとって読みやすく理解しやすいコードであることを指します。それにはソースコード中に適切なインデントと改行が入っており、意味のあるトークン(変数名など)が使われている状態が担保されている必要があります。
BeautifulCode は @debiru_R が2025年に提唱した概念です。表記はアッパーキャメルケースで BeautifulCode としています。
BeautifulCode を実現するためには、実装者の工夫とテクニックが求められます。BeautifulCode は、いわゆる Prettier が適用されたコードであるだけではなく、意味的に適切なトークンが使われており、コードが理解しやすい状態である必要があります。また、HTML で言えば、HTML 構造が理解しやすいようなセマンティックなマークアップがされており、class 属性がユーティリティクラスだらけになっていないような、本質的な HTML 設計である必要があります。
BeautifulCode は、React のような SPA (Single Page Application) や、Tailwind CSS のようなユーティリティクラス設計の CSS と相性が悪いです。BeautifulCode を実践したい場合は、原則的には MPA (Multi Page Application) を前提とし、従来のセマンティックな id, class 名を付与することによる HTML 設計と、それに対応した静的な CSS ファイルによるスタイル適用を想定する必要があります。なぜなら、意味が読み取りづらく人が直感的に理解できないコードは、いくらコードが整形されていても美しいとは言えないからです。SPA の場合でも SSR (Server Side Rendering) をサポートしているのであれば BeautifulCode を実現できるかもしれません。
PHP を使う場合のテクニック
PHP を例にテクニックを紹介しますが、他の言語やテンプレートエンジンにも共通する部分があるかもしれません。
綺麗なインデントをキープする
PHP ではしばしば次のようなコードが書かれます。
<?php function output($str) { echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } $members = ['Alice', 'Bob', 'Carol']; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <?php foreach ($members as $member) : ?> <li><?php output($member); ?></li> <?php endforeach ?> </ul> </body> </html>
余談ですが、HTML 混在型の PHP を記述する際に、if
や foreach
ブロックを書くに当たって <?php foreach (...) {
と <?php } ?>
のような波括弧による記法を使う方がいます。PHP においては、制御構造に関する別の構文というものが用意されています。具体的には foreach
ブロックは <?php foreach (...) : ?>
と <?php endforeach ?>
のような記法が使えます。可読性および保守性を高めるためにも HTML 混在型の PHP を記述する場合は、この別の構文を積極的に用いるようにしましょう。
上記の PHP は次のような HTML を生成します。インデントが崩れてしまっていますね。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <li>Alice</li> <li>Bob</li> <li>Carol</li> </ul> </body> </html>
綺麗なインデントをキープするためには、次のように PHP 処理命令区切り子を行頭に記述します。
<?php function output($str) { echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } $members = ['Alice', 'Bob', 'Carol']; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <?php foreach ($members as $member) : ?> <li><?php output($member); ?></li> <?php endforeach ?> </ul> </body> </html>
改善された PHP では、次のような HTML が生成されます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <li>Alice</li> <li>Bob</li> <li>Carol</li> </ul> </body> </html>
PHP は行末に処理命令を書いた場合、処理命令終了区切り子 ?>
直後の改行は無視されるため、改行が意図せず反映されないケースがあります。
<?php function output($str) { echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } $members = ['Alice', 'Bob', 'Carol']; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <?php foreach ($members as $member) : ?> <li> <?php output($member); ?> </li> <?php endforeach ?> </ul> </body> </html>
このように、出力処理を行末に PHP 処理命令を記述した場合は、次のような HTML が生成されます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <li> Alice </li> <li> Bob </li> <li> Carol </li> </ul> </body> </html>
これを改善する場合は、改行文字(PHP の場合は、定数 PHP_EOL
)を出力するようにします。
<?php function output($str) { echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } $members = ['Alice', 'Bob', 'Carol']; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PHP Sample</title> </head> <body> <h1>List</h1> <ul> <?php foreach ($members as $member) : ?> <li> <?php output($member); echo PHP_EOL; ?> </li> <?php endforeach ?> </ul> </body> </html>
Apache の mod_ext_filter で Prettier する
Apache には mod_ext_filter というモジュールがあり、HTTP レスポンスボディを加工することができます。PHP 処理後の HTML を加工することができるので、このタイミングで Prettier を実行して出力ソースコードを整形することも考えられます。
しかし、このアプローチは非推奨です。動的に Prettier を実行すると、その処理時間だけ HTTP レスポンスが遅れてしまいます。処理時間に影響がないような軽微なレスポンスボディの書き換えや追記を行う場合には適したアプローチと言えます。
参考までに設定方法を紹介します。
私の Web サーバーは「Ubuntu 24.04 LTS サーバ構築手順書」の手順で構築しています。環境が異なる場合は、適宜読み替えてください。
(手順1)sudo a2enmod ext_filter
を実行して mod_ext_filter を有効にします。
(手順2)Apache の conf ファイル(httpd.conf
や /etc/apache2/sites-available/your.conf
)に以下のような内容を記述します。ExtFilterDefine ディレクティブは .htaccess
には記述できないので conf ファイルで設定する必要があります。
ExtFilterDefine prettier-html mode=output intype=text/html cmd="/etc/apache2/includes/prettier-html.sh" <Directory "/var/www/hosts/web/sandbox"> SetOutputFilter prettier-html </Directory>
(手順3)sudo apache2ctl configtest
で構文チェックした上で sudo service apache2 restart
で conf ファイルの設定内容を反映します。
(手順4)/etc/apache2/includes/prettier-html.sh
に次のようなスクリプトを記述します。
#!/bin/bash export PATH="/gbin:$PATH" npx prettier --parser html
ここでは、/gbin
に node
, npm
, npx
を設置して任意のユーザーが npx
コマンドを使えるようにしています。Apache ユーザーである www-data
が npx
コマンドを実行できるようにするためには、/var/www/.npm
ディレクトリを作成し、所有権を www-data:www-data
に設定しておく必要があります。
Astro を使う場合のテクニック
Astro でも PHP と同様に、出力ソースコードを意識して入力ソースコードの記述方法を工夫する必要があります。特に Astro の場合、View で表示するコレクションデータを JavaScript の変数で保持して、それをテンプレート内で Array.prototype.map
メソッドを用いて JSX のように展開することでコレクションを HTML 要素に変換できます。
<ul>{ getLinkItems('specialContents').map(([key, array]) => <Fragment> <li><span class="dateLabel">{key}</span> <ul>{ array.map((item) => <Fragment> <li set:html={generateAnchor(item)} /></Fragment>) } </ul> </li></Fragment>) } </ul>
上記のような形式で map
メソッドの返り値を記述すると、綺麗な形で出力ソースコードが生成されます。
このコードは、私のポートフォリオサイト https://debiru.net の実際のコードです。実際のソースコードは debiru.github.io - GitHub リポジトリ を参照してください。
<ul> <li><span class="dateLabel">2025-01-31</span> <ul> <li><a href="https://example.com/" target="_blank">リンク1</a></li> </ul> </li> <li><span class="dateLabel">2025-01-30</span> <ul> <li><a href="https://example.com/" target="_blank">リンク2</a></li> </ul> </li> </ul>
Astroの構文にも書かれていますが、以下のような形式で記述すると、インデントと改行が少し崩れます。
<ul> { getLinkItems('specialContents').map(([key, array]) => ( <li><span class="dateLabel">{key}</span> <ul> { array.map((item) => ( <li set:html={generateAnchor(item)} /> ))} </ul> </li> ))} </ul>
<ul> <li><span class="dateLabel">2025-01-31</span> <ul> <li><a href="https://example.com/" target="_blank">リンク1</a></li> </ul> </li><li><span class="dateLabel">2025-01-30</span> <ul> <li><a href="https://example.com/" target="_blank">リンク2</a></li> </ul> </li> </ul>
ただし、この書き方であっても、私のリポジトリ上で適用している js-beautify を利用することでビルド時に綺麗な出力ソースコードを得ることができます。
おわりに
私は昔から BeautifulCode を意識して HTML コーディングを行ってきました。しかし、私以外の Web アプリケーション開発者の実装を見る機会が増えてくると、出力ソースコードを美しく保つことに関心のない人や、フレームワークが増えていることに歯痒い思いをしてきました。
入力ソースコードを美しく保つことは、コードレビューやコード保守の観点から関心のある人は多いようです。フロントエンドの開発現場では Prettier を導入することは常識的になってきています。しかし、出力ソースコードについては、圧縮することが正義と言わんばかりに闇雲に圧縮する人が多い印象です。その圧縮に何の意味があるのかと尋ねても、的確な答えが返ってきたことはありません。
HTML, CSS, JavaScript の出力ソースコード圧縮は止めたほうがよいと私は思っています。JSON ファイルを管理する際もそうです。Minify するのではなく Prettier しましょう。その出力ファイルを直接開いたときに可読性が担保されている状態を目指しましょう。改行がない巨大なファイルは、迂闊に開くとテキストエディタやターミナルがクラッシュします。
私の提唱する BeautifulCode という概念に賛同してくれる人に出会ったことがないのですが、ここまで読んでくれたあなたはどう感じますか?Twitter 等で本記事 https://beautifulcode.lavoscore.org/
について感想を頂けると嬉しいです。