ユニットテストをしよう

2018年6月11日(月)
佐々木 俊介
【最新JavaScript開発~ES2017対応モダンプログラミング】 株式会社インプレスR&Dより発行された「最新JavaScript開発~ES2017対応モダンプログラミング」の立ち読みコーナー第5回です。

Appendix JavaScriptの歩き方

 本書ではJavaScriptについて、モダンな言語仕様、ユニットテスト、型などについて取り上げてきました。ここでは付録として、JavaScriptエンジニアとしてのコツを紹介します。

A.1 できる限り公式ドキュメントを読む

 一番重要なコツはこれです。JavaScriptで資料を読む場合、できるかぎり公式ドキュメント1を読むべきです。Node.jsのAPIであればhttps://nodejs.org/api/ですし、Reactであればhttps://facebook.github.io/react/docs/です。

 ECMAScriptの仕様を調べる際、MDN(Mozilla Developer Network)はある程度有用ですが、やはり公式ドキュメントであるECMAScriptの仕様書2を読むのが一番確実です。

 日本人が作ったプロダクトならともかく、たいていは英語です。日本語翻訳も一部ありますがあまりお勧めしません。なぜなら、ほとんどの日本語翻訳は過去のものになっているためです。

 もちろんちゃんと最新版に追従しているプロジェクトならば日本語翻訳を読むのも一つの方法ですが、リアルタイムで本家のドキュメントを翻訳してる事例は希です。やたら古い翻訳などもあり、むしろ有害なくらいです。

 ましてやJavaScriptは数多の言語の中でも特に開発が活発な言語であり、さまざまな概念・フレームワーク・ライブラリが生まれては消えている状況なので、翻訳がまったくもって追いつかないのです。検索をして日本語翻訳がヒットした場合は本家を探した方がいいでしょう。

 JavaScriptの場合パッケージはnpmでホスティングされているため、npmを探すことがとても多くなります。検索ワードにnpmを含めるとパッケージの話に絞り込みやすくなります。たとえば「MySQL npm」のような検索ワードでMySQLにアクセスのできるパッケージを探せるでしょう。

 公式というのはドキュメントに限りません。オープンソースプロダクトならソースコードも重要です。大半のプロダクトはGitHubでホスティングされていることから、GitHubをよく参照することになります。GitHubではソースコード以外にもissueやwikiがあります。自分のところで発生したトラブルが、実はissueで取り上げられているということはよくあります。

 英語が苦手な人はGoogle翻訳で大丈夫です3。Chromeにも翻訳機能があります。最近のニューラルネットワークを応用した自動翻訳はたいへん優秀な翻訳をしてくれます4

A.2 公式以外なら

 公式以外に読むべきは、stack overflow5やQiita6とjser.info7でしょうか。stackoverflowはトラブル関係で検索するとよくヒットします。Qittaは日本語でまとまったノウハウ集としての価値があります。jser.infoはJavaScriptの情報を広く集めている方のサイトなので、JavaScriptについて俯瞰するうえでとても有用です。

 stackoverflowは検索経由でたどり着くことがほとんどでしょうし、Qiitaはかなり玉石混淆なところがあります。そこらへんは注意した上でバランスを適切に守っていれば、役に立つと思います。

 検索をする時には、「js」とか「npm」のような検索ワードを追加すると絞り込みやすくなります。言語仕様にまつわる問題は前者で、パッケージにまつわる問題は後者ですね。ECMAScriptの新しい要素ならES2015、ES2016、ES2017といった検索ワードも有用でしょう。async/awaitなどは元々はC♯に登場した構文のため、jsでの絞り込みが重要になってきます。

 SNSを活用するのも一つの方法ですが、Twitterの場合、みんなJavaScriptの話題ばかりしゃべっているとは限りません。むしろ別の話題をしている人が大半のため、S/N比は悪いと考えた方がいいでしょう。

 Podcastを聞くというのも良い方法です。mozaic.fm8やrebuild.fm9などが日本語では有名なところでしょうか。ただ前者はJavaScriptというよりウェブ技術のもので、後者はそもそもJavaScriptの話題が出るとは限りません。

