くりにっき

フルスタックキュアエンジニアです

ドリコムを支える中間ポイントシステム

はじめに

これは ドリコムAdventCalendar の1日目です

1日目:ドリコムを支える中間ポイントシステム

僕はドリコムに入社してからほぼずっと課金周りのシステムに携わっているのでその話を書きます。

一応自己紹介

  • HN
    • sue445
  • お仕事
    • 社内ライブラリや社内ツールの開発
    • その他サーバサイド全般
    • ドリコム歴 = Ruby歴 = 2年半くらい
    • TDDおじさんから火消しまで 「はい、喜んで!」(血涙)
  • 主なコミュニティ

プリキュアおじさん

嫁はキュアピース

開発の背景

ソーシャルゲームの歴史

2012年くらいまではGREEmixiといった国内ソーシャルゲームプラットフォームが台頭してブラウザ系のソーシャルゲームがメインでした。そこのプラットフォーム上でゲームを公開するにあたって、課金周りはプラットフォームで提供しているAPIを使うだけなので自社で課金システムを開発する必要はありませんでした。

しかしパズドラのヒットとともにソーシャルゲーム業界全体にiOS/Androidのネイティブアプリ化の波がやってきて弊社でもネイティブアプリを開発する流れがやってきました(たしか2013年頭くらい)

ここではiOSAndroidソーシャルゲームを「ネイティブアプリ」と呼称します

中間ポイントについて

ネイティブアプリのソーシャルゲームで遊んだことのある人なら分かるかもしれませんが、ネイティブアプリだとiTunesやGooglePlay(以下ストア)から直接ガチャをまわすのではなく、ストアでは一度中間ポイント(パズドラであれば魔法石)を購入して、それを使ってガチャを回していると思います。

これには理由があって、確かアメリカの法律によりストアから直接ガチャを回すことができないことになっているのが大きな理由です。

そこで各社のソーシャルゲームはストアでは中間ポイントだけを購入するような仕様になっています。

*1

前受金と資金決済法について

細かい話ははしょりますが

  • 購入されたけど使われていないポイントに関してはいつでもユーザに返金できるように管理する必要がある
  • 毎月月初に前月末時点の前受金を経理に提出する必要がある

が日本国内の法律との兼ね合いで必要になると思います。

有償ポイントと無償ポイント

  • ユーザがストアで購入した中間ポイントは有償ポイントと呼ぶ
  • 運営が配布した中間ポイント*2は無償ポイントと呼ぶ

ユーザからの見た目にはどっちも中間ポイントということに変わりはないのですが、売上が発生するのは有償ポイントが使われた時だけです。(無償ポイントは0円なのでいくら使われても売上に計上できないのは当然ですね)

複雑な売上計算

  • 会社としての売上は「中間ポイントが購入された時」ではなく、「ガチャなどで中間ポイントが消費された時」に発生する
  • 有償ポイントは購入時の単価がそれぞれ異なるので、同じ1ポイントの消費でも購入時の単価によって売上が異なる
    • 例)6ポイント500円 ≒ 1ポイント辺り83.3円、85ポイント5400円 ≒ 1ポイント辺り65.52円、無償ポイントは全て0円

ドリコムの中間ポイントシステム(dpoint)について

以上のような面倒くさい背景があり各アプリで個別に中間ポイントシステムを開発していくと工数が膨れ上がるため、全社で共通化した中間ポイントシステムを僕が開発しました。

drecomの中間pointシステムなので「dpoint」という名前です。

社内でも割と誤解されがちなのですが、ポイントサーバみたいなのがあるわけではありません。dpointはアプリ内に内包しているgem(ライブラリ)です。課金データは各アプリDBに同居しています。

dpointは社内のgemリポジトリホスティングしています

自分の役割

自称「アーキテクチャ設計」兼「ライブラリメンテナ」兼「リリースマネージャ」です

