パロニムの Web フロントエンドの課題と改善 -ART OF CODE -

Tomohiro IKEDA
paronym
Published in
9 min readDec 8, 2019

--

この記事は, PARONYM Advent Calendar 2019 の 12 / 8 (Sun) の記事です.

技術スタック

わたしが入社する前の Web フロントエンドの技術スタックです.

  • Babel (ES2015 ~)
  • Vue.js
  • webpack によるバンドル

最低限のモダン環境という感じではあります. しかしながら, パロニムの Web フロントエンドは, TIG プレイヤー (動画プレイヤー) のみのページとなるので, いわゆる SPA (Single Page Application) ではなく(ルーターは利用していない), また, Flux も採用していません.

課題

ひとことでいうと, コードがきれいではありません. わたしが趣味で開発し続けてきた, XSound の方がきれいと言えるぐらいです. では, きれいでないコードとはどういうことなのか …もう少し具体的に掘り下げます.

デザインパターンやプログラミング作法などに反している

古くは, GoF の 23 のデザインパターン, 近年だと, Atomic Design や Flux といった設計, また, リーダブルコードなどで知られている作法に反したコードがプロダクトのコードになってしまっています.

GoF 23 のデザインパターン

具体的には, 双方向に依存したコードがあげられます. 一般的に, 双方向に依存したコードは, 変更に弱いことが知られていますし, Atomic Desigin にみられるように, 依存関係が一方向というのは大事です.

Vue.js の props やより上位コンポーネントにビジネスロジックを追い出すことで, 一方向依存のコードにできるのに, ES Modules を乱用して, 双方向なコードになってしまっています (現在も, リファクタリング進行中ですが, ここがもっとも困難な課題となっています).

Controller.js

import { Const } from './Const';class Controller {
//
}
export new Controller;

Const.js

import { Controller } from './Controller';class Const {
//
}
export new Const;

また, 細かいことではありますが, ブール型との比較をしたり, 否定演算子を無駄に使ったりと, プログラムを読みにくくするコードもたくさんあります.

// Bad
if (b === true) {
// do someting ...
}
// Good
if (b) {
// do something ...
}
// -----------------------------------------------------------------// Bad
if (!c) {
console.log('false');
} else {
console.log('true');
}
// Good
if (c) {
console.log('true');
} else {
console.log('false');
}

凝集度が低く, 結合度が高い

これは, 基本情報技術者の試験にも出題されるレベルのことですが, 先ほどあげた Controller というクラスに, 何でもかんでも詰め込んでいます(命名も適切でなくなります). これは, 一般的に, 凝集度が低いモジュールとされます (逆に, 単一の役割を担うモジュールほど凝集度が高い). また, ES Modules の乱用によって, あらゆるファイル (クラス) を結合させています (逆に, 引数のみで結合されているモジュールほど結合度が低い).

モジュールの独立性 (凝集度と結合度)
// Bad// ES Modules の乱用によって, 結合度が高くなる
import { Const } from './Const';
class Controller {
do() {
Const.do();
}
// 関係ないメソッドをどんどん追加し, 凝集度が低くなる
play() {
}
pause() {
}
startAnimation() {
}
stopAnimation() {
}
updateAnimation() {
}
// ...
}
export new Controller;//------------------------------------------------------------------// Good// 関係のないメソッドは他のクラスへ移動し, 引数で渡すことで独立性を高くする
// また, インスタンスの export をやめる (双方向依存を不可能にするため)
export class Controller {
do(callback) {
callback();
}
}
export class VideoPlayer {
play() {
}
pause() {
}
}
export class Animation {
start() {
}
stop() {
}
update() {
}
}

レビューされていないコードがプロダクトになっている

コードレビューは人間がすることなので, 当然ミスはつきものです. しかしながら, あきらかにレビューされていないコード (あるいは, ビジネス制約上, やむをえずマージされたコードも含む. また, そういうコードには, 最低限 TODOコメントを残しておくか, issue にしておくべきだと思います …) がたくさんあります. 上記にあげた 2 つの問題も, レビューがあれば防げたのでは ? というコードもたくさんあります.

