Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

Classi のエンジニア3名が RubyKaigi 2022 に参加しました

はじめに

こんにちは!開発本部所属のエンジニアの id:kiryuanzu です。
9月8日(木) 〜 9月10日(土) にRubyKaigi 2022が開催されました。
今回弊社ではシルバースポンサーとして協賛し、3名のエンジニアがオフラインで RubyKaigi に3日間通して参加しました。本記事では参加メンバーによる感想レポートをお送りします。

参加する前

筆者自身は学生時代に何度かオフライン参加を経験したのですが、同行した2名の新卒エンジニアは今回が初参加となりました。行く前にできるだけ RubyKaigi がどんなものか知っておこうということで、開催1ヶ月前から igaigaさんによるプログラム解説を実施していただき、発表される内容の予習を行いました。
他にも、津の気になる飲食店をみんなで探して事前に予約したり、会ってみたい他社のエンジニアさんや OSS開発者の方について話すといった活動をやりながらイベント当日を心待ちにしていました。

当日の様子

1日目最初の発表から Ruby の WebAssembly でブラウザで動かすといった視覚的にもインパクトが強く今後身近に使われそうな機能の紹介と内部の実装の解説から始まり、自分含めたメンバーみなとても興味津々で聞いていました。発表後に「楽しそう、使ってみたくなった!」と「実装の話難しかったけどわくわくした……!」という感想をみんなで語り合うことができRubyKaigi ならではの会話だなと思いました。

現地にはいなかったメンバーの方も Slack 上での RubyKaigi 談義に花を咲かせており、過去に参加した先輩メンバーから「自分もわからない話が結構ある。だけど、内部実装の話とかめっちゃ面白いので今はわからなくても来年はわかるようになりたいと思えて何を勉強したらいいだろうと考えられる。だから、わからないけれど面白そうな話に出会えただけで価値があるよ」とアドバイスをもらいました。わからないことだらけであっても前向きに学ぼうと考えることができ、参加したメンバーにとって勇気をもらえるメッセージで印象に残りました。

イベント中では様々な他社の人にお会いし交流することができました。その中でも、筆者が個人的に印象に残っている出来事はエス・エム・エスさんの社員の方達と交流したことです。Classi に今在籍されている方で、以前エス・エム・エスさんで働いていた方がいたことがきっかけで会話が弾み、当時の思い出やどんな活躍をされていたかといった話題で盛り上がりました。

他にも、エス・エム・エスさんの社員さんの中には Classi ユーザーの方もいて機能の気になるところについて教えてもらうといったことがあり、想定していないところでユーザーの声を聞けて大変楽しかったです。

社員のみなさんと一緒に写真も撮っていただきました。

また、業務中に技術記事を探している時によく名前が出てくる方や新卒研修に入っていた課題図書を翻訳されている方など、会ったことはなかったけれどお世話になっていた方々 のお顔を拝見することができたのもRubyistが集まるイベントならではといった体験でメンバー一同とても刺激を受けることができました。

ご飯の方面でも大変満足な日々を送ることができました。参加したメンバーの @_da1kong が美味しそうな写真を撮ってくれたので一部公開します。

1日目夜は津駅周辺にある松重というお店に行きすき焼きを食べました。前菜含めて素敵なお味でした。

2日目は Ruby コミュニティでお世話になっている方達と一緒に竹屋牛肉店というお店に行きました。新卒メンバーが肉だけでなくご飯大やガーリックライスも注文していて大変良い食べっぷりだったのが印象に残っています。

他にも、泊まったホテルでは毎朝まむし丼(鰻が使われたご飯料理)、運営の方達が用意してくださったお弁当など毎日美味しいものをいただきました。

運営のみなさん、素敵なお店に連れて行ってくれたRubyコミュニティの方々、そして今回のイベントに行く際「せっかくなのだから美味しいものを食べてほしい」と快く出資してくださった先輩メンバーに改めて感謝の気持ちを伝えたいです。

ここからは新卒エンジニア2人による感想パートです。

@_da1kong さんの参加レポート

2022年4月から新卒エンジニアとして入社しました @_da1kongです。
このような大きいカンファレンスのオフラインでの参加はかなり久しぶりでした。
参加する中で特に印象深かったことについて報告します。

発表

カンファレンスの発表はかなり難しいものも多かったのですが、会社で予習して取り組んでいたおかげでより多く吸収することができました。
魅力的な発表は多くありましたが、そのなかで自分が特に印象に残った発表に触れたいと思います。

他の発表は予習をしていたのですが、この発表だけは「調べないで見た方がいい!」と言われていたので準備しないで臨みました。
内容はすごく読みにくいコードや芸術性の高いコードを紹介するもので、シンプルにプログラミングのアイデアの面白さを感じられる内容でした。
プログラミングに初めて出会った時のような新鮮な気持ちを思い出す発表で、オフラインイベントならではの盛り上がりに興奮しました。

社内でも演者のしおいさんが書いた記事をおすすめされることも多く、一方的にお世話になっている方でした。
Wiresharkというnetwork packet analyzerをmrubyで動かせるようにしたという内容で、個人的に関心のあるCTFで使用されているツールだったので興味のある内容でした。
機能が足りないと思ったら自分で作る!というエンジニアの気概を感じて楽しかったです。
また、観客とのコミュニケーションが活発でコミュニティからの愛を感じる発表でした。

Stringのエンコーディングの速度を改善していくという発表でした。
問題提起からボトルネックを見つけ改善するという、探索的に進めていく過程がまとまっていて、すごく勉強になりました。
ちょうど業務でサービスのパフォーマンスの改善をやっていたこともあり、親近感を感じる発表でした。

そのほかにも魅力的な発表は多くありましたが、中には難しいものも多かったです。
ただ、「なんか面白そう!」「来年は理解できるようになりたい!」というモチベーションが湧くものが多かったです。

RubyKaigiでの発表がすぐに日常の仕事に活かせるかは分かりませんが、純粋に技術を楽しむ気持ちやそれを試したくなる好奇心を感じることができました。

交流

このようなオフラインのイベントでは、休憩時間やお昼休みで実際にコミュニケーションができる時間が多くありました。
Rubyコミュニティの方々は良い意味ですごく砕けていて、楽しく話しやすい方が多かったです。
以前からRubyKaigiに参加されているid:kiryuanzuさんが繋いでくださって、本当に多くの方と交流させていただきました。

