Quantcast
Channel: メドピア開発者ブログ
Viewing all 215 articles
Browse latest View live

RubyKaigi 2023にメドピアは今年も全力でブース出展します

$
0
0

皆さんこんにちは!

サーバーサイドエンジニアの伊藤(@yuma_ito_bd)です。

2023年5月11日から開催のRubyKaigi 2023まであと少しですね! 今回はPlatinumスポンサーとしてブース出展も行う、メドピアの協賛企画の詳細をご紹介します!

目次

ブース企画

今回は、

「Rubyistの中でお酒が飲める人はどれくらいいるのか?~How many Rubyists Good at Drinking?~」

と題して、来場者の皆様に「アルコールパッチテスト」をお試しいただく企画です!

実は、このアルコールパッチテストの企画は2019年のRubyKaigiでも実施していて、今回満を持して?リバイバルさせました!

▼2019年当時

参加方法は簡単!

メドピア採用担当みどり&ひろこをフォローしていただいた方に、メドピアブースにてアルコールパッチテストをお渡しします。

パッチテストは、腕の内側に貼って20分待つだけ!その間、会場をブラブラしてもOK!

アルコールに対する自分の体質を理解し、RubyKaigi後に飲むお酒の量が問題ないか、いまいちど見極める機会にしてみましょう! また、結果を投票形式で集計するツイートを当日用意します。 投票&リツイートで広めていただくと、 メドピアオリジナルのBIGなノベルティをプレゼントしています!

Twitterクイズ企画も!

さらに今回は、もうひとつ企画があります。

現地参加の方のみならずオンライン参加の方も一体となって楽しめるように、Twitterを介したクイズ企画にチャレンジ!

RubyKaigiの期間中、メドピア採用担当みどり&ひろこからRubyやRubyKaigiに関するクイズを1日1問ずつ、計3問出題します。

引用リツイートで答えてみてくださいね👍

今年の注目ノベルティ

皆さんぜひノベルティをもらいにきてください!

1. 大きなビニールバッグ

エンジニアと採用広報チームでの企画会議を通して、「欲しい!」の意見がもっとも挙がった、大きなビニールバッグを今年新たに制作しました!

アルコールパッチテストの結果を投票&リツイートしてくださった方へのノベルティです!

VPoEより「これで会場でいろんなものをもらっても、収納ヨシ!」

アルコールパッチテストの結果を投票し、リツイートしてくださった方へお渡しします。

お一人様1点までとなりますが、ぜひゲットしてくださいね!

2. フライヤーがうちわになりました

イベント配布物にはつきものの企業紹介フライヤー。どうにか見る人の印象に残る工夫ができないかと考えた結果、今回は振り切ってみなさんの「Ruby推し」の想いを存分にアピールできる「丸うちわ」を制作しました!

メドピアはもちろん「Ruby推し」であり、これからもコミュニティに貢献&応援し続けたいという熱い思いを込めました。

ぜひセッション会場でも、このうちわでMatzさんや登壇者のみなさんを応援しましょう!

もし暑くなってきたら本来の機能通り、あおいで使ってくださいね!

この場所でお待ちしています

メドピアのブースは、受付から入った場合の奥側にあります! もしブースの位置がわからなくても、メドピアカラーの濃い緑色のTシャツを着たメンバーは約30名います。 誰かが会場を練り歩いているはずですので、目に留まったら気軽に話しかけてみてくださいね。

最後に(告知あり)

今年のRubyKaigiが2020年当時にはかなわなかった念願の松本市で開催されること、感慨深いですね。 そして現地の熱気を感じられるオフラインの場で、今回も出展ができることに感謝しています! 当日、皆さまとお会いできることを楽しみにしております!

そして最後に、告知があります。

「After RubyKaigi」を3社共同で今年も開催!

メドピアはRubyKaigi 2023の開催後日、5/18(火)にアフターイベントを3社共同で開催します!

After RubyKaigi 2023

概要:RubyKaigi 2023に関するLT・ディスカッション

共催:株式会社ZOZO、ファインディ株式会社、メドピア株式会社

時間:5/18(火)19:00~21:30

場所:メドピア株式会社本社 および オンライン

RubyKaigiでも登壇されるスピーカーの方もお呼びする予定です!

詳細および参加登録についてはconnpassよりご確認いただけます。

https://zozotech-inc.connpass.com/event/281473/

ぜひご参加ください!

参考

昨年の取組みをまとめた記事 https://tech.medpeer.co.jp/entry/2023/03/08/100000

After RubyKaigi2022に参加しました https://engineer.medpeer.co.jp/stories/rubykaigi/2022after


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp


3年半ぶりの開発合宿に参加してきました

$
0
0

はじめに

はじめまして。今年の4月に新卒として入社した金淵(Twitter : @Deee_U_ )です! 今回は4/19 ~ 4/21に開催された開発合宿の様子を記事にしました。

開発合宿とは、メドピアが年に2~3回ほど日常業務外で業務の改善や技術研鑽を行う為に開催している合宿のことです (前回の開発合宿の様子はこちら↓)。 tech.medpeer.co.jp

今回は湯河原、軽井沢の2チームに分かれて3年半ぶりの合宿を行いました。私は湯河原の合宿に参加させて頂いたので湯河原の様子をお届けします。 (軽井沢の開発合宿の様子はこちら↓) tech.medpeer.co.jp

湯河原駅前でパシャリ

旅館

今回、湯河原の合宿ではおんどや惠(めぐみ)さんに宿泊させて頂きました。メドピアは過去に何度かこの旅館に宿泊させて頂いことがあり 今回も湯河原の「おんやど 惠(めぐみ)」さんにお邪魔しました。 おんどや惠(めぐみ)さんは開発合宿プランがあり、その場合会議室は13時から翌日正午まではほぼ1日中(23時間)利用が可能であり、連泊での利用の場合はなんと最終日以外は24時間利用できるので開発に打ち込める環境が揃っているのが魅力的です!ちなみにモニターも別途料金を支払うことで借りられるのでモニターを用意しなくても大丈夫!

おんどや惠(めぐみ)さんの開発合宿プラン↓ www.onyadomegumi.co.jp

開発の様子

旅館についたのちに各々が合宿でやる内容の発表をしてから開発がスタートしました! 皆さん持ち寄ったテーマを黙々と進めていますね。

勿論、折角の湯河原での合宿なので足湯駆動開発も積極的に取り入れてきました。 オフィスでは出来ない貴重な開発手法を取り入れられるのも合宿の良いところですね!

新卒の私たちも開発を頑張りながら、先輩たちの開発の様子を見させて貰ったり自分達のコードに意見を求めることができたりと良い経験をした合宿でした!

ご飯の記録

合宿の朝夕は旅館から食事が提供されるのでご飯に困ることなく美味しいご飯が食べられるのが素敵。。。 旅館に入る前にステーキを食べたり2日目のお昼は海鮮丼を食べたりと旅館以外のご飯も美味しいので探索する価値があります! (他にも海鮮丼を食べたけど写真が....)

成果発表LT

成果発表会は後日オンライン上で行いました。 合宿の特徴として今話題の「ChatGPT」などのOpenAI APIを使った開発を行なった人が多い印象です。

私は新卒のメンバーと一緒に「タスク管理アプリ」を作成しました。 少しハプニングがありつつもメンバーの力を借りながら何とか皆んなで発表するところまで達成しました!

(事前に設計や要件定義をまとめて合宿に入れたので動くものは作れたのでほっとしてます。。。)

他にも業務改善や気になっていた技術の調査、ロボを動かすなど皆さん自由に開発をしていていました。 発表には社内の方が約50人ほど傍聴に来て貰えワイワイとした成果発表でした。

最後に

旅館の前でパシャリ

開発合宿では、普段の業務では触らない技術などの技術研鑽は勿論、普段話せないチーム外の人との交流もできるので新たな発見ができる良い機会でした。また今回改めて自分に何が能力として何が足りないか(自分は特にRuby力...)であったり、次回の開発ではもっとこうするべきでは?などやるべき宿題に気づけた良い合宿だと思います。 以上!2泊3日の湯河原側の開発合宿の様子でした。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

開発合宿に行ってきました!@軽井沢

$
0
0

こんにちは!サーバサイドエンジニアの森です。

開発合宿は2019年11月の以降、コロナの影響でしばらく開催できておりませんでしたが、 約3年半ぶりに4月19日から21日まで3日間、開発合宿を開催しました!

今回は参加者数が多いため、神奈川県湯河原と長野県軽井沢の2箇所に別れて開催しました。

この記事では、軽井沢(BEB5軽井沢)での開発合宿の様子をご紹介します!

※BEB5軽井沢: www.hoshinoresorts.com

※湯河原の様子: tech.medpeer.co.jp

※前回の開発合宿の記事: tech.medpeer.co.jp

1日目 開発スタート!

12時に軽井沢駅へ集合、お昼を食べて、開発がスタートしました!

私は3人1チームとして参加していたので、ゴール設定や作業分担について話し合い、それから作業を開始しました。 直前の天気予報では小雨だったのですが、快晴だったのでテラスで会議しています。 BEB5は自然で覆われており、テラス席では小鳥のさえずりを聞きながら作業できます!

日が落ちてからは焚き木に癒やされつつ、夜中まで開発しました。

2日目 全力開発!

2日目は朝から晩までひたすら開発です! 前日の疲れもあるので、朝やお昼など開発の合間に自然の中を散歩をしてリフレッシュしながら進めました。

夜は温泉、サウナで回復します。 そして結局、2日目も夜中まで開発しました!

3日目 ラストスパート!

とうとう最終日。 朝からホテルのチェックアウトの時間ギリギリまで、もくもくと作業しました。

最後はみんなでお昼を食べて解散!

私たちのチームは軽井沢駅近くのカフェで、発表資料の最終調整をしました。 他のメンバーの進捗が気になります!

成果発表

土日休みを挟み、開発合宿の成果発表会をオンラインで実施しました。 エンジニア以外のビジネスサイドの方々も参加しており、60名ほど集まりました!

約半数の発表がChatGPTに関連しており、今回の開発合宿を通じて、ChatGPTを活用して実現可能なことや、導入が難しいと思われることなど検証できた点が多くあります。 各メンバーの試行錯誤した箇所や成果を見て、今後の業務においてChatGPTなどを効果的に活用するための知識やアイデアを得ることができました。

私はフィッツプラス所属の3人で1チームとして、「ChatGPTを利用した特定保健指導用のメール文面の下書き作成機能」の開発に挑戦しました。フィッツプラスの全体会議で発表も行い、今後の業務オペレーションへの取り入れを目指しています!

おわりに

今回、私が一番学びになったことは、 開発から発表資料の作成までを3日間で行うという時間制限の中で、「期間内でどのように形にするか」「実装する要件」「諦める要件」などを先輩エンジニアと検討したことです。日々の要件定義や開発に活かして行きたいです。

そして、自然の中での開発は最高でした!(個人での開発旅行も良さそうだなと思っています)

開発合宿の復活を機に、今後も定期的に開催したいです。

そのためにも、得たものを業務効率や質の向上につなげ、次回も開発合宿へ快く送り出していただけるようにしていきます!

以上、メドピア開発合宿@軽井沢の様子でした!


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

Rails APIサーバーで Ruby 3.2 の YJIT を有効化してみた。

$
0
0

サーバーサイドエンジニアの内藤(@naitoh) です。

Rails で構築された小規模な APIサーバー(Rails の API モードで構築したもの)で、Ruby 3.2 の YJITを有効化する事で性能アップすることができましたので、喜びを分かち合いたく共有させて頂きます。

shopify.engineering

We’re able to measure real speedups ranging from 5% to 10% (depending on time of day) on our total end-to-end request completion time measurements.

YJIT を開発した Shopify では 5%から10%の処理速度の改善があったという事で、以前から弊社でも本番で運用を開始したかったのですが、比較的検証のし易い APIサーバーで本番投入の準備が整ったので導入を実施してみました。

YJIT 有効化までの道のり

1. Ruby 3.2 へのアップグレード

YJIT は Ruby 3.1 では 実験的機能の位置付けでしたが、Ruby 3.2 以降では正式にサポートされました。

www.ruby-lang.org

  • YJIT は実験段階ではなくなりました
    • 1年以上にわたって本番環境でテストされ、安定して稼働する実績があります。

そのため、事前に Ruby 3.2 までアップグレードする必要があります。

2. YJIT 有効でBuildされた Ruby を用意する。

Ruby の DOCKER OFFICIAL IMAGEは YJIT 有効でBuildされているので、そちらを利用します。

$ docker pull ruby:3.2.2
$ docker run -it ruby:3.2.2 ruby --yjit -e 'p RubyVM::YJIT.enabled?'
true

--yjitオプションで YJIT有効で起動できる事が確認できました。

--yjitオプションを付けないで起動すると YJIT 無効で起動された事がわかります。

$ docker run -it ruby:3.2.2 ruby -e 'p RubyVM::YJIT.enabled?'
false

なお、YJIT無効でBuildされたRuby の場合は下記のようなエラーが出るので、この場合はYJITを利用できません。

