読者です 読者をやめる 読者になる 読者になる

5年モノのサービスに1ヶ月で Sass(SCSS) を導入したお話

皆さんこんにちは、メディカルサービス部エンジニアの中村です。
好きなブキはスプラチャージャーワカメです。

先日、5年程稼働しているサービスへ Sass(SCSS) を導入したのでその技術的知見を共有させていただきます。

f:id:yuzurunakamura:20160128143430p:plain

なお、Sass は現在 SCSS Syntax が主流の為、CSS プリプロセッサについては Sass, ファイルタイプについては SCSS と表記します。

状況と経緯

メドピア株式会社では、医師限定サービス MedPeer を運営しています。
MedPeer サービス内ではさらに、薬剤評価掲示版、症例検討会、症例相談、ディスカッションなど、機能の異なる約10種のサービスを提供しております。

2011年頃、MedPeer サービスは現在稼働している Web アプリケーションフレームワークに移行しました。
長らく運用されてきたサービスのフロントエンドは、外注や様々な経緯を経て以下のような問題を抱えておりました。

  • Bootstrap v2.0.2 のまま。しかし bootstrap.css が大量に書き換えられておりバージョン追従不可
  • DOM が Grid system に沿っていない
  • bootstrap.css に一部でしか使われていないスタイルが大量に定義、さらに個別サービス用 CSS にも重複したスタイル定義
  • CSS 構文エラーが大量に存在する
  • HTML テンプレート内の style 要素に直書き
  • !important の嵐。そして !important をオーバーライドする為、別箇所でさらに !important の嵐...

要するに、長年運用されてきたサービスによくある技術的負債が積もった状態でした。

HTML テンプレートの DOM を見直したい、CSS も見直したい、UI も見直したい。しかし量が多い。
やりたいことは幾つもありましたが、問題を切り分け、Bootstrap3 に対応した新しい HTML テンプレートへの移行を少しずつ進めつつ、サイトデザインやボタン類に利用されている CSS 部分を、既存のこんがらがった bootstrap.css や他 CSS からサルベージし、新規に CSS を構成し直すことにしました。

そして、折角なのでこのタイミングで Sass へ移行する舵取りを行いました。
CSS をスクラッチで大量に書き換える必要がある為、CSS プリプロセッサを導入してもっと楽で管理しやすい形で書きたいよねというのが動機でした。

Bootstrap は v4 から less から Sass になったことと、デザイナからゆくゆくは Compass Helper Functions も使いたいとの要望を考慮に入れ、less ではなくSass 導入を決めました。

Sass 導入プラン

導入プランとして、以下の3案がありました。

1. *.css をすべて *.scss に一括置換し、リポジトリから CSS はすべて無くす。

1.は、リポジトリから一括で CSS を排除するプランです。
CSS はそのままでも SCSS Syntax として読み込み可能なので 、機械的に一括置換し後々 SCSS を整理していこうというプランです。

しかし、既存コードの中には CSS 構文エラーやブラウザハック系の CSS が含まれており、置換後の SCSS から生成した CSS が当初の CSS と異なるケースがありました。
さらに、機械的に構文エラーを修正すると、適用されていなかったスタイルが適用されるようになり、思わぬ所でデザイン崩れが発生する可能性もありました。
構文エラーの種類によってはブラウザ側で許容し適用してくれていた箇所もあり、これもまた当初のデザインと異なる可能性が否定できません。

一括置換しただけではスタイル定義重複などの煩雑な構成が解決されないことからも、このプランは見送りとなりました。

2. CSS をすべて SCSS に手動で全部一度に書き換える。エンジニアとデザイナは死ぬ。

2.も、リポジトリから一括で CSS を排除するプランです。 HTML も CSS も問題があるから両方全部直したい!というよくあるプランですが、一度に複数のことをやろうとすると大抵破綻します。 他の開発案件も当然ありますし、作業量が膨大になることからこのプランも見送りとなりました。