人生で初めての名刺交換もできました。

クリアコードの方と名刺交換した筆者

また以前Classiで特別講義をしていただき、RubyKaigiを主催しています松田さんにも直接お礼を言うことができました。

tech.classi.jp

自分と同じぐらいの若手エンジニアから、世界で活躍するようなトップエンジニアまで分け隔てなく交流できるのは、このようなカンファレンスならではだと感じました。

エンジニアとして頑張るモチベーションが上がる充実した時間になりました。

まとめ

このような大きいカンファレンスは初めての参加で不安もありましたが、会社の先輩方やRubyKaigiに参加されていたエンジニアや運営の方々のおかげでとても有意義な時間となりました。
今回の参加で満足せずに、引き続きRubyコミュニティに参加していきたいという気持ちが強くなりました。
貴重な機会を頂きありがとうございました。

すずまささんの参加レポート

こんにちは!新卒1年目のすずまさです。

今回 RubyKaigi 初参加で、こういった技術カンファレンス自体も初めてでした。

また、入社してから Ruby を触り始めたので、Ruby 歴も半年未満と浅く、界隈に知り合いがたくさんいるわけでもないので、行く前は緊張と不安が少しありました。

行ってみた印象

印象を一言で表すと「面白くて質の高い大学講義 兼 大規模オフ会」でした。 参加する前に抱いていた不安は杞憂で、実際始まってみると私のような Ruby 初心者でも共感できたり、勉強になる発表が多かったです。

また、何度も参加されている id:kiryuanzu 先輩が引率してくださったおかげでたくさんの方と交流できました。 みなさん初対面でも優しく朗らかで、普段の仕事の話や趣味の話もできてとても楽しかったです。

弁当待ちで並んでいる Matz さんと津餃子の話をしたりもしました(笑) まさかこんなに気軽に話せるとは思っていなかったので面白かったです!とても気さくで優しい方でした!

印象に残った発表はたくさんありますが、特に記憶に残ったものを 3 つ紹介しようと思います。

Ruby meets WebAssembly
1 日目の一番最初に見たのがこの発表でした。
WebAssembly を使って Ruby をブラウザ上で動かすという話で、実演が多くすごさがわかりやすかったので、私も含めて会場全体が盛り上がっていました。
後半の実装の話は正直難しくてあまり理解できませんでしたが、「よくわからないけどすごい…!!」という感動が味わえて、この後の発表へのわくわく感が溢れてきました。

Create my own search engine.
ポケモンカードの検索エンジンを作った話をされていました。
技術的にも勿論おもしろいのですが、趣味が前面に出ておりとても楽しそうに話をされていたのが印象に残っています。
趣味で開発して未完成のままやめてしまったプロジェクトがあるので、この発表に感化されて久しぶりにまた再開したくなりました。

The Better RuboCop World to enjoy Ruby
RuboCop で遭遇しがちな問題点を整理し、それに対してこうすればもっと良くなるんじゃないかという話をされていました。
初心者にありがちな悲劇として「RuboCop に怒られたので通るようにメソッドを分けました!」という例が挙げられていたのですが、まさに自分が最近通った道だったので耳が痛かったです。
私のような Ruby 初心者から熟練者まで共感できるポイントのある、とても良い発表でした。

感想

私がプログラミングを始めてからの大半の時間はコロナ禍でオンラインになっていたので、こういったオフラインのイベントはとても新鮮で楽しかったです。

初めてで経験も乏しい中ここまでちゃんと楽しめたのは間違いなく先輩たちのお陰なので、本当に感謝の気持ちでいっぱいです。

交流する人たちもすごい人ばかりだったので非常に刺激を受けました。 発表に関しては理解が追いつかないことがかなり多かったので、次回までにもっと強くなって完全に理解できるようになりたいです!

終わりに

以上が新卒2人の感想パートとなります。2人とも最初は、オフラインのカンファレンス参加の経験が少なく不安な面もあったようでしたが結果的にすごく楽しんでいただけたようで何よりです。
今回の RubyKaigi では筆者含めて参加メンバーみな刺激を受けたイベントになりました。この経験を経て、次に勉強したいことや挑戦してみたいことなど、新たな目標がたくさん生まれたように思います。
次回に向けて、RubyKaigi の良さを周知する活動を続けることで一緒に参加するメンバーを増やせたらと考えています。
それでは、来年の松本でまたお会いしましょう!読んでいただきありがとうございました。

最後に、Classi では一緒に働いてくれるメンバーを絶賛募集中です!Ruby を書くのが好きな方、教育サービスに携ることに興味がある方など、ぜひご連絡いただけると幸いです。

hrmos.co

hrmos.co

フルリモート環境である開発本部の課題を解決するために始めてみたこと

こんにちは。技術戦略室にてエンジニアをしています、中島です。

以前、リモートワーク環境におけるコミュニケーション課題の一つである「質問」についてブログを書かせていたただきました。 tech.classi.jp

弊社では今でも全社的にフルリモートを続けていますが、やはり人と人との接触機会は以前より減っています。そのため、計画的偶発性も少なくなっているのではないか、と考えています。

※ 計画的偶発性とは?

個人のキャリアの8割は予想しない偶発的なことによって決定される。その偶然を計画的に設計し、自分のキャリアを良いものにしていこうという考え方。 計画的偶発性理論 - Wikipedia

今回は、こういった状況を改善するために始めてみた施策について紹介します。

開発本部の課題と仮説

施策の内容へ入る前に、今私が所属している開発本部の抱えている課題と、それに対する仮説をお伝えします。

「Classi」というサービスはユーザー様から見ると1つのサービスですが、その中には先生・生徒・保護者様向けに様々なサービスが存在します。 classi.jp

開発チームはそれぞれのサービス毎に存在していますが、サービス内の機能は他のサービスとも複雑に依存しあっています。そのため、ちょっとした修正、お問い合わせの調査、システムアラート対応などをするだけでも他のチームとのコミュニケーションが必要になります。

しかし、チームを越えてコミュニケーションを取ることが少し難しいという現状が明らかになってきました。 なぜ難しいのでしょうか? リモートワーク以前であれば、コミュニケーションを取るために少し社内を歩き回るだけで、お互いに気軽に話しかけやすいという側面がありました。

