ENECHANGEで働いている id:tetsushi_fukabori です。
昨日一昨日に引き続きRubyKaigi 2019 Day 3の様子をお伝えします。 遅筆なため速報性のない記事になってしまいごめんなさい。
本記事で書くこと
「私がRubyKaigi 2019に参加して感じたこと」「私が参加したセッションで話されたことの要約と感想」を主に書きます。
RubyKaigiはYouTubeチャンネルがあるので、講演を全て観たい方はこちらもどうぞ(本記事執筆時点ではまだ2019年分はアップされていません)。
本日のお写真
スポンサー企業さまの中には飲食物を提供してくださっているところも。アイスコーヒーとラテ、ありがとうございました。
本文に入る前に:RubyGems.orgの多要素認証を設定しましょうのお知らせ
RubyKaigi 2019でRubyコミッターが発表する前には必ずアナウンスされていた重要事項ですのでここでもお知らせです。
- RubyGems.orgのアカウントに対して攻撃が行われている
- 攻撃防止のため、現時点でRubyGems.orgのアカウントを持っている方、これからRubyGems.orgのアカウントを作成される方は必ず多要素認証(MFA: Multi-Factor Authentication)を設定してください
- 実際にbootstrap-sass gemへの攻撃が行われ、version 3.2.0.3に攻撃のコードが含まれていました
みなさんでコミュニティを守っていきましょう!
Day 3のセッション
Ruby Committers vs the World
本日の一発目、RubyKaigi恒例(らしい)Rubyコミッターのみなさんによるトークセッションです。 Matz & オールスターズ的な感じですね。
まずは事前に集めた質問にRubyコミッターのみなさんが答えるコーナー。
最近のruby保守的じゃない?
- 「みなさん手を挙げて投票して下さ〜い。もっと攻めて/このままで/もっと保守的に の三択で。」
- (壇上でも客席でもほとんどが「もっと攻めて」)
- Matz「投票を信じるなっておじいちゃんに言われたから…」
- 「後方互換性が生きてればなんでもいいよ。でも古いコードを壊す人は「××××」って感じ。」
- Matz「母集団が福岡まできてるアグレッシブな人たちだから…このまま「攻める!」って言って何かあったら俺は怒られるよ!?」
「そういえばキーワード引数の後方互換性のために一旦汚い仕様にしたと思うけど、3.0以降に壊すつもりはあるの?」
→Matz「既成事実化を図ってRuby4.0くらいで…」「10年くらいかければいいんじゃないかな。1.8から10年くらい経ってる。使ってる人いないでしょ?」
→「会場に1.8使ってる人いる?」 →いた
→Matz「バージョンあげたほうがいいよ笑」
- 「みなさん手を挙げて投票して下さ〜い。もっと攻めて/このままで/もっと保守的に の三択で。」
最近どんな言語やってる?RubyとC以外。
- Matz「Elixirとか」
メンテナがいたら嬉しい分野ってある?
- 「放置状態のlibはあるけど…使ってないからほっとかれてるのであって、メンテナはいらないかな…?」
- 「windows対応は足りてないと思う」
- 「GVLは誰かやってくれ…2.6は壊れてるかも?」「一気に作った人がいなくなって困ってる」「これからソース解析する」
- 「ドキュメントが少ないから作ってくれると助かる」
「JSONのライブラリをメンテナンスしてる人の反応がない…的な話があったような気がする」
→「オリジナルの作者が忙しいっぽくて反応がないから長期的には問題になるかも」
→「基本的には機能は足りてるしリポジトリのコミット権限は別の人にもあるから、緊急時はなんとかなると思う」
(話はやや飛んで)「DateはTimeに寄せていきたいと思ってる」
→Matz「やっていいと思う」
→「Date.parseは今の機能はすごいヒューリスティックだから直したい…」「Time.parse(123)とか何になると思う?これだけ読んでわかる人なんかいないでしょ?でもこれ特定バージョンでは通るようになってたんだよ?」
→「DateTimeは?」
→「DateTimeは削除するけどRailsでは拡張してるみたいだから…まあActiveRecordとかActiveSupportが頑張って対応すればいいんじゃないかな?」
Rubyコミッターのみなさん、結構アグレッシブではないでしょうか。
そしてまさかのJSONメンテナが半不在という状況…無くなるわけにはいかないライブラリなので、良い方向に向かって欲しいですね。
次はミニ開発者会議。
Matzから新しいsyntaxの提案
- 提案
- Range.eachとかやりたいけど、今だとカッコ囲みが必要。dotと同じメソッド呼び出しで、range(
..
)よりも優先順位が低い演算子あっていいんじゃない? a .. b |> each do
とかどう?- Elixirだと
x |> func1 |> func2
はfunc2(func1(x))
- 提案は
x |> mechod(1) |> method(2)
をx.mechod(1).method(2)
- (みんなリアルタイムで新しい演算子考えてはスクリーンに書き連ねる)
- Range.eachとかやりたいけど、今だとカッコ囲みが必要。dotと同じメソッド呼び出しで、range(
「むしろ右代入の方が欲しくない?」
→「記号どうする?今は絵文字とか使えるけど使ってみたら?」
→「👉」は?
「むしろ今はUnicodeは変数にも使えるけど、使えないようにした方がいいんじゃない?って議論がずっとある」
- 「なんと
🍣 = Sushi.new
はvalid」
- 「なんと
「Elixirで有名な演算子を違う挙動で使うのはどうなんでしょう?混乱しません?」
→「Elixirはまだいきてるの?」
→Matz「いきてるよ!」
→「じゃあまずElixirを潰して…」(会場爆笑)
「Unicodeの大文字も定数になるようになった(2.6から)。影響を受けた人いたりする?」 →いませんでした
- (
Π = 3.14 # Constant
π = 3.14 # local variable
)
- (
- 提案
Numbered parameters
- 提案
1.times do |i,| p i end
を1.times do p @1 end
みたいにアクセスできるようにしては?
- 意見
- 現在だと
@
はインスタンス変数を想起させる - 使いたいシチュエーションはだいたい1要素の時だから
it
とかあればいいのでは?
- 現在だと
「でも
it
はRspec殺しちゃうのでは…?」→「誰かRspecって使ってる人っている?」(会場爆笑)
Matz「この議論は9月くらいまでには結論出したいと思ってる」
- 提案
上手く文章では伝えられませんが、会場はドッカンドッカンでした。
バチバチ意見を戦わせたり、時に冗談を言い合ったりしながら新しい仕様を決めていく、コミュニティの雰囲気が感じられるトークセッションでした。
Cleaning up a huge ruby application
cookpadのコードリファクタリングプロジェクト、通称「Odaiba project」の報告発表です。
会場には人が溢れており、いかにアプリ開発者がコードを綺麗にしたいと思いながらも方法を模索しているのかが分かる様相でした。
cookpadさんは自ら発信されているように、本体部分は巨大なモノリシックのRailsアプリだそうで、実行されていない処理や無意味な処理などリファクタすべき箇所が多々あるようです。
- Odaiba Projectはコードを綺麗にしている
- 何を消せるか?
- 既知の未使用コード
- 未知の不要なコード
- 無意味コード
- 実行されないコード
- なぜ消すのか
- 単純に嬉しい
- 依存性が減らせる
- テストや起動が早くなる
- 未使用コードが溜まることによる性能低下を下げられる
- でもコード削除が進まないのはなぜ?
- 時間がかかる
- 優先順位低い
- 継続的にコードが増える
- 要はサービスが発展している
- 自然に溜まっていくけど誰も使ってないから誰も読んでない
- ホコリみたいなもの
- 何を消せるか?
- どうやって消すのか
- 優先順位をあげて合意を取る
- 継続的にコードを消す
- 実例
- 継続的にコード削除
- キッチンクリーナー
- 未使用っぽいコードを探すバッチ(月一)、担当者探してメンションしてissueを作ってくれるツール
- 1年以上呼ばれないコントローラや一定期間呼ばれないバッチコードを検知する
- git logから当時の担当者を探す
- iseq logger
- productionで呼ばれてないコードを記録
- iseq = instructiopn sequence、メソッドとかに振られるid
- loggerを仕込むことで起動時にはやや遅くなるが実行時はさほどでもない
- logを蒐集し、fluentdで蒐集・分析する
- 普通にやるとコードベースが新しくなると古いログが使えなくなる
- 出来るだけ過去分も使いたいので「ファイル名、行番号、メソッド名とかが一致したら過去のログ使ってもいいのでは?」と割り切っている
- このやり方だとviewテンプレートの未使用も捕まえられる
- erbとかは一回rubyコードに変換されて実行されるから
- 弱点
- rubyにパッチを当てる必要がある
- 使われないif文内にあるメソッドとかが、別のところから呼ばれてると判定できない
- ファイル単位の削除には向いてた
- oneshot coverage
- ruby2.6新機能
- 各行が一回でも実行されてかどうかを記録
- ruby2.6じゃないと使えなかったから頑張ってバージョンをあげた
- 行単位にレコード作ってるから、ソースの変更に弱い
- 2.6にするだけでつかえるし、行単位なので解像度が高くていい、可視化に普通のカバレッジツールが使える
- データ量が多いのは辛い
- iseqと違って「読み込むだけで記録される行」がある
- キッチンクリーナー
- ツールはあくまで参考。年1のコードは年1でしか動かないんだから、Nヶ月使われてないんだから即消すなんてできない。
- 継続的にコード削除
やはり銀の弾丸はありませんでした…が、再認識したのは「コストをかけることに同意をとる」ことの大事さですね。
大抵の場合リファクタは「余ったリソースでやろう」になりがちですが、そもそもコードベースの成長がリファクタの必要性を生んでいる=成長中のプロダクトな訳で、それに関わる人員に余剰リソースなどないわけです。
そもそも巨大なモノリシックRailsアプリのバージョンを上げるのはそれだけで大仕事ですが、それをリファクタのために(だけでは無いでしょうが)やってのけたというのは感服です。
Best practices in web API client development
Web APIのクライアントをどのように作るべきか、の知見をまとめた発表です。
発表者さんは「僕のトークは闇のAPIについてです」から初めており、黒魔術やら闇の力やらRuby界隈にはカッコいい言葉が流行っているようですね。
最近のサービスは大抵APIの連携で機能を拡大することをやっているので、どう外のAPIと付き合っていくべきか、は大事なテーマです。
- APIクライアントの分類
- APIクライアントには4種類がある
- API開発者と利用者が同じかどうか?
- public APIか?private APIか?
- public api made by ourselves
- 利用者としては公式がメンテしてる安心感
- publicなのでクライアントをgem化するとAPI提供側はマルチ言語対応は大変
- private apis made by ourselves
- 社内向けAPI
- クライアントをgem化しておけば導入が早い
- ドッグフーディングとしても良い
- public apis made by others
- だいたいAPIクライアントは既に作られてる
- 勉強のためだったり、他の人が作らないようなマイナーなサービスとかは自分で作る
- private apis made by others
- API for B2B
- どこにAPIクライアント作るべきか問題がある
- app/? lib/?
- gem化して切り出すべきだと思ってる
- gem化しないと
- 密結合
- APIクライアント部分だけの改修でも全部一緒にリリースしなきゃいけないとか
- 結果CIも大変
- gem化して切り出せば
- 疎結合
- コード小さくテスト早い
- シンプルに作れる
- APIクライアントには4種類がある
- (以降は主にgem化する想定での発表)
- APIクライアントの責務
- 何より「単一責任の原則」
- 責任は対象のAPIを対象の言語で使えるようにすること
- 不要機能は混ぜるべきでは無い
- 何を作って何を作らないか?
- 作る
- ruby <> httpのパラメーター変換
- エラーコードのラップ
- HTTPのエラーを独自エラークラスでラップ
- アクセストークンの自動更新
- トークン切れの時にリフレッシュする処理は定型的
- 隠蔽してあげれば楽
- 作らない
- APIレスポンスキャッシュ
- 要件はアプリ側にあるのに、APIクライアント側がキャッシュ機構に依存しなきゃいけなくなる
- リクエストパラメーターのバリデーション
- APIサーバー側にやらせるべきだが、必須とか空白チェックくらいはいいも
- APIサーバー側が公式にスキーマ定義してたらやっていいと思う
- APIレスポンスキャッシュ
- 作る
- 何より「単一責任の原則」
- 7 Good Patterns
- 依存は可能なだけ最小に
- だいたいNet::HTTPを使った実装でいい
- Faradayは銀の弾丸
- Net::HTTPだけでは足り無い場合で、CRUD全部使いたいならこれを使うべし
- 主題を目立たせる
- API実行で必要な引数は認証とエンドポイント固有の引数
- どのエンドポイントでも使う認証などはインスタンス変数化せよ
- エンドポイントの主題が引数で分かるようにする
- hash引数ではなくキーワード引数
- メソッドの定義から必要な情報がわかる
- 不要な引数渡しても死なない
- 必須引数と任意引数をデフォルト値で示せる
- パラメーターオブジェクトの導入
- 引数10以上だともうキーワード引数は辛い
- パラメーターオブジェクトはメソッドの引数専用のクラス
- オブジェクト自身にメソッドを持たせられる
- YARDコメントが書きやすい
- メソッドアクセス可能なレスポンス
- rubyっぽい実行ができる
- メソッド化するとmapで強い(
map(&:xx)
で実行できる) - mashifyおすすめ
- curlは国際言語
- APIサーバー側に質問とか投げる時はcurlでワンライナーで情報が伝えられる
- レスポンスがJSONならパイプでjqコマンドに食わせれば読みやすい
- macならさらにpbcopyまで渡せばクリップボードにまで渡せる
- 手でcurlコマンドを組み立てると大変だから、faraday_curlを入れておくとデバッグ環境では毎回ログにcurlコマンドを吐いてくれて便利
- 依存は可能なだけ最小に
- 質問
- タイムアウトエラーなどはどうハンドリングしてる?
- アプリ側からいじれるような設定をgem側に設ける
- タイムアウト時間をデフォルトでAPI側に持たせて、アプリ側から値渡しできるようにする、とかは工夫する
- publicなAPIって必ずしも仕様を全部公開していないのでは?どうハンドリングしてる?
- 即答は難しい…頑張るとか…。
- 冪等性が保持されてないAPIに対してfaradayを使うとオートリトライされて困ったりしないか?
- ポリシーとしてはAPI側には極力不要な機能を実装しない
- 見たことあるのはアプリ側でAPIのレスポンスをキャッシュするスマホゲーのような事例がある
- タイムアウトエラーなどはどうハンドリングしてる?
実践的で実装に繋がる、ためになる発表でした。
「7 Good Patterns」はどのプロジェクトでも実践して良い内容だと思います。
The future of the Bundled Bundler with RubyGems
RubyGemsにbundlerが含まれたので、Ruby2.6からはbundlerが標準で入るようになった、という発表です。
みんなRubyを入れたら「じゃあbundlerをインストールします」をやっているので、これはもう一緒にしたほうがいいですよね、という対応です。
今回の対応の中で古いバージョンのruby用のコードが捨てられていますが、非互換の変更はありません。
今後ですがRubyGems4で以下のような非互換の変更も入れていきたい、とのことです。
- コンサバティブオプション:すでにインストール済みのものはインストールしない
- デフォルトオプションの挙動修正:bundlerから特別なgemとしてインストールする時、バイナリ作ってくれない。経緯もよくわからない。
- 標準のインストールパス:
~/.gem
とかに入れるべきかと考えている。
コードクイズ答え合わせ@午後のブレイク
発表ではありませんが、cookpadさんがスポンサーブースで3日間に渡って出題したコードクイズの解説がありました。
問題と回答はcookpadさんのページをご参照ください。
出題は全9問で、「より短いコード改変で」「より早く指定の挙動("Hello World"の出力)を達成」した人が勝者となります。
難問揃いで、後日のAsakusa.rbでも集まったみなさんが頭をひねっていたので、ぜひトライしてみてください。
そんな中、弊社エンジニアの id:cuzic が9問目で勝者となりました!
津々浦々のRubyエンジニアが集まる中で受賞できたこと、本人も喜んでいました!
Reducing ActiveRecord memory consumption using Apache Arrow
Apache Arrowという「共通データ形式でメモリ上にデータを持つことでアプリ間でデータの共有を高速化し、開発効率もあげよう」という注目のプロジェクトがあるそうです。
今は個々のツールが個々のやり方でデータを取得・格納するため、間に変換が必要で、全般的に非効率な状態になっているようです。
Apache Arrowの形式でデータの格納を行うとメモリ効率が良いとのことで、今回はActiveRecordの取得データをApache Arrowの形式でメモリ格納することで、よりメモリ利用効率の良いものに作り直す試みの発表です。
- AR::Relationの中身をArrow::Tableに置き換える
- arrowは行メジャーなレイアウトなので
- フィルタリングが早い
- フィルタするときに同じカラムの値が並んでるから取ってくるのが楽だしまとまってるからメモリに乗りやすい
- メモリ効率がいい
- C++だからrubyより早そう
- フィルタリングが早い
- arrowは行メジャーなレイアウトなので
発表者は Red Data Tools に参加されている方で、Rubyでデータサイエンスを行うために様々な対応をされているそうです。
コードを書くのが楽しいRubyでデータサイエンス、熱くていいですね!
Optimization Techniques Used by the Benchmark Winners
「ヤバいものを見てしまった」感が凄かった発表です。
SequelというRubyからDB接続を行うツールの製作者によるSequel高速化の話、なのですが、完全に魔術です。
私の知識レベルと英語レベルではほぼメモが取れませんでしたが、「可読性をどこまでも下げていいならRubyはここまで早くなる」みたいな発表でした…。
改めて発表資料(「→」キーで次スライドに進めます)を見直しましたが、やはり理解できません。 口頭での説明が多かったのもありますが読み解くのも困難です。
もはや執念とも言えるべきレベルで高速化を達成した、という発表で、発表者はめちゃくちゃ笑顔で発表していたので良かったのでしょう。
クロージング
これで全日程が終わり、松田さんによるクロージングです。
「みんなコードを書こう」「我々はRubyFriendsだ」「Rubistがナイスだから、Rubyがナイスなんだ」などなど感動的なメッセージでした。 動画が公開されたら是非とも見てください。
そして次回のRubyKaigi 2020の開催地は…?
長野県松本市が次回の開催地として発表されました!
Matzこと松本氏が松本市と書かれたボードを掲げている画像に「Matsumoto」とキャプションが付いているというハイコンテクストな発表形式でした。
最後にRubyKaigi運営スタッフ一同が壇上に上がった際には会場中から割れんばかりの拍手が鳴り響きました。 僕はこのタイミングで軽く泣いたのですが、会期中のどの発表よりも大きく、長い拍手が運営スタッフに惜しみなく贈られていました。
参加者全員がこのカンファレンスを良いものにしようとしていたことを感じるクロージングでした。
RubyKaigi 2020
今年が初参加のRubyKaigiでしたが、学びあり笑いあり出会いありの素晴らしい三日間でした。
また是非来年も参加したく考えています。
来年、松本で会いましょう!