株式会社LITALICO でエンジニアをやっています、@kentya6です。
『LITALICO Advent Calendar 2016』22日目の記事となります。
前回、スクリーンショット拡張Macアプリ「Fuwari」を作って公開しましたという記事を書きましたが、このアプリを開発するにあたって多くのライブラリを使用しました。これらのライブラリを利用しなければ、短期間でのアプリ開発は実現できなかったと思います。
今回は、こういったライブラリを利用してより短期間でアプリケーションを作るために抑えておきたい、ライブラリとの向き合い方や探し方、使い方の話をしたいと思います。
はじめに
ライブラリとは、ある機能を様々なプログラムに汎用的に再利用できるように切り出したまとまりのことです。ライブラリには様々な区別がありますが、ここではアプリ系やWeb系で扱うライブラリについての話をします。また、基本的にGitHubやBitBucketでホスティングされているようなオープンソースのライブラリを利用する場合についての話になります。
説明に用いる言語は、主にSwiftになります。ただ、コードの動作を理解する必要はなく、見た目や規模感をなんとなく感じとってもらうだけで大丈夫です。
独断と偏見ではありますが、この記事を読んで、ライブラリを扱う上での心構えを知り、探し方や使い方を知り、より一層自分たちのアプリケーション開発の速度を上げれる一助になれば幸いです。
ある日こんな経験をするかもしれません
例えば、あなたがとある機能の開発をすることになり、1ヶ月の時間をかけ苦労してその機能を実装したとします。ところが、後日別プロジェクトで他の人が同じような機能を3日で作り上げてしまいました。どうやって作ったのか聞いてみたところ、**「ライブラリがあったからそれを使っただけ」という返事が。
そんなライブラリがあることを「もっと早く知っていれば...」**とならないために、ライブラリをうまく探して活用し、開発効率を最大化するために、私が普段意識していることを書いていきたいと思います。
ライブラリを使うかどうかを決めるにあたり、まず、ライブラリを使うと何がメリットで、どういったことに気を付けなければならないのか、を知る必要があります。私は次のように考えています。
ライブラリを使うメリット
1. ビジネスロジックに集中できる
ライブラリは、ある機能を汎用的に再利用できるように切り出したものなので、アプリケーション特有ではないよくある機能(ログイン機能、ネットワーク通信など)については、公開されているライブラリを使用することである程度目的を達成できることができます。こういった機能の実現をライブラリに任せることによって、ライブラリではなかなか代替することができない、アプリケーション特有の処理(ビジネスロジック)へ自身の実装リソースを割くことができるようになります。
2. アプリケーション全体の実装期間が短くなる
ライブラリを利用することによって、汎用的な機能の実装はライブラリに任せ、自身はビジネスロジックの実装に集中することができます。そのため汎用機能の実装分が自分で実装する場合よりも浮くので、アプリケーション全体の機能実装期間が短くなります。(もちろん、ライブラリを使ったからといって100%実装期間が短くなるわけではありません。後述します)
3. 他人の設計やコードの書き方を参考にできる
オープンソースで公開されているライブラリは、その機能を実現するために記述したプログラムを見ることが出来ます。そのため、どうやって実装すればいいか分からない場合に実装の仕方を学ぶ手段としてもライブラリを使用し、そのライブラリの中身を把握することで、今後の自分の糧とすることができます。また、ライブラリ開発者が機能を実現するために考えた設計やコーディングスタイルも知ることが出来るため、こちらも大きな勉強資料になります。
結果、今後の実装においてライブラリの中身を見ることによって得た知見を活かすことにより、より短い時間で目的の機能を実装できるようになっていきます。
私も、どうやって実装したらいいかよく分からない場合は、GitHubのコード検索をして解決の手がかりを掴むことが多いです。
QiitaやStackOverflowで中々見つからなくても、GitHubのコード検索ならプログラムが直接見え、かつ結構解決策を見つけることが多いなと感じています。なので、GitHubのコード検索はプログラマにとって宝の山です。
ライブラリを使う際に気をつけること
ライブラリという枠組みである、ある機能ごとにプログラムのまとまりを切り出し、他でも使えるように汎用的にすることは自然な流れだと思います。
ただ、ライブラリも利用すれば必ずうまくいくというわけではなく、以下のような項目を意識して気をつけて利用しないと、後々悲しいことになる可能性があります。
1. 設計がライブラリに大きく左右されるものもある
ライブラリの規模によっては、アプリケーション設計全体に影響を及ぼすものがあります。例えば、ReactiveXプログラミング系のライブラリは、設計思想が各プラットフォームとは別の思想を持ち込んでいる場合が多いため、アプリケーション設計の考え方が従来とは全く別のものになりやすいです。
例えばiOSでUITabelViewを使ったデータ表示とセルのタップ判定をやる場合、標準で提供されている方法だと
import Foundation
import UIKit
class SimpleTableViewExampleViewController : ViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
private let items = (0..<20).map { "\($0)" }
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "\(items[indexPath.row]) @ row \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DefaultWireframe.presentAlert("Tapped `\(items[indexPath.row])`")
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
DefaultWireframe.presentAlert("Tapped Detail @ \(indexPath.section),\(indexPath.row)")
}
}
と記述できますが、Rx(今回はRxSwift)だと
import Foundation
import UIKit
import RxSwift
import RxCocoa
class SimpleTableViewExampleViewController : ViewController, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let items = Observable.just(
(0..<20).map { "\($0)" }
)
items
.bindTo(tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
cell.textLabel?.text = "\(element) @ row \(row)"
}
.addDisposableTo(disposeBag)
tableView.rx
.modelSelected(String.self)
.subscribe(onNext: { value in
DefaultWireframe.presentAlert("Tapped `\(value)`")
})
.addDisposableTo(disposeBag)
tableView.rx
.itemAccessoryButtonTapped
.subscribe(onNext: { indexPath in
DefaultWireframe.presentAlert("Tapped Detail @ \(indexPath.section),\(indexPath.row)")
})
.addDisposableTo(disposeBag)
}
}
となります。(RxSwiftのTableViewExampleを元に実装しています)
このように、ライブラリの種類によっては、ここまでアプリケーション設計に変更を及ぼすことができるものもあります。
2. 実行環境のバージョンアップの際に動かなくなる場合がある
セキュリティや性能改善、新機能追加などで、様々な開発環境、開発言語が日々アップデートされています。その潮流に乗り、自分たちで作ったアプリケーションもアップデートを決断して実行したとします。
その時、アプリケーションで使用しているライブラリが新しいバージョンの環境に対応しておらず、結果プログラム全体が動作しなくなる、ということが起こる場合があります。
アップデートにはこういったリスクが常にあります。Swift2 -> Swift3へのアップデートや、Rails4 -> Rails5へのアップデートなど、アップデート時に大量に出てくるエラーがライブラリによって引き起こされたものである時、ライブラリを利用しなければ済んだのに...!と後悔することがあるかもしれません。
3. 開発の効率が下がる場合もある
ライブラリの選定をあまり考えずに実装を進めた結果、後日に先ほどのバージョンアップによるエラー対応や、ライブラリの機能拡張をしないと達成できないような事態に陥ったとします。その時にライブラリの中身を初めて見て、何が起こっているのかよく分からず、中身の挙動の理解に時間を割き、また実装もビジネスロジック側と密結合な実装になってしまった、、などです。こういう時はライブラリを利用することによって逆に開発の工数が上がってしまった、、ということになるかもしれません。
4. その分野の知識が疎かになりがち
ライブラリは、その使い方さえ知っていれば、ライブラリを作るために記述されたプログラムの中身については知らなくても動作させることが可能です。そのため、ライブラリの使い方は知っているけど、ライブラリと同様の機能を自力で実装することができないといったことになりがちです。趣味で軽くプログラムをやる程度ならば問題ないかもしれませんが、仕事でプログラムを書いているとなると、いずれこのようなライブラリの使い方では今後の開発が辛くなってくる時期を迎えると思います。
ライブラリを扱う上で身につけておきたい心構え
1. ライブラリの導入による効果を考える
ライブラリをいれてもあまり効果がないというか、とりあえず色々いれてみるといった考えだと、先ほど述べたようなライブラリ導入のデメリットに陥りやすいです。
このライブラリ導入によってどういった効果があるのか、どのくらいの工数が削減できるのか、そういったことを考えた上でライブラリを導入する価値があると判断したときのみ、導入を決定するといいです。
2. ライブラリの導入による今後のコストを考える
ライブラリを利用し始めた後に、ライブラリを利用しない方針に切り替えるのには手間がかかります。今までライブラリを利用していた箇所を何かしら修正する必要があるためです。なので、ライブラリを導入する際は、以下の項目を意識すると良いと思います。
a. 導入によってアプリケーションの記述はどの部分がどのように変化するのか
b. 今後新しい機能を作っていく時に、どういった部分で影響を及ぼすのか
c. 今後別の人が開発に関わり始めたとき、スムーズに開発できるようになるためにどのくらいのコストがかかるのか
cについては、例えば最初の方で説明した、ReactiveXライブラリ(Rx)の導入といったことが挙げられます。Rxの概念を知らない人にとっては、最初はRxの概念を知ることから始まるため、スムーズに開発するのに多少時間がかかると予想できます。もちろん、そういったコストよりもライブラリを導入しない状態のコストのほうがコストが上回ると判断するのであれば、ライブラリを導入する価値は充分にあると思います。
3. アプリケーションの肥大化を意識する
ライブラリを導入するということは、プログラムのまとまりを自分のアプリケーションに加えるということになります。ライブラリ導入の手軽さや、ライブラリのインタフェースのみを意識すると、実はライブラリ自体はとても大きなものだった、ということに気づきにくいです。
例えば、Androidでは単一のdexファイルに含まれるメソッド数が65536を超えるとビルドできないという、64K参照制限というのがあります。自分のアプリケーションがそんなメソッド数に達するわけがないと思っていても、ライブラリ導入によって簡単にこの65536メソッド数を超えてしまいます。
例として、com.google.android.gms
のパッケージをアプリに組み込むとしましょう。このパッケージのメソッド数は約3万です。そのため、あっという間に65536に達してしまいます。実際は、このパッケージをより細分化して少ないメソッド数の状態で利用したり、65536メソッドを超えてもビルドできる仕組みがあったりはするのですが、身近なライブラリでも巨大なものがあるというのを意識しておくと良いです。
気をつけるポイント、意識すべきことを把握したら、次は私がやっているライブラリの探し方を説明します。
どうやってライブラリを探せばいいのか
ライブラリは、世界中で日々新しいものが出ています。その中から1つ1つ探していたのではキリがありません。
ここでは、効率的なライブラリの探し方について、iOS、Android、Ruby on Railsを例にして、ライブラリを探してみます。
まとめサイト・リポジトリを見る
各言語・プラットフォームごとに、人気のライブラリをまとめているサイトやリポジトリがある場合が多いので、これを利用しています。(もし存在していない場合は自分で作ってみるのもアリです)
iOS
awesome-ios
awesome-ios-ui
awesome-swift
CocoaControls
Android
awesome-android
awesome-android-ui
AndroidArsenal
jcenter
Ruby on Rails
awesome-ruby
awesome-rails-gem
Best Gems
どうやってライブラリをまとめているリポジトリを見つけるのか?
今回例として挙げたもの以外の言語・環境のライブラリはどうやって探せばいいのでしょうか。
例えば、GitHubのリポジトリ検索で、「awesome」と入力し、「Most stars」でソート、探したい言語を選択すると、大体ヒットします。(下図はpython言語で探した場合の例です)
こういうリポジトリ名の先頭にawesome-
と付いているのは大体ライブラリをまとめているリポジトリと判断していいと思います。
また、↓のようなバッジをREADME
の先頭につけているものはawesomeシリーズということになります。
そして、このawesomeシリーズのリポジトリをさらにまとめたawesomeやawesome-awesomenessというリポジトリもあります。ここから自分の興味のある分野のawesomeリポジトリを見つけるのもアリです。
また、Qiitaでもライブラリをまとめている投稿が数多くあるので、ライブラリ まとめとかで検索すると、awesomeシリーズよりライブラリの種類は少ないものの、それぞれに考察を交えて説明している記事などもヒットするので、こちらもライブラリ選定の情報源として参考になります。
ライブラリのトレンドから探す
GitHubのトレンドから検索することでも、多くの有用なライブラリの発見があります。iOSならばSwift、AndroidならばJavaやKotlin、Ruby on RailsならばRubyでトレンド検索をするといったところでしょうか。
トレンドにランクインしているライブラリというのは、多くの人が求めていたライブラリであったり、有名な企業、人物が公開したてのライブラリであることが多いです。
今現在で自身のアプリケーションに使わないようなライブラリでも、このトレンドを定期的に眺め、気になるものがあれば少し中身も見てみる、ということを習慣として行っていると、今後の開発で使うライブラリの引き出しが増えたり、ライブラリの設計を真似してアプリケーションにも反映できたりもできるのでオススメです。
こういったトレンド情報は常に変化していくものなので、トレンドをRSSとしてまとめているサイトの利用やトレンドを呟くTwitterアカウントなどをフォローして、自分が継続してウォッチしやすい経路でトレンドを把握しやすくしておくと良いです。
ライブラリの選定基準
ライブラリをまとめているawesomeシリーズや様々なサイトの中でも多くのライブラリが紹介されていて、何個か自分のプロジェクトに合いそうなライブラリ見つかった!けれども、どれを使ったらいいか確信が持てない...という時があると思います。そんなとき、私は以下の項目から使用するライブラリを判断しています。
1. 継続的な開発(メンテナンス)がされているか
ライブラリのREADMEを見た感じ結構よさそうなものであっても、バグが全くない完璧なライブラリなどほぼありえません。なので、その使用するライブラリのコミットログを見ます。最後のコミットの日時が直近のものであったり、コミットログから頻繁にコミットされていることが分かるようであれば、そのライブラリは継続的な開発がされているものと判断できます。
例えば、メンテナンスされていないライブラリAとメンテナンスされているライブラリBの違いはこんな感じです。ライブラリAは最後のコミットが2年前でコミットログも少ないですが、ライブラリBは最後のコミットが1ヶ月前でコミットログも少し多いです。ライブラリBも頻繁に開発が進められているわけではありませんが、少なくとも、ライブラリAよりは継続的な開発がされていると判断できます。
2. issue、PullRequestが長時間放置されていないか
issueやPullRequestというのは、ライブラリの使用者が実際にライブラリを使ってみて感じた問題点や要望を表したものです。このissueやPullRequestがしばらく放置されている(半年待ってもライブラリの開発者からレスポンスがない、など)と、今後自分がこのライブラリを使った時に不具合が発生しても、自分で対応せざるを得ないという状況になりやすいです。
3. どこかのアプリケーションで使われている使用実績があるか
アプリケーションのライセンス欄を見ると、実際にどういったライブラリを使っているのかを把握することができます。ここで様々なアプリでよく名前が記載されているライブラリの場合、アプリケーション開発において充分力を発揮するライブラリであると言えます。
また、ライブラリのパッケージ管理サイトでは、ライブラリがインストールされているアプリ数や1週間以内の新規利用数なども分かるので、ここの数値を見ることでもよく使われるライブラリなのかどうかが分かります。
例えば、iOSのデファクトスタンダードな通信ライブラリであるAlamofireのダウンロード数やインストール数を見ると、とても多く利用されていることが分かります。
4. Star数がどのくらいあるか
利用実績と似たようなものがありますが、GitHubのStar数も、そのライブラリの人気度・注目度を表すものとなります。そのため、Star数が多いライブラリであるほど、みんなの関心が高いライブラリとなります。Starの付け方は人それぞれなので、Star数が多いから多くの人が利用しているとは一概に言えないかもしれませんが、利用者数が多いライブラリであると言えるのではないかと思います。
5. READMEが充実しているか
READMEとは、ライブラリを初めて見る人のためにどんな機能を持っているか、どのように使えばいいのか、などを記述するものです。そのため、READMEが充実しており、ライブラリの情報を分かりやすく読み手に伝える気配りがあるライブラリは、使い手のことをよく考えられたライブラリであることが多いと思っています。
読みやすいREADMEを書くの項目が網羅されているものは、網羅されていないものに比べて安心してライブラリを使用することができます。
6. テストが書かれているか
UIライブラリでは不要だと思いますが、例えばHTTPリクエストやJSONのパースといったライブラリでは、正しくリクエストができているのか、パースできているのかを判断する材料として、テストが書かれているかどうかも見ると良いです。
そのテストコードを見て、どういう機能をサポートしているのかを見ることができ、品質を担保しながら開発していることが分かるので、安心してライブラリを使うことができます。(テストコードのカバレッジにもよりますが)
7. ライセンスの種類
ライブラリには、何かしらのライセンスが開発者によって定められています。このライセンスの制限が厳しいものである場合、素晴らしいライブラリであっても使用を遠慮する場面が出てくるかもしれません。
Qiitaでも、ライセンスの種類についてまとめられた記事がいくつかあります。
参考: ライセンスの選択を恐れる必要はありません
8. 何か問題が起きた時に最悪自分で修正できそうか
動作環境のバージョンアップなどでライブラリが動かなくなってしまった場合に、自分でライブラリのプログラムの動作を理解し、自分で修正する気持ちがあるかも選定要因の1つかなと思います。ただ、上記1~7の選定基準を満たすようなライブラリである場合、誰かが同じような問題を報告し、修正したり、ライブラリ開発者が修正したりする場合が多いので、自分で修正できないライブラリを必ず使ってはいけない、というわけではないとは思います。(オープンソースの素晴らしいところです)
こんなときどうする?
ライブラリが原因でプログラムが動かなくなってしまった
動作環境・ライブラリのバージョンアップ時や他のプログラムとの競合などで、アプリケーションが動作しなくなってしまった時の対応は、以下の項目で対応しています。
- 既に似たような不具合がないかを調べる(GitHubのissueやGoogleで検索)
- もし調べても見つからなかった場合は、開発者にissueの報告をする
- ライブラリのアップデートを試してみる
- それでもエラーがなくならず、issueが放置されているならば自分で修正する
- 開発者へPullRequestを投げる
issueを投稿して返事を待つということはせずに、いきなりPullRequestを送るときもあるので、必ずこの順番でやっているわけではないですが、大体上記の項目で対応しています。ライブラリというのはただの機能のまとまりであり、それを自分で記述したか、他の人が記述したかの違いでしかありません。自分で記述したプログラムでも同じような対応をすると思います。
自分の用途に合うライブラリがない
上述した「ライブラリの探し方」を活用して探してみても、全然目的のライブラリが見つからない!ということがあるかもしれません。その時は、自分で実装するという選択肢をとっています。
ただ、もしそれがライブラリとして切り出せそうならば、自分が作ったライブラリとしてGitHubに公開するのもオススメです。
ライブラリとの向き合い方、探し方、使い方が分かり、よりライブラリが身近になったということで、ライブラリを利用する側だけではなく、利用される側になるという意識になれるといいなと思っています。
ライブラリは高度なものでないと公開するほどではないのでは、、と思うかもしれませんが、ライブラリは、あくまである機能を切り出したものでしかないので、高度なプログラミングテクニックが必須なわけではありません。
例えば、iOSでの色の記述を16進数でも行えるようにしたライブラリは、コメントを除くとコードが100行程度の規模となっています。しかし、アプリへのインストール数は数千を超えており、多くのアプリで使われていることが分かります。
こうしてライブラリを利用する側から利用される側になることで、多くの開発者を助けるライブラリが増え、自分自身のスキルアップにもなります。
おわりに
今回は、ライブラリを使うということについて、私が思っていることを書いてみました。
この記事を読む前と読んだ後で、ライブラリを使うということについて少しでも意識が変わっていれば嬉しいです。
ライブラリをうまく活用し、より素早く、素晴らしいアプリケーションを作っていきましょう!
明日は、@Takuan_Oishiiさんの「AWS CodeBuildにRailsのテストを実行してもらう」です。よろしくお願いします!