最近メンテナが増えましたが、1年以上ほぼ僕1人でメンテしています ('A`)

重要なこと

開発当初の俺氏のスペック:Ruby歴1年未満(今は2年半くらい)

初めてRubyで作ったgemが課金の超コアなdpoint

dpointが導入されているアプリ

2013年9月以降にリリースされている弊社のネイティブアプリ全般で導入されています。

etc

余談ですがフルボッコヒーローズは事前登録でも開発に関わっていました。

課金フロー

iTunesでの中間ポイント購入

f:id:sue445:20141130174443p:plain

  1. 端末で購入処理を行う
  2. iTunesストア上で決済が完了したら端末にレシートが送信されてくる
  3. 端末からアプリサーバにレシートを送信する
  4. アプリサーバからAppleのレシート検証APIに端末から送られてきたレシートを送信して、レシートが正しいかどうかチェックする
  5. レシートが正しければ中間ポイントを付与する

GooglePlayでの中間ポイント購入

f:id:sue445:20141130174523p:plain

  1. 端末で購入処理を行う
  2. GooglePlay上で決済が完了したら端末にレシートが送信されてくる*3
  3. 端末からアプリサーバにレシートを送信する
  4. レシートをGooglePlayの管理コンソール上でダウンロードできる公開鍵で検証する
  5. レシートが正しければ中間ポイントを付与する

ポイント消費

f:id:sue445:20141130174535p:plain

自社のアプリで完結するのでそんなに難しいことはしていないです

dpointのリリースノート

詳しいリリースノートは書けないので日付と主なバージョンだけ

  • 2013/05/27 : v0.0.1(初版)
  • 2013/11/01 : v0.3.2(0系最終バージョン)
  • 2013/11/15 : v1.0.0
  • 2014/05/30 : v1.1.7(1系最終バージョン)
  • 2014/05/09 : v2.0.0
    • v1.1.7と日付前後していますがこの辺は1系と2系並行メンテしてました
  • 2014/11/28 : v2.3.0(現行最新バージョン)

課金というヘビーなライブラリですがカジュアルに月2〜3回ペースでアップデートしています。(アプリの皆さん申し訳ありません。。。)

gemのボリューム

f:id:sue445:20141130214201p:plain

LOC 4500行くらい。課金系のライブラリなのでテストは割と手厚く行っています

date_discreterというgemを作りました

dpointのソースを見ていたら

# TODO: 汎用的なのでgem化したい

ってあったので、切り出してgem化して公開しました。

どういうgem?

月、日、時間の歯抜けを調べるためのgemです。

dpointでレポートの仕組みが

  • 1時間に1回:課金系の実レコードの集計hourlyレポートを作成
  • 1日1回:hourlyレポートを積み上げてdailyレポートを作成
  • 1ヶ月に1回:dailyレポートを積み上げてmonthlyレポートを作成

のような積み上げ方式(キングスライム方式)になっているのですが、レポートを積み上げる時に途中のレポートが1つでも欠けていると正しく売上レポートが出ないのでhourlyやdailyの歯抜けを調べるための処理をgemにしました。

サンプルコード見てもらうのが一番手っ取り早いかと思います。

月の歯抜けを調べる

continuous_months = [
  Date.parse("2014-10-01"), 
  Date.parse("2014-11-01"), 
  Date.parse("2014-12-01"),
]

DateDiscreter.discrete_months(continuous_months)
#=> []

DateDiscreter.continuous_months?(continuous_months)
#=> true

discrete_months = [
  Date.parse("2014-10-01"), 
  Date.parse("2014-12-01")
]

DateDiscreter.discrete_months(discrete_months)
#=> [#<Date: 2014-11-01 ((2456963j,0s,0n),+0s,2299161j)>]

DateDiscreter.continuous_months?(discrete_months)
#=> false

日の歯抜けを調べる

continuous_days = [
  Date.parse("2014-12-01"), 
  Date.parse("2014-12-02"), 
  Date.parse("2014-12-03"),
]

DateDiscreter.discrete_days(continuous_days)
#=> []

DateDiscreter.continuous_days?(continuous_days)
#=> true

discrete_days = [
  Date.parse("2014-12-01"), 
  Date.parse("2014-12-03"),
]

DateDiscreter.discrete_days(discrete_days)
#=> [#<Date: 2014-12-02 ((2456994j,0s,0n),+0s,2299161j)>]

DateDiscreter.continuous_days?(discrete_days)
#=> false

時間の歯抜けを調べる

continuous_hours = [
  Time.parse("2014-12-01 00:00:00"), 
  Time.parse("2014-12-01 01:00:00"), 
  Time.parse("2014-12-01 02:00:00"),
]

DateDiscreter.discrete_hours(continuous_hours)
#=> []

DateDiscreter.continuous_hours?(continuous_hours)
#=> true

discrete_hours = [
  Time.parse("2014-12-01 00:00:00"), 
  Time.parse("2014-12-01 02:00:00"),
]

DateDiscreter.discrete_hours(discrete_hours)
#=> [2014-12-01 01:00:00 +0900]

DateDiscreter.continuous_hours?(discrete_hours)
#=> false

課金システムを作ることがあれば(作ることがなくても)是非お使い下さい

dpoint改修時のつらみ

DBのスキーマ変更する場合は導入してるアプリ全部での影響を調べる必要がある

dpoint要因でアプリのメンテ時間を長くしたくないので、既存アプリへの影響が最小になるように気をつけています

数千万レコードあるテーブルに対して気軽にカラム追加できない。。。

PKだけで検索したら非効率な場合がある

created_atパーティションきってるテーブルに対してidだけで検索すると全パーティションに対して検索して非効率なので、idとcreated_atをセットで検索する必要があります。

idだけで絞り込んだ時のexplain

explain partitions
SELECT `dpoint_some_models`.* FROM `dpoint_some_models` WHERE `dpoint_some_models`.`id` = 5648239 LIMIT 1 \G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dpoint_some_models
   partitions: p20140316,p20140323,p20140330,p20140406,p20140413,p20140420,p20140427,p20140504,p20140511,p20140518,p20140525,p20140601,p20140608,p20140615,p20140622,p20140629,p20140706,p20140713,p20140720,p20140727,p20140803,p20140810,p20140817,p20140824,p20140831,p20140907,p20140914,p20140921,p20140928,p20141005,p20141012,p20141019,p20141026,p20141102,p20141109,p20141116,p20141123,p20141130,p20141207,p20141214,p20141221,p20141228,p20150104,p20150111,p20150118,p20150125,p20150201,p20150208,p20150215,p20150222,p20150301,p20150308,p20150315,p20150322,p20150329,p20150405,p20150412,p20150419,p20150426,p20150503,p20150510,p20150517,p20150524,p20150531,p20150607,p20150614,p20150621
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: const
         rows: 1
        Extra:
1 row in set (0.00 sec)

idとcreated_atで絞り込んだ時のexplain

explain partitions
SELECT `dpoint_some_models`.* FROM `dpoint_some_models` WHERE (`dpoint_some_models`.`created_at` BETWEEN '2014-03-03 10:58:00' AND '2014-04-03 11:28:00') AND `dpoint_some_models`.`id` = 5648239 LIMIT 1 \G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dpoint_some_models
   partitions: p20140316,p20140323,p20140330,p20140406
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 16
          ref: NULL
         rows: 4
        Extra: Using where
1 row in set (0.00 sec)

Railsから呼ぶ時にはこういうmoduleをincludeしています*4

  • near_partition : created_atの前後1日くらいを検索対象のパーティションにするscope
  • xxxx_with_partition : パーティションを絞りつつupdateやsaveを行うメソッド
    • Railssaveupdate_attributes で発行されるUPDATE文には WHERE id = ? しかつかないので、パーティショニングしてると1回のsaveやupdateで全パーティションを探索するため効率が悪いです。
    • UPDATEがパフォーマンスで問題になることはほぼほぼないのですが、後述の通常の10倍の課金が走った時にUPDATE詰まりでアプリが止まったのでその対策です。
    • パフォーマンス上ボトルネックになりうるUPDATEのみに適用したかったので alias_method_chain はつけていません

正規化していたらレコード数多すぎてJOINできなくなった

ポイントの購入と消費を入れるテーブル(各500万レコードずつ)をJOINしようとしたらMAX_JOIN_SIZE エラーが出て月次レポートが作成できなくなった*5

  1. 1ヶ月分まとめて月次レポートを作成するのをやめて、毎時(hourly)レポートと日次(daily)レポートに分割して積み上げ集計するようにした
    • この時の経緯によりレポートの抜けをチェックするためにdate_discreter が必要になった
  2. 非正規化した。(消費テーブルに購入の情報も持たせてJOINしないようにした)

の順で対応しました

糞でかいテストデータがあったのでgemのサイズがでかくなった

バグ修正&リグレッションテスト用に本番のデータをテストデータ*6として使っていたらそれに伴ってgemのサイズがでかくなってアプリから苦情を受けたので、実行に関係ないファイルはgemに含めないようにしました

gemspecをこういう風にすればOK

  spec.files         = `git ls-files`.split($/)

  # ファイルサイズがでかくて実行に関係ないファイルはgemから除外する
  EXCLUDE_DIRS = %w(spec/ tools/)
  EXCLUDE_DIRS.each do |exclude_dir|
    spec.files.reject! {|filename| filename.start_with?(exclude_dir) }
  end

この修正でgemのファイルサイズが13MBから48KBくらいまでに減りましたw

# Before
ls -l pkg/dpoint-1.1.3.gem
-rw-r--r--  1 sue445  staff  13420032  3 14 01:52 pkg/dpoint-1.1.3.gem

# After
ls -l pkg/dpoint-1.1.3.gem
-rw-r--r--  1 sue445  staff  48128  3 14 01:52 pkg/dpoint-1.1.3.gem

gem単体でdb:migrateするのがつらい

Rails Engineならまだ違ったと思うのですが、dpoint作り始めた当時はRails Engineの存在は知らなかったので。。。

いろいろ試行錯誤した結果

  1. db:drop
  2. spec/config/database.yml の内容を元に db:create
  3. lib/generators/templates/db/migrate/*.rb を元に db:migrate

相当のことを全部 spec_helper.rb の中で行っています

設定ファイルによってgemの振る舞いを変えるとテストのパターンが増えてしんどい

業務要件により特定のindexを適用するかどうかを設定ファイル中の値を見てmigrationファイルの中で分岐するするようにしたのですが、index有り無しの場合のテストをそれぞれJenkinsのジョブで作りました。

これにさらに activerecord-turntable で水平分割するかどうかの設定も入れることになってJenkinsのジョブが爆発しかけたのでマトリクステストを手軽に行えるためのgemを作りました

現在は

  • Rails 4.0系 / 4.1系
  • 前述のindexの有り / 無し
  • activerecord-turntable の垂直分割有り / 無し

で 2 x 2 x 2 = 8通りのマトリクステストをJenkinsの1つのジョブで行っています。*7

paraductは既に実用段階です!

それでも減らないJenkinsのジョブ

paraduct入れて緩和されたとはいえ、いっこうにジョブが減る気配がない

f:id:sue445:20141130214505p:plain

各ジョブの説明

FAQ

なんでアプリ内DBに?

Appleの規約上、中間ポイントを複数アプリ間でまたがって使うことができないためです。

複数アプリ間で連携するのであればポイントサーバを立てる必要がありますが、アプリ内に閉じているのであれば外部サーバを立てる必要はないという判断です。(ポイントサーバのAPIをコールする分オーバーヘッドが発生するし、APIサーバに対してトランザクション制御するのは困難)

アプリDBにポイント系のデータも同居することでパフォーマンスの懸念があると思いますが、弊社では Fusion-io ioDrivePercona Server *8 を載せて、データベース自体も カジュアルに 適切に垂直・水平分割することでdpointのパフォーマンス上の問題点はほとんどありませんでした。(dpointの処理が詰まったのはガチャ更新でいつもの10倍以上の課金が走った時くらいだけど、それは前述のmoduleを入れることで解決済み)

詳しくはこちらを御覧ください

ブコメレス

id:dekokun

このライブラリのメンテ、相当大変だろうなぁと思いを馳せる。

バージョンの上がり方で察していただけると幸いです ('A`)

12/4: Twitterでのやりとりを追記

簡単にまとめ

  • 日付でRANGEパーティショニングしててもJOINした場合に、条件によっては両方のテーブルで同時にパーティションを絞れるとは限らない
  • 自分の時は業務仕様的にJOINして2テーブル同時にパーティション絞る方法がなかったので非正規化に踏み切った(つらい)
  • mysqlだとexplain partitionsで検索したパーティションも表示できる(上の方のexplain参照)

2日目

次は id:onk さんの onk.ninja - Mountable Engine だらけの Rails アプリ開発 です

*1:AppStoreの規約でも禁止されていたはず。GooglePlayは(少なくとも自分の開発当初は)なかったと思うけど、両プラットフォームで公開するに辺り厳しい方に合わせていることが多いと思います

*2:詫び石とか

*3:厳密にはPurchaseオブジェクトなのですがここではレシートと呼びます

*4:社内gemから適当に持ってきてるのでいくつか修正必要かも

*5:消費は購入時の情報も必要になるため正規化してた

*6:課金レコードがtsvで10MBくらい

*7:本当はRubyのメジャーバージョンごとでもやりたかったのですがrbenvと相性が悪かったので断念

*8:後日Railsアドベントカレンダーに詳しい説明を書く予定ですが、簡単に説明するといろいろカスタマイズされたMySQLのforkです

RSpec Performance Turning

社内で開催されたRSpec勉強会テストのパフォーマンスチューニングについて話したので資料を公開してみます。

RSpecの名は冠しているものの他の言語やテスティングフレームワークでも応用できるところがあるかもしれません。

 

8/3追記:はてブコメント返信

テストのテストにはテスト対象を使えばいいんでしょうか。

場合によりますね。

 

基本的にはテストコードとテスト対象のプロダクトコードはペアであるはずなので、テストにバグが混入したとしても対応するテスト対象が変更されていなければテストがなんらかの形でエラーになるので、そこで検知できると思います。

 

テストコードのリファクタリング(共通処理をメソッド抽出など)は、既存のテストが品質を担保してくれてます(グリーンのままであればリファクタリング成功)

 

0からテスト書く場合でテストのテストが必要になるほど複雑なことは極力やらないようにしてます。ちゃんとテストされているサードパーティ製のライブラリを使ったり、最初はシンプルな形で作っておいてそこから少しずつリファクタリングするようなアプローチをとってます。

 

あと@moritzplassnigさんが英語版ないの?って言ってた 
「レベルを上げて物理で殴ればいい」ってどう表現すればいいんだろw

ドリコムに入社して1年経ちました

退職エントリや転職エントリはよく見るけど転職して1年経ちました系はあまり見ないので書いてみます

お仕事

社内ツールとかいろいろ作っています。

ソーシャルゲーム開発だと数10人でチームを組んで開発しているのですが、自分の所属してるチーム(ソーシャルゲームや分析系の基盤)だと細かい案件を1〜2人くらいで回すことが多いです。

自分の場合はマーケティング部関係の雑用お手伝いすることが多いです。アプリを作ることもあればLPをサーバにアップロードしたり、マーケがやりたいことの技術検証とかやってます。

自分の仕事の性質上あまり表に出ることはないのですが、最近だと ファンタジスタドール ゲーム公式サイト の事前登録のシステム全般見ています。

お仕事以外

  • 業務以外でもツール作成
    • Jenkins Gitlab Logo plugin
      • Gitlabで設定されているアイコン*1をJenkins上で表示するためのプラグイン。Custom Job Icon PluginのGitlab版
  • 社内勉強会開催
  • 社内LT大会で発表
    • 四半期ごとくらいでLT大会が開催されて、面白かった発表には「カリスマ」、ためになった発表には「総帥」の称号が授与されます

書いたテスト

入社時に


とか言ってしまったので、1年間でどれくらい書いたか数えてみました。

Jenkinsの Ruby metrics plugin で計測しています。(仕事で書いたアプリだけカウントしてます)

種別 test counts 開発人数 1人あたりのテスト数 Total lines Line of code Total Coverrage (%) Code coverrage (%)
Railsアプリ 265 1 265 1968 729 97.31 92.73
Railsアプリ 92 2 46 575 244 92.17 81.56
gem 168 1 168 1133 291 98.76 95.19
gem 36 2 18 287 148 98.26 96.62
gem 9 1 9 111 49 97.3 93.88
gem 22 1 22 320 149 99.38 98.66
Railsアプリ 140 1 140 1212 532 97.69 94.74
Railsアプリ 166 1 166 1918 819 89.21 74.73
Railsアプリ 353 2 176.5 2051 1000 89.86 79.2
Railsアプリ 345 1 345 3049 1226 93.41 83.61
Railsアプリ 103 1 103 647 290 93.04 84.08
1596 1355.5 12624 5187 平均95.34% 平均89.09%

2人で開発してたものに関してはテストの総数 / 2 して換算していますが、それでも1年間で1000個以上テストを書いていました。

自分のデスク

こんな感じでもみんな生暖かい目で見守ってくれます

http://instagram.com/p/Z9hli4g5NR/

http://instagram.com/p/a-AKCPg5Av/

http://instagram.com/p/YGq3AAA5I-/

*1:弊社のGitlabはBitbucketみたいにリポジトリ単位でアイコンが設定できるようになってます

社内でGit基礎勉強会を開催しました

Gitの基礎*1について詳しく紹介した勉強会がないなぁと思い、試しに社内で開催しました。

勉強会で使った資料

https://github.com/sue445/git-base-study

社内Gitlabからcloneしたのをgithubにpushした時に一部バイナリデータが壊れていますorz

Gitの履歴を履歴管理する

$ mkdir git-base-study
$ cd git-base-study/
$ git init
Initialized empty Git repository in ~/git-base-study/.git/
$ cd .git/
$ git init
Initialized empty Git repository in ~/git-base-study/.git/.git/

Gitの各コマンドが実行された時のローカルリポジトリの変化を説明するのに分かりやすい方法を模索していたら最終的にこれに行き着きました。*2


Gitって便利!

先述のリポジトリでもgitの1コマンドに対して1コミットの粒度でコミットを作っているため、リポジトリの状態変化がgithub上でdiffをとりやすいのではないかと思います。

勉強会の反応

社内で告知した時に「日常的にGitを使ってる人対象。」とは言ってたので中級者以上はきてたと思うのですが、それでもみんなポカーンとしてました。

基礎の恐ろしさの片鱗を伝えられたのは良かったですが、勉強会主催する側としては失格ですね。。。

*1:入門の方ではなく、いわゆる名古屋の人たちが使う方の基礎

*2:これを思いつく前の資料は半分以上はtreeコマンドの実行結果でした

新卒向けLT大会で意識の高いLTをしました

ドリコム社内で「先輩から新卒エンジニアの皆さんへ」という新卒向けLT大会があったので意識の高い発表をしました。

技術的なLTやネタLTはよくやりますが、エンジニアとしての生き方について語ったのはたぶん初めてだと思います。

ちなみに @ さん、 @ さん、@さんと言った頭のおかしい人達による計7人による発表でした。*1

お前らLTなんだから長々と話すなw

スライド

 

新卒からあった質問

Q. 新卒が読んでおくべき本は?

A. 気になった本全部読んでればいいんじゃないかなw
本を読んだら脚注とかに「~から引用」とかあるのでその辺を辿るとか、Twitterで話題になってるのかたっぱしから読むとか。
 
質問コーナーでは言いそびれたけど、勉強会でよく取り上げられる本は読んどくべき
達人プログラマやクリーンコード辺りは頻出。こういう本がその界隈での共通言語になってる

達人プログラマー―システム開発の職人から名匠への道

達人プログラマー―システム開発の職人から名匠への道

  • 作者: アンドリューハント,デビッドトーマス,Andrew Hunt,David Thomas,村上雅章
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/11
  • メディア: 単行本
  • 購入: 42人 クリック: 1,099回
  • この商品を含むブログ (344件) を見る

Clean Code アジャイルソフトウェア達人の技

Clean Code アジャイルソフトウェア達人の技

Q. (sue445は) 1年目に何冊くらい読んだ?

の上2~3段と
くらい。1年目は言うほど読んでないです。
当時は会社の制度で資格取るのが推奨されてたのでそっちの勉強が多かったです(Oracle Master BronzeとSJC-Pは1年目で取ったけど、簿記3級と応用情報は受けたけど落ちた(´・ω・`))

Q. 1冊を全部読み切って次の本にいくのがのがいいか?それとも少しずついろいろ読み進めるのがいいのか?

A. 自分は全部読み切ってから次の本を読むことが多い。読んでる最中に新しい本が出てそっちに流れちゃうけど、そういう時は戻ってきた時に内容を忘れてるのでおすすめしない。
あと写経する時はほんたったおすすめ

EDISON ほんたった黒(ハードケース入り)

EDISON ほんたった黒(ハードケース入り)

 

資料について補足

  • いろいろ語りたいことはありましたが、時間が限られていたので「本を読め」について重点的に話してます
    • 1回のLTにあまり内容を盛り込みすぎると論点がぼやけるのでやりたくない
  • 今期の嫁と本妻について
    • ボーナスで買いました(・∀・)

大事なこと

 

*1:僕はただのプリキュアヲタです

社内でJenkins勉強会を開催しました

他の会社だと1つのJenkinsをみんなで使うという運用(な気がする)ですが、うちの会社の場合だと個人やアプリで好き勝手にJenkinsを乱立させてます。

他の人はどんな風にJenkinsを使ってるのかなーと思って何人かに声をかけて開催してみました。

登壇者は自分を含めて3人。参加者は20人くらい。勉強会初主催にしてはまぁまぁだったと思います。

唯一の心残りは、新卒がたくさん来ると思って資料を張り切ってたのですが新卒研修とかぶってしまったという(´・ω・`)

 

社内で使ったスライド(無修正版)に社外公開用に修正を加えたスライド(モザイク修正版)をslideshareにアップしたのでよければ御覧ください。

 

Railsアプリに特化してるので他の言語の方はどうもスミマセン

一緒に発表した @ さんのエントリ

関連エントリ

おすすめ書籍

入門Jenkins―実践「継続的インテグレーション」

入門Jenkins―実践「継続的インテグレーション」

僕がJenkinsを立てようと思った時に丁度発売されたのですんごい助かりました!