A.3 技術選定には注意する

 技術選定はJavaScriptに限らず難しいものですが、JavaScriptは人気があり新陳代謝の激しい世界であるため、他の技術よりも難しい傾向があります。実際のところ、いろいろなツールや手法が登場しては消えています。そのため、できれば技術選定の際にはせいぜい2・3年単位を尺度にして考えるべきでしょう。長いものに巻かれるのも手ですが、長いものも簡単に非主流に転落することが多いことを念頭に置いておきましょう。

 スタープログラマとして有名な人が選択する技術が生き残るとも限りません。むしろ技術力の高い人は技術選択をミスしたところで乗り換えに困らない可能性もあるので、技術力の高さや有名度は、その人が選択する技術の将来性にあまり直結しません。これを忘れると失敗することになりやすいので注意しましょう。

 そのため、JavaScriptでうまくやっていくためには、いつでも技術の乗り換えができるように技術のアンテナ感度を上げておくべきです。

 あとは基礎が重要です。基礎をしっかりこなしていれば、技術の乗り換えもそんなに大変ではなくなります。技術者としてうまくやっていくなら、JavaScript以外の知見も重要になります。JavaScriptは色々な言語から影響を受けたマルチパラダイム言語であるため、他の言語を触るというのはヒントになりやすいのです。

 基礎知識の基準としては、個別の細かいやり方にこだわるよりは、より汎用的な方法や本質的な知識を得ることを重視すべきです。タスクランナーなどに時間を注ぐのはほどほどにすべきです。フレームワークの選定もあまり熱心に調べても仕方ないかもしれません。

A.3.1 その時代の定番にある程度従う

 情報量が多い定番を採用すると無難です。ただし重要なコツが1つあります。Google検索では期限の指定を駆使しましょう。一年以内のデータに絞るといいでしょう。

A.3.2 Githubのスター数を参考にする

 人気の高さはGithubのスター数に現れます。スター数が多いとそれだけ利用者も多いはずです。

A.3.3 技術選定の難しさ

 技術選定の難しさは、実際の技術の発展を振り返れば一目瞭然です。たとえば2007年時点、つまり今から10年前にSSD全盛の時代が来ることを想像できたでしょうか?カジュアルにRDBをSSDで構築できる時代が来ると想像できた人はそんなに多くないでしょう。

 現代はAWS全盛10の時代ですが、AWSやGCP、Azureのようなクラウドサービスが覇権を握り始めたのはいつ頃でしょうか?10年前からAWS全盛の時代が来ることがわかっていた方は多くはないでしょう。

 一時期ずっと冬の時代だったニューラルネットワークの技術ですが、2000年代後半から深層学習として再度花開くようになりました。現代ではいくつかのサービスで実際に使われています。驚くほどの翻訳能力をもつGoogle翻訳や人間を遙かに凌駕する囲碁のAIであるAlphaGoなどがわかりやすいところでしょう。皆さんが深層学習について知り始めたのはいつ頃でしょうか。10年前の時点で深層学習に手をだしていた方はどれくらいいるのでしょう。

 2006年に生まれたjQueryですが、一時期はJavaScriptといえばjQueryでした。ですがjQueryを負の遺産と見なす現代の傾向を、当時予見できた人はあまりいなかったはずです。

 技術のはやり廃りを10年単位できっちり予測できるような人はかなりレアです。

 もっと短いスパンでみると、Dockerは2013年に登場したプロダクトですが、現代においてはインフラ構築で必須技術になっています。2013年時点ではまだchefやpuppet、あるいはAnsibleなどにスポットライトが当たっていた時代でしょうか。Reactも2013年です。今となってはReactは欠かさざるメインストリームの技術です。つまり4年前ですら、趨勢を読み切るのは難しいのです。

 もちろん新陳代謝があまり無い、枯れた技術もあるでしょう。しかし、枯れたと思われているRDBですらSSDによるランダムアクセスの高速化やメモリの増大など、ハードウェアのトレンドの変化の影響をうけるのです。つい先日発表されたIntelとMicronが開発した3D XPointという技術では、SSDよりも早いランダムアクセスを実現していますが、この技術はそれだけではありません。メモリの永続化が可能なのです。他の会社でもこのような不揮発メモリと呼ばれる技術は開発されています。不揮発メモリがこなれてくればデータベース設計も大きく変わることになるでしょう。

