【フロントエンド】新規 Web アプリケーション開発案件をふりかえってみた
wakamsha
4 ヶ月あまり続いた新規開発案件がようやく落ち着きを見せたので、ここらで振り返りをしてみたいとします1)リリースまでに巻き取れなかった不具合や未実装箇所が幾つか残っているので、まだ気持ち的には終われていないのですが…。
サービスのおおまかなアウトライン
- コンシューマ ( 一般ユーザー ) 向けサービス
- ブラウザで動く Web アプリケーション
- ワンソースによるレスポンシブレイアウト
- サポートするブラウザは IE9 以降や Android4.x 以降のモダンブラウザのみ
- Ruby on Rails
- 多言語対応あり
- SEO 対策はそれなりに必要
- わりとフワッとしか要件が決まっていないままスタートしたので、TRY & ERROR を繰り返しながらの開発
すごく大雑把ですが、だいたいこんな感じの Web アプリケーションです。これを踏まえてフロントエンドをどのように開発していくかを設計していきました。
設計思想 ( 開発前 )
フロントエンド担当は僕を含めて二人。主担当は僕ではなく、もう一人のエンジニア君でした。当初僕は別件を掛け持ちしていたので、僕は主にコードレビューやちょっとした修正等で精一杯だろうと考えていました2)スクラム開発案件を掛け持ちというトチ狂ったことをしていました。。
HTML
- テンプレートエンジンは Slim を採用
- セマンティックなマークアップは後回しにして、まずは
div
タグをメインにして構造化だけを進める - ただし、自明な箇所に関してはエンジニア個人の判断でセマンティックなタグを記述する
- パーシャル化などの共通化はそこまで徹底せず、後から共通化出来そうな箇所に対してコメントを書いておく
最初はとにかく動くものを素早くアウトプットして全体の方向性を定めていく必要があるので、あまりコードの美しさにはこだわらないようにしました。その辺りに関しては、後述するリファクタリングで改修していけば良いだろうと思ったわけです。
CSS
- CSS は Bootstrap3 をベースにしつつ、足りないところは独自のコンポーネントを新規に作って継ぎ足していく
- CSS の命名規則は Bootstrap に依存する箇所は Bootstrap の命名規則、それ以外の独自コンポーネントは BEM に則る
- どうしても Bootstrap コンポーネントをカスタマイズしたい時は、フレームワークに直接編集を加えて魔改造してしまう
- CSS プリプロセッサは SCSS を採用
- アイコンフォントは終盤まで FontAwesome を仮として使用し、終盤に全てのアイコンのデザインが Fix したらデザイナーからデータを受け取ってオリジナルのアイコンフォントを作成して差し替える
Bootstrap と BEM を組み合わせた命名規則に関しては過去のエントリーにまとめているので、そちらをご参照ください。
CSS のディレクトリ構造はこんな感じにしました。
stylesheets/
├── active_admin/
│
├── commons/
│ ├── _icon.scss
│ └── _text.scss
│
├── customs/
│ ├── _grid.scss
│ ├── _scaffolding.scss
│ │
│ ├── mixins/
│ │ └── _grid.scss
│ │
│ └── variables/
│ └── _variables.scss
│
├── fonts/
│ └── _icomoon.scss
│
├── inherits/
│ ├── _alerts.scss
│ ├── _button.scss
│ └── _forms.scss
│
├── mixins/
│ ├── _xxx.scss
│ └── _xxx.scss
│
├── modules/
│ ├── _xxx.scss
│ ├── _xxx.scss
│ │
│ └── pages/
│ ├── mypage
│ │ ├── _xxx.scss
│ │ └── _xxx.scss
│ │
│ └── top/
│ ├── _xxx.scss
│ └── _xxx.scss
│
└── variables/
└── _variables.scss
commons/ |
テキストやアイコンなど共通で使われるもの |
---|---|
customs/ |
Bootstrap コンポーネントをオーバーライドしてカスタマイズするもの |
fonts/ |
アイコンフォントに関するもの |
inherits/ |
Bootstrap コンポーネントを継承した独自コンポーネント |
mixins/ |
完全独自コンポーネント用の Mixin を定義 |
modules/ |
完全独自コンポーネント |
modules/pages/ |
ページ固有の独自コンポーネント |
variables/ |
カラーコードなど共通変数を定義 |
今こうしてふりかえってみると、commons/
と modules/
の違いが曖昧だなぁと思えてきますが、皆さんはどう思われますかね?
JavaScript
- 要件に SEO 対策があったのと IE9 をサポートする必要があったので、SPA3)Single Page Application の略 や PushState 等はナシ
- jQuery に関しては、Bootstrap が既に依存していることから、ゴリゴリの DOM 操作を極力避けるという条件付きでの使用を許可
- 習熟度がまだ不安だったのと必要性をそこまで感じなかったことから、MV* フレームワークの採用はひとまず見送り
- AltJS は CoffeeScript を採用
- なるべく Class 化する
JS 界隈の猛者たちの間では、jQueryが許されるのは小学生までだよねー キャハハなんて発言が散見してますが、用法用量を心得れば大丈夫だろうということで使用を許可しました。
jQuery オブジェクトを作る時に使うセレクターは id
や class
が一般的ですが、当案件では全てデータ属性をセレクターとして使うことにしました。class はあくまでスタイルを定義するためのものであって、CSS だけが使うべきです。デザインはいくらでも変更が入ります。当然それによってクラス名もしかるべき名称にどんどん変更してかなくてはなりません。もし jQuery 側でセレクターに Class 名を使用していたら、デザイン変更のたびに JS 側にも影響がないかチェックしなくてはなりません。結果として、これは大成功だったと思います。当案件が始まる直前に某先輩からこの設計案を教えてもらっていたので、運良く最初から導入することが出来ました。
<div class="hoge">Lorem ipsum dolor</div>
<script>
var $hoges = $('.hoge');
</script>
<div class="hoge" data-foo="bar">Lorem ipsum dolor</div>
<script>
var selector = '[data-foo="bar"]';
var $bars = $(selector);
</script>
曲がりなりにもエンジニアですから、やっぱり技術的挑戦はしたいですよね。これを期に AngularJS か Ember.js あたりを本格的に導入してみたかったのですが、そもそも事前の学習が不十分だという理由からいきなり実践導入は良くないよねという話になりました。開発と平行しながら各自で学習して、タイミングを見計らって検討することにしました。まぁ途中から AngularJS や Ember.js を導入出来るとは思っていなかったので、やるとしたら Backbone.js + Marionette.js になるのかなぁと思っていましたが…。
設計思想 ( 開発中盤以降 )
HTML
開発も中盤に差し掛かったことで、少しずつ形が見えてきました。それまで重複しまくっていたコードを積極的に共通化していきます。が、汎用性への過度な執着は後の変更や断片化への足かせになりかねないのと工数の浪費を招くので、適度に妥協する勇気が求められるなと思いました。
また、適宜リファクタリングをしていくことで、少しずつセマンティックなコードに整えていきました。
CSS
SCSS フレームワークに Bourbon を導入
普段から Middleman を愛用していたせいもあって、てっきり Rails には標準で Compass がバンドルされているものだと勝手に思い込んでいました。が、いざ使おうとしてみたら入っていなかったので、より軽量な Bourbon を導入しました。Compass との互換性が高く Mixin 名も殆ど同じなので、Compass を使うのとおなじ感覚でコーディングすることが出来ました。
コメント記述やプロパティの記述順などのコーディング規約を定義
フロントエンドは僕を含めて二人で開発していたのですが、それぞれコメントの書き方やプロパティの書き方に違いがあったため、途中からではありますが、ルールを設けることにしました。
margin
、padding
などは一行でまとめて記述する- プロパティ名以下の順序で記述する
- ベンダープレフィクスあり
- ノーマルなプロパティをアルファベット順で
- Mixin 呼び出し
ごく一部ですが、だいたいこんな感じの規約を定めました。これについては後述する反省点に繋がります。
Hologram を( 一部に ) 導入
フォント、ボタン、カードといったデザイン要素のサイズについて、デザイナーとの間でルールの取り決めをしました。そのタイミングに併せて、「せっかくだからドキュメント化しておこう」という話になり、ちょっとした技術的挑戦ということで Hogoglam を導入してみました。まだごく一部しか記述できていないので全然ドキュメントとしてお見せ出来るシロモノではありませんが、徐々にリファクタリングしつつ整備していきたいところです。
JavaScript
Vue.js を一部に導入
リッチな SPA ではないにしろ、この頃から JS 周りがきつくなり始めてきました。と言っても jQuery を駆使して DOM 操作しまくれば全然実現出来る範囲の表現なのですが、一度それに手を出してしまったらおしまいなので NG です。
考えた結果、比較的学習コストが低い Vue.js を試しに導入してみることにしました。Angular.JS といったフルスタックなフレームワークと比べると軽量で機能が少ないですが、Viewに特化していることが功を奏して上手い具合に導入することが出来ました。後発なフレームワークなだけあって非常に使い勝手が良く設計されており、これからMV*フレームワークを学んでいきたいという方にはうってつけではないかと思います。また、 Angular.JS からインスパイアされていることもあり、ゆくゆく Angular.JS にステップアップするための足がかりにしたり、Web Component っぽい使い方も出来るので、そういった開発を修得するのにも効果的かと思います。
React.js?知らない子ですね。
反省点
CSS
CSSComb の存在を忘れていた
プロパティの記述順、インデント、スペースの取り方、カラーコードの表記方法などを定義することが出来るプラグインです。さまざまなテキストエディタや IDE に対応しており、設定も JSON ファイルで管理するので簡単に共有することが出来ます。プロパティはアルファベット順という規約を設ける際にこいつを導入するべきでした。僕は普段からそのように記述するクセが付いているので何の負担にもなりませんが、普段順序を意識しない人にとっては多少なりとも負担になったそうです。反省。
Bootstrap は assets
以下で管理する
Rails 開発においては Bootstrap を gem で導入するのが一般的なのかもしれませんが、これだとフルセットで読み込まれることになります。Bootstrap はあくまで土台なので全てのコンポーネントを使うわけではありません。不要なコンポーネントは除外して少しでもファイルサイズを削減したり、不毛なオーバーライドを避けるよう努めるべきでした。反省。
アイコンフォントの差し替えが意外と手間取る
オリジナルのアイコンフォントは IcoMoon というサービスを使って作成します。開発終盤にようやく全てのアイコンが出揃ったため、ササッとアイコンフォントを作成して単純にフォントファイルを差し替えてクラス名を書き換えれば OK だと考えていたのですが、いざ差し替えてみると同じフォントサイズでも微妙に大きさが異なるためにレイアウトに僅かなズレが生じてしまいました。オリジナルで行くことが決まっているならば、仮当て用のアイコンを一つだけでも用意して、最初から IcoMoon で作ったものをとりあえず当てておき、最後にアイコンフォントを作りなおして、クラス名を差し替える方がずっと楽だなと思いました。反省。
JavaScript
AssetPipeline を全く考慮に入れてなかった
Rails で作ったアプリを本番環境ないしステージング環境にデプロイすると、AssetPipeline が発動して HTML、CSS、JavaScript が圧縮されます。圧縮レベル MAX で行うので、改行、インデント、コメントが消えるのは勿論のこと、JS であれば変数名や関数名も全て圧縮(書き換え)されます。それ自体は以前から認識していたのですが、クラス(関数)名が書き換えられたことによって一部動かなくなる不具合に遭遇してしまいました。設計ミスというか汎用性を追求しすぎたあまりにはまってしまった落とし穴と言えます。厄介なことに、再現するのがステージング環境や本番環境のみなため、原因を突き止めるのにやたら時間がかかるのと、修正して確認するのもいちいちステージングにデプロイしなくてはならないというのが非常に骨が折れました。しかも不具合が発覚したのが、リリース直前の QA (動作確認テスト)時ということもあり、相当焦ったのを覚えています。反省。
Vue.js をもっと積極的に使いたかった
ある程度開発が進んでから導入したために、使いたくても工数の兼ね合いから妥協せざるを得ない箇所が幾つかありました。ほぼすべての View が Rails 側でレンダリングされてしまっているために、v-component を使ってフロントエンド側で動的にレンダリングしたくとも、JSON を吐き出すための API から作りなおさなくてはならないなどの課題があったためです。結局そういう箇所に関しては v-component でのレンダリングを諦めるか、Rails 側で非表示状態の DOM をレンダリングして、そのデータを v-component から拾うなどといったバッドノウハウを駆使して乗り切りました。ここは後の機能追加等のタイミングでサーバー実装担当にお願いして一緒に作り変えてしまいたいところです。反省。
やってよかったこと
反省点ばかりではなく、やってよかったことも書いていくとします。
こまめにリファクタリング
正直なところスケジュールに余裕は全くありませんでしたが、定期的にリファクタリングはしていこうと頑なに決めていました。プロダクトオーナーは「そんなことしてるヒマあるなら、早いとこ画面作れや!とにかく動くもの作れよ!ここバグってんじゃねーか(#゚Д゚)ゴルァ!!」みたいな様子でしたが4)実際はこんなこと言ってません。苦い顔をしていたのは本当ですが、えぇ…、後になって技術的負債に悩まされるくらいなら、例え進捗が滞ってもキッチリ整備するべきだと信じて敢行しました。
結果として、開発終盤には不毛な修正パッチや全く呼び出されないコードなどは殆どなくなり、つまらないデグレを引き起こすこともありませんでした。波もありましたが、最終的には遅れることなくリリースまでこぎつける事が出来たので、良い施策だったと思います。
editorconfig の導入
エンジニア間でテキストエディタの設定を統一するために導入しました。不要なスペースの削除やインデントサイズなど簡単な内容だけですが、意外とそういった所の違いがコードレビューの妨げになったりするので、きちんと足並みを揃えておくことにしました。さまざまなテキストエディタや IDE に対応しており、設定もファイル一枚を任意のディレクトリに配置するだけとシンプルなので、気軽に導入することが出来ます。
おわりに
CSS に関してはそれなりに自信があったので、ちょっとした要件もトリッキーに対応出来たりしましたが、JS に関しては単純に功夫(クンフー)が足りないなぁと痛感しました。