$ ruby --yjit -e 'p RubyVM::YJIT.enabled?'
ruby: warning: Ruby was built without YJIT support. You may need to install rustc to build Ruby with YJIT.
-e:1:in `<main>': uninitialized constant RubyVM::YJIT (NameError)

p RubyVM::YJIT.enabled?
        ^^^^^^

※ 補足:Ruby 3.2 の YJIT は Rust-lang で実装されているため、Build 時には Rust-lang が必要ですが、実行時は Rust-lang は不要なため、Docker IMAGE には Rust のパッケージは含まれていませんでした。

3. YJIT 有効化

環境変数 RUBY_YJIT_ENABLE=1でも YJIT有効化が可能なので、deploy 時の環境変数に設定する事で有効化しました。

YJIT 有効化後と有効化前(1週間前の同一曜日、グラフ破線部分)との比較になります。

Rack Request : p95 Latency 比較 (低い方が性能が良い)

Rack Request

ave: 35.6 ms -> 34.6 ms (2.8% 短縮) とLatency が改善された事がわかります。

Shopify の実績の 5%〜10%の改善に比べると半分程度の改善になりますが、今回はAPIサーバーでフロント周りの処理が無いので妥当かもしれません。

この Rack Request の結果が、Latencyの状況全体を表しているのですが、もう少し詳細を見ていきます。

ActiveRecord Instantiation : p95 Latency 比較 (低い方が性能が良い)

ActiveRecord Instantiation

Ave: 105.8 μs -> 77.9 μs (26.3% 短縮) と、こちらは改善度合いがかなり高いです。

Render Template : p95 Latency 比較 (低い方が性能が良い)

Render Template

Ave: 791 μs -> 701 μs (11.3% 短縮)と、こちらも改善度合いが高いですね。

Action Controller : p95 Latency 比較 (低い方が性能が良い)

Action Controller

Ave: 33.1 ms -> 32.7 ms (1.2% 短縮) と、こちらは効果は低めで、オーダー的にもここが改善されると効果が顕著に出ると思われます。

Request/CPU/Memory 状況

Rack Request 状況

Rack Request Hits

Max は 308 hits -> 282 hits と下がっていますが、Ave 116 hits -> 117 hits と、ほぼ同様なアクセス状況です。

CPU 使用率 (低い方が性能が良い)

Rails Container CPU

Ave 0.195(各コンテナの平均) -> 0.17(各コンテナの平均) とCPU使用率が低下しています。

Memory 使用量 (低い方が性能が良い)

Rails Container Memory

Ave 143(各コンテナの平均) -> 174.5(各コンテナの平均) と Memory 使用量が 22.0% 増加しています。 今回は Memory に余裕があったため大丈夫でしたが、事前にメモリの使用状況を確認してメモリに余裕がある状態でYJITを有効化した方が良さそうです。

まとめ

Memory 使用量が増える可能性はありますが、YJIT を ON にするだけで全体で 2〜3%のレイテンシ改善が見られました。 最新のRuby に追随することで Ruby 開発者の成果の恩恵を受けられるのは非常にありがたいです。

今年のRubyKaigi も直前ですが、どのような発表があるのか期待しております。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day1

$
0
0

松本駅にあるRubyistを歓迎する横断幕こんにちは!サーバーサイドエンジニアの中村(@_naka_0)です。

RubyKaigi 2023に現地参加しています。 1日目(5/11)に聞いたセッションの中でいくつかをピックアップしてレポートしていきたいと思います。

タイムテーブル

タイムテーブルは以下から確認できます。 rubykaigi.org

Matz Keynote

Matz Keynote - RubyKaigi 2023

Rubyが生まれてから30年が経過し、そこで学んできた教訓についてのお話でした。

その中でも印象に残った教訓についていくつか紹介していきたいと思います。

良い名前を選ぶ

Rubyは1993年2月24日に「Ruby」という名前が決まったそうですが、この名前が決まる前に「Coral」、「Tish」のような名前の候補があったそうです。 もし、「Ruby」ではなく「Tish」という名前であったら、ここまで多くの人に使われることはなかったかもしれないとのことでした。 この時のことを振り返り、良い名前を選ぶことの重要性を学んだそうです。

最初に決めた基本的原則は保持する

実際にMatzの手元にある一番古いコードは最新のRubyのコードと大きくは変わっておらず、開発当初の時点で基本の部分はすでに完成していたとのことです。

他の人の意見を聞いて、自分の視点では得られない気づきを得ること

1995年12月にRubyをインターネットに公開する前に有志の人に意見を募ったところ、20名程度の人の意見が集まったそうです。 その際、他の人の意見を聞くことで自分の視点では得られない気づきを得ることができたとのことでした。

人と人とのつながりの重要性

2001年9月に行われたJAOOというデンマークのカンファレンスにMatzが参加した際に、当時学生インターン生として参加していたDHH(Railsの生みの親)と話していたそうです。

この後、DHHはRailsを作ることになったそうですが、「この時は後にその人がRailsを作ることになるとは思ってもみなかった」というエピソードが特に印象に残りました。

これらの他にも本の出版や様々な人との出会いが今のRubyにつながっているという素敵な話でした!

リーダーシップとビジョンの提示

Ruby3x3(Rubyを3倍早くする)という目標を掲げた際、Matzは確実にそれが実現できるとは思っていなかったそうです。

しかし、Matzがビジョンを提示したことでMJITやYJITに繋がり、結果的にRubyの性能改善を実現できたそうです。

develop chrome extension with ruby.wasm

develop chrome extension with ruby.wasm - RubyKaigi 2023

本セッションの登壇者の方は実際にruby.wasmを使用してChrome拡張機能を開発してみたそうですが、Rubyらしい書き心地が得られないことに課題感を感じたそうです。

そこで、Rubyのスクリプトを読み込むだけでChrome拡張機能を作成できるようなフレームワークである「unloosen」を開発することに至ったとのことでした。

https://github.com/aaaa777/unloosen

このフレームワークを使用してChrome拡張機能を開発すると、上記リポジトリのREADMEの「Quickstart」に書かれているように少ないコード量とシンプルな構文で拡張機能の開発が実現できるとのことでした。 今後の展望としては、このフレームワークのドキュメント充実化やmanifest.jsonを自動生成できるようにする仕組みを挙げられていました。

また、このフレームワークはGemでは配布しておらず、npmとして配布しているそうです。

https://www.npmjs.com/package/unloosen-ruby-loader

まだ作成したばかりとのことなので、コントリビュートして貢献するのも良さそうだなと感じました!

Power up your REPL life with types

Power up your REPL life with types - RubyKaigi 2023

Ruby irbの補完は便利ですが、メソッドチェーンやブロックパラメータの補完が正確ではない(誤った補完やありとあらゆる候補が出てきてしまう)といった問題があります。 そこで、より正確な補完にするため、irbとRBS(Rubyの型定義を提供する機能)を組み合わせたGemである「katakata_irb」を作ったというお話でした。

「katakata_irb」のリポジトリは以下です。 https://github.com/tompng/katakata_irb

この「katakata_irb」をrequireすることで、メソッドチェーンしてても、ブロック引数・変数などを使ってても、ある程度正しく型を推測して補完候補を出せるようになるとのことでした。 他にはプロダクトにRBSをきちんと書いている場合、rails consoleでもこの補完機能が使えるようになるようです。 また、Gemに型定義がされている場合(rbs_collectionにも型定義されている場合)でも「katakata_irb」の恩恵を受けることができるとのことでした。

既存のプロダクトでも型を定義していればrails consoleでも使えるとのことなので、RBSを導入して型情報を増やしていけると開発体験が上がりそうだと感じました。

Lightning Talks

LT - RubyKaigi 2023

1発表5分で合計12のLT登壇がありました。

その中でも私が一番興味深いと感じた「RBS meets LLMs - Type inference using LLM」というセッションについてご紹介します。

このセッションでは、LLM(大規模言語モデル)を使って型の推測を行うことができないかという発想から、ChatGPTを使ってコードの型を推測する方法をデモを交えながら紹介していく発表でした。

ChatGPTにコードの型を推論させようとすると、出力されるRBSの構文がおかしいことや不要な出力がある等の課題が出てきたそうです。 そこで、Fewshotを用いて意図した出力が得られるように改良していった様子が紹介されていました。

Matz KeynoteでもLLM等で型を推論できないかどうかの話があったため、個人的に今後の発展に期待が持てそうな分野だと思いました。

おわりに

セッションの中では自分の理解が追いつかなかった部分もありましたが、普段は学べないことを学べたり、多くの刺激を受けることができた1日目でした! Day2以降の記事も公開予定なのでぜひご覧ください。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day2

$
0
0

バックエンドエンジニアの貞元勝幸(@greendrop269)です。
RubyKaigi 2023で長野県松本市に来ています。2日目(5/12)に聞いたセッションについて、いくつか紹介していきたいと思います。

タイムテーブル

タイムテーブルは以下から確認できます。 rubykaigi.org

How resolve Gem dependencies in your code?

How resolve Gem dependencies in your code? - RubyKaigi 2023

RubyGems, Bundlerがどのように依存を解決しているかというお話しでした。

普段、rubyを書く中でgemを使用するためにgem install や bundle installを使用していると思います。 使用する側は、使用したいgemを指定するだけですが、そのgemが依存しているgemも取得する必要があります。 その依存を解決しているResolver Engineは、RubyGems, Bundlerで異なっています。

  • RubyGems: Molinillo
  • Bundler: v2.4からPubGrub
    • PubGrubはDart言語のパッケージマネージャーpubで使用されている次世代の依存解決アルゴリズム

また今後は、RubyGemsもPubGrubになるそうです。

RubyGems, BundlerのResolver Engine

そして、以下のようなRubyGems, Bundlerの例から、複雑さが垣間見れました。

  • C Extensionのgemで、リモートではOKだが、ローカルではNGとなる
  • bundle update --conservative rails で期待通りのgem以外が対象になる

Rubyで開発する中で、RubyGems, Bundlerは当たり前のように使用しており、日々進化していてとても支えられていると感じました。

Revisiting TypeProf - IDE support as a primary feature

Revisiting TypeProf - IDE support as a primary feature - RubyKaigi 2023

現在開発中のTypeProfのv2に関するお話でした。 TypeProfとは、Ruby 3.0から標準搭載されている静的型解析器のことです。 型解析ができることにより、変数の型チェックやメソッドの補完など開発者体験を向上させることができます。

発表ではまず最初にTypeProf v2を使ってVSCodeでの開発デモがありました。

推論された型情報が表示されている様子

推論された型情報が表示されたり、型注釈を記述した状態で型が合わない場合にエラーがリアルタイムに表示されるデモでした。

TypeProf v1では、解析速度に大きな課題がありIDEでのサポートも難しかったようです。

そこで、TypeProf v2では解析速度の向上とIDEでのサポートを目標として再開発することになりました。 v2ではデータフローグラフを用いて、解析結果を差分更新することで、パフォーマンスの向上を目指しました。 Rubyのコードをデータフローグラフに変換し、それを用いて型情報を伝播させることで、型推論を行います。 呼び出すメソッドが変更された際は、変更があった部分のみ新しいグラフを作成してその部分のみ型情報を更新します。

コード変更時のデータフローグラフ

v1ではコードの変更があった際にコードすべてに対して型推論を行っていたのに対し、 v2ではコードの変更があった部分のみ型情報の伝搬を行うことでコード変更時の型推論の速度向上が見込めます。

その結果、解析にかかるパフォーマンスは以下のように大きく改善されたようです。

  • v1: 約3秒
  • v2: 初回は約1秒、コードの変更時は0.029秒

今後の展望としては、キーワード引数や例外などの対応やIDE機能の改善などを行い、Ruby 3.3までに実用可能にすることを目指すようです。

私の感想としてはVSCodeでのデモを見たときにリアルタイムに型情報が更新されてとても驚きました。 最近はGitHub Copilotなどのツールで開発者体験の向上が話題になっていますが、 静的型解析によってより正確により安全に開発することができ、開発者体験がさらに向上するのではないかととても期待が高まる発表でした。

Introduction of new features for VS Code debugging

Introduction of new features for VS Code debugging - RubyKaigi 2023

VS Codeを使用したデバッグの新機能、Trace inspectorとDebug Visualizerのお話でした。

まず、Trace inspectorについてですが、ブレークポイント間の処理内容を保存しておき、それを表示したものとなります。 これがない場合、ブレークポイントから一つずつステップインしながらデバッグすることになり、どのメソッドが呼ばれたかなど忘れてしまうことがあるので、Trace inspectorはとても便利だと感じました。 デフォルトの設定では、呼ばれたメソッドの引数と戻り値を記録していますが、「record and replay」を有効にすることでローカル変数の状態も記録できるようになっていました。

https://github.com/ruby/debug/pull/959

Debug Visualizerは、すでにVS Codeの拡張機能であり、それをRubyに対応させたものとなります。 デモでは、ArrayやHash、NokogiriオブジェクトやActiverecordオブジェクトをグラフや表、ツリー形式などで表示されており、見やすいなものとなっていました。

https://github.com/hediet/vscode-debug-visualizer

Trace inspectorやDebug Visualizerを使用することで、さらに開発者が行う調査や生産性が向上すると感じました。

Optimizing YJIT’s Performance, from Inception to Production

Optimizing YJIT’s Performance, from Inception to Production - RubyKaigi 2023

YJITのパフォーマンス最適化についてのお話しでした。 この中で、印象に残ったものを紹介したいと思います。

まず、パフォーマンス最適化を行うためにYJIT用のペンチマークを準備されていました。 これによりYJIT開発者は簡単にベンチマークを実行できる環境となっていました。

https://github.com/Shopify/yjit-bench

YJIT-Bench

次に、メモリ使用に対するコードGCです。 初期化処理など一度しか実行されないようなものは、マシンコードが開放されていました。

コードGC

ベンチマーク手法やメモリ使用の改善などによるパフォーマンス最適化がRuby 3.2 よりさらに進んでいるので、Ruby 3.3のYJITが楽しみに感じています。

おわりに

私が参加したDay2のセッションでは、IDE向けの機能やYJITなど、今後使ってみたいと思うものが多かったように感じました!
Day3の記事も公開予定なのでぜひご覧ください。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day3

$
0
0

こんにちは!サーバーサイドエンジニアの伊藤(@yuma_ito_bd)です。

RubyKaigi 2023に参加されていた皆さん、お疲れ様でした。 3日目(5/13)のセッションの中で印象に残った発表をご紹介します。(現地参加したエンジニアの複数人による共同執筆になります。)

Day 1のレポートはこちら tech.medpeer.co.jp

Day 2のレポートはこちら tech.medpeer.co.jp

タイムテーブル

タイムテーブルは以下から確認できます。

rubykaigi.org

Ruby Committers and The World

(執筆:伊藤)

YJITやパーサ、GC、Ractorなどの各トピックに対してRuby 3.3で実装したい機能に関してコミッター同士の公開トークがありました。 Ruby 3.3だけでなく、その次のバージョンに向けた将来的な話もあり、興味深かったです。 5/12(RubyKaigiの2日目)にRuby 3.3.0-preview1がリリースされたようなので、自分でも触ってみようと思います!

www.ruby-lang.org

セッションの最後にはコミッターの方々の写真撮影タイム 📸

Ruby Committers

The Adventure of RedAmber - A data frame library in Ruby

(執筆:貞元)

Rubyでデータフレームを取り扱うRedAmberについてのお話しでした。 RedAmberはApache Arrow の Ruby 実装である Red Arrowと連携し、Rubyらしいインターフェース用意し、Rubyistsにとって扱いやすいデータフレームとなっています。

https://github.com/red-data-tools/red_amberhttps://arrow.apache.org/

RedAmber on Red Arrow

RedAmberの以下のような機能の紹介があり、メソッド名など使用しやすく感じました。

  • Aggregation: vec.sum
  • Element-wide: vec.cumsum
  • Binary Element-wide: vec > 3
  • pick, drop(列選択, 列削除)
  • slice remove(行選択, 行削除)

Methods

また、RubyKaigiが行われた都市のデータを用意し、都市と緯度経度を結合した内容をプロットした例もあり、より実践的な例でイメージしやすい内容でした。

RubyKaigi Locations

Ruby向けのデータフレームはまだ触ったことがなかったので、これを機会にRedAmberを使用してみたいと思います。

Let's write RBS!

(執筆:千葉)

speakerdeck.com

このセッションでは、RBS 3.1で導入された新機能と既存アプリケーションにRBSを導入する方法について紹介されました。

紹介されたRBS 3.1の新機能は以下の2つです。

  • rbs subtract: 複数ファイルでRBS定義が重複している場合に片方を削除してくれる機能
  • rbs parse: RBSファイルをパースし、Syntax Errorを教えてくれる機能

これらの機能は、既存アプリケーションにRBSを導入する際に詰まりがちな部分を補ってくれるため、今後RBSを導入する予定がある方にとっては非常に重宝する機能になります。

また、既存アプリケーションにRBSを導入する方法については以下の手順でデモンストレーションが行われました。

  1. rbs collectionの導入
  2. steepの導入
  3. rbs subtractの導入
  4. RBSの記述方法

デモンストレーション形式で実際にコーディングを行いながら型についての解説が行われたため、これまでRBSに馴染みがなかった人でも理解しやすいセッションでした。

今回のセッションを通じて、RBS導入への敷居がまた一段と低くなったと感じたため、今後さらに多くのプロダクトでRBSが導入されることが期待できると思いました。

Load gem from browser

(執筆:熊木)

ruby.wasmでサードパーティ製のgemを動かしたいというお話しでした。 ruby.wasmとはRubyスクリプトをブラウザ上で実行できるようにする技術です。

サンプル github.com

現状の課題として、ruby.wasmでは標準gemは使用可能ですが、サードパーティ製のgemが使用できない点が挙げられていました。

その理由は、ruby.wasmのファイルシステムが読み取り専用であり、あらかじめ用意されたgemしか読みこめないためです。 この問題への対応策を考えるに当たり、JavaScriptの歴史が参考になったそうです。その結果、以下の2つのアプローチが提案されました。

  • 実行前にバンドリングして一つのファイルにまとめる
  • import-mapsのように実行時にgemを読み込む

しかし、スクリプト言語の特性を維持したいなどという考えから、後者の方法が採用されることになったようです。

後半では、現在の実装内容や困難な点が紹介されました。

まずrequire_relativeを使ってgemを呼び出す様子がデモで示されました。

デモの様子

現在、Rubyのファイルから別のファイルを呼び出すことは可能ですが、再帰的な読み込みはまだできないことが示されました。 また、ブラウザでファイルをダウンロードする際に使用するFetch APIから返却されるPromiseがRubyではないため、これを適切に扱うのに苦労したとのことでした。

将来的な展望として、ブラウザ向けのプログラミングをRubyでJavaScriptと同等の簡便さで行えるようにしたいと述べられました。 具体的には、UNpkgのような仕組みでgemを実行可能にし、デプロイを容易にし、初心者でも簡単にアプリケーションを公開できる環境を作りたいとのことでした。

発表の中で、何度も「Rubyプログラミングをより楽しくしたい」とおっしゃられていたのが印象的で、 Rubyが楽しく書ける言語として評価されるのは、こういった開発者の方々の努力のおかげだと、強く感じる発表でした!

Ruby + ADBC - A single API between Ruby and DBs

(執筆:内藤)

slide.rabbit-shocker.org

Red Data Tools (Ruby でデータ処理ツールを提供するプロジェクト)で開発されている ADBC(Apache Arrow Database Connectivity) API を用い 大量( 1カラム100万レコード以上)のデータ読み書きを Ruby で実施する話です。

データ交換処理では、通常、シリアライズ・デシリアライズに伴うデータ変換が行われます。 この変換処理時間は少量のデータでは無視できるのですが、大量のデータの場合には無視できない時間になります。

ADBCを用いることで、下記の三つの点から大量データを扱うのに最適化しているとのことでした。

  1. Apache Arrow データFormat を使う:データ交換コストがめっちゃ安い
  2. 結果セットの分割:並列で分割して読み込みを行っているので、高速なデータ読み込みが可能
  3. バルクインサート:高速なデータ書き込みを実施

なお、Apache Arrow データFormat を使うとなぜ良いのかは、下記の資料を読んで下さいとのことでした。

slide.rabbit-shocker.org

アーキテクチャ

ADBC Architecture

Apache Arrow データ形式を返す形で、各種DBにアクセスするための Single(単一形式) API を定義しているため、(Active Record のように)DBを変更してもクライアント側のコードを変更は不要です。

ただし、このAPIはそのままでは Active Recordから使えないので、 Active Record ADBC adapterを開発中(ただ、普段 Active Record を使っていないので、協力してくれるメンバーを募集中)とのことです。

利用可能な Driver は下記になり、MySQLの Driver は存在しないようです。

ADBC Available drivers

Current ADBC

現状、1000万レコードの処理で libpq と比較して ADBC(libpq Driver)は変換のオーバーヘッドがあり、まだ最適化されていないので遅く、ADBC(Flight SQL Driver) はArrow フォーマットを使った高速RPC通信ができているので、2倍速い結果となっています。

Flight SQL(Apache Arrow Flight) は下記の特徴を持っており、Arrowフォーマットのまま(=シリアライズ・デシリアライズに伴うオーバーヘッドが無い)並列転送をしているのがポイントのようです。

Apache Arrow Flight

今回、 PostgreSQL で Flight SQL を話せるようにPostgreSQLの拡張機能を用いたアダプタも作ったそうです。

前述のセッションレポートで述べた RedAmber が ADBC で処理できるようなるとDBに大量データが格納されている場合でも、全てApache Arrow データFormat で処理できる事になるのでオーバーヘッドが無く、高速に処理できそうですね。

Ruby でのデータ処理に興味を持った方は、ぜひ Red Data Toolsに参加しましょう!

Parsing RBS

(執筆:伊藤)

rubykaigi.org

3日目のkeynote講演です。 RBSの型定義ファイルで文法エラーが発生していても抽象構文木(Abstract Syntax Tree、以下ASTとする)を取得できるようにパーサを改善したお話でした。

正しいRBSのコードの場合、パーサによってコードに対応するASTを得ることができます。

RBSのコードと対応する抽象構文木(AST)

文法エラー時にツリーを返すように変更

既存のパーサでは文法エラー時は例外が吐かれるように設計されており、抽象構文木を得ることができない仕様でした。 そこで、文法エラー時はMissingTreeというツリーを返却するように変更しました。

これによって、文法エラーが発生していてもASTを得ることができるようになりました。

最後にパースが成功した結果を利用して正しいASTを得る

文法エラーが発生している場合、コーダーの意図とは異なるASTを取得してしまうパターンがありました。

例えば以下のようなConferenceクラスを定義したRBSのコードがあるとします。

classConferencedef initialize: (String, Integer) -> void
end

そして、以下のようにTalkクラスの定義を(途中まで)追加します。

classConferenceclassTalkdef initialize: (String, Integer) -> void
end

class Talkに対応するendがないので文法エラーとなっています。

ここでinitializeメソッドはもともとConferenceクラスのメソッドだったのですが、Talkクラスのメソッドとして解析されてしまっていました。

以下のようなイメージです。

- class Conference
  - class Talk
    - def initialize

そこで、最後にパースが成功したASTの結果を用いて、変更があったトークンによるツリーを挿入することで正しくASTを取得できるようにしました。

先程の例では最後にパースが成功したASTは

- class Conference
  - def initialize

なので、そこからclass Talkというトークンが追加されると

- class Conference
  - class Talk
  - def initialize

のようなASTを取得することができます。 このようなアプローチをすることで文法エラーが発生していてもコーダーの意図に近いASTを取得でき、文法エラーであることを適切に表示することができるようになりました。

私は今回のRubyKaigiでパーサに関するセッションをいくつか聞いたのですが、どのセッションでも文法エラー時のエラートレランスについて言及しており、パーサ共通の課題であり難しい部分であることを認識しました。 パーサが改善されることによって、IDEで適切にエラー表示されて開発者体験が向上するので、今後もパーサについて着目してみようと思いました。

おわりに

3日間に渡るRubyKaigi 2023が終了しました。 どのセッションも難しい内容でしたが、普段使っているRubyという言語がどのような実装であるのか、これからはどのような方向を目指して成長していくのか知ることができたので、非常に有意義な3日間でした。

さて、最後のセッションの後のClosingでは、次回のRubyKaigiの日程と場所が発表されました!

次回のRubyKaigiはなんと!!

2024年5月15日から5月17日、場所は沖縄県那覇市です!

次回のRubyKaigiも楽しみですね!

After RubyKaigiを開催します!

最後に、弊社では「After RubyKaigi」を3社共同で今年も開催します! まだご参加登録は間に合いますので是非お申し込みください!

  • After RubyKaigi 2023
  • 概要:RubyKaigi 2023に関するLT・ディスカッション
  • 共催:株式会社ZOZO、ファインディ株式会社、メドピア株式会社
  • 時間:5/18(木)19:00~21:30
  • 場所:メドピア株式会社本社 および オンライン

RubyKaigiで登壇されたスピーカーの方もお呼びします! 詳細および参加登録についてはconnpassよりご確認いただけます。

zozotech-inc.connpass.com

読んでいただきありがとうございました。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 「Reading and improving Pattern Matching in Ruby」 感想

$
0
0

こんにちは、サーバーサイドエンジニアの草分です。
先日のRubyKaigi 2023に参加された皆様お疲れ様でした!

"感想記事を書くまでがRubyKaigi"ということで、今回は1つのセッションを掘り下げた感想記事を投稿します。

rubykaigi.org

このセッションではRubyのパターンマッチの機能を題材に、Rubyの機能の実装を「読んで理解する」そして「パフォーマンスを向上させる」といったプロセスを、いかにして進めていくのか。その方法について紹介されていました。

このセッションはRubyのパターンマッチやメタプログラミングを知った状態で聞くと、より深く理解することができます。 それらの前提知識を軽くおさらいしつつ、セッション内容を振り返っていきましょう。

パターンマッチとは

パターンマッチとは、「データ構造による条件分岐」「構成要素の取り出し」という要素を備えた機能です。

Rubyでは case/in 構文で記述します。
対象のオブジェクトとマッチさせるデータ構造(パターン)を比較し、マッチした場合処理が行われます。

また、マッチした場合はパターンに記載した各変数に値がバインドされ、内部の処理で利用することが可能となります。

User = Struct.new(:role, :name)

defhello(user)
  case user
  inrole: 'member', name:
    puts "ようこそ、#{name}さん"inrole: 'guest'
    puts "ようこそ、ゲストユーザーさん"else
    puts "ようこそ"endend

hello(User[:member, "太郎"])  # => ようこそ、太郎さん
hello(User[:guest, "ゲスト"])  # => ようこそ、ゲストユーザーさん
hello(nil)                   # => ようこそ

Arrayパターンについて

複数あるパターンマッチの型の内の1つで、オブジェクトの #deconstructが配列を返す場合、その配列と指定パターンがマッチするかを検査する機能です。

classArraydefdeconstructselfendendcase [0, 1, 2]
  in [0, *a, 2]
    puts a #=> 1end

※実際にはArray#deconstructはデフォルトで利用可能です

その他のパターンマッチの使い方については過去に解説記事を書いています。 こちらも是非ご覧ください。

tech.medpeer.co.jp

tech.medpeer.co.jp

Arrayパターンを改善していく

Rubyのソースコードは構文解析などによりAST(抽象構文木)に変換され、そこからYARV命令列に変換され実行されます。
実際にソースコードがどう変換されるかは RubyVM::InstructionSequenceを使うと簡単に表示することができます。

puts RubyVM::InstructionSequence.compile("puts 2 + 2").disasm
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,10)> (catch: false)0000 putself                                                          (   1)[Li]
0001 putobject                              20003 putobject                              20005 opt_plus                               <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0007 opt_send_without_block                 <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0009 leave

パターンマッチのソースコードがどういうYARV命令に変換されるかを確認すると… とてつもない量の命令が出力されてしまったとのこと。
(本当に量が多かったので本記事では省略)

落ち着いて、ASTをYARV命令列に変換しているcompile.cファイルを見てみましょう。とのこと。
パターンマッチの部分には素晴らしいコメントが記載されており、理解の手助けになるようです。果たして本当でしょうか。

compile.cは1万行をゆうに超える上に、ASTノードの変換を行う関数 iseq_compile_each0には100近い分岐のswitch文が存在し、それぞれの処理を行っています。

いやはや、何の知識もない状態で読んでも全く理解できないことでしょう。 実際にファイルを開いてみます。 https://github.com/ruby/ruby/blob/8d242a33af19672977dcdcb8d32e9ad547bc0141/compile.c#L6363

compile.c

おお!読めるぞ!?(一部分のみ)

さすがに実際のコードはすぐに読み取れる訳ではありませんが、 読む範囲が定まっており、読みやすいコメントが記載されていれば、なんだかいけそうな気になりますね!(気のせいかも)

改善方法について

Array patternでパターンマッチする場合、検査対象が#deconstructを持つかどうか、その戻り値がArrayかどうかのチェックが実行されます。
しかし対象がArrayの場合は#deconstructの検証は不要なはず、その処理をスキップすれば速度改善になるのではないか。

といった仮説から改善が行われました。

結果、ベンチマークでは速度が2倍に向上!やった!
しかしながら、Array#deconstructがオーバーライドされているケースで問題があったとのこと。

オーバーライドによる問題点

Rubyには「オープンクラス」という機能があります。 既存のクラスを任意の場所で再オープンし、メソッドの修正や追加を行うことができる機能です。 組み込みクラスのArrayも例外ではなく、既存メソッドの挙動を好き放題書き換えることができます。

オープンクラスについてはメタプログラミングRubyなどの書籍を読むと詳しく理解できるでしょう。

前述の改善方法は、Array#deconstructの内容の検証をスキップするものです。 オープンクラスによりメソッドが書き換えられていた場合、検証をスキップすると期待値と異なる動作をしてしまいますね。

さて、その問題に対してはどう対処するかというと……

Do not override Array#deconstruct !

といったオチに繋がるセッションでした。

感想

コーナーケースへの対処の難しさ

Array#deconstructの上書きですが、普通にRubyを使っている場合はおそらく書かないコードでしょう。
Rubyの利用者からすればあまり考慮する必要はなさそうです。

一方で、Ruby言語としては許されている実装です。
言語の改善のためにはそういったコーナーケースへの対処も必要なようです。

「これらを全て考慮するのは大変そうだぞ……」という気分になってしまいますね。

Array patternはArrayだけのものでない

パターンマッチのArray patternはArrayだけのものではありません。
以下のように、#deconstructが定義済の任意のオブジェクトで利用することが可能です。

Spot = Struct.new(:latitude, :longitude)

deflatlng(spot)
  case spot
    in [0.. => lat, 0.. => lng]
      puts "北緯#{lat.abs}度 東経#{lng.abs}"in [..0 => lat, 0.. => lng]
      puts "南緯#{lat.abs}度 東経#{lng.abs}"in [0.. => lat, ..0 => lng]
      puts "北緯#{lat.abs}度 西経#{lng.abs}"in [..0 => lat, ..0 => lng]
      puts "南緯#{lat.abs}度 西経#{lng.abs}"endend

latlng(Spot.new(35, 135))   # => 北緯35度 東経135度
latlng(Spot.new(-35, -135)) # => 南緯35度 西経135度

この場合のパフォーマンスはどうなってしまうのでしょう。 チェック処理が増えた影響はあるのでしょうか。 手元での検証はできていませんが、ひとつの事柄を掘り下げていくと次の疑問点も見えてきますね。

おわりに

RubyKaigiのセッションはRubyの内部に踏み込んだ内容が多く、前提知識がないと理解すらままならないこともあります。 ということで今回は、1セッションの前提知識の内容も含めた記事を投稿いたしました。 今後のRubyKaigiのセッションを理解するための一助になれれば幸いです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp


Railsプロジェクトへの「頑張らない型導入」のすすめ

$
0
0

こんにちは。サーバーサイドエンジニアの三村(@t_mimura)です。

主に保険薬局と患者さまを繋ぐ「かかりつけ薬局」化支援アプリ kakariのサーバーサイド開発(Ruby on Rails)を担当しています。

突然ですが!
この度kakariプロジェクトは「型導入」をしました!

kakariのRailsリポジトリに型導入PRがマージされた様子

皆さんのプロジェクトは「型導入」していますか?
「型導入」しているRailsプロジェクトはまだ少ないのではないでしょうか

なぜ型導入しないのか

型を導入すると何かしらが便利になることは分かっているのに何故やらないのでしょうか(煽り気味)

「型の恩恵」と「型を自分たちで書くコスト」の2点を比較していませんか?
RubyKaigi 2023開催前の私がまさしくそう考えていました。
本当にその2点を比較するべきなのかをここで再考してみましょう。

「型導入」とは何か

まず「型導入」とは何を指すのかを整理するために、「型定義」と「型活用」で分けて考えます。

「型定義」について

ここではRuby公式のRBSについて考えます。

github.com

Railsプロジェクトにおいての「型(RBS)定義」の方法は主に5種類あります。

  • ruby/gem_rbs_collectionや各Gemのリポジトリに定義されている型を取得
  • pocke/rbs_railsなどのツールによる型の自動生成
  • typeprofによる型の静的解析
  • rbs prototypeによる型のプロトタイプの自動生成
  • 自分で型を書く

※ 各ツールの詳細はここでは割愛します。

他にもYARDからRBSを生成するツールなんかもあるようですが、現時点で主流なのは上記の5つだと思います。

ここで分かることは「自分で型を書く」以外にも型定義の方法があるという点です。

「型活用」について

次に「型活用」について考えます。

RBSは型が定義されたファイルでしかないため、それを何らかの形で活用しないと意味がありません。

現時点で私が観測できた型の活用方法は以下の3つです。

  • Steepを利用した型検査
  • コーディング時の補完強化
  • KatakataIrbによるirb補完強化

一つずつ深掘りしていきましょう。

Steepを利用した型検査

github.com

「型導入」という言葉を聞いて一番最初に思い浮かべるのはこれではないでしょうか。 型検査による利点は知り尽くされているためここでは省略します。

Railsアプリケーションで型検査を意味あるレベルで運用するにはいくつかのハードルがあります。

まずは、Railsアプリケーションコードに対応する型を手動で定義する必要がある点です。
rbs prototypeを活用しプロトタイプな型の自動生成はできますが、「意味あるレベルの型検査」となると手動での型定義は避けられない現状です。 当たり前ですがこの型定義は「型導入」初回の一度きりではなく、アプリケーションコードの変更の都度更新する必要があります。

「型定義・型検査はそもそもそういうものだから今更何を言っているのか」と厳しいご指摘もあるかと思います。

そこで二つ目のハードルとして、この手動での型定義作業をチームメンバーに強制する点が挙げられます。
一人プロジェクトならまだしも、我々はチームで一つのRailsプロジェクトを開発しているため独断で決めるわけにはいきませんね。
ただ作業を強制するだけでなく「型を定義し活用し続ける文化」を今まで型と無縁だったRubyエンジニアに浸透させるのは簡単な話ではないでしょう。

ちなみに、偉そうに語っている私は自分で型を書きたくありません

つらつらと書きましたが、現時点でRailsプロジェクトで「Steepを利用した型検査」を運用するのはコストが高いと考える人は多いかと思います。

※ 「modelsなど特定のディレクトリのみ型検査対象にする」「型検査はするがCIには組み込まず、エラーも無視して良い体制にする」など折衷案はありますが、今回の話の本質ではないため割愛します。

コーディング時の補完強化

上述のSteepにはLSPを提供する機能があるため、これを補完強化に活用することができます。 他にもRubyのLSPとして有名なsolargraphについてもRBSをサポートしていくと明言しています。

KatakataIrbによるirb補完強化

github.com

KatakataIrbとは型定義を活用することでirbの補完を強化するツールです。 作者のtompngさんによる記事もここで紹介します。 qiita.com

Rubykaigi 2023でも紹介されましたね。 rubykaigi.org

本セッションを聞いたことにより「Steepを利用した型検査」以外の活用方法について意識が向くようになり、kakariプロジェクトの型導入と本ブログの執筆が始まりました。


まだまだRBSの活用方法は多くはありませんが、導入ハードルが高い「Steepを利用した型検査」以外にも活用方法があることが分かります。

また、「コーディング時の補完強化」と「KatakataIrbによるirb補完強化」に関しては、一部のクラス・メソッドの型が定義されているだけでその分恩恵を受けることができます。

つまり「型導入」とは

「型定義」と「型活用」を整理してみました。

「自分で型を書く」以外の「型定義」 + 「Steepを利用した型検査」以外の「型活用」を採用する選択肢が見えてきました。

この形も立派な「型導入」だと提唱します。
今までの「型を自分で書きたくないから型導入しない」という考えから、「これならちょっとお試しで型導入してみようかな」という気持ちになった人もいるのではないでしょうか。

そんな方のためにちょっとお試しで型導入するための手順を以下に記します。
※ 本ブログ執筆(2023/05/29)時点の情報です。RBS周りは進化が早いため常に最新の情報を参照することをオススメします。

お試し型導入の流れ

ここでは実際にkakariプロジェクトに「型導入」した際の手順をベースに流れを記します。 各作業の詳細については、それぞれの公式ドキュメントなどを参照してください。

0. KatakataIrbの導入

下準備としてKatakataIrbを導入します。

KatakataIrbは「型を活用する」と前述しましたが、型定義がなくともirb補完が強化されるため型に興味がない方にもオススメします。

以下の記事が詳しいためこちらを参照ください。 www.timedia.co.jp

1. RBS Gem導入

ここからが「型導入」です。
まずはともかくRBS GemをRailsプロジェクトに導入します。

gem'rbs', require: false

production環境では不要なためgroupの指定や require: falseをお忘れなく。

2. RBS Collectionのセットアップ

次に以下のコマンドでGemの型定義をインストールします。

$ rbs collection init # https://github.com/ruby/gem_rbs_collection に登録されているgemの型定義を利用するための初期設定
$ rbs collection install # gem_rbs_collectionの型定義を取得

git ignore対象への追加を忘れずに

# .gitignore
/.gem_rbs_collection/

# 型検査を真面目に活用する場合は型定義のバージョンもGit管理下にするべき
# 現時点では型定義のバージョン管理するほど真面目に型を活用していない
rbs_collection.lock.yaml

今回kakariプロジェクトでは rbs_collection.lock.yamlをGit管理下にしない方針にしました。 メンバー間で型のバージョンを揃える必要性は低く、それ以上に「型バージョン更新が無関係のコミットに混ざる」「型バージョン更新だけのPullRequest」が現状ノイズに感じられるためです。

DependabotでのGem更新PRで rbs_collection.lock.yamlも更新してくれる未来が来ると話が変わってきそうですね。

kakariプロジェクトの rbs_collection.yamlが以下です。
meta-tagsの型定義が破損しているためスキップしています。 gem_rbs_collectionではなく各Gemのリポジトリに型が定義されているとこういう問題もあるようですね。

# Download sourcessources:- type: git
    name: ruby/gem_rbs_collection
    remote: https://github.com/ruby/gem_rbs_collection.git
    revision: main
    repo_dir: gems

# You can specify local directories as sources also.# - type: local#   path: path/to/your/local/repository# A directory to install the downloaded RBSspath: .gem_rbs_collection

gems: # Skip loading rbs gem's RBS. # It's unnecessary if you don't use rbs as a library.- name: rbs
    ignore:true # 型情報が破損しているためスキップする # https://github.com/kpumuk/meta-tags/issues/253- name: meta-tags
    ignore:true

ここまでの恩恵

ActiveSupportが提供しているメソッドの型定義がKatakataIrbの補完で活用される様子(Before)

Before

After

3. RBS Railsの導入

ここまでででも型の恩恵は得られていますが、更に便利にするためにRBS Railsを導入しましょう。

導入手順はシンプルなので公式READMEをご参照ください。

こちらのPRで対応されていますが、 bin/rails g rbs_rails:installで生成されたrake taskがproduction環境で読み込まれないように考慮する必要がある点にご注意ください。

rbs_rails:allタスクを実行することでsig/rbs_rails配下にActiveRecordが自動生成するメソッドの型情報が出力されます。
それにより以下のように補完が強化されます。

ActiveRecordが自動定義している「アカウントテーブルのemailカラムに関するメソッド」の型定義がKatakataIrbの補完で活用される様子

kakariプロジェクトでは現在sig配下をgit ignoreとしています。「型は自動生成のみ」に振り切っての型導入としているためバージョン管理は不要という判断です。
手動での型定義の運用方法が定まってきたらこの辺の設定は変更すると思います。

4. Steepの導入

一旦型定義についてはここまでとし、型活用に目を向けましょう。 Steepを導入することでコーディング時の型補完が強力になります。

Steep Gemの導入手順もシンプルなので公式READMEをご参照ください。

kakariプロジェクトでは「型検査はしない」と割り切ったためSteepfileは以下の形で利用しています。

D = Steep::Diagnostic
target :appdo
  signature 'sig'

  check 'app'# 型検査はせずに補完強化用途でのみSteepを利用する
  configure_code_diagnostics do |hash|
    D::Ruby::ALL.each do |error|
      hash[error] = nilendend# https://github.com/soutaro/steep/pull/800 の対応がリリースされたら↓に書き換えること# configure_code_diagnostics(D::Ruby.silent)end

Steepが提供しているLSPを活用する手順はご利用のテキストエディタによって変わります。以下は一例です。

また、RubyMineに関してはSteepのLSPを必要とせずに自前でRBSを参照して便利にしているようです。

VSCodeのSteep拡張を導入して、それとなく補完が強化されている様子

NeoVimにcoc.nvimとSteep LSPを組み込んで補完が強化されている様子

5. 便利Rake Taskを定義

最後に仕上げで日々の運用上便利なRake Taskを定義します。 と言っても、pockeさんがRubyKaigi 2023で発表していたRake Taskをありがたく流用させていただきました。
後述しますが、一部アレンジをしています。

returnunlessRails.env.development?

require'rbs_rails/rake_task'

namespace :rbsdo
  task setup: %i[clean collection rbs_rails:all]# prototype+subtractを活用したいところだが、自前の型定義が必要になるため保留中# task setup: %i[clean collection prototype rbs_rails:all subtract]

  task :cleando
    sh 'rm', '-rf', 'sig/rbs_rails/'
    sh 'rm', '-rf', 'sig/prototype/'
    sh 'rm', '-rf', '.gem_rbs_collection/'end

  task :collectiondo# lockファイルに定義されているバージョンに従わず最新の方情報を取得したいためinstallではなくupdateを利用している
    sh 'rbs', 'collection', 'update'end

  task :prototypedo
    sh 'rbs', 'prototype', 'rb', '--out-dir=sig/prototype', '--base-dir=.', 'app'end

  task :subtractdo
    sh 'rbs', 'subtract', '--write', 'sig/prototype', 'sig/rbs_rails'

    prototype_path = Rails.root.join('sig/prototype')
    rbs_rails_path = Rails.root.join('sig/rbs_rails')
    subtrahends = Rails.root.glob('sig/*')
                       .reject { |path| path == prototype_path || path == rbs_rails_path }
                       .map { |path| "--subtrahend=#{path}" }
    sh 'rbs', 'subtract', '--write', 'sig/prototype', 'sig/rbs_rails', *subtrahends
  end

  task :validatedo
    sh 'rbs', '-Isig', 'validate', '--silent'endendRbsRails::RakeTask.new do |task|
  # If you want to avoid generating RBS for some classes, comment in it.# default: nil## task.ignore_model_if = -> (klass) { klass == MyClass }# If you want to change the rake task namespace, comment in it.# default: :rbs_rails# task.name = :cool_rbs_rails# If you want to change where RBS Rails writes RBSs into, comment in it.# default: Rails.root / 'sig/rbs_rails'# task.signature_root_dir = Rails.root / 'my_sig/rbs_rails'end

kakariプロジェクトは現在ここまで対応しています。 いかがでしょうか、立派に「型導入」しているように見えます?が一つも自分で型を書いていません

すごい勢いでRBS周りのエコシステムが便利になってきているおかげで、自分で型を書かずとも型の恩恵を受けることができるようになっています。ありがたい!

また、本導入方法のアピールポイントとして「型に興味がある人は型を活用でき、型に興味がない人にデメリットがない状態」となっていることが挙げられます。 このレベルの型導入であれば負債になりませんし方針転換も容易です。

「型導入をしない理由」を考えるまでもなく「とりあえず型導入してみる」と気軽に試せるのではないでしょうか。

今後の展望

ここまで自慢げに型導入について解説しましたが、正直なところまだまだ入り口段階だと思っています。

型に対するチームの成熟度に沿って以下の取り組みについて継続してチャレンジしていきたいと考えています。

「rbs prototype( + subtract)」を活用して、独自クラス・メソッドの型のプロトタイプを自動生成

rbs prototypeにより、クラスやメソッドのI/F(引数情報なども含め)が定義されている型のプロトタイプを生成することで、上述の補完が更に便利になることが予想できます。

当初はここまで対応しようと考えていましたが、prototypeで生成した型が不十分でSteepでエラーが発生し今回は見送りました。 実際のところこのエラー自体は一つ一つ解消すれば良いだけの話なのですが、「クラスやメソッド更新の都度rbs prototypeとsubtractを実行し、エラーが発生した場合は対処する」というフローを現段階のチームに組み込むのはハードルが高く感じられました。

僕自身RBS周りはまだ未熟なので過剰にビビっているだけかもしれませんが、今回は「意味のある形で、尚且つ普段の開発速度が低下することなく型を導入する」を目標にしていたため今回は見送ったという経緯です。

typeprofの活用

typeprofを活用するとrbs prototypeより詳細な型情報が自動生成されるため更なる型補完強化が望めます。

解析速度について現状(v1)は課題がありますが、v2で大きく改善されるとRubyKaigi 2023で発表されましね。とても楽しみです!

正直のところまだほとんど触れられていないため、Railsプロジェクトでの解析精度がどの程度か不明な状況です。 型の目的を「型検査」ではなく「補完強化」に振り切っている現状は精度が完璧である必要はないため十分に実用段階かもしれません。

積極的に活用していこうと思います!

Gemの型定義追加を含むOSS貢献

ここまではひたすらRBS周辺OSSの恩恵を頂いているだけなので、私たちもRBS周辺の成長に貢献していくべきです。 gem_rbs_collectionの型定義更新やRBS,Steepなどのツール群への貢献は然り、 RBS Railsのような型自動生成ツールの開発にも興味があるのでチャンレンジしていきたいところです。 (ActiveHashの型定義なんかもうまいこと自動生成したいですよね。)

また、今回のRBS導入とブログ執筆の最中にRBSの些細な不具合を見つけたのでIssue起票しました。

github.com

github.com

このような不具合だけでなくRBS導入のハードルを下げるための提案なども積極的にしていきたいと思っています。
まだまだRBS周辺は貢献できる余地が多く敷居が低い領域なので「OSS貢献」の文化をチームに浸透させる良い機会として精進していきます!

まとめ

以上、型導入の敷居を下げるための記事でした。

「型定義なし」と「型検査バリバリ組み込んでいる」の間の選択について理解し、「ちょっと弊プロジェクトにも型導入してみようかな」と思えてくれたら幸いです。

小さく型導入してみて、チームが型に馴染んできたらもう一歩踏み込んでいく

そんな形で型を徐々に浸透していけると良いのではないでしょうか。

参考記事

本ブログを執筆する際に多くの記事を参考にさせていただきました。 紹介しきれませんが一部列挙します。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

After RubyKaigi 2023 参加レポート

$
0
0

メドピア株式会社でバックエンドエンジニアをしている徳富(@yannKazu1)です。

先日、RubyKaigi 2023が開催されました。このイベントは、Ruby コミュニティの開発者が一堂に会し、最新のトピックや開発手法について議論する場として知られています。 そのアフターイベントとして、株式会社ZOZO、ファインディ株式会社、そしてメドピア株式会社の3社によるLT・ディスカッション「After RubyKaigi 2023」を開催しました!

今回はそのイベントの様子を簡単にレポートします!

(イベント概要は https://zozotech-inc.connpass.com/event/281473にて案内してます。)

After RubyKaigi 2023の概要

メドピア株式会社、株式会社ZOZO、ファインディ株式会社3社合同で行うRubyKaigiのAfterイベントです。 昨年度はオンラインでの開催でしたが、今年度はメドピア株式会社オフィスでオフラインとオンラインのハイブリットで行われました。

前半、各社のエンジニアによるLTが行われ、後半に、各社のエンジニアのマネジメントを務める方からのパネルディスカッションがありました。

発表されたLT

  • 「REPLとデバッガを取り巻く環境の変化 -Pry, IRB, そしてdebug.gem」 (メドピア株式会社 古川 健二 @frkawa_)
  • 「ruby.wasm + unloosenでChrome拡張機能を作ってみた」 (株式会社ZOZO 近 海斗さん @Ver3Alt
  • 「そうだ RubyKaigi、行こう。 〜初めてのRubyKaigiの歩き方〜」 (ファインディ株式会社 遠藤 薫さん @aiandrox)
  • 「Road to RubyKaigi Speaker」 (Speakers枠 Go Sueyoshiさん @sue445)
  • 「After RubyKaigi 2023〜メドピア、ZOZO、Findy〜」 (Speakers枠 unasuke (Yusuke Nakamura)さん @yu_suke1994)

パネラー

LTの様子

弊社からは古川にLT枠として登壇いただきました。 古川は医師のクリニック開業支援サービス「CLINIC Support」のサーバーサイドを担当しています。

古川の発表ではタイトルにもある通り、デバッガを取り巻く環境の変化について説明がありました。

デバッグする際には現状だと、pry-byebugが主に使われるケースが多いです。 しかしirbのデバッグ機能もかなり充実してきており、サードパーティーのデバッガーを使わなくても、十分デバッグができます。という説明があった後に、実際にデモンストレーションが行われました。

私自身、pry-byebugを使ってデバッグをしていた人間ですので、irbでのデバッグ機能の充実さにかなり驚いたのと同時に、irbを使ってデバッグする方が楽なのではと感じました。

パネルディスカッション

弊社からはVPoEの平川が参加しました。 今年度のAfter RubyKaigiはオフライン開催ということもあり、お酒とピザが用意されており、乾杯のコールと共に和やかな雰囲気でパネルディスカッションがスタートしました。

RubyKaigiでスポンサーをしてみての簡易的なレポートや今回出展したブースの紹介などが行われました。

メドピアでは、今年度のブースでアルコールパッチテストを行いました。 実は、2019年のRubyKaigiのブースでもアルコールパッチテストを行なっており、なぜ今年度もアルコールパッチテストを行なったかなどの経緯などが話されました。

また、RubyKaigiの話としては、世界中から多くの人が集まり、オフラインで様々な人と話せたのは非常に有益な時間だと感じたと話がありました。

懇親会

懇親会では、各参加者がドリンクを手に、様々な交流を行いました。 特に興味深かったのは、登壇者への質問タイムでした。それは参加者が直接、疑問点や思索に対する答えを得る絶好の機会でした。

そして忘れてはならないのが、参加者が持参した「Rubyメソッドかるた」です。それは既知のメソッドと新たに学んだ知識をテストする楽しいゲームで、全員が大喜びで参加しました。

最後に

これらの素晴らしい思い出と共に、参加者一人ひとりが、この経験を自身のプログラミングスキルや視点に活かし、Rubyコミュニティ全体がさらに発展することを願っています。

次回のRubyKaigiでお会いできるのを心から楽しみにしています。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

2ヶ月半で Nuxt サービスを 3.5 個つくった話

$
0
0

フロントエンドエンジニアの小林和弘です。

去年の 4 月に「Web アプリを 3 つ使ったサービスを 3 ヶ月でつくりたい」という話が降ってきて、無茶を現実にした話をします。

新規サービス「やくばと」で Workspacesのモノレポを使って、医療機関画面、薬局画面、患者画面の 3 つの Nuxt を構築しました。

やくばととは

医療機関、薬局、患者さまの間でやり取りされる医療機関起点の薬局向け処方せん画像事前送信サービスです。処方せんに関する業務負荷を軽減すると共に、患者さまの大きな負担である医療機関、薬局での待ち時間を軽減するということを主な目的としたサービスです。

医療機関、薬局の DX を進め、患者さまの負荷軽減に貢献できる素晴らしいサービスになっています。

開発チームでは医療機関、薬局、患者さまに提供している Web 画面をそれぞれ医療機関システム、薬局システム、患者システムと呼んでいます。

モノレポとは

モノレポとは、ひとつのリポジトリ内で複数プロジェクトを管理するソフトウェアの開発手法を指します。

フロントエンドのモノレポツールでは Lernaが有名です。やくばとでは複雑なビルドフローを組む必要がなかったことと、開発当初は Lerna v4のメンテナンスが止まっていたことから、技術選定の選択肢からは外していました(やくばと開発中に Nx 社が Lerna のメンテナンスを開始したため、現段階ではモノレポツールとして Lerna は選択肢に入ると思います)。

やくばとでは、yarn の Workspaces を利用してモノレポを実現しています。

「Nuxt サービス 3.5 個」の 0.5

記事のタイトルに書かれている 3.5 個の Nuxt とは何かを説明します。

やくばとでは医療機関システム、薬局システム、患者システムをすべてひとつのリポジトリで管理しているわけではなく、患者システムと薬局システム、患者システムと既存サービスの 2 つのモノレポリポジトリで開発しています。

患者システムは、すでに存在していた既存サービスのリポジトリをモノレポ化してそこに組み込むという形をとっています。既存サービスのフォームコンポーネントなどのコードを、患者システムに流用するために大幅に書き換えています。0.5 はこの既存コードの変更部分を指しています。

ひとつの巨大なフォームコンポーネントを、入力項目ごとにコンポーネント化し、小分けにしたコンポーネントを共通利用するようにしています。作業としては単純ですが、ディレクトリ構造の変更を行っているため、リポジトリ内のほぼ全てのファイルが修正対象になっています。

患者システムは既存サービスのコードを使い回しているので QA テストの工数も大幅削減することができました。

既存リポジトリのモノレポ化 PR

モノレポの設定

モノレポの設定方法は非常に簡単です。

医療機関システムと薬局システムを管理しているモノレポを例に見ていきます。

package.json の設定

モノレポでは package.json をプロジェクトの複数箇所に設置する必要があります。

.
├── package.json
└── packages
    ├── medical
    │   └── package.json
    ├── pharmacy
    │   └── package.json
    └── ui
        └── package.json

まず、プロジェクトルートの package.json の workspaces プロパティにモノレポ対象になるディレクトリパスを指定します。workspaces プロパティには文字列の配列を定義しますが、ワイルドカード *も利用可能なのでやくばとでは下記のように指定しています。

{"workspaces": ["packages/*"
  ]}

次に医療機関システム、薬局システム、共通 UI コンポーネントのための package.json を作成していきます。

{"name": "@yakubato/medical"
  "dependencies": {"@yakubato/ui": "*"
  }}

{"name": "@yakubato/pharmacy"
  "dependencies": {"@yakubato/ui": "*"
  }}

{"name": "@yakubato/ui"
}

各 pacakge.json の name プロパティに指定した文字列が package を呼び出すときのネームスペースに使われます。

たとえば、UI の Button コンポーネントを呼び出すとすると、

import Button from '@yakubato/ui/components/atoms/Button.vue'

といった形になります。

yarn workspace コマンド

yarn workspaces を使うと、node_modules のインストールコマンドが変化します。

プロジェクトルートの package.json に Nuxt を追加する場合は、 yarn add nuxt -Wといった形で -Wオプションをつける必要があります(オプションをつけなかったらエラーログが流れるのですぐに気づけます)。

また、workspaces で指定したディレクトリ配下の package.json に node_modules を追加する場合は、別のコマンドが必要になります。

packages/ui に @storybook/vue を追加する場合は、プロジェクトルートのディレクトリで yarn workspace @yakubato/ui @storybook/vue -Dを実行する必要があります。workspace コマンドの第 1 引数に追加したい package.json の name プロパティを指定して、第 2 引数に追加する node_modules を指定します。

モノレポの利点

モノレポの利点はいくつかありますが、やくばとプロジェクトでは下記のような恩恵を受けられました。

  • 設定ファイルの共通化
  • node_modules のアップデートが楽
  • UI コンポーネントの共通利用

設定ファイルの共通化

個別のリポジトリで複数サービスを管理した場合、開発ツールの設定ファイルをそれぞれのリポジトリで管理する必要があります。

モノレポ管理をすると、ひとつのリポジトリで .eslintrc.js, .prettierrc, jest.config.js, stylelint.config.js 等々が一元管理できます。lint のルール変更も 1 ファイル変更するだけで各システムに変更が適応されます。

node_modules のアップデートが楽

node_modules のアップデートには dependabotrenovateといった、PR を自動作成してくれるボットがよく使われると思います。

モノレポ管理をすると、当然ですがリポジトリがひとつなのでこれらの PR 対応が一度で済むことになります。

また、今後発生するであろう Nuxt3 対応も一度で対応できるのも大きいです。

UI コンポーネントの共通利用

やくばとでモノレポを採用した一番の目的は UI コンポーネントの共通利用でした。

デザイナーさんに医療機関システムと薬局システムの UI デザインを共通化していただいた上で、汎用的な Button や Modal コンポーネントを一元管理することで開発工数を大幅に削減しました。

UI の開発工数を抑えるということであれば、UI ライブラリ(Vuetify, Chakra UI, Tailwind UI, etc)を使えばよいのでは? という話なんですが、長期運用で UI ライブラリが負債化するリスクと作成するコンポーネントの数や UI・UX の品質担保を天秤に掛けて、独自 UI コンポーネントの開発という選択をとっています。

UI コンポーネントのみをパッケージ管理できているため、他のプロジェクトでも同じ UI を使いたいという話が出てきたときに、 npm や GitHub Packages でパッケージとして UI を提供できる状態にあるというのもモノレポ管理の良い点です。

各システムの簡略図

やくばとの医療機関システムと薬局システムのモノレポ簡略図

医療機関システムと薬局システムは 2 つの Nuxt と、共通の UI コンポーネント管理を行う Storybook のプロジェクトがひとつの GitHub リポジトリ内で管理されています。

CircleCI 上で 2 つの Nuxt を generate し、生成物を AWS の 各 s3 にデプロイしています。CI 設定が共通化できるのもモノレポの利点です。

患者システムもほぼ同じ構成で、共通 UI コンポーネント管理に Storybook を使っていないくらいの違いしかありません。

患者システムは、Storybook が導入されていない既存サービスをモノレポ化しています。すでに UI が構築されていてテスト済という状態だったので、Storybook の導入を行わないという選択をしています(そもそもスケジュールがタイトでインストールする余裕がなかった)。

まとめ

複数ドメインで Web 画面を管理する必要があるようなサービスの場合、UI コンポーネントを共通化してモノレポ開発すると、初期構築や運用コストが抑えられるといった話でした。

複数の Web 画面を利用しないサービスでも、汎用 UI コンポーネントを切り出しておくとパッケージとして切り離しやすくなるという利点があるので、軽い気持ちでモノレポ構成にしてみるのも面白いかもしれません。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023をテーマにした社内LT会に参加しました

$
0
0

今年の4月に新卒で入社したエンジニアの冨家(@asahi05020934)です。
今回は、RubyKaigi 2023をテーマにした社内LT会に参加したので、それについてレポートします。

今回の社内LT会に参加する前

私は、新人研修としてRubyKaigi 2023に参加しました。
皆さんは、どのくらいセッションを理解できましたか?

私は、ほとんど理解できませんでした。
その原因の1つがセッションの内容を理解するために必要な知識が足りていないことだと思いました。
例えば、Power up your REPL life with typesを聞いたとき、
「そもそもこのセッションは何をしようとしているのか?」
ということや
「そもそも型を使うメリットは何なのか?」
ということがセッション中に引っかかって、全然頭に入ってきませんでした。

5月29日の社内LT会

しかし、先日、足りていない知識を少し補えた出来事がありました。
それは、RubyKaigi 2023をテーマにした社内LT会です。
弊社は、カンファレンス等で得た知見を定期的に勉強会や社内LT会で共有しています。
今回の社内LT会は、RubyKaigi 2023をテーマに参加したメンバーが有志で、セッションについて自分なりに調べたことや自作したものを発表しました。

社内LT会の要約

ReDoSの話RubyKaigi 2023レポート

この発表は、ReDoSの概要と対策方法に関する内容でした。

関連するセッション

Make Regexp#match much faster
Eliminating ReDoS with Ruby 3.2

JITの講演を聞いてじっとしていられなくなったのでruby-jit-challengeやってみた

ruby-jit-challengeとは、JITコンパイラ作りを試せるチュートリアルのことです。
この発表は、実際にruby-jit-challengeを試してみたという内容でした。

関連するセッション

Fitting Rust YJIT into CRuby

iseq_compile_pattern_each Hacking Challenge

Findパターンは、Ruby3.0で追加されたパターンマッチ機能の一つです。 この発表は、Findパターンを前方一致から後方一致に改造してみるという内容でした。

関連するセッション

Reading and improving Pattern Matching in Ruby

unloosen を使って Chrome Extension を作ってみた

unloosenとは、ruby.wasmによる Chrome Extension 開発に特化したフレームワークのことです。
この発表は、実際に、unloosenを使ってみてつまづいたところを共有するという内容でした。

関連するセッション

Develop chrome extension with ruby.wasm

型検査なしの型導入のすすめ 〜katakata_irbを添えて〜

この発表は、型の定義方法と型の活用方法に関する発表でした。 詳しい内容は、Railsプロジェクトへの「頑張らない型導入」のすすめに記載しているので、ぜひ読んでみてください。

関連するセッション

Gradual typing for Ruby: comparing RBS and RBI/Sorbet
Let's write RBS!
Parsing RBS

ruby mini_debuggerのコードを読んでみた

mini_debuggerとは、Stan Los(@_st0012)さんがBuild a mini Ruby debugger in under 300 linesで発表されたmini Ruby debuggerのことです。
この発表は、コード量が少ないので、mini_debuggerを読むことは意外と難しくなさそうという内容でした。

関連するセッション

Build a mini Ruby debugger in under 300 lines

DIYインタプリタ

この発表は、インタプリタを作って、プログラミング言語が何をしているのか体験してみたという内容でした。

関連するセッション

The future vision of Ruby Parser
Find and Replace Code based on AST

「演算子 "++"の実装、parse.yへの踏み込み」の講演を聞き、得たこと感じたこと

この発表は、Misaki Shioi(@coe401_)さんのImplementing "++" operator, stepping into parse.yのセッション内容を調べ直して、解説する内容でした。

関連するセッション

Implementing "++" operator, stepping into parse.y

発表を聞いた感想

これら8つのうち、個人的に印象に残った発表が、「型検査なしの型導入のすすめ 〜katakata_irbを添えて〜」でした。
この発表を聞いたことで、型を使うメリットの1つに、コーディング時の補完強化があることを知りました。
そして、改めてPower up your REPL life with typesを見返すと、「このセッションでは、型情報を使用してIRBの自動補完をより正確にしようとしている」ということをなんとなく掴むことができました。
LT会に参加する前は、型を使うメリットやPower up your REPL life with typesでやろうとしていることが全くわからなかったので、雰囲気を掴むことができて今回のLT会に参加して良かったです。

終わりに

今回参加して、LT会にはセッションの内容を理解するために必要な知識を補える可能性があることを知りました。 これからも積極的に参加していきたいです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。   ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

「EM勉強会」をやってみたらすごく良かった話

$
0
0

メドピアでエンジニア/マネージャーをしている濱田 (@yut_h1979) です。

昨年末から半年以上かけて、「EM勉強会」(エンジニアリングマネジメント勉強会)と題したエンジニアリングマネージャーのしごと』の輪読会を実施しました。これがとても良い取り組みになったので、その経緯や内容について共有しようと思います。

目次

1. なぜやろうと思ったのか?

メドピアの組織拡大に伴う課題感

ここ数年でのメドピアの組織拡大に伴い、「マネジメント人材の育成/拡充」が課題であると自分は感じていました。

一定以上の規模に成長した開発組織では、エンジニア個々人の技術力だけではなく事業部との協働なども含めた開発組織の総合力 (生産性) を上げられるような「マネジメント人材」も一定以上必要になってくる、そして現状ではここがボトルネックになっている、という課題感です。

EM学習に最適な書籍との出会い

そんな中、昨年の秋口にエンジニアリングマネージャーのしごとという書籍に出会いました。この書籍は下記の点が素晴らしく、学習に最適であると感じました。

  • これまで自分が学習・経験してきた「EM (エンジニアリングマネジメント) のノウハウ」が、網羅的・体系的に言語化されている
  • EMに関するトピックが、「心構え・姿勢」といった精神論ではなく「メソッド・テクニック・スキル」といった実践論的に書かれている
  • 「他者とのコミュニケーション」「タスクの委譲」「プロジェクトマネジメント」など、マネジメント職でなくても役立つトピックが多く記載されている

さらに、メドピアではテックサポートという学習・スキル向上のための制度があります。そこで「この書籍を使って勉強会をやると良いことが起こりそう!」と感じ、「EM勉強会」を提案・実施するに至りました。

2 . 勉強会の始め方と進め方

勉強会ニーズの把握

「EM勉強会をやると良いことが起こりそう!」と自分が感じていても、それが独りよがりだと意味がありません。そこで、自他部門のEMやメンバーに事前ヒアリングを行い、本勉強会のニーズを探りました。その結果、「おおー良さそう!」「いつからやるの?早くやりたい!」といった反応を頂けたので、「よし、イケる!」という感触を得ることができました。

Slackチャンネルの開設と導入

「EM勉強会」の開始にあたり、まずは勉強会用のSlackチャンネルを作ることにしました。そして全エンジニアが入っているSlackチャンネルにて勉強会用Slackチャンネルの開設と「EM勉強会」の実施日時について案内し、興味あるメンバーに勉強会 and/or Slackチャンネルへの参加を促すようにしました。

読書ペースと進め方の決定

アジャイル開発でもプロジェクト推進でも、「結果の一部から全体を予測する」ことは肝心であると考えます。

そこで、勉強会の第一回では参加者と今後の進め方を相談しつつ、実際にみんなで「読書ペース」を計測しました。その結果、だいたい「15分で5ページ前後 ≒ 30分で10ページ前後」ということが分かったので、そこから次回以降の進行ペースと進め方を決定しました。

  • 一回の範囲:10ページ前後
    • 10数ページの章は1回で読み切る
    • 15ページ超の章は2回に分ける
  • 前半約30分:読書&呟きタイム
    • 黙々会的にその日の範囲を読み進める
    • その中で気になった箇所や意見/感想などをSlackに適宜呟く
  • 後半約30分:深掘り&議論タイム
    • Slackに呟かれた内容をピックアップし、みんなで深掘る

3. 工夫点と留意点

予習不要でハードルを下げる

エンジニアリングマネージャーのしごと』は、300ページ超のボリュームを持つ書籍です。一回に10数ページの進行ペースだと、読み終えるのに「26回」週一開催だと「約7ヶ月」という長丁場になることが分かりました。そこで、参加者が途中で脱落しないようにハードルを極力下げる配慮が必要と考え、事前の予習は不要としました。

内容をSlack上でオープンにする

勉強会はGoogle Meetで実施しましたが、前半30分の呟きと後半30分の深掘りや議論など、勉強会の内容はすべてSlackチャンネル上でオープンにしました。深掘りについてはSlackのスレッド返信で行うようにし、Google Meetのチャットコメントや口頭での意見も極力Slackに転記するようにしました。このようなやり方にした狙いは以下の通りです。

  • ワイワイ盛り上がっている感が出る
  • 欠席者/未参加者も内容を簡単に追える
  • 勉強会後の追加コメントも簡単に行える

「次回予告」で興味を惹く

先述の通り、本勉強会は「26回」、週一開催で「約7ヶ月」という長丁場になります。そこで、次回への期待を持たせて参加継続率を高める目的で、勉強会終了時に「次回予告」を行うようにしました。

「次回予告」のコンテンツは オライリー書籍ページの目次情報をコピペすれば良いので簡単でした。また、これには「次にどこまで読めば良いかを事前に把握できる」という効果もありました。

4. やってみてどうだったか?

興味ある人が想像以上に多かった

本勉強会を始めるまでは、事前にニーズ把握はしていたものの「果たしてどれくらいの人が参加してくれるだろうか…?」という懸念がありました。しかし蓋を開けてみれば、勉強会には常時10名前後が参加し、勉強会用のSlackチャンネルには30名以上が参加してくれました。勉強会を始めた時期 (2022年後半) のメドピアのエンジニア総数は約80名なので、割合としてもかなり多くのメンバーが興味を持ってくれたということになります。

もしかしたら、勉強会の内容をSlack上でオープンにしていたことも良かったのかも知れません。実際、最初はSlackチャンネル参加だけしているメンバーから「自分も勉強会に参加したい!」というケースも一定数ありました。

様々な立場からの議論ができた

本勉強会には前職からマネジメント経験のあるメンバーやマネジメント未経験者など、多様なバックグラウンドを持つメンバーが参加しました。そのため、同じトピックでも興味のある部分が異なったり、マネジメントする側でなくマネジメントされる側ならではの意見が出たりなど、様々な立場からの議論ができました。

また、マネジメント経験者からは書籍の内容を補強するような参考情報も随時頂けたりしました。こういった体験は一人で本を読む場合では得られないと思うので、本勉強会は自分を含め参加者にとって非常に有意義な場になったと思います。

事業部メンバーからも反応を頂けた

さらに本勉強会は、エンジニアだけでなく事業部メンバーからも興味を持って頂けました。個人的にも本勉強会を通じて、『エンジニアリングマネージャーのしごと』の内容の一部 (というか半分くらい) は「職種を問わず通用するマネジメントの基礎になり得る」という実感を持ちました。そこで現在は、本書の内容をピックアップした「ビジネス職向けのマネジメント勉強会」を企画中で、近いうちに実施する予定でいます。

最後に、参加したメンバーからの全体感想を抜粋で紹介します。

  • 『エンジニアリングマネージャーのしごと』、めちゃめちゃ良い本だなと思った
  • エンジニアリングマネージャーのいろんな観点を理解できて良かった
  • 本の内容だけでなく、参加者の経験談とか感想とかも聞けたのが良かった
  • 自分の欠席時も、Slackをあとでキャッチアップできて良かった
  • 総じて、一人で本を読むのとは違った体験ができた

5. まとめ

以上、メドピアで実施した「EM勉強会」(エンジニアリングマネジメント勉強会)の取り組みについて共有しました。マネジメントは唯一解が存在しない難しい世界ですが、だからこそこういった勉強会を通じて様々な知見や学びを得るのは意義深いことであると考えます。

本エントリが、マネジメントや勉強会運営に際して何らかの参考になれば幸いです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

potatotips主催させていただきました

$
0
0

iOSエンジニアの小林(@imk2o)です。 メドピアでモバイルアプリのテックリーダーをしております。

さる2023年7月21日、弊社でpotatotips #83を主催させていただきました。 以前にもオンラインで主催させていただいたことはあったのですが、オフラインを交えたハイブリッド開催は初めてでした!

弊社のモバイルアプリエンジニア総勢5名がLT登壇しましたので、その内容を紹介させていただきます。

speakerdeck.comspeakerdeck.comspeakerdeck.comspeakerdeck.comspeakerdeck.com

発表内容には、実際の業務の中で生じた課題に対する取り組みもありますので、参考になれば幸いです。

ハイブリッド開催の所感

技術系イベントもオフライン開催が復活してきたこともあり、企画当初から社内を会場として実施したいという声があがっていました。 ただ実際のところエンジニアだけで開催準備や当日の進行をするのは難しく、弊社HRチームに協力を仰ぎました。 ここで多忙な中でも快諾いただいたのが、開催成功の一番の要因だったと思います。 この場を借りて、あらためてHRチームには御礼申し上げます🙇‍♂️

また発表に関して、オンライン配信と会場での投影を同時に行うための機材準備についても色々工夫をしてもらいました。

  • 会場発表者を含め、すべての参加者にZoomへ入ってもらう
  • 発表者は投影したいスライドをZoomで画面共有する
  • ホスト用PCを会場のプロジェクターに接続し画面投影
  • 音声は別のマイクで収録し、ホスト用PCと会場スピーカーに接続

とすることで発表者が機材への繋ぎ変えをすることなく、ハイブリッド配信を実現しました。 事前リハーサルでMacと機材の相性問題が起きていたため、その解消と手順の簡略化をも実現したナイスソリューションですね!

LTの後に懇親会をさせていただきましたが、技術的な話から雑談まで色々なお話しをさせてもらい、とても楽しい時間を過ごせました。 エンジニア同士だと、初対面でもすぐ会話できるのが不思議ですよね! 対面でエンジニアの方々とコミュニケーションする機会となったのが、開催して一番よかった理由かなと思いました。

一方でオンライン参加いただいた方にはたくさんの拍手をいただいたものの、こちらのコミュニケーション不足でやや置き去りにしてしまったのかなと反省しております。振り返りをした上で、今後のイベント企画・開催に生かしていきたいと思います。

お知らせ: iOSDCスポンサーセッションに登壇します!

メドピアは今年も iOSDC Japan 2023に協賛しております。 弊社iOSエンジニアの新井がスポンサーセッションに登壇しますのでよろしくお願いいたします!


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

AWS DevDay 2023 Tokyoへの参加レポート〜「セキュリティ/ガバナンス系サービスを使った安全なSandbox環境の作り方」の紹介

$
0
0

はじめに

 今年の4月に新卒で入社したバックエンドエンジニアの冨家(@asahi05020934)です。現在は、全国の医師が経験やナレッジを 「集合知」として共有し合う医師・医学生専用のドクタープラットフォームの開発を行っています。
 新しいAWSのサービスをキャッチアップするときに、触ってみることは効果的です。 「自宅で学ぼう!AWS 初学者向けの勉強方法 6ステップ!」によると、

勉強したサービスをハンズオンなどを通じて実際に触ってみることで、知識と実践を結びつけて理解することができます。

とあり、アウトプットの重要性が身に沁みます。
 しかし、AWSのサービスを実際に触っていると、想定外のコストが発生し後悔する可能性があります。私も以前使っていないリソースを消し忘れて、月数千円の想定外の費用が発生し後悔したことがあります。
 どのようにすれば、AWSの安全なSandbox環境を作ることができるのか知りたいと思っていたところ、先日 AWS Dev Day 2023 Tokyoというイベントに参加し、セキュリティ/ガバナンス系サービスを使った安全なSandbox環境の作り方というセッションを聞いたので紹介します。

Sandbox環境構築に役立つサービス

 「セキュリティ/ガバナンス系サービスを使った安全なSandbox環境の作り方」では、主にコスト管理系のサービスで「AWS Budgets」「AWS Cost Anomaly Detection」、ガバナンス系のサービスで「AWS Organizations」「AWS IAM Identity Center」、発見的統制のサービスで「Amazon GuardDuty」「AWS CloudTrail」を紹介していました。

表1 AWSのSandbox環境構築に役立つサービス

コスト管理系 ガバナンス系 発見的統制
AWS Budgets AWS Organizations AWS CloudTrail
AWS Cost Anomaly Detection AWS IAM Identity Center Amazon GuardDuty

出典: セキュリティ・ガバナンス系サービス を使った安全な Sandbox 環境の作り方をもとに作成

コスト管理系

AWS Budgets

カスタム予算を設定してコストと使用状況を追跡し、しきい値を超えた場合に E メールまたは SNS 通知でアラートを受信できるサービス
出典: AWS Budgets

  AWS Budgetsには「予測とアクション」という機能があり、「月末ではいくらになるか」を予測してアラートを出してくれます。 これによりコスト面で危険な状態になりそうなとき、より早く気づくことができます。
 私も「このまま使っていけば月末どのくらいかかるんだろう」と不安になることが多いので、月末の金額を予測できるのは便利だと思いました。

AWS Cost Anomaly Detection

高度な Machine Learning テクノロジーを活用して、異常な支出と根本原因を特定できるサービス
出典: AWS Cost Anomaly Detection

 AWS Cost Anomaly Detectionを使用することで、想定外の請求リスクを減らすことができます。
 私も過去にどこでコストが発生しているかよくわからず特定するのに苦労したことがあったので、このサービスを使えばスマートに特定できそうで良いなと思いました!

ガバナンス系

AWS Organizations

複数のアカウントをまとめるサービス
出典: セキュリティ・ガバナンス系サービス を使った安全な Sandbox 環境の作り方

 AWS Organizationsが役に立つ理由は、2つあります。
 1つ目は、SCPが利用できるからです。SCPは、アカウントや組織単位にアタッチするポリシーです。SCPを使いSandbox環境のアカウントでも使用してはいけないサービスをあらかじめ禁止することで、Sandbox環境のアカウント利用者が本番アカウントでも使用できない事を事前に気づくことができます。
 2つ目は、アカウントの閉鎖による不要リソースの削除を行うことができるからです。 リソースを消し忘れる原因の1つは、消して良いリソースと消してはいけないリソースが混ざってしまうことです。そこで、本番環境のアカウントとSandbox環境のアカウントを分け、お試し的な用途はSandbox環境のアカウントを利用し、使わなくなったらSandbox環境のアカウントごと削除することでリソースの消し漏れを防ぐことができます。
 私はこのセッションを聞くまで「リソースの消し漏れを抑制するためにアカウントごと閉鎖してしまう」という発想に至らなかったので、とても斬新な方法だと感じました!

AWS IAM Identity Center

ワークフォースのアイデンティティを安全に作成または接続し、AWSアカウントとアプリケーション全体でそのアクセスを一元管理するサービス
出典: AWS IAM Identity Center

 AWS IAM Identity Centerが役に立つ理由は、3つあります。
 1つ目は、AWS Organizations全体のユーザー管理をAWS IAM Identity Centerでまとめて行うことができるからです。AWS IAM Identity Centerの方にログインをすれば、複数のAWSアカウントに対して、シングルサインオンが可能になります。これにより、IAMユーザーを勝手に作ってID・パスワードをいい加減に使い、インシデントを起こすリスクを減らすことができます。
 2つ目は、AWS CLIとAWS IAM Identity Centerが連携可能だからです。AWS CLIとAWS IAM Identity Centerを連携することで、一時的な認証情報(アクセスキー)を必要に応じて発行することができます。これにより、永続的なアクセスキーを発行することが不要になり、アクセスキーをGitにコミットしてしまったときなどのセキュリティリスクを下げることができます。
 3つ目は、WebAuthn対応のTouch ID / Windows Hello カメラ などの MFA が利用可能だからです。MFAの1つに、TOTPがあります。TOTPとは

ワンタイムパスワード(OTP)の生成手法の標準の一つで、秘密鍵と現在時刻からハッシュ値を求め、これを6桁程度の数値列に変換してパスワードとする方式

です。TOTPは、パスワードの生成・入力する分手間がかかります。そこで、WebAuthn対応のTouch ID / Windows Hello カメラ などの MFAを使うことで、パスワードの生成・入力が不要になり、多要素認証の手間を減らすことができます。
 私は毎回多要素認証する時にすぐにスマホを開けられずに困っているので、WebAuthn対応のTouch IDのような多要素認証が使えるのはとても魅力的に感じました!

発見的統制

AWS CloudTrail

AWS インフラストラクチャ全体のアカウントアクティビティをモニタリングして記録し、ストレージ、分析、および修復アクションをコントロールできるサービス
出典: AWS CloudTrail

 AWS CloudTrailを使うことで、「いつ誰が操作したのか」や「どんな操作をしたのか」ということを追跡することができ、調査をするとき便利です。
 もし、SandBox 環境がハッキングされ侵入されていた場合、作業時刻から不審な操作に気付けるかもしれませんね!

Amazon GuardDuty

悪意のあるアクティビティのために AWS アカウントとワークロードを継続的にモニタリングし、可視化と修復のための詳細なセキュリティ調査結果を提供する脅威検出サービス
出典: Amazon GuardDuty

 Amazon GuardDutyを使うことで、AWS アカウントの脅威を様々な情報ソースから機械学習を使って検知することができます。
 セキュリティ問題は自分の想定外な部分に潜んでいて、私はいつも「本当にこれで問題ないかな」と不安に思っているので、潜在的な脅威の検出に役立てられるAmazon GuardDutyはとても心強いツールだと感じました。

最後に

 以上、「セキュリティ/ガバナンス系サービスを使った安全なSandbox環境の作り方」を紹介してきました。今後はこの話を聞いたことをもとに安全なAWSのSandbox環境を構築して、後悔なくAWSのサービスに触れていきたいと思います。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。   ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp


大規模サービスのデータベースエンジンを MySQLからAurora MySQLへの移行 〜リードレプリカ, DNSを利用した最小ダウンタイム移行方法〜

$
0
0

バックエンドエンジニアの徳富(@yannKazu1)です。先日、メドピアのメインサービスであるmedpeer.jpで使われているデータベースエンジンを、MySQLからAurora MySQLへと移行しました。今回はその移行のプロセスについて詳しくお話しします。

移行したデータベースの簡単なインフラ構成

移行方針

今回移行するデータベースは複数のアプリケーションから参照されており、ダウンタイムによるユーザー影響が大きいため、移行方針の検討の段階で重視したのは、ダウンタイムの最小化でした。これを達成するために、DNSのCNAMEレコードと、Auroraのリードレプリカを活用し、移行させることにしました。

DNSのCNAMEレコードの使用

データベースエンドポイントをアプリケーションに直接記述する代わりに、DNSのCNAMEレコードを利用して間接的に参照するようにしました。これにより、データベースエンドポイントの変更が必要な場合でも、DNSのCNAMEレコードを更新するだけで対応できます。 今回移行したデータベースは複数のアプリケーションで参照されており、すべてのアプリケーションでデータベースホストを手で変更していくのはかなり時間がかかるため、この方法で行うことに決めました。

Auroraをリードレプリカとしての起動

Auroraはリードレプリカとして起動し、必要に応じてプライマリーに昇格させることが可能です。これにより、既存のデータベースと新しいAurora データベースとのデータ同期を保ちつつ、最小限のダウンタイムでデータベースをAuroraに切り替えることができました。

具体的な手順

以下に、具体的な手順を説明します。

①Auroraをリードレプリカとして起動

MySQLのリードレプリカとしてAurora MySQLを起動します。

②CNAMEレコードを作成

データベースエンドポイントを参照するCNAMEレコードを作成し、データベースエンドポイントを参照している全てのアプリケーションをDNS経由に切り替えます。これにより、Auroraに移行後にDNSのエンドポイントを変更するだけで全てのアプリケーションの参照先を切り替えることができ、ダウンタイムが大幅に削減できます。

③既存のRDSを読み取り専用に設定

既存のRDSにread_onlyパラメータを設定し、書き込みを停止します。 書き込みがなくなることで、Auroraレプリカのレプリケーションを追いつかせます。

④Auroraをプライマリーに昇格

リードレプリカとして起動していたAuroraのレプリケーション設定を削除し、フェールオーバーすることでプライマリーに昇格させます。

⑤DNSレコードのエンドポイントをAuroraに変更

DNSレコードの向き先をAuroraのデータベースエンドポイントに変更します。これにより、アプリケーション側で変更を加えることなく、エンドポイントが更新されます。

移行に際して検証したこと

本データベースは多くのアプリケーションが参照するメインデータベースであったため、慎重に検証を行った上で移行を実施しました。特に以下の2点を重視しました。

データベースエンジンの変更による予期せぬエラーの発生

まず、ステージング環境でAurora化を実施し、予期せぬエラーが発生しないかを検証しました。今回AuroraのエンジンはMySQLと互換性のあるバージョンに揃えたので、基本的には問題ないと予想されましたが、確認のためにステージングで検証を行いました。その結果、アプリケーション側の改修は必要ありませんでした。

Auroraのフェールオーバー時の接続切り替えの検証

AuroraとRailsを組み合わせて使用すると、Railsのコネクションプールの仕組み上、フェールオーバー時に新しいライターへのコネクション切り替えが自動的に行われず、リーダー(旧 ライター)にデータの書き込みを行ってしまう事象が知られています。これにより、予期せぬタイミングでデータ更新ができない状況が発生します。

今回、Auroraに移行したデータベースはRails、Go、PHPで書かれた複数のアプリケーションからアクセスを受けており、この問題への対策が必要でした。

対策方法としてはいくつかあることが知られていますが、今回書き込みエラーが発生した際に接続をリセットし、再接続を行うミドルウェアを開発しました。このミドルウェアは、RailsのActionDispatch::ContentSecurityPolicy::Middlewareより先に呼び出されるよう設定しました。

以下に、ミドルウェアの読み込みとそのコードを示します:

moduleRdsConnectionAdaptersclassReconnectclassRdsReadOnlyError< ActiveRecord::ActiveRecordError; endCONNECTION_ERROR = ['READ ONLY', '--read-only'].freeze
    CONNECTION_ERROR_REGULAR = /#{CONNECTION_ERROR.map { |w| Regexp.escape(w) }.join('|')}/definitialize(app)
      @app = app
    enddefcall(env)
      @app.call(env)
    rescueActiveRecord::StatementInvalid => e
      raise e unless should_clear_all_connections?(e)

      ActiveRecord::Base.clear_all_connections!
      raiseRdsReadOnlyError, 'DBが読み込み専用になっていたため、再接続を行いました'endprivatedefshould_clear_all_connections?(e)
      if e.kind_of?(ActiveRecord::StatementInvalid)
        returnCONNECTION_ERROR_REGULAR === e.message
      endfalseendendend

config/environments/production.rb(ミドルウェアの読み込み)

config.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware,
                                  RdsConnectionAdapters::Reconnect

移行の成果と今後の展望

CNAMEレコードの活用とAuroraレプリカの特性をうまく組み合わせることで、1時間程度のダウンタイムで抑えることができました。 DNSの工夫やAuroraの活用など、新しいアプローチに挑戦することで、予想よりもスムーズな移行が実現できたのは大きな成果です。

この経験から得た学びは、社内で共有し、会社全体の知見向上に努めたいと考えています。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

毎年恒例の「Swift Quiz」(for Swift 5.8) を公開しました!

$
0
0

Swift Quiz に挑戦してね!

メドピアでアプリ開発を担当している高橋です。

メドピアが iOSDC Japan 2023にスポンサードする度に毎年恒例で掲載している Swift Quizの 2023 年度版を公開しました!今回は Swift 5.8 (Xcode 14.3 相当) 対応になります。社内の iOS エンジニア 3 名にクイズを作成いただきました!

swiftquiz.medpeer.co.jp

今年は例年にも増して難易度が高くなっていると思います・・・🧑‍💻

この辺りは Swift の基礎的な範囲ですが、

ヒント:struct に注目!

少し複雑な例文があったり、

ヒント:lazy は遅延評価

UI についての理解が必要だったりします。

ヒント:iPhone だけじゃないよ!

個人的には Swift 5.8 の特性に着目したこの問題はとてもいい問題だと思っています!

ヒント:Swift 6 を予習している人には簡単かも?

iOS / Swift エンジニアな方もそうでない方も、 ぜひチャレンジしてみてください〜! 👍 👍 👍

swiftquiz.medpeer.co.jp

ちなみに回答後の 𝕏 ツイートボタン押下時に iOSDC トークンが表示されるようになっております🙏

iOSDC Japan 2023 のメドピアセッションも見にきてくださいませ!


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

Terraform Provider を自作し SendGrid リソースを管理した話

$
0
0

SRE の田中 @kenzo0107です。
Terraform Provider kenzo0107/terraform-provider-sendgridを作成し、SendGrid のリソースを terraform で管理する様にした話です。

https://registry.terraform.io/providers/kenzo0107/sendgrid

まず弊社の SendGrid リソース管理の Before/After をご覧ください。

弊社の SendGrid リソースの管理方法 Before

SendGrid コンソール上で手動でリソースの作成・更新・削除していました。

特にチームメイトは以下運用で工数が肥大化していました。

  • 定期的な棚卸し
  • メールアドレスを元にプロジェクト担当者(or 退職者)かをチェック

弊社の SendGrid リソースの管理方法 After - Terraform 管理

コード管理することで以下メリットがありました。

  • 運用工数削減
    • メンバーがどのプロジェクトに所属しているかコード上で確認できる
    • 運用フロー改善*1
    • 変更理由が commit history に記録される
  • 設定の知見が伝えやすい
    • 「あのプロジェクトと同じ設定にしたい!」をコードのコピペでできる

何故自作したか?

まず既に SendGrid の Terraform Provider あるんじゃないの?と思い探しました。

世の中には SendGrid 用 Terraform Providerが沢山あるではないですか?!

利用頻度の高いリソースをサポートしているかを軸に探してみました*2が、以下のことがわかりました。

  • チームメイトをサポートしているのは以下のみ
    • SyedK1987
      • テストをしていない
      • チームメイトの管理は問題なし
      • API Key リソースをサポートしていない
    • octoenergy
      • テストが少ない
      • チームメイトリソースを定義し terraform apply 後、terraform plan 実行するとエラーになる*3

上記踏まえて以下理由から新たに SendGrid 用の Terraform Provider を自作することとしました。

  • テストの少ないプロジェクトへの関与が大変そう 💦
  • Terraform Provider 制作の知見を得たい ✨
    • 今後 SendGrid 以外にも terraform 管理を推進したい場合がありそう
    • Terraform のこと、もっと知りたい 💕

Terraform Provider を作成する為にまずやったこと

Terraform Custom Provider 作成のチュートリアルで学ぶ 📝

  • Terraform Plugin Framework を使ってHashiCupsという架空のコーヒーショップアプリケーションの API に対して Provider を作っていくチュートリアル
  • 2時間以内で終わる!*4
  • Golang をインストールしておく

手順通り進めればできるのでやり遂げられる気になれます!

SendGrid API の利用方法確認 📝

  • 管理したいリソースに対する API を実行しレスポンスを確認し検証*5

上記をまず実施した上で以下ステップに進みました。

Terraform Custom Provider 作成する際の Plugin の選定

Provider を作成するには Hashicorp 社が提供する Plugin を利用します。 Plugin は以下2種類あります。

  1. SDK
  2. Framework (後発)

公式ドキュメントで推奨されている Framework を採用しました。

We recommend using the framework to develop new providers because it offers significant advantages as compared to Terraform Plugin SDKv2.

Golang 製 API Client ライブラリの選定

Terraform Provider が Golang 製なので API Client も Golang 製にする必要があります。

sendgrid/sendgrid-goが公式のライブラリですが、
以下理由の為、回避しました。

  • API 実行時に毎回 API Key を渡す必要があり処理が冗長
  • API 実行時に必要なキーやパス、メソッド、レスポンスのパースは利用者が実装する必要がある
  • Terraform Provider の API Client として利用する場合、Provider 側で API Client について上記を実装・テストが必要になる

例) チームメイト招待

        apiKey := os.Getenv("SENDGRID_API_KEY")
        host := "https://api.sendgrid.com"
        request := sendgrid.GetRequest(apiKey, "/v3/teammates", host)
        request.Method = "POST"
        request.Body = []byte(`{"email": "teammate1@example.com","scopes": ["user.profile.read","user.profile.update"  ],"is_admin": false}`)
        response, err := sendgrid.API(request)
        if err != nil {
                log.Println(err)
        } else {
                fmt.Println(response.StatusCode)
                fmt.Println(response.Body)
                fmt.Println(response.Headers)
        }

上記解消の為、 SendGrid ライブラリを自作し採用することとしました。*6

github.com

自作することで公式と比較し以下メリットがありました。

  • API は関数名で把握できパスを意識する必要がなく、リクエストに必要なキーは引数から確認できる
  • API のレスポンスのパースはライブラリ側で実施しており、 利用者は意識する必要がない
  • ライブラリのテストをライブラリ側で完結できる*7

例) 自作 SendGrid ライブラリの チームメイト招待

    c := sendgrid.New(apiKey)
    u, err := c.InviteTeammate(context.TODO(), &sendgrid.InputInviteTeammate{
        Email:   "engineer-boshuchu@example.com",
        IsAdmin: false,
        Scopes: []string{
            "user.profile.read",
            "user.profile.update",
        },
    })
    if err != nil {
        return err
    }
    log.Printf("invite user: %#v\n", u)

他 SendGrid Provider はどの API Client ライブラリを使ってる?

Terraform Provider 管理リポジトリ内で sendgrid/sendgrid-goを利用し API の処理をサポートするケースが多かったです。

この場合、Terraform Provider 管理リポジトリ内でライブラリのテストを実施する必要があります。
そして、ほぼテストしてなかったです 😢

Terraform Provider 作成でわかったこと

開発関連

  • まずチュートリアルを終え、以下を学ぶことが大前提
    • 開発環境を整備する方法
    • Provider リソースの定義の仕方
    • スキーマの定義の仕方*8
    • API Client の設定方法
    • data, resource リソースの作り方
    • terraform init, plan, apply でリソースを管理する方法

チュートリアルでも利用しますが、
Terraform Plugin Framework のクイックスタート用のリポジトリがあるので、そこにテストやドキュメント生成、Terraform Registryへのリリース等の GitHub Actionsが用意されています。

テスト関連

SendGrid API 関連

  • リソース作成時と取得時でレスポンスが異なる
    • API Key は作成時のみキー値が取得できるが、Read する API がない
      terraform import時は API Key のキー値が tfstate に保存されない*9
  • SendGrid API はサーバエラーが時折発生する
    • terraform plan/apply実行時にサーバエラー (502) で正常終了できない場合がある
    • GitHub Actions 上でテスト実行時に too many requests (429) が発生することが多い*10
  • チームメイトの権限は招待後に SendGrid 側が自動で付与する場合がある*11
    • 付与する権限により権限を更新できない場合がある

自作 SendGrid Terraform を運用してみて得た知見・問題

アカウント毎の管理方法

  • 親アカウントは親アカウント用の terraform で管理*12
  • 子アカウント(サブユーザ)は各種プロジェクト毎の terraform で管理

弊社では子アカウントはプロジェクトに紐づくのでプロジェクトの terraform に寄せることでプロジェクト毎のライフサイクルで管理できる様にしています。

provider で指定するもの

各ワークスペースで環境変数として以下設定しています。

  • 親アカウント
    • SENDGRID_API_KEY
  • 子アカウント(サブユーザ)
    • SENDGRID_API_KEY
    • SENDGRID_SUBUSER

親アカウント・子アカウントのワークスペースで利用する SendGrid API Key は親アカウントで発行した API Key を指定します。

上記について Terraform Cloud を管理する Terraform Provider を利用し Terraform で管理されています。

秘匿情報の扱いについて

これはあくまで回避策として実施したことです。
別途解決策があればご指南いただきたいです🙇‍♂️

  • 秘匿情報は data "aws_kms_secrets"を利用し暗号化し管理
    • 開発者がアクセスできる KMS 鍵を用意し暗号化・復号できる様にした
    • 利用用途: サブユーザ作成時に必須のパスワードの暗号化

SendGrid は秘匿情報を暗号化する仕組みがないので、 terraform-provider-aws の力を借りました。*13

まとめ

Terraform Provider - SendGridSendGrid Goライブラリを自作し SendGrid の管理工数が削減され、Terraform・SendGrid への知見が高まりました。

これからサポートできるリソースを増やし、より管理しやすくしていきたいと思います。

また、個人 OSS を社内導入時にレビューいただいた皆様に感謝です。

以上 参考になれば幸いです。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

*1:以前はチケットを作成し、そのチケットを上長が承認した上で Sendgrid コンソール上でメンバー追加等をしていましたが、 GitHub の Pull Request で上長が Approve したら OK という簡易な運用になりました。

*2:各種 SendGrid Provider の対応状況 2023-08-31 時点

チームメイト サブユーザ API Key Sender Authentication GitHub
download count
at 20230831 14:00
GitHub
archived ?
GitHub
star count
anna-money x o o o 16108 no 4
daco-tech x o o o 848 no 0
davidji99 x x o x 21,191 no 0
indentinc x o o x 1765 no 0
Meuko x o o o 740 no 0
nanasess x x x x 23 no 0
octoenergy o o o o 1,093 no 0
phogolabs x x x x 2275 no 0
SpotOnInc x o o o 106 no 0
SyedK1987 o o x o 144 no 0
taharah x o o x 3711 no 2
tatsuo48 x x x x 26,232 no 2
theaox x o o x 21,198 no 0
Trois-Six x o o o 287,579 yes 13
yinzara x o o x 1,488 no 0

*3:チームメイトとして招待後、保留中のチームメイトとなり、チームメイト取得 API で取得できないことを考慮できていない。

*4:チュートリアルのセクション毎に掛かるおよその時間が記載されているのでその合計を取りました。また自身が試して2時間以内には終わりました。

*5:ドキュメントと実際の挙動が異なる API がいくつかあった

  • 例: https://docs.sendgrid.com/api-reference/domain-authentication/add-an-ip-to-an-authenticated-domainの response の dns キーの値
  • 例: https://docs.sendgrid.com/api-reference/domain-authentication/remove-an-ip-from-an-authenticated-domainの response の dns キーの値
  • *6:以前 https://github.com/kenzo0107/backlogを再利用したので然程手間ではなかったです。エラー時のハンドリングが異なる程度でした。

    *7:Provider 側でライブラリのテストを実装する必要がない

    *8:スキーマ関連は一番苦労したのでここは知見を溜めて別途執筆したいと思います 📝

    *9:ドキュメントに記載

    *10:調査中。MacOS ローカルで実施した場合は然程発生しない

    *11:明確な仕様がドキュメント上に見当たらなかったので、意図せぬ動作を避けるべく ignore_changes = [scopes] で変更無視する運用にし、権限だけはコンソール上で変更する運用にしている

    *12:親アカウントが複数あるのは、請求を分けたい意図です

    *13:SendGrid のワークスペースであるのに AWS の credentials が必要なのは問題であると判断し、Vault の様な秘匿情報の管理の仕組みを別途用意する必要がある旨、社内の GitHub Issue に登録してます。

    お財布に優しいCI改善小ネタ集

    $
    0
    0

    こんにちは。サーバーサイドエンジニアの三村(@t_mimura)です。

    主に保険薬局と患者さまを繋ぐ「かかりつけ薬局」化支援アプリ kakariのサーバーサイド開発(Ruby on Rails)を担当しています。

    今回はRailsシステムのCI時間をコスト追加なしで半減した話をします。

    目次

    前提

    対象プロジェクト

    まず、今回対象となるRailsシステムの規模感について簡単にご紹介します。

    kakariは2019年にリリースされ、今現在も活発に開発が進んでいるプロジェクトです。 大まかに以下のような構成となっています。

    • バックエンド Ruby on Rails
      • フロントエンドとモバイルアプリ向けのAPIを提供
      • 一部システムではwebpackを利用しVue.jsが組み込まれているRails Viewも提供
    • フロントエンド Vue.js
      • Rails Viewに組み込まれている
    • モバイルアプリ Kotlin, Swift
      • ※ 今回のスコープ外

    今回はこれらのうち「Ruby on Railsとそれに組み込まれているVue.js」を管理しているGit リポジトリのCIを改善した話となります。

    $ rails stats
    +----------------------+--------+--------+---------+---------+-----+-------+
    | Name                 |  Lines |    LOC | Classes | Methods | M/C | LOC/M |
    +----------------------+--------+--------+---------+---------+-----+-------+
    | Controllers          |  25114 |  18962 |     700 |    2768 |   3 |     4 |
    | Helpers              |    336 |    240 |       1 |      42 |  42 |     3 |
    | Jobs                 |      6 |      3 |       1 |       0 |   0 |     0 |
    | Models               |  68048 |  46214 |    1537 |    5532 |   3 |     6 |
    | Mailers              |   1128 |    871 |      60 |     117 |   1 |     5 |
    | Views                |    182 |    172 |       0 |       0 |   0 |     0 |
    | Libraries            |   1473 |   1140 |      16 |      77 |   4 |    12 |
    | Controller specs     |    459 |    389 |       0 |       2 |   0 |   192 |
    | Decorator specs      |    135 |    111 |       0 |       0 |   0 |     0 |
    | Haml_lint specs      |     39 |     26 |       0 |       0 |   0 |     0 |
    | Helper specs         |    320 |    240 |       0 |       1 |   0 |   238 |
    | Lib specs            |   1126 |    936 |       0 |       2 |   0 |   466 |
    | Mailer specs         |   6797 |   6046 |       0 |       3 |   0 |  2013 |
    | Model specs          |  77120 |  61488 |       3 |     140 |  46 |   437 |
    | Push_notifier specs  |   2547 |   2173 |       0 |       6 |   0 |   360 |
    | Request specs        |  54132 |  45976 |       0 |     407 |   0 |   110 |
    | Serializer specs     |   1820 |   1509 |       0 |       0 |   0 |     0 |
    | System specs         |  28542 |  22830 |       0 |      20 |   0 |  1139 |
    | Task specs           |    203 |    169 |       0 |       0 |   0 |     0 |
    | Validator specs      |   1355 |    957 |      20 |       6 |   0 |   157 |
    | Worker specs         |   7776 |   6388 |       0 |      25 |   0 |   253 |
    +----------------------+--------+--------+---------+---------+-----+-------+
    | Total                | 278658 | 216840 |    2338 |    9148 |   3 |    21 |
    +----------------------+--------+--------+---------+---------+-----+-------+
      Code LOC: 67602     Test LOC: 149238     Code to Test Ratio: 1:2.2

    CIの状況

    kakariでは主にCircleCIを利用しており、以下のようなJobを実行しています。

    • Ruby on Railsのテスト(RSpec
      • 各種モデル群の単体テスト
      • APIのRequest Spec
      • Rails Viewを提供しているエンドポイントに対してのSystem Spec
    • JavaScriptのテスト(Jest
      • JavaScriptで完結する各種Vue.js Componentの単体テストなど
    • 静的解析

    他にも細かなJobが幾つかありますが、いずれも1分前後の小さなJobのため今回は割愛します。 また、Playwrightを活用したE2Eテストもありますが、これに関しても実行環境・タイミングなどが特殊なため今回の対象外とします。

    改善結果

    細かな改善内容はさておき、具体的な改善結果をお見せします。

    改善前のCI Workflow 14分18秒

    改善後のCI Workflow 6分35秒

    「14分」から「6分半」への大幅な改善を達成することができました 🎉

    また、 「お金💰の力で並列数を増加」のような対応はとらずに一定改善できたため副次的効果ですがコストの削減にも繋がりました。

    改善内容

    前提知識: CIのキャッシュ機能

    具体的な改善内容紹介の前に、ここで「CIのキャッシュ機能」について前提知識として理解しておきましょう。

    CircleCI公式ドキュメントが丁寧のためそちらをご参照ください。 circleci.com

    例示されている通り、yarn packageやruby gemsなどのライブラリのインストール処理にてキャッシュを活用し、各Job内でのインストール時間を短縮することが典型的な活用事例かと思います。

    また、今回はCircleCIを利用しましたがGitHub ActionGit Lab CI/CDなど大抵のCIサービスには同等の仕組みがあるため適宜読み替えてください。

    それでは、改善内容を紹介します。

    webpack buildのキャッシュを活用

    出落ちになりますが、本改善がかなり大きなものとなります。

    前述した通り、本Railsシステムには一部Vue.jsが組み込まれているエンドポイント(画面)が存在します。 そのエンドポイントに対してのSystem Specを動作させるにはwebpackのbuildが必要な構成となっています。

    このwebpack buildの実行時間が毎回5分弱ほどでした。

    この時間を短縮するためにwebpackのpersitent cachingという仕組みを活用しました。 これは「キャッシュ」という名から推測できる通りビルド結果をキャッシュし、ビルド時間を短縮してくれる仕組みです。

    改善効果

    「5分弱」から「1分弱」(4~5分)の時間短縮

    ※ キャッシュが適用される場合

    対応方法

    以下のようにwebpackの設定を記述してキャッシュ情報をファイル出力し、そのファイルをCircleCIのキャッシュ対象に含めるだけです。

      // webpack.config.js
      cache: {
        type: 'filesystem',
        cacheLocation: path.resolve(__dirname, 'tmp/cache/webpack'),
      },
      snapshot: {
        buildDependencies: { hash: true },
        module: { hash: true },
        resolve: { hash: true },
        resolvebuildDependencies: { hash: true },

    ポイントはキャッシュデータ有効性の判定方法を「hash 方式」としている点です。 キャッシュの有効性判定の方法は以下の二種類が選べます。

    • hash(ファイルコンテンツハッシュ) 方式
      • ファイル内容のハッシュ値が同一かどうかで判定
      • 公式原文 Compare content hashes to determine invalidation (more expensive than timestamp, but changes less often).
    • timestamp(ファイルタイムスタンプ)方式 デフォルト
      • ファイルのタイムスタンプ(更新日時)が同一かどうかで判定
      • 公式原文 Compare timestamps to determine invalidation.

    CIでは毎回git cloneする都合上ファイルタイムスタンプが更新されます。 そのため、timestamp方式を利用するとキャッシュされているスナップショットが不正と判定されてしまいキャッシュを活用することができなくなってしまいます。

    参考資料

    RuboCopのキャッシュを活用

    RuboCopにも同様のキャッシュの仕組みがあります。

    docs.rubocop.org

    キャッシュの有効性判定は以下の通りで、「Ruby・RuboCopのバージョン、Cop設定が変わらない限り同一ファイル内容に対してのCop結果は不変」ということから、Cop結果をキャッシュとして記録し次回実行時にキャッシュされているCop結果を流用する仕組みです。

    Cache Validity
    Later runs will be able to retrieve this information and present the stored information instead of inspecting the file again. This will be done if the cache for the file is still valid, which it is if there are no changes in:
    
    ・the contents of the inspected file
    ・RuboCop configuration for the file
    ・the options given to rubocop, with some exceptions that have no bearing on which offenses are reported
    ・the Ruby version used to invoke rubocop
    ・version of the rubocop program (or to be precise, anything in the source code of the invoked rubocop program)

    改善効果

    「2分」から「10秒弱」(2分)の時間短縮

    ※ キャッシュが適用される場合

    対応方法

    実はRuboCopはデフォルトでキャッシュファイルを生成します。 生成されるキャッシュファイルをCircleCIのキャッシュ対象に含めるだけです。

    以下のように --cache-rootオプションでキャッシュファイルの生成先を指定することができます。

    bundle exec rubocop --cache-root tmp/cache/rubocop

    参考資料

    ESLintのキャッシュを活用

    そろそろキャッシュにも慣れてきた頃でしょうか。
    冗長な説明となるため簡単にご紹介します。

    eslint.org

    改善効果

    「30秒前後」から「3秒前後」(30秒)の時間短縮

    ※ キャッシュが適用される場合

    対応方法

    eslint app/bundles/javascripts spec/javascripts --ext js,vue,ts \
      --cache \ # キャッシュを有効化
      --cache-location tmp/cache/eslint/ \ # キャッシュファイル生成先を指定
      --cache-strategy content # キャッシュの有効性判定を「ファイル内容方式」とする(理由は上記webpack buildと同様)

    参考資料

    Jestのキャッシュを活用

    キャッシュはもう飽きてきましたね。
    これが最後のキャッシュ対応です。

    jestjs.io

    改善効果

    「3分前後」から「2分前後」(1分)の時間短縮

    ※ キャッシュが適用される場合

    対応方法

    cacheDirectoryを指定し、生成されたキャッシュファイルをCircleCIのキャッシュ対象に含めるだけです。

    # jest.config.js
    module.exports = {
      cacheDirectory: 'tmp/cache/jest',
    };

    参考資料

    RSpec Jobをテスト特性ごとに分割

    RSpec実行Jobを「webpack buildが必要なSystem Spec rspec_with_assets」と「それ以外(Model Specなど) rspec_without_assets」の二つに分割し、「時間がかかるWebPack Buildを待たずに rspec_without_assetsを開始」することで全体の総時間を短縮する試みです。
    こちらは同チームのサーバーサイドエンジニアの谷(@yuyat137)が対応しました。

    RSpecを分割実行しJob依存関係を最適化(before)

    RSpecを分割実行しJob依存関係を最適化(after)

    改善効果

    これまでの改善とは異なり特定のJobが改善されるようなものではないため改善効果の計測が難しいのですが、最大(webpack buildのキャッシュが適用されない場合)は4,5分の時間短縮が期待できます。
    また、後述するリソースクラスと並列数の最適化にも繋がります。

    対応方法

    特別な工夫はなく、単にRSpec実行Jobを分割するだけです。 Jobを分割する分メンテナンスコストは増えるため、期待できる改善効果とのトレードオフで採用・不採用を検討すると良いかと思います。

    CircleCIのリソースクラスと並列数の最適化

    まず前提知識として、CircleCIには特定のJobを並列稼働させる仕組みがあります。 例えば以下はRSpec実行Jobを8並列で稼働している様子です。

    CircleCIにてRSpecの実行Jobを8並列で稼働している様子

    このように並列数を上げることでCIの総時間を短縮することができます。 ただし、CircleCIは「サーバー稼働時間の合計 ≒ コスト」となるため、並列数を上げるとその分コストが増えます。(※ Job実行のための事前処理が並列数分実行されるため)

    circleci.com

    もう一つの前提知識として、CircleCIでは各種Jobのリソースクラス(サーバーのスペックのようなものと解釈してください)を調整することが可能です。 例えばCPUやメモリをあまり消費しないようなJobの場合は小さめの smallなんかを指定するとコスト削減に繋がります。

    今回の改善は「並列数」と「リソースクラス」の両方を調整し、最適化しました。

    端的に言うと「RSpec実行Jobのリソースクラスを下げて並列数を上げた」となります。 実際の最適化内容は以下です。

    ※ 消費クレジット= ここではCircleCI上のコストの単位と解釈してください。 (参照: https://circleci.com/docs/ja/credits/

    最適化前

    • 「Medium(CPU=2, Mem=4, Credit=10/min)」を8並列

    最適化後

    前述の「RSpec Jobをテスト特性ごとに分割」が完了していたため、それらのJobの特性に応じてリソースクラスと並列数を最適化。

    • 重ためのSystem Spec系実行Job
      • 「Medium(CPU=2, Mem=4, 消費クレジット=10/min)」を4並列
    • テストケースが多いSystem Spec以外実行Job
      • 「Small(CPU=1, Mem=2, 消費クレジット=5/min)」を8並列

    最適化前後で消費クレジットの変化なし(共に80/min)

    改善効果

    「10分30秒」から「7分弱」(4分弱)の時間短縮

    細かなテストが多いRSpec群に対しては「スペック抑えめで並列数を増やす」というアプローチが良さそうでした。

    採用しなかった・見送った改善候補

    HAML-Lint, Fasterer, Brakemanのキャッシュを活用

    RuboCopやESLint同様に静的解析のJobは極力キャッシュを活用したかったのですが、ツール側が機能提供していなかったので断念しました。
    OSSコントリビュートチャンスですね。

    Stylelintのキャッシュを活用

    Stylelintにもキャッシュの仕組みがあるためこれを活用することができたのですが、元々のJob実行時間が10秒未満と短かったため今回は見送りました。

    bootsnapを活用

    Bootsnapは各種ファイルパス情報などをキャッシュすることで、Rubyアプリケーションの起動を高速化するGemです。
    詳細は公式ドキュメントなどを参照ください。

    github.com

    Bootsnapが生成するファイルをCircleCIのキャッシュ対象に含めるだけでJob内のコマンド実行が高速化する可能性があります。 Bootsnapは巨大なアプリケーションほど効果を発揮しますが、kakariはせいぜい小〜中規模程度なため「Bootsnapで受けられる恩恵 < Jobの複雑さ」と判断し今回は見送りました。

    Jestの並列実行化

    kakariではJestをシリアル実行していたので、並列で稼働する(デフォルト)の設定に変更してみましたが、逆にCI時間が延びたため今回は見送りました。 JestのCI時間はそこまで支配的でなかったため、深く調べてはいませんが恐らくCPU/メモリリソースが十分に割り当てられていなかったことが原因かと思われます。 jestjs.io

    まとめ

    以上が最近kakariプロジェクトで取り組んだCI改善の小ネタ集でした。

    繰り返しになりますが、追加コストなしでCIの実行時間を「14分」から「6分半」に短縮することができました 🎉

    いずれも裏技のような改善ではなく、公式ドキュメントに記載されているような正攻法ばかりでした。
    「重たいテストを一つ一つ修正する」ことも大事ですが、まずは落ち着いて公式ドキュメントを読み込むことも大事ですね。

    本記事がきっかけで世界で動いているCI時間が少しでも短縮することを願っています。


    是非読者になってください!


    メドピアでは一緒に働く仲間を募集しています。
    ご応募をお待ちしております!

    ■募集ポジションはこちら

    medpeer.co.jp

    ■エンジニア紹介ページはこちら

    engineer.medpeer.co.jp

    Raspberry Pi + ChatGPT で会話ロボットを作った

    $
    0
    0

    会話の間が長く、スムーズでなくてすいません💦

    長期休みに入る子供対策 R&Dの一環で子供達と会話してくれる AI ロボットを作りました。

    github.com

    会話の例

    日々このような会話が繰り広げられる様になりました。

    例1

    • 🤖「こんにちは。今日はどんなサッカー練習をしましたか?」
    • 👦「今日はダイレクトシュートの練習をしました」
    • 🤖「それは良いですね。ダイレクトシュートは難しいですよね。どんなダイレクトシュートの練習ですか?」
    • 👦「インサイドキックです」
    • 🤖「サッカーの試合で使われるキックの7割がインサイドキックと言われていますね。試合でもインサイドキックをしますか?」

    例2

    • 🤖「こんにちは。どんなサッカー選手が好きですか?」
    • 👦「ドリブルやパスが上手な選手です。」
    • 🤖「どの選手が好きですか?」
    • 👦「モドリッチです。」

    などなど、普段親子での会話では出ない様な回答が引き出せて面白いです。

    ロボットには敬語使うんだなというのも興味深いです。

    システム概要図

    *1

    大まかな処理の流れ

    1. モーションセンサー検知
    2. OpenAI に会話開始テキストを送信し、レスポンスを音声変換しスピーカーから再生
    3. 以下繰り返し
      1. ビープ音再生
      2. RPi 録音開始
      3. 子供しゃべる
      4. 録音内容を音声ファイルに保存
      5. 音声ファイルを Cloud Speech to Text API でテキスト変換*2
      6. テキストを OpenAI に送信しレスポンスを音声変換しスピーカーから再生

    工夫点

    • 会話のきっかけはロボットから
      • 「話しかけらた」方が子供は嬉しいと思ったので 👦
    • 子供の話すターンの終了は無言3秒経過したら
      • 「最初は録音開始から10秒以内に話す」ルールにしたが、話したいことが言えなかった
      • pyaudio の無言時間計測できる仕様があったのでそれを利用した
    • 子供が話して良いタイミングにはビープ音を鳴らす
      • 話して良い合図がないとロボットとの会話は難しかった
      • ビープ音と一緒に話している間は LED を光らせる様に視覚的にも会話期間を認識させる
    • 会話は返答無視で自動終了
      • ある程度遊んだらロボットの返事待たずどこかへいってしまう為
      • 返答を 5秒無視で会話終了
        • 再度モーションセンサーが検知したら会話開始となる
    • 日本語・英語どちらでも話せる様にした
      • 私個人の英語学習の為に英会話もさせたかった

    ChatGPT にお願いする会話の制約

    英語・日本語で制約を指定しています。

    先の動画では以下のような指示をしています。

    ja:
      system_content: >-
        #指示:
        以下の制約の下でチャットしてください。
    
        #制約条件:
        私は日本のサッカーが大好きな小学生2年生です。
        会話のトピックはどんなサッカー選手が好きか、とか、今日はどんなサッカーの練習をしたとか、をお願いします。
        回答は極力短くお願いします。

    使ったもの

    • RPi 4B
      • OS: Raspbian GNU/Linux 10 (buster)
    • OpenAI API
    • Cloud Speech to Text API
    • モーションセンサー
    • スピーカー
    • マイク

    諸々購入しましたが、弊社エンジニアは実質 0 円です😄
    弊社のテックサポート制度で購入をサポートしている為です 🎉

    ブレッドボードの設定

    SunFounder で各種モジュールの設定方法があったので参考にしました。

    https://docs.sunfounder.com/projects/kepler-kit/en/latest/

    難しかったこと

    3歳の舌足らずなところはうまく聞き取ってもらえなかったです。 でも会話が継続されているのでこれはこれで良いのかもしれない😅

    また、こちらの話終わりを「無言になってから3秒経過したら」としましたが、ノイズを拾ってしまい3秒以上かかることがありました。

    マイクの性能にも依存するのかもしれませんが、スマートホームのようなスムーズな会話を実現するには録音ライブラリの微調整と機材の向上は必要かと感じました。*3

    もしくは、視覚的にローディング待ち状態がわかる仕掛けがあると良いかなと思いました。

    子供達の反応

    • 長男:
      • もっと自分に合ってるサッカーの練習方法を教えて欲しい⚽️
      • これいつまで話せばいいの?
    • 次男:
      • ロボットになりたい🤖

    カスタマーからストイックな要求も飛び出し、改良の余地がありました。
    会話を続ける限り続けられてしまうので質問攻めの苦痛もあった様です。*4
    次男のサイボーグ化は家族会議します。

    まとめ

    シンプルにモジュールと API を繋ぎ合わせて Raspberry Pi + ChatGPT で会話ロボットを作ってみました。

    今後、さらに会話した感が増すように唇が動いたり、表情筋を再現できるような仕組みを作りたいと思っています。(怖い)

    以上
    参考になれば幸いです。


    是非読者になってください


    メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

    ■募集ポジションはこちら

    medpeer.co.jp

    ■エンジニア紹介ページはこちら

    engineer.medpeer.co.jp

    *1:シーケンス図は https://github.com/kenzo0107/raspi-talk#system-overview

    *2:音声のテキスト変換について、julius: 日本語音声を認識できるものの、辞書の作成や各種ライブラリのインストールが必要で手間でした。 pocketsphinx はインストールの難易度が高く、解決しなかったので見送りました。最終的に音声ファイルからテキストに変換する際の精度と導入の簡易さから Cloud Speech to Text を選択しました。他良い選択があれば是非ともご教示ください🙇‍♂️

    *3:動画では途中早送りしてます

    *4:数ターンで会話終了する仕組みを入れても良さそう

    Viewing all 215 articles
    Browse latest View live