1, 2共に E2E テストを併用した HTML, CSS 同時改修も検討したのですが、この記事の執筆時点で MedPeer のリポジトリには、HTML テンプレートが451ファイル・53701行、CSS が123ファイル・47294行があり、ユーザの属性ごとに出し分けするコンテンツも多く存在する為、一度に行うのは困難だと判断しました。

3. 移行期間中、一時的に CSS と SCSS が混在することを許容し、各サービス単位で徐々に移行する。

最終的には無難なプランでの導入となりました。

CSS と SCSS がリポジトリ上に混在することで、SCSS から生成された CSS を別の人が直接編集し、SCSS と CSS の整合性が取れなくなる懸念がありますが、SCSS から生成する際には compressed されたワンライナーCSS にすることで、誰が見ても生成された CSS と分かるようにしています。
最初の段階で共通 CSS はすべて SCSS にしておき、新規ページも原則 SCSS で作成することで、徐々に CSS を減らしていきます。

CSSリポジトリから無くすのが最終目標になります。

Sass 導入

導入プランも決まった所でいよいよ Sass 導入です。
導入に際して以下を実施しました。

  • Node.js, gulp 環境の構築
  • コーディングスタイルガイドの策定
  • scss-lint の導入
  • Browsersync の導入

Node.js, gulp 環境の構築

Sass 導入にあたって、デザイナでも簡単に環境構築できる環境が必要です。
極力 $ npm install 一発で済むように package.json に gulp も含め管理するようにしました。 global でなく local にインストールした gulp を使うようにしておくことで、環境差異をできるだけ少なくしています。

local の gulp 実行は、package.json"scripts" に以下のように記述するとOKです。

  "scripts": {
    "start": "gulp default",
    "gulp": "gulp",
    "scss": "gulp scss",
    "scss-lint": "gulp scss-lint"
  },

上記の場合、 $ npm startgulp default が実行されます。

コーディングスタイルガイドの策定

社内には PHP のコーディングスタイルガイドはあったのですが、CSS に関してはありませんでした。 過去の技術的負債はサービス開発全体に於けるコーディング流儀が統一されていなかったことにも要因がありますので、この問題にも対応しなければなりません。

ちょうど弊社の凄腕フロントエンドエンジニアがスタイルガイド策定準備を進めていてくれたので、このタイミングで社内公開を行いました。
あくまで社内向けスタイルガイドの為この記事では公開しませんが、概ね以下の著名なスタイルガイドを継承したものになります。

しかし、コーディングスタイルガイドを策定したからといって、すべてのコードがスタイルガイドに従ったものになるとは限りません。

scss-lint の導入

すべてのコードをスタイルガイドに合わせるべく、 scss-lint も併せて導入しました。

  • 後で導入すると、既存コードの修正は放置されがち
  • 早い段階で一定のフォーマットに整えておき、後程の新たな技術的負債を防ぎたかった
  • 過去に既存リポジトリPHP CodeSniffer と PHP Mess Detector の導入を試みたが、なかなか徹底されず頓挫してしまった
    • 一方、当初からCircle CIと共に導入した別プロジェクトでは問題なくlint活動が行われていた

などが主な動機です。

SCSS の lint ツールは幾つかありましたが、コード内で特定箇所のみルールの除外が可能だった為 Ruby 実装ではありますが scss-lint にしました。
できれば Node.js だけで完結したかったのですが、Node.js 実装の lint ツールは現時点ではルールの除外ができなかったり、CLI 出力やオプション設定などで総合的に満足いくものが現状ありませんでした。

gulp-scss-lint と併用し、gulp watchで *.scss 変更時に自動で lint 警告を gulp log に流すことで lint の徹底を行っています。

GitHub - brigade/scss-lint: Configurable tool for writing clean and consistent SCSSgithub.com github.com

インデントやシングル/ダブルクォートなど、細かいフォーマットを指摘は lint ツールに任せた方が角が立たずオススメです。
前述のスタイルガイドは .scss-lint.yml に反映し、自然とスタイルガイドに沿ったコーディングになるようにしています。

また、 csscomb も適宜利用し、自動での修正もサポートしています。
PropertySortOrder などの修正は面倒ですからね。

Browsersyrc の導入

このタイミングで以前から個人的に使っていた Browsersync も配布しました。
大量に SCSS を書き換える際に抜群の効果を発揮します。

Browsersync については以前こんな記事を書きました。

tech.medpeer.co.jp

Browsersync 利用時の WebFont の Cross-Origin Resource Sharing 設定

defalut では Browsersync は localhost:3000 で動作します。
その為、CDN に WebFont を置いている場合には Cross-Origin Resource Sharing policy( CORS 設定) の為に読み込みが行われません。

とはいえ、CloudFront や S3 の CORS 設定に localhost:3000 を許可してしまうのはセキュリティ上好ましくありません。
そこで今回は、Browsersync 用の hosts を追加し、CORS 設定で許可することで WebFont も読み込み可能にしました。

127.0.0.1 browsersync.example.com
browserSync({
  host: 'browsersync.example.com',
  open: 'external',
});

(個人的な話) Vim

個人的に Vim 上でも scss-lint を実行するようにしました。

Vim での Syntax Check Plugin は Syntastic が著名ですが、同期実行の為 Vim が固まりがちなのが課題でした。
vim-watchdogs は非同期実行の為、この懸念がありませんのでこのタイミングで乗り換えました。

おおよそ以下のような設定を書くことで、指定の .scss-lint.yml を読み込んで実行が可能です。

let g:quickrun_config = {
  \ 'watchdogs_checker/_': {
  \   'hook/time/enable': 0,
  \   'hook/back_window/enable_exit' : 1
  \ },
  \ 'scss/watchdogs_checker': {
  \   'type': 'watchdogs_checker/scss-lint',
  \   'cmdopt': '-c $HOME/.config/.scss-lint.yml'
  \ }
  \}

let g:watchdogs_check_BufWritePost_enable = 1
let g:watchdogs_check_CursorHold_enable = 1
let g:watchdogs_check_BufWritePost_enable_on_wq = 0
call watchdogs#setup(g:quickrun_config)

詳しくは vim-watchdogs/README.md を参照してください。

また、CSScomb も vim-csscomb を利用することで :CSScomb で実行可能にしています。

社内周知と公開

さてさて、上記のような環境を用意しましたが、突然「使ってください!!!!」といっても、Sass 経験のないエンジニアや Terminal に慣れてないデザイナにはとっつきにくいかもしれません。

メリットを上手く説明し、納得して使ってもらう普及活動が必要です。
恐らく、新しい環境を導入する際に、最も重要で、最も難しいポイントです。

どうやって周知するか悩んでおりましたが、ちょうど弊社では月一でピザを食べつつ LT を行う、通称ピザ会を開催しているので、この時に Sass と開発環境の説明を行いつつ、社内公開を行いました。

なお、突然 LT で「merge しろ!!」「使ってくれ!!」 といっても「何言ってんだこいつ」となるかなと思ったので、関係者ほぼ全員に事前に承認ラリーを行いました。

tech.medpeer.co.jp

マイクロソフトVisual Studio Code をイベント中にライブで GitHub に公開したように、その場で Pull Request に LGTM を貰いカッコ良く公開したかったのですが、カッコ良くは行かなかったのが反省点です。

とはいえコレで Sass 環境の配布が完了しました。

まとめ

以上の流れで MedPeer では Sass 導入を実施しました。 Slack を眺めて振り返ってみたところ、大体1ヶ月程度で導入まで漕ぎ着けることができました。 導入しようと決めてからは、あれこれ決めることが多かったのですが、思ったよりすんなり進められた印象です。
今後は Sass 移行を進めつつパーシャルファイルに分け、modularize を進めていくことになりますが、CSSに比べ格段に書きやすいので以前より開発が進めやすくなったと実感しています。

Sass 導入を検討されている方の参考になれば幸いです。