式年遷宮という考え方

 皆さんは「式年遷宮」という言葉を知っていますか?伊勢神宮で社殿を20年に一度造り替えるという1300年以上続くやり方なのですが、耐用年数の低い建築技術だからとか、清浄さを保つために20年に一度作り直すなどの説がありますが、これはシステム開発のヒントになるのではないでしょうか。

 システムの耐用年数はどれくらいのものでしょうか?もちろん置かれている環境によりますが、サービス開発においては長くてもせいぜい3年程度であると筆者は考えます。本来の式年遷宮は20年ごとですが、さすがにIT業界で20年稼働し続けるのは困難です。5年クラスになるとたいてい大きなトラブルが住み着いています。

 非IT企業のSI案件などであれば事情も変わるかもしれません。しかし、ウェブサービスを中心としたビジネスの最前線では、顧客のニーズそのものがどんどん変化していくため、開発もそれに柔軟に対応できる必要があるのです。相次ぐ要求仕様の変更による「増築」はよほど注意深く行わない限りアーキテクチャやコードを汚します。よく技術的負債という言葉で語られるものです。

 また、長期稼働のシステムは大きなものになりがちです。規模が大きければそれだけ影響範囲も広く、メンテナンスしづらいものになります。大きくなったシステムでは技術的、政治的理由、あるいはひどい場合は決断できる人がいないために、いらないものを棄てることができなくなってしまう事例もよく見受けられます。

 流行りのマイクロアーキテクチャのように、小さいシステムをいくつか作った上で、すこしずつ新しいものに置き換えるという考え方もあります。システム分割はやりすぎるとデメリットが大きくなりますが、疎結合なシステムをいくつか組み合わせるというのは良いアーキテクチャです。

 バージョンアップの問題もあります。言語・ライブラリによりますが、5年なら大丈夫でも10年単位になってくると破壊的なバージョンアップが必要になる事例も珍しくありません。バージョンアップをサボって長期運用するシステムには、どうしてもバグ・セキュリティホールの問題が絡んできます。そのシステムが内製であったとして、社内だからいいや、という考え方は危険です。ましてや外部に向けてサービスしているものであればその影響は計り知れません。

 開発期間も見逃せないポイントです。3ヶ月以上開発にかかるシステムは、黄色信号がともっていると考えるべきです。もちろん業種によりますが、これが1年を越えるとけたたましくアラートが鳴り響いています。開発の遅れから仕様がさらに膨らんで開発がずるずる遅れる……という負のサイクルは、長期開発のプロダクトにありがちです。

 そこで、式年遷宮をシステム開発に導入してみましょう。数年で乗り換えられるようなシステムを最初から考えることが重要なのです。そのためには分割統治が重要です。疎結合なシステムを組み合わせて、個々のリプレイスが簡単にできるアーキテクチャを導入すべきでしょう。最初からすべてを織り込むのではなく、適切なシステムを都度開発していけるようにすることが重要です。もちろん拡張性や柔軟性はやりすぎると負債になりがちです。YAGNI原則を忘れないようにしましょう。

