ニコニコandroid(サーバ編) - scalaを業務で使って
DESCRIPTION
2013 JJUG(http://www.java-users.jp/?page_id=330) #jjug_ccc ※一部発表時と異なる点がありますTRANSCRIPT
ニコニコAndroid- サーバ編 -
間島 大智
目次
•ニコニコAndroidサーバって何?
• Scalaいいところ・わるいところ
•サーバチームの仕事の流れ
間島 大智
•株式会社ドワンゴ
•ニコニコAndroidサーバチーム
• 2012年新卒入社
間島 大智
•株式会社ドワンゴ
•ニコニコAndroidサーバチーム
• 2012年新卒入社
間島 大智
•株式会社ドワンゴ
•ニコニコAndroidサーバチーム
• 2012年新卒入社
•化学の知識を活用できるWebサービスのプロジェクトまだですかね
ニコニコAndroidサーバって?
社内向けAPI・DB
ニコニコAndroidサーバ
Androidアプリau STBアプリ
様々なアプリでニコニコを使うために
必要なAPIを提供
プロジェクトの歴史
• 2012/02 プロジェクト発足
• 2012/08 au STBアプリリリース
• 2012/11 Androidアプリリリース
•74 API•330万リクエスト/日
サーバ側メンバー
• mashijp (github)
• @kozo1215
• @mtgto
• @mitikage
• @kimwoonsung
• インフラ2人
環境
•言語: Scala
•フレームワーク: Play Framework 2.0
• IDE: IntelliJ IDEA 11, 12• CI: Jenkins
社内初
ニコニコAndroidサーバって?
ニコニコAndroidサーバの役目
社内向けAPI・DB
ニコニコAndroidサーバ
Androidアプリau STBアプリ
この位置に求められることって…?
ニコニコAndroidサーバに求められること
社内向けAPI・DB
ニコニコAndroidサーバ
Androidアプリau STBアプリ
アプリにとって使いやすいAPIを提供すること
使いやすいAPIとは?
•用語が統一されている
•用語が直感的に理解できる
•内部的な細かい仕様を知らなくていい
•ドキュメント化されている
•用語が統一されている
•用語が直感的に理解できる
•内部的な細かい仕様を知らなくていい
逆にこの3つを満たしていないAPIって?
社内APIを変えればいい?
長い歴史があるもの(社内API)にはどうしてもわかりにくい用語・仕様がまじってしまう
社内APIを修正すればいい…?
そう簡単には変えられない
•社内APIは多数の社内サービスが既に使っている
•仕様を変更するのは容易ではない
サーバで内部仕様を吸収
mylist.list deflist.listmylist.remove deflist....
GET /xxx/mylists/:id
社内向けAPI・DB
ニコニコAndroidサーバ
Androidアプリau STBアプリ わかりやすい!
モデルもScalaで再定義
ドメイン層
Androidアプリau STBアプリ
インフラストラクチャ層
社内向けAPI・DB
API層
ドメイン駆動設計による浄化
社内APIを叩くことだけに専念
ビジネスロジックだけに専念
リクエストの処理だけに専念
明確にレイヤー分離された設計をしています
社内向けAPIドキュメント
• Confluence(Wikiみたいなもの)で管理
•全てのAPIについて細かく記述している
ドキュメントの例
ドキュメントの例
ドキュメントの例
ドキュメントの例共通で使うレスポンスの形は別ページにしている(多重管理を避けるため)
APIドキュメントの記述
•社内向けAPIドキュメントだからといって決して手を抜いていない
•多重管理を避け、ドキュメントが古くなってしまうことを避けている
使いやすいAPIが実現できた
•用語が統一され、直感的に理解できる
•内部的な細かい仕様を知らなくていい
•ドキュメント化されている
→Scalaでモデルを再定義しアプリに提供
→社内向けだからといって甘えていない 管理しやすく見やすいドキュメント作成
Scalaの良い所・悪い所
環境
•言語: Scala
•フレームワーク: Play Framework 2.0
• IDE: IntelliJ IDEA 11, 12• CI: Jenkins
•昨年6月、新卒研修が終わりニコニコAndroidチームに配属された
•入社前
• Javaは趣味で触ってた
•が、 “Scala”は名前すら知らない
Scala…?!
Java利用者にとってのScala実体験をふまえて
Scalaって?•静的型付け言語
•関数型言語とオブジェクト指向言語のハイブリッド
• JVM上で動く
• Javaのコードも簡単に混ぜられる
Scalaって?•静的型付け言語
•関数型言語とオブジェクト指向言語のハイブリッド
• JVM上で動く
• Javaのコードも簡単に混ぜられる
って言われても何が良いのかわからん!
便利やでScala
•型推論
•コレクション関数
•対話環境
• Option型
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
↑書かなくても右辺見ればわかる
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
Scala
↑書かなくても右辺見ればわかる
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
Scala
↑書かなくても右辺見ればわかる
val str = “fugahoge”val piyo = str + “piyo”
型推論String str = “fugahoge”;String piyo = str + “piyo”;
Java
Scala
↑書かなくても右辺見ればわかる
val str = “fugahoge”val piyo = str + “piyo”↑コンパイラが勝手にStringと推測
型推論
Scala型推論
Scala型推論
def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)
}
val fuga = replaceAbc(“abcdef”)
Scala型推論
def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)
}
val fuga = replaceAbc(“abcdef”)
↑メソッドの返り値はStringと推測される
Scala型推論
def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)
}
val fuga = replaceAbc(“abcdef”)
↑メソッドの返り値はStringと推測される
↑よって、fugaもString
Scala型推論
def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)
}
val fuga = replaceAbc(“abcdef”)
↑メソッドの返り値はStringと推測される
↑よって、fugaもString
冗長な記述をしなくていい
コレクション関数Java
String[] strs = {“a”, “fuga”, “bb”, “hogee”};List<String> result = new ArrayList<String>();for(String str : strs) { if (str.length >= 3) { result.add(str); }}return result;
コレクション関数Scala
val strs = List(“a”, “fuga”, “bb”, “hogee”);return strs.filter(_.length >= 3)
強力なコレクション関数+型推論によりコード記述量を大幅に減らせる
ちなみに
•ニコニコAndroidサーバのコードにはfor文は7つしかない
Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)
}
Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)
}
出力結果: a1 a2 a3 b1 b2 b3 c1 c2 c3
Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)
}
出力結果: a1 a2 a3 b1 b2 b3 c1 c2 c3
複数のコレクションの組み合わせを網羅するのに非常に便利
強力なコレクション関数+for
•コード記述量が大幅に減った•単純なコレクションの処理をする際にfor文使うことほぼなし
•たまに「forどうやって書くんだっけ」という声がでるほど…
対話環境
強力なimport文
•ブロックの中にかける
•別名をかける
Scala
importをブロックの中にかける
import java.util.Date;public void methodA(){ Date date = new Date(). .......}
public void methodB(){ java.sql.Date date = new java.sql.Date(..). .......}
Java
名前が衝突するのでimportできず、フルネームで書くしかない
importをブロックの中にかける
def methodA() = { import java.util.Date val date = new Date(). .......}def methodB() = { import java.sql.Date val date = new Date(..). .......}
Scala
別名をつけられるimport java.util.Dateimport java.sql.{Date => SqlDate}def methodA() = { val date = new Date(). .......}def methodB() = { val date = new SqlDate(..). .......}
Scala
強力なimport文
•ブロックの中にかける&別名をつけることができる
•クラス名をつける際に「かぶりそうだな…」と怯えることがなくなった
•短いクラス名をどんどんつけちゃう
•例) Client (Redis接続用クラス)
Option型Java
・このメソッドってnull返すことあるっけ? えーとドキュメント...・あ、そうだ nullチェックしないと... if (result != null) { ....
Option型Java
・このメソッドってnull返すことあるっけ? えーとドキュメント...・あ、そうだ nullチェックしないと... if (result != null) { .... Null
PointerE
xception
Javaでぬるぽを起こす例
Java
Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}
Mapからとってきたものが3文字以上かどうかチェックするメソッド
Javaでぬるぽを起こす例
Java
Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}
Mapからとってきたものが3文字以上かどうかチェックするメソッド
Javaでぬるぽを起こす例
Java
Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}
NullPointerException!!! ( ・∀・) | | ガッ と ) | | Y /ノ 人 / ) < >__Λ∩ _/し' //. V`Д´)/ (_フ彡 /
そんなキーねえよ
Mapからとってきたものが3文字以上かどうかチェックするメソッド
nullチェック追加
Map<String, String> map = getMap();String result = map.get(fieldName);if (result != null && result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}
Java
nullチェック追加
Map<String, String> map = getMap();String result = map.get(fieldName);if (result != null && result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}
Java
どうしてもnullチェック忘れてしまう…!
Option型って?
Option[T]
Some[T] None
値がある 値がない
値がある・ないを型で表現している
Scala
Option型の挙動Scalaval map = Map("key1" -> "value1", "key2" -> "value2")// Map#getの返り値はOption型(ここではOption[String])println(map.get(“key1”))//→ Some(value1)println(map.get(“fuga”))//→ Nonemap.get(“key1”).getOrElse(“ないとき”)//→ value1map.get(“fuga”).getOrElse(“ないとき”)//→ ないとき
Someのときは中身を取り出し、Noneのときは引数を返す
型で値がないことに気づける
val map = getMap()val result = map.get(fieldName)
↑resultの型は Option[String]この時点で「値がないこともありうる」ということに気づける
Scala
val map = getMap()val result = map.get(fieldName)result.filter(_.length > 3).map(e => “長さは”+e.length).getOrElse(“短い”)
さっきの例をScalaで実装
Scala
ぬるぽがない安心感
• Scalaの標準ライブラリは原則nullを返さない(Scalaはnullを使わない文化)
•つまりnullチェックを書く必要がない
社内向けAPIドキュメント
社内向けAPIドキュメント
nullがありうるものをドキュメントに残すのに苦労しなかった
Scalaの悪いところ...
•コンパイル遅すぎ!
•社内にScalaについて聞ける人が少ない
コンパイル
•遅い!!とにかく遅い!!
•メモリ食い過ぎー!
•ビルド時のJavaの設定-Xms512m -Xmx1024m -XX:MaxPermSize=512m
Scala
コンパイル
•遅い!!とにかく遅い!!
•メモリ食い過ぎー!
•ビルド時のJavaの設定-Xms512m -Xmx1024m -XX:MaxPermSize=512m
Scala
Jenkinsで並行ビルドするとスワップアウト...→ Jenkinsサーバのメモリ増設(4GB → 8GB)
社内に聞ける人が少ない
•弊社内でScalaのプロジェクトはニコニコAndroidサーバが初めて
•社内ノウハウが少ない…
• Scala書ける人もあんまりいない…
学習コストはそんなにかからない
開発メンバー5人中4人がScalaをはじめて1年たっていない
学習コストはそんなにかからない
開発メンバー5人中4人がScalaをはじめて1年たっていない
でも1ヶ月勉強したら業務で書けるようになった
どうやって勉強?
•おすすめ本「JavaプログラマのためのScalaプログラミング」
•疑問は社内IRCでブツブツ言う
•誰か答えてくれる
• #scalaというチャンネルもできた
少しずつ広まっている...!
最近は社内でもScalaを使う流れができつつあり、今後は改善されそう!
ドワンゴでは4プロジェクトがScala使ってます
チームの実際の仕事の流れ
仕事のサイクル
計画
振り返り 実装
レビューリリース
マージ
1周=2週間のアジャイル開発
開発メンバー全員で2週間にやることを計画誰がどの仕事をするか割り振る
全員で計画する
計画の前準備
会議をする前に各自でやらないといけないことを洗い出し、チケットとして作成チケット管理にはAtlassian製品のJIRAを使っています
全員で見積もるチケットを作った人がチケットの内容を説明
工数見積もり(予想)を各自で出す
見積もりがあわなかったら最大3回やる
全員でやることを共有できているので突然誰かが倒れても大丈夫なチームに
実装する
• IntelliJ IDEA 12 Ultimate($200)使ってます
• キャンペーンのときに全員買った
• 買うまではCommunity Edition(無料)
• 会社で買ってくれるという話も出てる
• ScalaのIDEはIntelliJ IDEA一択
• EclipseでのScala開発はめっちゃ重い…
テストに力を入れています
1000以上のテストケースによりコードカバレッジ80%以上を達成
FishEyeでコードレビュー
全員が仕事を理解しているので誰でもレビューできるみんなで気軽にレビューコメントをつける
GitHub:eよりFishEyeのほうが便利
•誰がレビューOK出しているのか、どれぐらい見ているのかがすぐ分かる!
全員のレビューOKが出ればマージする
Jenkins•ビルド
•テストの実行
• developブランチにマージするとJenkinsが動き出す
•テストが落ちるとチームメンバーに罵られる
振り返る(KPT)
毎週水曜日にKPTKeep(続けたいこと), Problem(困ったこと),Try(次やりたいこと) をみんなで自由に書く
KPTで改善された例
Jenkins用サーバで並行ビルドできない
もっとスペックの高いJenkins用サーバを調達し、並行ビルド可能に
Problem
Try
思ったことを気軽に書けるので「ちょっとした問題」を改善していく
流れを作ることができた
強いチーム作りに成功
「全員で」計画し「全員で」振り返ることで常に意識を共有
全員が主体的に動く強いチームを作っています
ドワンゴで働きませんか
• Scalaに興味のある人もない人も
• 「ドワンゴ 採用」で検索!
• http://info.dwango.co.jp/recruit/
• 7月から新オフィス(歌舞伎座)
お待ちしています!
まとめ• 美しいAPIをアプリチームに提供できた• Scalaで再モデリング・ドキュメント整備
• Scalaを業務で楽しく使ってます!• 強力なScalaの機能で効率の良い開発が可能に
• 強いチームでやってます• 全員が仕様を理解できている
• 誰が風邪引いても困らない