くりにっき

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

社内gemとOSSのgemのメンテについて

これは 【その1】ドリコムAdventCalendar の1日目です *1

自己紹介

1日目:社内gemとOSSのgemのメンテについて

そういうポエムです

言葉の定義

  • gem : Rubyのライブラリ
  • 社内gem : 社内など特定の環境でしか公開されていないgem
  • OSSのgem : RubyGems.org など、OSSとして公開されていて誰でも自由にインストールできるgem

適当な説明ですが世の中のgemはだいたいこの2種類に分類されると思います。異論あると思いますが以降この2つの言葉を使います

OSSのgemのメンテで意識していること

他の人にリポジトリを見てもらい、その人が容易にPRを送れるように「おもてなしの心」を意識しています

どんなに小さいgemでも自動テストは必ず書く

  • 自分以外の人が修正した時に既存機能を壊していないことを手軽に確認するためです
    • テストのないリポジトリにPullRequest送るのは勇気が入るけど、rspecでもtest-unitでもいいので基本的なロジックに対してテストがあると安心感があります
  • リポジトリのトップにCIのバッジを貼ってテストが落ちてないことをアピールできればベストですね╭( ・ㅂ・)و ̑̑

f:id:sue445:20151125213819p:plain

バージョンごとにtagはpushする

読んでもらうREADMEを意識する

  • READMEはリポジトリの顔なので、そこにどんな機能があるかが一目で分からないと本当に素晴らしいgemであっても誰にも使ってもらえないです
  • サンプルコードやスクリーンショットなどがあると文章を読まなくても直感で分かるようになるのでベターですね

CHANGELOG(更新履歴)を書く

CHANGELOGがないとどのバージョンでどんな機能が入ったのか分からないのでgemの利用者は迂闊にアップデートできないので合った方がいいです

サンプル

https://github.com/sue445/rubicure/blob/master/CHANGELOG.md

粒度

  • 基本的には1行 1 PullRequestくらいの粒度がベスト
    • 逆に言うと1つのPullRequestに複数の変更が入ると変更が追いづらくなります
    • 「PullRequestにも単一責任の原則」 (1つのPullRequestには1つの変更だけを入れる)を適用することを意識しています
  • 各バージョンごとに前のバージョンとのcompareリンクを「full changelog」で書いていればバージョン間の差分がコミット単位で分かるので分かりやすいです

カテゴリ分け

量が少なければ羅列していてもいいのですが、量が多くなるとどれが重要な変更なのか分かりづらいので下記のようなカテゴリを使ってます

  • Breaking change:下位互換をぶっ壊す変更
  • Bugfix: バグ修正
  • Enhancements: 機能追加

この書き方は RSpecのCHANGELOG を参考にしています。gemの利用者にとって重要な変更(Breaking change)は上に書いて目立たせるようにしています

細かいRefactoring系を書くかどうかは好みが分かれるところなのでどっちでもいいかなぁw

CHANGELOGの半自動生成

gemのバージョンアップの度に毎回上記に当てはめてCHANGELOGを書くのは大変なので、普段からPullRequestやMergeRequestのタイトルをそのままリリースノートに書けるような形式*4にすることを心がけておくことで、いざリリースの時には GitHub Changelog Generatorgitlab_awesome_release などでCHANGELOGのテンプレだけを自動生成し細かい文言修正やカテゴリ分けだけ手動で済むようになります

参考になるリポジトリ

たまに bundle update おじさんをすることがあってアプリで使ってるgemのCHANGELOGを全部読んでたことがありますが、その中で一番参考になったのは Ruby on Rails, RSpec 辺りでした

gemを作ったら必ず宣伝する

  • githubrubygemsに公開しただけで満足すると自分以外誰も使わないのでブログや勉強会でどんどん宣伝した方がいいです。
    • Twitterだけだと流れるので1回流すだけでは宣伝効果は弱いと感じてます。

社内gemのホスティングについて

  • 弊社だと geminabox を立ててそこにgemをホスティングしています
  • drecom_gem という社内gemを自分の社内gemに入れることで、rake drecom:release コマンドでgeminaboxにgemのリリースを行ったり、 間違って https://rubygems.org/ へ社内gemをリリースできないように rake release コマンドを実行できなくしています
  • 詳しくは RubyKaigi2014で発表した - mitaku.log を御覧ください
  • 今見たら社内gemが157個あった。すげえ。。。

社内gemのメンテで意識していること

だいたい上とかぶるので違う部分だけ書きます

サポートするRubyRailsのバージョンなどは社内アプリ優先

当然ですね

  • サポートが切れているRubyRailsのバージョンであってもそれらを社内で使ってるのであれば社内gemでも基本的にサポートする
  • acts_as_mysql_partitionという社内gem*5は2015年6月当時 (Ruby 1.9系, 2,0系, 2.1系, 2.2系) x (Rails 3.2系, 4.0系, 4.1系, 4.2系) の16パターンのビルドをしていたのですが、Ruby 2.2系とRails 3.2系を組み合わせるとビルドがこける問題がありました。*6 この組み合わせだけビルドを除外するでもよかったのですが、せっかくなのでRuby1.9系とRails 3.2系のサポートをきるためにこのgemを使ってるアプリを全部洗い出し、プロダクションでは使ってないことを確認したのでサポートをきりました
  • ちなみに社内には Gemicom というGemnasium の社内クローンがあったのでどのアプリでgemを使ってるのか特定が容易に進みました

gemを作ったら社内gemとOSSのどっちにすべきか?

自分の場合下記を考えて社内と社外(OSS)のどっちに公開するかを考えています

OSSのgemのメリット

  • OSSにした方が周辺のエコシステムを享受しやすい
  • 社内gemにしたら仕事じゃない趣味のアプリでは使えないけど、最初からOSSにしておくと仕事と趣味の両方で同じgemを使えて便利かと
    • 社内gemを勝手に社外で使うとインシデントだけど、既に社外で公開しているgemを社内のアプリで使う分には大抵の場合問題ないはず
    • 「高級レストランのあの味をご家庭でも!」感w

社内gemのメリット

社内コンテキストに特化させることによるメリットが多いです

  • OSSにしていろんなパターンを考慮して汎用化するとその分仕組が複雑になりますが、社内gemだと社内の利用用途に限定させることで仕組をシンプルにすることができます
  • 外からのマサカリが飛んでこない *7
    • とはいえ外のマサカリを全く受けない温室育ちだと外に出られないので適度に外のマサカリを受けることは大事だと思いますw

OSSのgem -> 社内gem or 社内gem -> OSSのgem

途中で公開形態を変えることもできますが、後者だと会社のメールアドレスやパスワードなどの機密情報をgit filter-branch などで完全に書き換える必要があり面倒なので、個人的には可能なら最初からOSSにしてた方が楽なんじゃないかなーと思います

12/1 21:50 追記

Twitterのやり取りを追記

2日目

*1:今年は参加希望者が多かったのでその1とその2の2枠構成です

*2:主にAPI周り

*3:毎週日曜夕方に宗教上の理由で30分間で200ツイート以上TLが流れるのでw

*4:「NameError出ていたのを修正」のような利用者にはどうでもいい内部実装に関するタイトルではなく、「○○の時にエラーになっていたのを修正」「○○のような機能を実装」など

*5:いい感じにMySQLパーティションを作成するgem

*6:たぶんこんな組み合わせ本家でもサポートしてないだろうなぁ・・・w

*7:個人的には社内のマサカリの方がこわ(ry