A.4 エンジニアが身につけるべき基礎力

 JavaScriptエンジニアが身につけるべき技術にはどういうものがあるでしょうか?

 実のところJavaScriptと他の言語でほとんど変わりません。

 まずどういう形にせよ基礎は絶対に重要です。コンピュータの仕組み、アルゴリズムや計算量の考え方、それぞれのI/Oにまつわる速度感覚(オンメモリとディスク上とネットワーク先のそれぞれのスループットやレイテンシ)の把握。オブジェクト指向、ドメインモデリング、デザインパターンなどのパターンランゲージ。ソースコード管理(今ならgit)やコマンドライン、UNIX/Linuxの最低限の知識、UMLの読み書き、デザインスケッチやモックの作成、チーム開発を円滑にするための知識と経験(アジャイルやリーンを含む)などは必須でしょう。良いソースコードとはなにかを追求しつづけることも必要です。

 顧客やステークホルダーが望むことをちゃんと形にできる設計力やコミュニケーション力も必要になることが多いでしょう。ユーザーが本当に望んでいることは何なのか。決して言語にしたことがすべてではありません。ユーザーは得てして間違ったことを口にしてしまいます。エンジニアがユーザーの本当に希望を正しくくみ取って、仕様に落とし込む必要があります。

A.4.1 名前をつける力

 個人的に筆者が考えるプログラマに一番必要な能力は、適切な名前を考える力だと思っています。設計も実装も適切な名前が決まれば半分くらい終わったようなものです。実装が名前に見合っているか?名前は実装に見合っているか?をそれぞれ双方から徹底的に考えることです。

名前は責務を抽象化したもの

 まず名前はその関数・メソッドが持つ責務を抽象化したものです。責務というのは、それが果たすべき役割の事ですが、この時の責務は、やり方(how)ではなくて、何をやるか(what)で表現します。

 たとえば価格を消費税込みにする処理を考えてみましょう。「商品価格に1.08を掛ける」よりも「商品価格を消費税込みにする」の方が何を指しているのかわかりやすくなります。商品価格を1.08倍するのが消費税の計算であるというのは現時点では正しいのですが、税率が変わった時に困るかもしれません。また、消費税の計算以外に1.08倍しなければならない処理が必要なときに破綻する可能性があります。

 つまり、「1.08倍する」ことはどうでもよいのです。必要なのは、「商品価格を入れれば、それが税込み価格になっている」ということになります。

 それに、whatをうまく抽象化できている名前ならば、リファクタリングがしやすくなります。howを元にした名前は中身が名前に縛られてしまうので、リファクタリングがしづらいですが、whatを元にした名前であれば、その処理の内容をブラックボックスとすることができるからです。

 名前を付ける上でもう一つ考えなければならないことはその名前が適切な抽象度かどうか?です。たとえばRPGを考えてみましょう。「キャラクタが攻撃を受ける」関数と「キャラクタがHPを減らす」関数は同じものでしょうか?もしそのゲームの攻撃がHPにしか関連が無い仕様で、かつそれが拡張される見込みが無ければ同じと考えても大丈夫でしょう。しかし攻撃を受けることで何らかのペナルティを受けるタイプのゲームも実際にはあります。この場合「キャラクタがHPを減らす」だけではなくて「キャラクタがペナルティを受ける」関数も別途必要になります。

 これは抽象度の違いといえます。この事例ならば「キャラクタが攻撃を受ける」=「キャラクタがHPを減らす」+「キャラクタがペナルティを受ける」ということになります。どちらの名前を採用する方が仕様変更に強いでしょうか?当然前者です。もちろん、プライベートな関数などで「HPを減らす」と「ペナルティを受ける」を分けることはありえますが、APIとして外部化するなら確実に「キャラクタが攻撃を受ける」の方が適切でしょう。

 もし「キャラクタが剣で攻撃を受ける」と「キャラクタが矛で攻撃を受ける」が別の名前に分かれているなら、おそらくそれは抽象度を見誤っています。「HPを減らす」が「HPを10減らす」となっている場合も同様でしょう。抽象度が低すぎて、より具象的な関数(具体的な要素が強い)ということです。

 すこし長くなりましたが、名前は責務を抽象化したものです。関数・メソッドを例にしましたが、変数名も同じです。どういう意味合い・責務を負うべき変数なのかを考えて名前を付けると良いでしょう。