ですが、リモートワークでは、まずはSlackでの文字でのやりとり、話すためにはMTGの設定をしなければいけなくなりました。リモートワーク以前に入社している人からすれば、相手を見知っているので気軽さはさほど変わらないのかもしれませんが、リモートワーク以降に入社した人もたくさんいます。

チームを越えた仕事が発生した時に初めて、全く知らない人とコミュニケーションを取るのと、少しでも人となりを知っている人とコミュニケーションを取るのでは、違いがあるのではないでしょうか?

その仮説を基に、チーム外の人とのコミュニケーションを促進できるような施策=雑談をしていこう!となりました。

本当に?

コミュニケーションを取れば、雑談をすれば、本当に課題が全て解決するのでしょうか? そもそも、こういった制度はあまり受け入れられないことも重々承知しています。 なので開発本部の制度として建て付ける際に、それでも意義があると自分自身納得する必要がありました。そんな中、弊社VPoTであるしんぺいさんの記事を読みました。

nekogata.hatenablog.com

私には考えもつかない視点で書かれていてとても好きです。上記の文章とともに、弊社のesaの中ではさらに社内の課題を交えた議論がたくさんの人とされていました。

その中で自分が腑に落ちたことは、雑談してもコミュニケーションがうまくいかないことはあるしプロトコルが合わない人もいる。それをもっと早い段階で(仕事で関係性を持つ前に)認識しておく(器官を育てておく)ことが大事なのではないか。そうすれば本番ではもう少し上手くやれるのではないか?ということです。(上手くとは、無闇に衝突しないなど)

そういった事前の場としても機能するのであればやる意義はあるのかな、と思ったのでした。

新しく始めた制度について

新たに「Zatsu談制度」を始めました。名前にあまり深い意味はありません。

この制度の目的は、開発本部内で横の繋がりを作り、チーム外で気軽に話せる人(場)を作ることです。 開発本部内での、弱い関係斜めの関係を作ることを主目的としています。

弱い関係、斜めの関係ってなんぞや?と思った方は是非上記のリンクを御覧ください。

例えば、同じチーム(強い関係)にしか所属していないと、そのチームにだけ注目してしまい、他のチームから孤立してしまう可能性があります。 考え方・施策・行動など、そのチーム内で個別最適が進むと、いわゆる”サイロ化”が発生します。

「開発本部の課題」にも書いた通り、Classiはサービス(プロダクト)の依存関係が複雑に絡み合った状態で開発されてしまっているため、必然的に外のチームとコラボレーションしながら課題解決に取り組む必要があります。サイロ化した状態ではコラボレーションが進みません。情報伝播や相互理解を促進するためには、弱い関係も必要です。

ちなみに「弱い関係」についてはデメリットもあるそうです。気になる方は「組織デザイン」をご一読ください。

また、斜めの関係性を構築することで、チーム外の関係だからこそ相談できたり、異なる視点を得られたり、新たに別の関係性が生まれたりするのではないでしょうか。

そんな想いでこの制度が立ち上がりました。

Zatsu談制度の運用方法

制度をどのように運用しているかを紹介します。

  • Zatsu談制度は、基本は 1on1 形式で行います
    • 縛っているわけではないので、他のペアと開催するのでもOKとしています
  • ペアの選定仕様は以下となっています
    • 誰かの意図・思惑は入れず、ランダムとする
    • チーム外の人とペアにする
    • 過去にペアになった人と違う人をペアにする
  • ペア選定作業はGoogle Apps Script (GAS) で実現しています
  • 制度の1実施期間を、3ヶ月に固定しています
  • 期間が終了すると、ペアを再選定します
  • 一度のZatsu談期間が終わったら、開発本部メンバー全員にアンケート形式でフィードバックをもらいます
  • 相談窓口として、Google Formsを用意しています
  • 参加が難しいという方もいると思うので、強制はしていません

Zatsu談で何話そう?

突然、雑談してね!と言われても困りませんか?なので色々なZatsu談ネタをesaにまとめて共有しています。

例えば弊社ではチームごとに偏愛マップやモチベーショングラフを作っていたりするので、そのリンクを集めたり、16personalitiesStrength finderの結果を共有したりしています。

conversation topic
雑談ネタの紹介

また、Slackには色々な趣味チャンネルもあるので、そこから話題を広げることもできそうです。

 Recommended Slack channels
おすすめSlackチャンネル

esaには、自己紹介ページを作っている方も多数います。日報を書く方もいて、ペアの日報を読んでいるという方もいました。自分を知ってもらうためには一定の自己開示も大切ですね。

1回目の実施が終わってみて

7月末に1回目のZatsu談期間が終了しました。そのタイミングでアンケートを取ってみたので、一部の内容を紹介します。

Zatsu談回数

  • 複数回実施してくださった方が半数以上を占めました
  • 1回で終わってしまい続かない方が多いのではないかという予想でしたが、定期的な開催が多く見られました

満足度・目的達成度

1: 不満足 5:満足

1: 達成されてない 5:達成されている

  • まだ制度を始めたばかりなので、もっと左に寄るかなと思いきや、満足度・目的達成共に右に寄っていました。一定の評価をいただけてホッとしました

Zatsu談制度があってよかったこと

制度があってよかったことをたくさんいただいたので、一部を紹介します。

- 直接役立ったり効果があるかはわからないが、Zatsu談ペアになった相手とはちょっと気安い雰囲気は生まれると思う
- ペアになった人をハブに、今まで関わったことのない全く知らない人と話す機会を得られました
- 知らない部門の人や業務で関わりがない人と話せるのは良い
- 会社の歴史のシェアや暗黙知のシェアにもなる
- 業務に関係ないプライベートの雑談ができるのが楽しかった!
- 普通に人生の先輩として話すだけでも学びがあった!
- 合同Zatsu談しているところもあって良いと思った!
- 別の領域の人と接点を持てて、その人が別の会で発言されてるのを見ても「Zatsu談で話した人だ!」と顔が浮かんだり、雑談で話したことと紐づいたりして理解が深まった
- 普段絡みの無い人と話をすることができた
- たまに雑談する間柄だったけど、改めて機会ができてよかったと思う。
- 仕事以外のことを気軽に話せて楽しかった。今後、何かあったときに話しかけるハードルは下がりそう。
- 業務であまり話さない人と話すきっかけが強制的にできるのでよかった。
- 通常業務では関われなかった関係ができた。新卒とペアになったので、他の新卒や研修にも興味を持つようになった。
- 普段話さない内容であったりちょっとした相談を気軽にすることができた。
- 恐らく殆ど接点がなかった&これからも少ないであろう方とお話できたのが嬉しかったです。
- 話すきっかけはできました