大きく分類すると, 上記 3 つことが理由で, きれいなコードでないと思っています.

結果として,

  • テストができない
  • 新規ジョインにコストがかかる
  • 動画ストリーミングライブラリの入れ替えができない (video.js に依存している)

特に, パロニムでは, 今後 TIG LIVE (ライブ配信) を重視していくうえで, video.js に依存している状況というのは非常に問題でした.

改善

わたしが入社してから, およそ 1.5 ヶ月で取り組んだことは, きれいでないコードをきれいにすることでした. とはいえ, すべてをいきなり改善はできないので, 最大の問題であった, video.js に依存する状態をやめることから開始しました. つまり, UI コンポーネントのリファクタリングです.

ツールで改善できることはツールにまかせる

プログラミング作法にしたがっているかどうかは, ESLint でほとんど検証可能です. これを, git commit をフックとして実行するようにしました.

また, Storybook を導入して, コンポーネント単体で動作確認できるようにしました.

適切にツールを導入することで, エンジニアの負担を減らしつつも, 自ずときれいなコードになるように変えました.

設計論やイディオムにしたがう

プログラミング設計論 (古くは, 構造化プログラミング, GoF の 23 のデザインパターン, 近年では, Atomic Design や Flux) やイディオムにしたがうことは重要です. なぜなら, 正解不正解がはっきりしているので, レビューで無駄な議論が発生しませんし, プログラミングにおいて, 些細な決まりごとを頭から追い出すことは生産性を向上させるうえでも重要です.

独自の設計をしていると, 新規ジョインするメンバーの学習コストが高くなりますし, レビューで無駄な議論が発生します. また, イディオムから外れたことをすると, レビュワーが何か意図があるのかと質問をしてしまいます (質問と回答のコストがかかります).

今回のリファクタリングでは, Atomic Desigin という広く知られた設計論にしたがうことで, 正解不正解の判定材料を敷き (それでも判断できないことは, レビューで議論すればいい), 新規ジョインするメンバーの学習コストを低くできるようにしました.

段階をふむ

これがもっとも大事なことかもしれません. 大企業であれば, ジョインしているメンバーも多く, すべてをいきなり改善することも可能かもしれないですが, パロニムのような, メンバーが少なく, ビジネスチャンスも限られている環境でリファクタリングする場合, 現実解として無理です.

例えば, Storybook を導入したものの, 画像回帰テストまで導入しなかったのは, Storyboook の学習コストやStory ファイルの記述によるコスト増 … などを踏まえて, 画像回帰テストまで導入すると, ビジネスチャンスを失うと判断したからです. 現場のプログラミングは, 研究や趣味とは異なります. エンジニアリングの理想を目指すことは大事ですが, ビジネス的な制約も踏まえる必要があります.

今回のリファクタリングでは, UI コンポーネントの依存関係の解消 (双方向依存の解消) ができれば十分だと思っています.

きれいなコードにしていくためにやるべきことはまだまだあります.

  • テストコードの導入
  • 画像回帰テストの導入
  • TypeScript の導入

… しかし, あくまでも段階的にです.

また, パロニムの開発体制には, まだまだ課題がたくさんあります.

Pull Requestを課題にあげても, 粒度の問題, レビュワーことを考えた補助コメント, レビュワーとしてのスキルの底上げ …

しかしながら, そんな課題を解決しつつも, 世の中にない機能開発することが楽しいと思えるエンジニアであれば, 非常に適切な環境と言えるのではないでしょうか.

大きな組織で, すでに完成されたなかで, 生産性と品質のみをひたすら求められて開発することにあきているエンジニアには刺激のある環境だと思います !

--

--

Tomohiro IKEDA
paronym
Writer for

Multimedia (Video, Audio) Engineer. I’m expert in Sound Signal Processing and Streaming (hls.js / shaka-packager contributor). C/C++, JavaScript 💖🎸🎹