名前から適切な責務のサイズを見直す

 ここまでは責務を名前として表現する話でしたが、付けられた名前から逆に見直すという話です。

 たとえば「HogeとFugaを計算する」という意味の関数があった場合はどうでしょうか?英語で書くなら「calcHogeAndFuga」でしょうか。これはHogeの計算とFugaの計算という二つの役割を持っていますが、Andが登場してしまい、責務が複数になっています。では、それぞれ分けることはできませんか?もしトランザクションのようなアトミックな更新が必要な処理であれば仕方ありませんが、その場合は何かもう少し違う名前が考えられそうです。

 責務を形にしたのが名前である以上、名前が長いということは責務が多すぎる兆候です。責務が多すぎるクラスや関数・メソッドは、中身が密結合になっていてプログラムを複雑化する主原因となります。なるべくシンプルな名前にできるように責務をきれいに分割しましょう。

 もしあなたが作ったクラスがHogeManagerとかHogeContollerとかであれば、そのクラスは責務がぼやけている可能性があります。manager、contorollerのような何を管理・制御するのかよく分からない名前を付けたくなる場合は、責務がはっきりしていないのではないでしょうか?

名前の持つお約束

 名前とは、コミュニケーションのインターフェースでもあります。getHoge()と書かれたメソッドはhogeという値を取得するメソッドだと読み取れるはずです。実はgetHoge()なのに、値を変更する機能があったりhogeと無関係な値を取得してしまえば混乱を招くでしょう。

 また、大抵の場合getはコストの低い取得関数と見なされ、readやfetchなどであればコストの高い取得関数だと見なされます。そういうお約束はとても重要です。くれぐれもgetHoge()はhogeの取得を低コストで行うだけにとどめておきましょう。getHoge()といいつつ何かを更新したり、コストの高い操作が必要であれば、それは名前として不適切です。

まとめ

1. 名前を正しく付ける能力は、責務の抽象化の能力とイコールである

2. 付いた名前から責務について適切なサイズを考えることができる

3. 名前にはある程度お約束がある

 この3点を忘れないようにしましょう。適切な名前について考え続けると、プログラミング力は確実に向上します。

A.4.2 JavaScriptエンジニアとして身につけるべきものとキャリアパス

 基礎が大丈夫だとして、それ以後に関しては、何をやりたいかによってルートがわかれるとは思います。大きく分けると、サーバーをやりたいのか、ウェブをやりたいのか、モバイル開発をやりたいのかの三パターンがあると思います。もちろんこれらを複合的にやるというのも選択肢で、そのケースは多いでしょう。

A.4.3 ウェブ

 JavaScriptエンジニアのボリュームゾーンはやはりウェブでしょう。ウェブで厄介なのは広範囲に知識と経験が必要になる点です。どうしてもHTMLとCSSを避けることはできません。少なくともHTML5.111とCSSの仕様12を熟読した方がいいでしょう。最近のCSSは生で記述するのが面倒きわまりないため、SASS/SCSS13やPostCSS14といったAltCSSだったり、OOCSS, BEM, SMACSSといったCSSに秩序をもたらすためのルールも重要です。地味に感じるかもしれませんが、WAI-ARIA 1.115のようなアクセシビリティについても知っておくべきです。

 言語仕様としてのECMAScriptだけではなくDOMを知っておく必要もあるでしょう。Reactや仮想DOMを使ったフレームワークを使う場合はあまりDOMに詳しくなくても大丈夫かもしれませんが、たいていの場合はDOMの知見がある程度は必要になります。

 フレームワークに関しては、よほどこだわりが無い限りはReact+Reduxを覚えておくべきです。React自体とてもシンプルかつパワフルなことと、ReactNativeがあるため、ReactはJavaScriptエンジニアにとっては必修です。Reduxは現時点では主流ではなく、Reactほど確実な位置にいるわけでもありませんが、それほど習得コストが高いわけでは無いので、覚えておいて損はありません。今のところまだRedux以外を採用する積極的なメリットはあまりありません。

 vue.js16やRiot.js17などの軽量で良いライブラリもありますがそもそもこれらはさほど習得が難しいものでもありません。AngularJS18もReactに近い人気のプロダクトではありますが、重厚長大すぎるので人やプロダクトによりけりです。

 最近のツールはNode.js上に集約しているため、コマンドラインが苦手な人はコマンドラインに慣れておきましょう。前述しましたがUNIX/Linuxの最低限の知識と経験は必須です。

 多くの場合はサーバー関連の知識も覚えた方が良いでしょう。サーバーサイドレンダリングやウェブサービス全体の設計ができる方が良いです。

 ウェブ一本でやっていく場合、アニメーションだったり3Dだったりといった見た目に特化するという道はあります。Flashの経験があれば、FlashからHTML+JSへの移行案件をやるというのも一つの選択肢でしょう。