この他にも「やってみたら楽しかった!」「Zatsu談制度以外のところでもZatsu談してみた!」「チーム内のコミュニケーションも見直してみた」などの嬉しい声を聞くことができました。

しばらくこの制度を続けてみて、いずれまた状況をこちらで報告したいと思います。

他にやろうとしていること

Classiはサービスもシステムも複雑と何度か書いていますが、その影響もあり中途入社した方は入社当初のキャッチアップに苦労している面も見られます。

中途入社者が組織に適応するまでには一定の期間が必要だと言われています。(組織再社会化) 技術戦略室ではこの点をサポートするべく、中途入社者を受け入れるチームと協力して開発本部のオンボーディングを見直す動きも始めています。

まだまだやりたいこと・やれることはたくさんあるので、引き続きみんながごきげんにミッションを達成できるよう、開発本部の課題を解決していきたいと思っています。

RubyとRailsのコミッターである松田明さんの講演でプログラミングを楽しむモチベーションが上がりました

2022年4月から新卒エンジニアとして入社しました daichi ( id:da1chi24 )です。

先日社内でRubyとRailsのコミッターである松田明さんによる特別講義が開催されました。
開催の経緯や感想、当日出た質問を紹介します。

開催の経緯

2022年の新卒研修では、研修内容に関連した話題について自身で調べた内容を発表する機会がありました。
その発表の内容に、OSSのコードを読んだ発表など、それぞれの興味があることを深く掘り下げるものが多くありました。
発表が終わった後、その発表が良かったという話が上がり、OSS活動を積極的に行っている松田明さんをお呼びして講演していただくことになりました。

新卒研修に興味がある方は以下の記事もご覧ください。 tech.classi.jp tech.classi.jp

特別講義のテーマ

特別講義のテーマは、これから職業エンジニアとして人生を進める私たち新卒に向けて、楽しくプログラミングを続けていくためのヒントとなるものでした。

松田さんは20年以上職業プログラマーをされており、OSS活動やコミュニティ運営などもされています。そこで得られた経験や実感についての発表でした。

このような世界で活躍するエンジニアの方から直接話を聞ける機会はすごく貴重なので、楽しみにしていました。

印象に残った話

この講演の中では沢山の興味深い話がありました。 その中でも私個人が特に印象深かった話を紹介します。

プログラミングの最大の楽しみは「コミュニティ」

仕事で楽しめるプログラミングはプログラミングのごく一部でしかなく、最大の楽しみは「コミュニティ」であると話されていました。

松田さんは実際に、Asakusa.rbやRubyKaigiの主催など多くのコミュニティ運営に関わっていたり、世界中のカンファレンスで登壇を行っています。

その中で他のエンジニアとの関わりや新しい発見や経験を得ることができ、その活動がとても充実しているという話でした。

松田さんがコミュニティを大切にしている話は、自分にとって嬉しかったです。
自分もエンジニアコミュニティが好きでこの業界を選んだのですが、技術的に最前線を走るエンジニアの方も同じことを考えていることに親近感を覚えました。

また世界中のカンファレンスでの思い出や、そこでのエンジニアのコミュニティの面白さが語られました。
カジュアルな雰囲気で美味しいご飯やビールを楽しみながら技術の話ができるのは、すごく楽しそうで新鮮でした。

このようなイベントを通じて国内だけでなく世界中に友人ができることが、とても魅力的だと感じました。

OSS活動は義務ではなくて権利

OSS活動は「エンジニアがしなければならないもの」というものではなく、「誰でも参加することができる権利が与えられているもの」なので好きなところからつまみ食いしながら楽しんで欲しいという話がありました。

コード一つで世界中の人と関われるというのは、他にはないエンジニアの特権です。
松田さんも実際にOSSの活動を通じてエンジニアのコミュニティと関わったり、世界の有名なエンジニアと一緒に仕事をしたりしているという話もありました。

業務の中だとその会社のエンジニアとしか関わることができませんが、OSS活動であれば世界中の素晴らしいエンジニアと一緒に開発ができます。
そういった活動での経験がコミュニティの関わりに繋がったり、業務にも良い影響があるのだと感じました。

確かに今まで私は、OSS活動というと貢献しないといけないというイメージが強く、技術的に未熟な状態で貢献できることがあるのかと躊躇していました。

この話を聞いた後、自分にもできるとこから気軽にやっても良いと感じるようになり、敷居が下がりました。
今ではドキュメントの修正のPRを出したりするなど簡単な事から始めるようになったので、考え方が変わるきっかけになったと思います。

実際の自分が出したドキュメントの修正のPR

当日の質問

講義では話されなかった当日出た質問について一部紹介します。

Q. Ruby/Railsにずっと時間を投資できた理由を知りたいです。他の技術に目移りしなかったのですか。

Rubyの次に来る言語とかはひとりのプログラマーとしては気になりますが、自分はRubyやRailsのことが大好きで両足突っ込んでしまっているので、今更他の言語を主戦場にする気にはならないです。
むしろ「RubyやRailsの市場は俺が耕した」と思っているし、それで自分がRubyをやめてしまったら無責任だとも思うので、これからももっとみんなにRubyやRailsを使ってもらうように働きかけていく活動は続けていくと思います。

OSSへの人生最初のパッチはどうやりましたか。良いアプローチの仕方を教えて欲しいです。

僕の場合はあんまり参考にならないかもです。昔はもっと世の中は雑だったし、Railsとかも普通に使っているのに普通にバグっているという状態でした。
仕事で使うにしてもパッチを当てながらでないと使えなかったです。
なので敷居が高くなかったし、自分が困っているからやっていた感じでした。
今は整ってきてしまっているので、そこから問題を探すのは当時より難しそうではあります。
それでも、世の中のOSSからバグが無くなることは絶対にないので、まだまだ解決するべき問題は無限にあるはずです。

[予告] 9月のRubyKaigiに現地で参加します

Classiは2022年のRubyKaigiのスポンサーをしています。スポンサーチケットを頂いたので、新卒を含めたエンジニアと現地に行きます。

今回の松田さんの講演の中で、カンファレンスをより楽しむためには予習をすることが大事だという話がありました。

今私たちはRubyKaigiをより楽しむために、技術顧問のigaigaさんと毎週RubyKaigiのスピーカーセッションの内容などの予習会を行っています。

参加した後に現地のレポートも報告するので楽しみにしていてください。

UNIX 系システムのプロセスに関する社内勉強会に参加しました

こんにちは。新卒 1 年目エンジニアのすずまさです。

先日、弊社 VPoT の id:nkgt_chkonk が執筆した process-book の勉強会を修了しました。

process-book は *nix系のシステムにおけるプロセスやシグナルなどについて説明することを目的に書かれました。「プロセスとかよくわかってないからちゃんと知りたいな」みたいなひとたちが想定読者です。

参加する前は UNIX の基礎的な知識も乏しかった私ですが、学びがたくさんあり毎回楽しく参加できたので紹介します。

process-book の社内勉強会の様子

開催の経緯

Slack でシェルスクリプトの話題で盛り上がったのをきっかけに、プロセスモデルの重要性が話に挙がり、流れでその日のうちに「process-book 読もう会」が誕生しました。

進め方は下記の通りです。

  • 日時: 毎週木曜日 15:00〜15:30
  • 週に 1 章ペースで進める
  • 初めに各章ごと担当者を決めておき、担当者は esa に内容をまとめ、当日司会をする
  • 参加者は各自手を動かして予習しておく
  • 疑問や気付いたことがあれば会の中で発言する

印象的だった章

4 章のファイルディスクリプタの話が印象的でした。

  • OS は、プロセスから「ファイルを開いてね」というシステムコールを受け取ると、実際にファイルを開きます。
  • OS は、その開いたファイルを表す「番号札」を作成します。
  • OS は、その番号札をプロセスに対して返します。

この「番号札」のことを、「ファイルディスクリプタ」と呼びます

Ruby を使い、実際にファイルディスクリプタを出力してみると "5" が出力されます。

また、標準入力のファイルディスクリプタは 0、標準出力は 1、標準エラー出力は 2 ということを学びます。

ここで、「では 3 と 4 は何を指しているのか?」という疑問が生まれました。

会の中では「lsof コマンドを使うと調べられるよ!」とだけアドバイスを頂けたので、勉強会後に実際に実行してみると、下記のような結果が得られました。

# fd.rb
file = File.open("nyan.txt","w")
puts file.fileno
puts $$
while(1)
  sleep
end
$ ruby fd.rb &
$ 5 # ファイルディスクリプタ
8300 # プロセスID
$ lsof -p 8300
COMMAND  PID     USER   FD      TYPE DEVICE SIZE/OFF   NODE NAME
~~~ 省略 ~~~
ruby    8300 ssm-user    0u      CHR  136,1      0t0      4 /dev/pts/1
ruby    8300 ssm-user    1u      CHR  136,1      0t0      4 /dev/pts/1
ruby    8300 ssm-user    2u      CHR  136,1      0t0      4 /dev/pts/1
ruby    8300 ssm-user    3u  a_inode   0,14        0     13 [eventfd]
ruby    8300 ssm-user    4u  a_inode   0,14        0     13 [eventfd]
ruby    8300 ssm-user    5w      REG  259,1        0 259116 /home/ssm-user/004/nyan.txt

ここで、FD (ファイルディスクリプタ) の 3u と 4u を見れば良さそうだと分かりますが、これだけ見ても実際に何が行われているのかは分かりません。 そこで、man eventfd を実行してみると、下記のような文章が得られます。

eventfd() creates an "eventfd object" that can be used as an event wait/notify mechanism by user-space applications, and by the kernel to notify user-space applications of events.

この文章から、ファイルを読み込んだ後にそのファイルへのアクションを待つのが 3 で、アクションを受け取って通知するのが 4 だと予想できます。

では、その通知はどこに向けてされているのでしょうか?

Ruby の実装を読んでみましょう。

pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); https://github.com/ruby/ruby/blob/b6c1e1158d71b533b255ae7a2731598455918071/thread_pthread.c#L1767

この文が呼び出されているコメントを読むと、"communication pipe with timer thread and signal handler" と書かれています。 つまり、通知を出しているのは Ruby 内部のタイマーで、通知を受け取っているのはシグナルハンドラーなのだと分かります。

上記は勉強会後に Slack で質問したことで得られた学びなのですが、「ファイルディスクリプタの 3 と 4 が何を指しているのか知りたい」という当初のシンプルな疑問から Ruby の実装を読むことになるとは思わなかったので感動しました。

1 つの疑問から新たに疑問が生まれ、それがどんどん解消されていくのが気持ち良くて楽しかったです。

参加して嬉しかったこと

質問が歓迎される

本勉強会では先生役のような詳しい人が何人かおり、その方たちに出てきた疑問をよくぶつけていたのですが、たまに「良い疑問ですね」と言われるのが嬉しくてかなりモチベーションになりました。

疑問をぶつけるとその場で解消され、解消されなかったとしても考察が広がるので、疑問を持てば持つほど楽しい勉強会でした。

man を読むようになった

man はman コマンド名でそのコマンドのマニュアルを読むことができるコマンドです。

正直今まで man コマンドを使ってマニュアルを読んだことがほとんどなかったのですが、今回の勉強会を経て man をしっかり読むようになりました。

man を読むことで疑問が解決できたり、新しく洞察が得られたりすることが多く、改めて公式ドキュメントの重要さを認識しました。

ボリュームがちょうど良かった

文字数が多くないので予習が苦ではありませんでした。 余裕があったため、章の内容通りに手を動かすだけでなく、毎回気になった箇所をじっくり調べることもできました。

感想

最初の方は分からないことが多すぎて議論についていくので精一杯でしたが、途中からわかることが増えて様々な疑問が浮かび、そこから一気に楽しい勉強会になりました。

他の方の鋭い疑問に驚く場面も多く、一人でやっていたらここまで学びは多くなかったと思うので、本当に参加して良かったです。

開催してくださった先輩やサポートしてくださった方々には感謝の気持ちでいっぱいです。

今回学んだ内容を活かした発展的な内容もやりたいという話が挙がっているので、次回があれば是非また参加したいと思います!

Amazon OpenSearch Serviceをアップグレードしました

こんにちは、プロダクト開発部でバックエンドエンジニアをしている望月です。

Classiのサービスでは、先生や生徒がアップロードしたコンテンツファイルやWebテストなどの検索システムにおいてAmazon OpenSearch Service(以下、OpenSearch)を利用しています。
コンテンツファイルのメタデータやWebテストのデータ等はRDBでも保持しているのですが、以前検索におけるパフォーマンスが課題になった際にOpenSearchが導入されました。 ユースケースとしては、Webテストの文章からの全文検索や、ファイル名・(オーナーの)ユーザー名等でのキーワード検索、カテゴリや属性ごとの検索などがあります。

今回はこのOpenSearchのアップグレード実施で行ったことや学びになったことをお話ししていきます。

アップグレード方法

AWSがマネージドサービスとして提供しているインプレースアップグレードがありますが、当時のOpenSearchのバージョンが古く対応していなかったため、新しくOpenSearchドメインを立てて移行する形を取りました。また、同時にOpenSearchを配置するネットワークの移行も実施しました。

今回、新旧OpenSearch間で 移行したドキュメント数は約572万件でした。Classiの中でも比較的データ量の多いシステムで、移行に30時間程度必要という見積もりだったため、定期深夜メンテナンス(5時間)の中でデータ移行を含めた作業を完結させるのは厳しいという判断になりました。

新旧OpenSearchを並行稼働させて両方に書き込みを行う期間をもった上で切り替えるという案もありましたが、この登録処理でパフォーマンスに課題があり、処理を増やしたくないという理由で今回は採用に至りませんでした。

最終的に実施したのは、事前にデータ同期済みの新しいOpenSearchドメインをスタンバイさせ、定期夜間メンテナンスでは新旧OpenSearchの切り替えと、アップグレードに対応したクライアントアプリケーションのリリースのみを行う、という方法でした。

本番オペレーションの手順を詳細に書いてデモンストレーション

本番オペレーションは、以下のような手順で進めました。

  1. 新しいOpenSearchへ、事前に既存のデータを取り込む
    • RDBのデータをもとに、クライアントアプリケーションから 新しいOpenSearchへデータを再登録するスクリプトを実行
  2. 定期深夜メンテナンスまでは通常通りユーザーがサービスを利用しているため、新旧OpenSearch間で発生するデータ差分を定期的に同期
    • RDBのデータの更新履歴をもとに、クライアントアプリケーションから 新しいOpenSearchへデータを更新するスクリプトを定期的に実行
  3. 定期深夜メンテナンスで新旧OpenSearchを切り替え
    • 新しいOpenSearchのバージョンに対応したクライアントアプリケーションのリリース
    • クライアントアプリケーションが新しいOpenSearchへアクセスするよう切り替え
    • QAの実施

大量のデータを扱うことには個人的にこれまで苦手意識があり、検証段階ではデータ再登録・更新時のスクリプトで意図しないタイムアウトが発生するなど苦労もありましたが、本番のオペレーションは大きな問題もなく終わらせることができました。

前述の通り、今回のOpenSearchアップグレードは定期深夜メンテナンスで作業を行う必要がありました。そして、定期深夜メンテナンスはおおむね月に一度の実施となっているため、本番でチャレンジできるタイミングが限られていました。
そのため、事前にオペレーション手順(データ移行・新旧OpenSearch切り替え・クライアントアプリケーションのリリース・ロールバック)をすべてチェックリスト形式で詳細に記述しておき、ステージング環境にてQAを行う際に何度かオペレーションの練習をしました。AWS環境に反映してみて初めて表出した事象もいくつかあったため、都度対応しながらオペレーション手順へ取り込んでいきました。

結果的にロールバックは実施しませんでしたが、手順を詳細に書いておくことで当日に落ち着いて作業を進められました。また、移行後のデータに追加で処理を走らせる際にも役立ちました。

システムと組織の依存関係に合わせてメンバーを巻き込む

今回最も難しかったのは、OpenSearchからユーザーまでの間に複数の機能コンポーネントが存在していて、それぞれ担当するチームが異なっていたことでした。 大規模なシステムの場合、機能コンポーネントを複数扱うプロジェクトを進めていくのは技術的にも困難ですが、関係するメンバーの意向を確認しつつ進めていくことも大きな課題となるのではないでしょうか。

今回のケースですと、各機能コンポーネントを担当するチームは以下の通りでした。

  1. OpenSearchを管轄するチーム
  2. OpenSearchのクライアントアプリケーションを管轄するチーム
  3. ユーザーから見える機能を管轄するチーム

最初私は1のチームメンバーとして作業を進めていましたが、検証環境を作るにあたっては2のチームメンバーに相談相手やレビュワーになってもらいました。また、リリースするにはユーザーから見える機能がちゃんと動いているかどうか確認するプロセスが必要だと思い、3のチームメンバーに協力をお願いしたり、自分がチーム移籍したりして作業を進めました。
また、OpenSearchの切り替え後に発覚したデータ不整合において今回の移行が起因であるかどうかの調査を行うことがあり、こちらもチーム間で連携して過去の経緯を追いつつ調査を行いました。

最後に

以上、OpenSearchのアップグレード実施についてお話しさせていただきました。 今回、自分1人で抱え込まずに適切なチームにヘルプを依頼できたのは、振り返ってみると良かったのではないかと思います。力を貸してくれたメンバーには、本当に感謝しています。

今後もアップグレード対応は継続的に行なっていく必要がありますが、今回実施した際の知見を活かし、よりスムーズに行えるようにしていきたいと思っています。

We are Hiring!

Classiでは今回紹介した以外の機能ですと、学校内でやりとりするメッセージ機能や、ポートフォリオ(活動記録)の検索システムでもOpenSearchを利用しています。 また、他にもECS化やデータ基盤の活用など技術的に面白いトピックも多くあります。詳しくはぜひ、カジュアル面談等でお話ししましょう!

Jest v28 shard オプションを使い、CI でカバレッジを計測できるようにする

こんにちは、ラーニング・学習トレーニングチームの id:tkdn です。 今日は Jest shard オプションを使って CI でどうカバレッジ計測をしたか について書いていきます。

Jest v28 shard オプション

Jest v28 から shard オプションが入りました。このオプションでテスト実行を指定の数で分割することができます。

Jest's own test suite on CI went from about 10 minutes to 3 on Ubuntu, and on Windows from 20 minutes to 7.

公式のアナウンスでもあるとおりですが、Jest 自身の CI でのテストが 20 分から 7 分になったという速度の変わりようです。

私たちのチームではリモートという状況の中で TDD /モブプロを実践しており、コミットによりバトンをつないでいます。そのため CI のスピードアップは開発体験を良くするだけでなく、マクロな視点ではリリースのリードタイムを短くすることにもつながります。

shard オプション、これを使わない手はありません。

shard オプションの使い方

我々は GitHub Actions を使っているので以下のように設定ファイルを書き換えました。特に難しいところはありません。ワンラインの実行オプションについては Jest のドキュメントに記載があります

test:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      shard: [1, 2, 3, 4]
  steps:
    - uses: actions/checkout@v3
    - run: npx jest --maxWorkers=1 --shard=${{ matrix.shard }}/${{ strategy.job-total }}

node_modules のキャッシュなどほかのステップは省いていますが、最小構成であれば上記の設定で動きます。これで testジョブは 4 つ並列で動くことになります。

環境にもよりそうですが、我々の環境では約 500 のテストケースで 5 分弱かかっていたところ、shard で 4 つ並行させると半分の 2 分半に短縮できました。 めちゃくちゃインパクトがあるわけではないのですが、ごくまれにモブプロで全体通してテスト結果みたいねというときや、ちょっとした修正後のデプロイなど、待ち時間が短くてうれしいタイミングはいくらでもあります。

shard オプションで実行した後カバレッジをどうするか問題

ただし問題が出てきます。プロジェクトでは coverageThreshold を設定していたため Jest が機械的に分割したテストのサブセットではグローバルなカバレッジ閾値を満たすことができないという状況が発生しました(分割せずに実行した場合はもちろんカバレッジを満たします)。

並列テスト A のコードパスで通ったモジュールやコンポーネントが必ずしも並列テスト A でテストされている保証はないので、そうなるだろうという予想はなんとなくしていましたが…。

類似した Issue: [Bug]: shard option and global coverageThreshold config · Issue #12751 · facebook/jest

解決策:並列テストのカバレッジを別ジョブで統合する

この問題についてはチームメンバーが解決策を持ってきてくれました。

参考になる Issue: Expose istanbul/nyc's check-coverage functionality in jest · Issue #11581 · facebook/jest

まずは並列で実行したテストのカバレッジを個別で Artifact として持っておきます(Artifact 自身は次のジョブで使います)。

test:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      shard: [1, 2, 3, 4]
  steps:
    - uses: actions/checkout@v3
    - run: npx jest --maxWorkers=1 --shard=${{ matrix.shard }}/${{ strategy.job-total }}
+   - run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json
+   - name: Upload Artifact
+     uses: actions/upload-artifact@v3
+     with:
+       name: tmp-coverage
+       path: ./coverage

次に新しいジョブで並列実行により得られた Artifact を集めて nyc を使ってマージし(A)、nyc を使ってカバレッジ計測し(B)、簡易的なスクリプトでカバレッジを満たしていないファイルなどを出力します(C)。

test:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      shard: [1, 2, 3, 4]
  steps:
    - uses: actions/checkout@v3
    - run: npx jest --coverage --maxWorkers=1 --shard=${{ matrix.shard }}/${{ strategy.job-total }}
    - run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json
    - name: Upload Artifact
      uses: actions/upload-artifact@v3
      with:
        name: tmp-coverage
        path: ./coverage
+ report-coverage:
+   needs:
+     - test
+   runs-on: ubuntu-latest
+   steps:
+     - uses: actions/checkout@v3
+     - uses: actions/download-artifact@v3
+       with:
+         name: tmp-coverage
+         path: tmp-coverage
+     - name: Merge coverage
+       # (A)ここで並列で得られたカバレッジをマージする
+       run: npx nyc merge tmp-coverage/ coverage/coverage.json
+     - name: Check coverage
+       # (B)マージしたカバレッジ計測判定を行う
+       run: npx nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100 -t coverage/
+     - name: Report coverage
+       if: failure()
+       run: npx nyc report --reporter=text -t coverage/ > coverage/coverage-result
+     - name: List Low coverage files
+       if: failure()
+       # (C)CIでの出力のためにスクリプトを実行します
+       run: node .github/tools/extract-low-coverage.js

これで大方やりたいことは実施できました。

注意点

気にしなくてはいけないこととしては、GitHub Actions の従量課金は実行時間の合計が対象となることです。実行時間が半分になり嬉しい限りなのですが、2 分半 * 4 並列が実際の実行時間となる点はしっかり踏まえて、お使いのプラン内の無料枠を考慮しましょう。少なくとも最初から並列する数を多めに設定しすぎてクォータを食いつぶした! なんてことがないようにするとよいですね。

まとめ

Jest v28 から使える shard オプションとそれを利用したカバレッジ計測について書きました。参考記事として以下に挙げた URL で狂喜したのですが、shard で実行後のカバレッジ計測のプラクティスがなかったので今回筆を取らせていただきました。

shard オプションは嬉しいのですが、実際には Jest のカバレッジの仕組みでフォローできるようになると良いですね。

Classi では CI の速度が気になった際にカッとなってやってしまうエンジニアを歓迎しています!!

参考 URL

ClassiのアダプティブラーニングエンジンCALEの品質を高める取り組み

はじめに

こんにちは、Pythonエンジニアをしてます工藤( id:irisuinwl )です。 この度、Classi独自のアダプティブラーニングエンジンである Classi Adaptive Learning Engine (CALE) をリリースしました。

corp.classi.jp

自分は主にCALE開発において、レコメンドを行うエンジン部分のバックエンドを担当しました。 今回の記事では、0からレコメンドシステムを開発し、システムが安定稼働する品質を実現したノウハウを紹介したいと思います。

CALEの概要

アダプティブラーニングとは、個別最適化学習のことを言います。 CALEでは、従来のClassiでのテスト機能である先生から生徒へのテストを配信、テスト解答に加えて、テスト終了後に生徒のそれぞれの理解に応じて問題を出題し、そのテストについて生徒それぞれの理解を深めるための機能を実現しました。

アーキテクチャ

CALEは Google Cloud (Google Cloud Platform, 以下 GCP) 上で構築されており、アプリケーション基盤としてGoogle Kubernetes Engine (以下 GKE) を使っております。

推論するためのモデルとAPIのpodは分離し、モデル開発とAPI開発の責務分割および、将来的に様々なモデルによって推論できるように設計をしました。

レコメンドエンジンのAPI部分はFlaskを用いて実装しており、ストレージには Firestore, Cloud SQL, Cloud Storage を利用しております。

工夫した点

CALEは新規システムであり、個別最適化学習を実現するレコメンドエンジンの要求仕様や、良いレコメンドの体験など、見えない部分が多いです。そのため、システムが安定稼働すること、つまりシステムの品質を守ることを重視しました。 以下ではシステムの品質を高める上で、工夫したTipsを紹介していきます。

本記事で紹介する取り組みを実施することで、CALEでは

  • valid rateが99.99%,
  • 95%ile latencyが779ms

という安定したシステムを実現することができました。

テスティング

テストを書くことは品質を保つ点において重要です。 CALEでは以下のテストをおこなっていました。

  • ユニットテスト
  • インテグレーションテスト
  • 負荷テスト
  • QA

ユニットテスト、インテグレーションテストではPythonのテストフレームワークであるpytestを用いてテストしました。

コードカバレッジはQAチームと相談し、C1 Coverageを選択しました。最終的にカバレッジを95%以上高めることができました。

負荷テストは、LocustというPythonの負荷テストライブラリを用いて実装しております。

各API呼び出しのユースケースに対して、今期想定されるユーザー利用数から負荷のテスト設計・実装を行い、ビジネスサイドに近いプロダクトオーナーを含めてレビューを行いました。 また、レイテンシが高まる観点として、登録したユーザー数、レコメンドされる問題数、蓄積された解答数といった、データ量を観点に入れて負荷テストの設計・実装を行いました。

Locustの詳細な使い方は以前自分が書いた こちらの記事 を参考に頂ければ幸いです。

QAではAutifyを使って、実際にユーザーを想定したユースケースのE2Eテストを定期実行しました。E2Eテストの自動化によって、アジリティ高くデプロイすることが出来ました。

運用・監視

運用・監視はCloud Operationsの内、以下を利用しました。

  • Cloud Logging
  • Cloud Monitoring
  • Cloud Profiler

特にCloud LoggingのPython clientがv3.0.0 になってからさまざまな情報を構造化ロギングすることが出来、使い勝手が良かったです。 以下のようにextra引数に値を入れることでログのjsonPayloadに値を入れることが出来ます。

import logging
from google.cloud.logging.handlers import CloudLoggingHandler
import google.cloud.logging

client = google.cloud.logging.Client(project="test-project")
handler = CloudLoggingHandler(client)
# setup_logging(handler)

cloud_logger = logging.getLogger('cloudLogger')
cloud_logger.setLevel(logging.INFO)
cloud_logger.addHandler(handler)

data_dict = {"hello": "world"}
cloud_logger.info("cloudLogger logging lib test1", extra={"json_fields": data_dict})

上のコードを実行すると、以下のようにログが出力されます。

また、運用と開発を両立するために SLI/SLO を設定しました。 GCP が提供している SLI/SLOに関するドキュメント を参考に、以下の流れで作成しました:

  • 利用ユースケースをまとめる
    • 利用ユースケース (クリティカルユーザージャーニー)の一覧化
    • ユースケースごとの影響度合いを考える
  • データ分析
    • 各ユースケースにおける処理の性能を測る
    • 現状の利用状況に対して、厳しく設定するのか、緩く設定するのかを考える
  • SLIの設計
    • 上記で洗い出したユースケースごとに設計する
    • ユーザーがサービスを問題なく使えるために見るべき数値は何かを指標に落とす
  • SLOの設計
    • ユーザーがサービスを問題なく使えるために指標をどの程度にすれば良いかを考える

そして、考えた基準をCloud Loggingのログベース指標でメトリクスを取得し、Cloud Monitoringのサービス経由でSLI/SLOに設定しました。

Kubernetesやインフラの運用テスト

開発環境で運用のテストも行いました。 テストの手順としては先述したLocustでのシナリオテストを常に行いながら、インフラの構成変更、スケーリングなどをテストし、ダウンタイムが生じるかを確認しました。 基本的には考えられる運用を列挙して、その洗い出した項目をテストをしました。 以下がその項目の一例となります。

  • 高負荷を掛けて、GKEのオートスケーリング時の挙動をテストする
  • クラスタのアップデート
  • ノードのアップデート
  • 誤ったイメージをpush, rollbackする
  • Cloud SQLのフェイルオーバー
  • 稼働中のモデルデプロイ

このテストをすることにより、スケーリング時のダウンタイムが発見でき、Kubernetesの Pod Terminate時のベストプラクティス に従い、対処することが出来ました。

まとめ

アダプティブラーニングエンジンであるCALEの高いシステム品質を実現する取り組みを紹介しました。

100%安定したシステムを実現することは不可能にせよ、今回の記事で紹介した

  • 利用に則したテストを行う
  • SLI/SLOといった品質の基準をビジネスサイドおよび開発者全体で合意をとる

といった取り組みを続けていれば、システムが運用できるか、期待するシステム品質の実現できるかが自ずと分かり、高い品質のシステムの実現を達成できると考えております。

Classi では学校教育の現場で使われる高い品質のサービスを実現していく必要があります。 レコメンドエンジンを初めとした全国の学校で利用される新しい教育×データ活用サービス、そして、教育現場で安定稼働するシステムを一緒に作っていきたいと思った方は是非、Pythonエンジニアにご応募ください!

https://hrmos.co/pages/classi/jobs/0000003hrmos.co

© 2020 Classi Corp.