モダンなWebプロジェクトにおけるベストプラクティス

Oktavillaでは、私たちは定期的に新規プロジェクトを立ち上げています。数年にわたって、私たちはこうしたプロジェクトを通してベストプラクティスを見つけ出してきました。そのおかげで、新規メンバーがスムーズにプロジェクトに参加できるようになり、エラーを減らすこともできました。こうしたベストプラクティスを、組織内部、クライアントを問わず大半のプロジェクトに活用しています。結果として、私たちは高品質のWebプロジェクトを実現しています。ここでお伝えするのは、そのプロセスの一部です。

このブログ記事では、技術面に関わるベストプラクティスに焦点を絞りたいと思います。例えばセットアップや、プロジェクトのツールやプロセスを選択する際に考慮すべきことなどについてお伝えします。各プラクティスの文末に、詳細な情報へのリンクをいくつか貼っています。

READMEファイル

まずは、プロジェクトで最も重要なファイルから始めましょう。

プロジェクトでは、ルートディレクトリにREADMEファイルを置かなくてはなりません。このファイルには、プロジェクトの情報や作業の始め方などが記載されています。プロジェクトの概要や、どのように仕事に取り掛かればよいかについて、新しいチームメンバーがこのファイルを読めば簡単に分かるようにしておきます。

READMEファイルを書く時は、プログラミング環境の基礎知識しかない初心者を想定します。読み手が理解しやすいように、テキストのフォーマットにはMarkdownを使います。Markdownはソースコードのままで簡単に読めますし、HTMLにも変換できます(例えば、GitHubは同じことを自動的に行います)。

READMEファイルには少なくとも、以下のセクションが含まれています。

前書き

プロジェクトの目的の要約です。より大きなアーキテクチャの一部である場合はその中における役割も記述します。クライアント名など、最初に知っておいたほうがよいと思われる情報です。

依存関係

別途インストールが必要となる依存関係のリストです。例えばデータベースサーバやパッケージマネージャ、プログラミング言語などです。どのバージョンが必要なのかも忘れずに記述します。

ローカル開発環境のセットアップ

ローカルの開発環境を立ち上げて稼働させる方法を、プロジェクトの新規メンバーは、READMEファイルを読むことで簡単に理解できます。最新のプロジェクト規約を入手する方法を記載しておきましょう。例えば、必要なパッケージのインストール方法やアプリケーションの起動方法、テストの実施方法などを記載しておくと良いでしょう。

様々なタスクを実行する際に入力するシェルのコマンドなど、具体的な例をあげて説明しましょう。

環境設定

アプリケーションの設定に環境変数が使われている場合は、環境変数のリストを記載します。何のための環境変数か、説明を記述します。

デプロイ

プロジェクトに参加するということは、メンバーは自分の変更をデプロイできるということでもあります。本番環境やステージング環境へコードをデプロイする方法を記述します。

例えば、Herokuを利用する場合は、デプロイメントに使われる様々なGitのリモートのセットアップ方法やコードをプッシュする方法の例などを記述します。

バージョンコントロールとGit

まず何より大切なのは、バージョン管理システムを使うことです。できればGitを使用してください。それが最新かつ最適な管理方法です。大半の開発者が快適に使用しています。もし使っていなくても、使いたいと思っているシステムなのです。

いつでも展開可能な状態

マスターブランチを常に展開可能な状態にしておきます。いずれ、本番環境で稼働しているコードにバグが見つかり、すぐに修正しなければならなくなります。現在取り掛かっている作業を別のブランチで行っていれば、すぐにマスターブランチに戻ってバグの修正ができます。さらに、テストしていない新規作成のコードを、うっかりユーザに渡してしまう心配もないので、安心して展開ができます。

コミットメッセージ

コミットメッセージには情報を十分に書き込みましょう。短いタイトルをつけ、簡潔な本文で表すとよいでしょう。タイトル部分で変更の目的を説明し、変更が必要となった理由を本文に書きます。コミットメッセージとはすなわち、コードのドキュメンテーションです。

コミットを確認する人にとって有益だと思われる補足情報があれば、それも本文上に残します。課題トラッカー上で対応している課題があるならば、かならずそのリファレンスを記載しておきましょう。

プルリクエスト

GitHubBitbucketといった、ウェブベースのGit対応ホスティングサービスは、ブランチへのプルリクエストをサポートしているので、ぜひ使ってみましょう。コードの変更がマスターブランチにマージされてしまう前に、変更があることを他のチームメンバーに知らせてくれます。

ホスティングサービス上では議論を行うこともできますし、コードレビューにおけるコードベースのさまざまなパーツに関する知識を共有する場としても使えます。コードレビューは詳細に、そして徹底的に行うべきです。書き手以外の第三者が変更点に目を通し、コードがマスターにマージされる前に”承認”されることが重要なのです。

オープンな議論は大切ですが、かといって必要以上にレビューに時間をかけてしまうと、マージ作業が遅れてしまいます。妥当な時間内でレビュープロセスを迅速かつ円滑に進めることが、チームメンバー全員の責任です。

議論の場では誤解や不毛な消耗を避けるため、あえてフレンドリーな調子で臨んでみたらいかがでしょう。楽しい前向きな雰囲気を演出するために絵文字を使ってもいいかもしれません。

環境設定

ほとんどのアプリケーションには、なんらかの環境設定が必要です。データベース用接続URLや、外部サービスに対するAPI認証などのことです。

Unixライクなオペレーションシステムは、実行時に設定値をアプリケーションに付与するすばらしい手法を有しています。いわゆる環境変数という仕組みです。この方法を利用し、全てのアプリケーションの環境設定を環境変数として保存しておきましょう。

環境変数を活用すると、コードベースから独立した環境設定が可能となります。すなわち展開済みのアプリケーションのコードを変更することなく、簡単に設定値を変えることができるのです。

アプリケーションとは、特に認証の必要もなくいつでもオープンソースであることが望ましいものなのです。

パッケージマネージャ

全てのプロジェクトには、ライブラリとの何らかの外部依存関係があります。そしてほとんどのプログラミング言語には少なくとも一つは、関係のあるライブラリのインストールを管理できるパッケージマネージャが含まれています。例えばRubyにはBundlerがありますし、Node.jsはnpmを持っています。

“優れた”パッケージの大多数はセマンティック・バージョニングを採用しており、パッケージの新しいバージョンには何がアップデートされているのか分かりやすくなっています。バージョンに後方互換性があるならば、新しい機能を追加するか、もしくは単にバグを修正すれば良いのです。

デプロイ

デプロイメントは自動化されるべきです。人間の手による介入は最小限しか求められていません。READMEファイルの情報と正しい認証があれば、デプロイメントはシェル内でコマンドを走らせるだけか、もしくはアプリケーションの新バージョンをオンラインで更新するためにボタンを押すだけですむはずです。

ヒューマンエラーを避けるためにも、自動化されることが大切です。デプロイするためにはマニュアル作業が必須となると、遅かれ早かれ誰かが作業を忘れてしまったり、ミスを犯してしまったりするでしょう。デプロイスクリプトにもバグはありえます。でもバグが発見されて修正されてしまえば、同じバグはもう二度と起こらないのです。

アプリケーション環境

アプリケーションは、実行環境によって異なった動作ができるようになっているべきです。

例えば、エラーページにどれくらい詳細が表示されるか、ログファイルには何が入るのか、JavaScriptのコードから不要な文字を削除するか、アプリケーションはメールを送信すべきか、といったことは、アプリケーションが実行されている環境によって動作が異なってくるでしょう。環境を使い分けることで、一般的な方法でこういった特殊なケースに対処できます。

デフォルトのアプリケーション環境は、「開発」、「テスト」、「ステージング」、「本番」の4つです。開発環境は、アプリケーションをローカルで実行する際のデフォルトのモードです。テスト環境では、テストが行われます。ステージングと本番環境は、展開される際にアプリケーションが実行される環境です。本番環境は、実際にエンドユーザがアプリケーションを使用する環境です。ステージング環境は本番環境の複製で、本番へ移行する前に変更をテストするために使用されます。

アプリケーションは、どの環境で実行されるべきかを環境変数から判断します。通常これらの環境関数はNODE_ENVやRAILS_ENVなどと呼ばれています。

CDNを介した静的ファイル

本番アプリケーション用の画像やフォント、CSS、JavaScriptといった静的ファイルは、アプリケーションと同じサーバからエンドユーザに配信されるべきではありません。コンテンツデリバリネットワーク(CDN)を介して配信してください。静的ファイルを世界中のユーザへ素早く配信できるように構築され、最適化されています。より速く配信することでユーザエクスペリエンスが向上します。

CDN上のファイルの取得は、自動化されたデプロイメントのプロシージャに含まれるべきです。一般的な方法は、カスタムオリジンの設定とファイル名によるキャッシュバスティングができるCDNを使用することです。これらの機能を用いて、アプリケーションは本番環境で実行される際に、CDNを介して配信される全ファイルに、CDNホストとファイルコンテンツのハッシュを追加します。

例えば、スタイルシートのlinkタグは開発環境では次のように見えるでしょう。

<link href="/style.css" rel="stylesheet" type="text/css">

本番環境では以下のように見えます。

<link href="http://my-cdn.com/style-a06ae46033959f7563b20c5faff6f5e60175253f.css" rel="stylesheet" type="text/css">

CDNは欠けているファイルのリクエストを受け取ると、そのファイルを要求してアプリケーションからキャッシュします。ハッシュはファイルコンテンツに対して一意なので、CDNはアプリがマークアップに正しいリンクを出力するかぎり、常に正しいバージョンをフェッチします。また、この機能はアプリに対し、ファイル名に追加されたハッシュを持つ静的ファイルのフェッチに対処するよう要求します。これは、展開する際に、単純なルートの書き換えを行ったり、プリコンパイルタスクを実行したりすることで解決できます。

Amazon S3はCDNではないので、CDNのように使用されるべきではないということを覚えておいてください。よく間違われますが、Amazon S3はファイルを格納するサービスで、配信用に最適化されていません。Amazonが提供しているCDNはCloudFrontです。

最後に

プロジェクトはそれぞれ違うので、当然この記事で述べたプラクティスが全てのプロジェクトに有効というわけではありません。しかし、組織内でプロジェクトを立ち上げ、維持し、貢献するという一般的なプラクティスを続けることで、物事がよりスムーズに進みます。セットアップにかかる時間が減り、新しいメンバーがプロジェクトに貢献しやすくなります。ただし、プラクティスのドキュメンテーションが常に更新されているかは確認するようにしてください。

このようなことに関心がありますか? 私たちは現在、Oktavillaで私たちのチームに参加できる、細かいことにもよく目が届くようなJavaScriptとRubyの開発者を探しています。詳細はこちらにお問い合わせください

このブログ記事を校正してくれた同僚のGustaf ForsslundAlexis Felleniusに感謝します。