A.4.4 サーバー

 この場合Node.jsを前提にすることになります。

 RubyやPHP, PythonなどのLL言語やJava, Scalaなどでサーバープログラミングをしていた人の場合、それらの知識は流用が効きます。

 定番であるexpressや、MySQLやMongoDBなどRDBやNoSQLデータベース、Amazon S3のようなストレージサービス、ファイル処理、ロガー、APIの為のHTTPクライアントなどを駆使することになるでしょう。他にも色々なパッケージが必要です。

 API仕様との付き合いが深くもなるでしょう。その場合、JsonScheme19やswagger20、API Blueprint21などのAPI仕様を記述・変換するのに有用なツールは是非とも覚えておきましょう。

A.4.5 モバイル

 現状ではReact/ReactNativeを主軸にすることになる流れですが、他にもいくつかJavaScriptでモバイル開発できるプラットフォームがあります。

 モバイルの場合、JavaScriptだけでいくのか、ネイティブも覚えるのかという選択肢はあります。たとえばReactNativeの開発チームだと、チームに一人か二人はネイティブができる人が必要になります。ネイティブを覚えるのは大変ですが損は無いと思います。

 逆に、今後はネイティブをやっている人がReactNativeに合流してくる流れになるのではないでしょうか。

A.4.6 設計

 JavaScriptでの開発においては、TDDやスパイクを活用して技術検証しながら、スピーディーに設計する力を磨いておいた方がいいでしょう。開発速度は武器になります。重厚長大な設計が求められる現場もあるでしょうが、JavaScriptを導入しようという現場では、アジャイルやリーンの考え方の方がなじみ深い可能性が高いのです。

A.4.7 関数型言語のエッセンス

 JavaScriptにもいろいろと関数型言語のエッセンスはすでに取り込まれていますが、さらに今後も色々合流してくるでしょう。その他、関数型言語的なライブラリ(RxJSとか)も有用なため、関数型言語をやっておいて損はありません。Haskellを趣味で触っておくと刺激になるのではないでしょうか。Scalaも大変ではありますが、十分有用だと思います。

(この項、了)


高校生のときにパソ通にハマリ、その後紆余曲折を経てテキストエディタやMSXエミュレータその他を開発。技術者として勤務した後、現在はフリーランスエンジニア。技術同人誌や技術ブログやマッハ新書などを書いている。新しい技術に目が無い。アルゴリズム大好き。著書に『最新JavaScript開発~ES2017対応モダンプログラミング』(インプレスR&D)など。

連載バックナンバー

開発言語
第5回

ユニットテストをしよう

2018/6/11
【最新JavaScript開発~ES2017対応モダンプログラミング】 株式会社インプレスR&Dより発行された「最新JavaScript開発~ES2017対応モダンプログラミング」の立ち読みコーナー第5回です。
開発言語書籍・書評
第4回

型の恩恵をうける

2018/6/5
【最新JavaScript開発~ES2017対応モダンプログラミング】 株式会社インプレスR&Dより発行された「最新JavaScript開発~ES2017対応モダンプログラミング」の立ち読みコーナー第4回です。
開発言語書籍・書評
第3回

ECMAScript

2018/5/29
【最新JavaScript開発~ES2017対応モダンプログラミング】 株式会社インプレスR&Dより発行された「最新JavaScript開発~ES2017対応モダンプログラミング」の立ち読みコーナー第3回です。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています