TECH PLAY

プログラミング

イベント

マガジン

技術ブログ

ミイダステックチームです! 2026年6月にt_wadaさんをお招きし、「2026年のソフトウェア開発を考える」と題した社内勉強会を開催しました。前回ご登壇いただいた2024年の講演から2年、ソフトウェア開発の現場はすっかり様変わりしました。社内からも再講演のリクエストが多かったt_wadaさんのご講演をレポートします。
はじめに ITD2-2-3のI.Hです。今回、RubyKaigi 2026 in 函館に参加する機会をいただきました。 RubyKaigiは、Ruby本体やエコシステムに関する最新の知見が集まる技術カンファレンスです。 今回の参加を通して、Rubyそのものについて学べたのはもちろん、他社エンジニアとの交流やスポンサーイベントでのLT登壇など、普段の業務だけでは得られない多くの経験をすることができました。 この記事では、Rubyに関する知見の共有だけでなく、技術イベントに参加すること自体の意味や価値についてもあわせてまとめました。 Day0(イベント前日) 函館に前日入り 場所が北海道の函館と言うことで、当日出発では間に合わないため前日の夜に出発しました。 空港に到着すると、空港の至る所にRubyKaigiの看板や垂れ幕がありました。 また、路面電車の車内外にもRubyKaigiの看板が。町おこしさながらの盛り上がりでした。 空港で明らかにrubyistの方(第一rubyist)を見かけたので、タクシーに相乗りしました。 有名なラーメン屋さんで降りるとのことだったので、一緒に食べながら技術の話やお互いの会社の制度・AIツールの導入状況などに花を咲かせました。 Day1 会場へ 会場は、JR函館駅から市電で30分ほどの場所にある「 函館サーモン・まるなまアリーナ/ホール 」でした。命名権を地元の水産会社が取得されたらしく、函館感全開の良い会場名とロゴになってました。 チェックインすると、首から掛けるストラップと名札をいただきました。 会場で「どこの会社の方ですか?」と度々聞かれるので弊社のロゴを手書き。 なんとか伝わりました。 基調講演 The Journey of Box Building 発表資料はこちら 初日の講演は、rubyコミッターでもある田籠 聡さんの基調講演から始まりました。 内容は、2025年12月25日にリリースされた  Ruby4.0.0  の新機能の一つである、 Ruby::Box  についてでした。 Ruby Boxはクラス等の定義の分離/隔離のための機能を提供する、実験的機能です。 実験的な機能として位置付けられているとのことでしたが、安全にコードの分離ができるようになる点は業務にも活かせそうでした。 Exploring RuboCop with MCP 発表資料はこちら Keynote以外の公演は基本的に大ホール・小ホール・サブアリーナで行われ、同時刻に開催される講演に同時に参加することはできないため、タイムスケジュールと発表内容を見て選ぶ形式でした。 英語での講演も多く、同時翻訳アプリで聴くことにも挑戦しましたが、やはり日本語の講演の方が理解しやすく、自然と日本語の講演を選ぶことが多かったです。 そんな中で、初日の午後に参加した伊藤浩一さんの RuboCop  x MCPの話が印象に残りました。 RuboCopは人間や他のプログラムによってトリガーされていました。AI時代においては、AIエージェントが新たなトリガーとして登場しました。本講演では、生成型AIとリンターおよびフォーマッターを組み合わせる実践的な方法について議論します。 決定性がLinterとしての価値だった中で、非決定性を持つLLMを組み合わせることでどんな価値が創出できるのか(又は失われるのか)という試行錯誤の話が面白く、こういったイベントに参加して直接コミッターの方のお話を聞く醍醐味だと感じました。 Day2 基調講演 Twenty Years of JRuby 2日目は20年以上 JRuby (Java仮想マシン上で動作するRubyの処理系)を開発している、Charles Nutterさんの基調講演から始まりました。 内容はJRuby が誕生してからの20周年の振り返りと、JRuby 10.1 のリリースについてでした。 業務を含めこれまでCRuby(C言語で実装されたRubyの処理系)しか触ったことがなかったのですが、JRubyやMRubyのような別の処理系にも興味が湧きました。 Practical TypeProf: Lessons from Analyzing Optcarrot 発表資料はこちら 2日目は TypeProf 開発者の 遠藤侑介さん の講演が印象に残りました。 メインの話は、TypeProfを実際に適用してみた事例です。題材に選ばれたのは、発表者自身が開発したRubyで書かれたNES(ファミコン)のエミュレータで、約6000 行の複雑なコードに TypeProf を適用したところ600個以上のエラーが出たとのことです。 対応としては、型推論結果に強く影響する箇所にRBSを書くことと、TypeProf 側の誤認識の根本原因を直すことの2方向から進められ、最終的には Ruby 55行+RBS 67行、合計122行の変更でゼロエラーに到達したとのことでした。SteepやSorbetと比べても、既存コードへの変更量がかなり少ないという結果でした。 終盤では、AI コーディングエージェントが普及する中でTypeProfをどう位置づけるのか、という問いも提示されていました。エディタ支援を主目的としてきたツールが、AI 時代にどんな価値を持てるのかという点についても言及されていました。 Day2の交流会 2日目の夜は、Drink Upに参加し、LT枠が空いていたので発表しました。 3日目のRubyKaigiがより面白く聞けるようにと思い、Rubyが実行されるプロセスをParserの話からGCの話まで、一通りまとめてみました。 資料は marp  + Opus4.6で作成 Day3 Lightning-Fast Method Calls with Ruby 4.1 ZJIT 発表資料はこちら RubyKaigi最終日は、 国分崇志 さんのZJITの講演が印象に残りました。 注目度の高い ZJIT ですが、今回の発表では特にメソッド呼び出しの高速化に焦点が当てられていました。 Rubyでは依然としてメソッド呼び出しのオーバーヘッドが大きく(動的型付けだから仕方ないが)、YJIT によって改善が進んできた現在でも、なお大きなボトルネックとして残っているそうです。ZJIT ではこの処理の最適化が大きなテーマになっており、バックトレースや例外処理、ローカル変数アクセスに必要なメタデータをどう保持しつつ、無駄なメモリライトを減らすかが論点になっていました。 そこで紹介されていたのがLightweight Framesでした。これは、メソッド呼び出し時に必要なフレーム情報を最初からすべてメモリに書き込むのではなく、必要になるまで遅延させ、まずは最小限の情報だけを持つことでコストを下げるアプローチです。 Rubyの柔軟な書き心地は維持しつつ、高速に動くようにしていく取り組みをしていただいてる事に感謝です Matz Keynote もちろん最後は、Rubyを作った「 まつもと ゆきひろ 」さん(Matz)の基調講演でした。 ここ半年くらいは自分でコードを書かずに、ほぼ全てAIに書かせるという縛りを自らに課しているとのことでした。コードレビューやプロンプトを細かく制御できるからこそできる事だと思いますが、実務の現場でもAIエージェントは欠かせない存在になってきているのではないでしょうか。 そんな中で大きなトピックは、新しいRubyのAOTコンパイラ「 Spinel 」の発表です。RubyコードをC言語に変換してからコンパイルすることで、ネイティブバイナリを生成するという試みで、すぐに業務レベルで役立てられるものではなさそうですが、インタプリタ言語×AOTコンパイルという発想と開発過程が興味深かったです。(Rubyは動的機能が多いので、機械語にできる=高速化ではないことに注意してください) まとめ RubyKaigiに行って良かったと思う1番のポイントは、Rubyやプログラミングが大好きな人達の「熱」を感じられたことでした。普段「仕事」としてプログラミングをしていると商業的な観点で価値を測り測かられる癖がついてしまって、楽しいとか知的探求という側面を忘れていたなぁと思いました。 また、現地での他社のエンジニアとの交流も刺激的でした。今まで自分がマイナビ色に染まっている自覚はありませんでしたが、他社のエンジニアと交流すると明らかに各社個性というか雰囲気があって、たまには視野を広げるためにも交流して新しい知見を持ち帰ってくる必要があると感じました。
はじめに こんにちは、バックエンドエンジニアの小笠原( @yukineko_819 )です。 Checkout Reliabilityチームに所属し、負荷試験環境の構築や購入ロジックの最適化など、購入体験の信頼性向上に向けた取り組みをしています。 今回は、「どのショップでも、いつでも安定して購入できる」という購入体験を守るために、購入時のクレジットカード決済へ流量制御(以降、レートリミッタ)を導入した話をご紹介したいと思います。とくに、 特定のショップに購入が集中しても、ほかのショップでは決済ができる状態にする ために、アクセス集中の影響をできる限り抑え、全体の購入可用性を平準化する設計をどう考えたか、というところが中心になります。 あるショップにアクセスが集中している時、ほかのショップで購入ができない まずは、私たちが解きたかった問題からお話しさせてください。 一般的に、決済処理には安定稼働のために単位時間あたりに捌ける流量の上限が設定されており、これを決済流量制限(以降、決済レートリミット)と言います。平常時であればこの決済レートリミットを意識することはほとんどありません。しかし、大型の販売イベントや限定商品の販売といったケースで、特定のショップに対してごく短期間に購入が一気に集中している時、この決済レートリミットが問題になります。 このとき、購入が集中しているショップの決済で決済レートリミットに達すると、まったく無関係なほかのショップにも影響が及んで制限がかかり、購入できなくなってしまう可能性があります。 これは購入者から見れば、自分にもショップにも何の落ち度もないにもかかわらず購入できない、というマイナスの体験となってしまいます。ネットショップの買い物体験として、これほど避けたいものはないでしょう。 いつでも買えるカートを目指して そこで私たちが目指したのは、特定のショップに購入リクエストが集中している状況でも、ほかのショップではいつもどおり購入できる状態を保つことでした。これを実現するために、次の2つを満たすレートリミッタの設計を考えました。 1. 流量を自分たちで把握して制御する ひとつめは、流量を自分たちでカウントして能動的に制限をかけることです。決済レートリミットに到達しているか否かわからないまま決済を実行するのではなく、安定して捌ける範囲の上限を自社側で定義し、あえて能動的に流量を絞る方針を取りました。 2. その他のショップの取り分を「引き算」で残す ふたつめは、アクセス集中ショップとその他のショップを分けて管理することです。これまではアクセス集中ショップが流量を独り占めしてしまい、その他のショップの決済が通らなくなってしまう危険がありました。そこで、あえてアクセス集中ショップに本来の上限値よりも少しだけ低い上限を設定し、残った流量をその他のショップが使えるようにする、という方針を取りました。 この設計のポイントは、その他のショップ用の流量を予め確保しておくのではなく、アクセス集中ショップの上限値にキャップをかける形にしたことです。アクセス集中ショップといっても常に上限に張り付いているわけではありませんし、その他のショップでの購入がたまたま同時刻に重なってリクエスト数がはね上がる可能性もあります。この方針は、その他のショップ側の流量に不要な制限をかけてしまわないようにするという意図があります。 図1: アクセス集中の影響を抑え、その他のショップの取り分を「引き算」で残す どう作ったか ここからは、より具体的な処理フローの話に移りたいと思います。 決済を実行する直前に、1トークンを要求する レートリミッタをどこに挟むべきか検討した時、私たちは決済処理を実行する直前に挟むことにしました。 決済処理は、その実行前にレートリミッタへ「1トークンください」と要求します。このレートリミッタの実体は、専用のRedis上でアトミックに実行される Luaスクリプトで、古典的なトークンバケットとして振る舞います。 なぜLuaなのかというと、「トークンの回復 → トークン残量を確認する → トークンを消費する」という一連の判定をひとつのアトミックな操作にまとめたかったからです。こうしておけば、同時に大量のリクエストが来ても、競合することなく正しくカウントできます。 このレートリミッタは、トークンが残っていればトークンを消費してOKを返し、制限と判定されればNGを返します。レートリミッタを呼び出したアプリケーション側は、判定がOKであればそのまま外部APIを実行して決済に進みます。NGであれば外部APIを実行することなく、決済を諦めて処理をエラーで終了させます。 グローバルバケットとアクセス集中ショップ用バケットの2段構成にする ただ、総量を制御するだけでは足りません。先着順でトークンを奪い合うと、アクセス集中ショップが全てのトークンを独占してしまい、その他のショップが締め出されてしまうからです。これでは今までと何も変わりません。 そこで中核になるのがアクセス集中ショップ用に別途専用のトークンバケットを用意するという方法です。 全ショップが共通のグローバルバケットからトークンを消費する アクセス集中ショップだけアクセス集中ショップ用バケットからも追加で消費する アクセス集中ショップ用バケットの上限をグローバルバケットより小さく設定する ポイントは、アクセス集中ショップではないショップ向けのバケットを明示的に確保しているわけではない、というところです。あえてアクセス集中ショップ側を絞ることで、その差分がその他のショップの取り分として引き算で自動的に残るようにしています。 この設計には、 work-conserving (余力を遊ばせない)という嬉しい性質があります。アクセス集中ショップがなければグローバルバケットは誰でも使えるので、トークンが余ることはありません。アクセス集中ショップが現れて初めて、その分だけアクセス集中ショップ用バケットの制約が効き始める、というわけです。 判定のルールを整理すると、次のようになります。 アクセス集中ショップ : グローバルバケットとアクセス集中ショップ用バケットの両方に空きがあってはじめて許可。 その他のショップ :グローバルバケットに空きがあれば許可。 図2: その他のショップはグローバルバケットだけ、アクセス集中ショップは両方の空きが必要 なお、レートリミッタが制限と判定したときはトークンを消費しないようにしています。これは、弾いたリクエストでトークンを消費してしまうと本来なら通せたはずの後続のリクエストにまで影響が及んでしまうからです。 アクセス集中ショップをどう見分けるか アクセス集中ショップをどう判定するか、という点にも少し工夫が要りました。 素朴に閾値だけで判定すると、ショップの状態が閾値の前後で行ったり来たりしてしまい、挙動が安定しません。 そこで、ショップごとに単位時間あたりの決済リクエスト回数をカウントし、一定以上に達したらアクセス集中ショップと判定するようにしました。さらに、一度アクセス集中ショップと判定されたらしばらくはそのフラグを引き継ぐクールダウン期間を設けています。このクールダウン時間は、実際の過去のリクエストの状況を分析して決定しました。 Off → Observe → Enforce、3つのモードで段階的に出す 決済という事故が許されない経路に新しい制御、それも決済を能動的に遮断する機能を入れるわけですから、リリースの手順は慎重に検討しました。いきなり遮断するようなことはせず、フィーチャーフラグで3つのモードを切り替えられるようにして段階的にリリースできるように設計しました。具体的には、以下の3つのモードを管理画面から瞬時に切り替えられるような作りにしました。 Off :完全な機能OFF状態。Redisにも通信しない、完全に無害な状態で本番に導入する。 Observe :制限の判定はするが、制限はせずに全て許可する状態。「もし遮断していたら、いつ・どれだけ弾いていたか」を記録するだけにとどめる。このモードで誤って制限してしまうケースがないかを本番のデータで実測する。 Enforce :制限の判定を行い、遮断も実行する状態。この状態で初めて本格的なレートリミッタの挙動がリリースされる。 万が一 Enforce で誤った遮断が起きても管理画面からフラグを即座にObserveへ戻すだけで、デプロイなしに誤遮断を解除することができます。さらに、この状態の計測自体は継続することで、何が起こったか後から分析できるように情報を残すこともできます。この「デプロイなしで止められる」「後から分析できるデータも残せる」という安心感は、本番に投入していくうえで大きく効いたと感じています。 レートリミッタで決済を止めないようにする 当然ですが、流量制御のために導入したレートリミッタ自身が新たな障害点になってしまっては本末転倒です。レートリミッタの不調で決済全体が止まってしまうのは、避けたい事態の中でも最悪の部類だといえます。 そこでRedisに異常があったときは判定をスキップして決済を通すfail-open方針にしました。「厳密に上限を守ること」よりも「決済を止めないこと」を優先する、という価値判断です。あわせて、判定にかけるタイムアウトはごく短期間に設定し、Redisが重くなったときも素早く安全側に倒れて、決済の本流の足を引っ張らないようにしています。 すべての判定を観測する 最後は観測の話です。すべての判定経路で、レートリミッタの呼び出し毎に1つのイベントを記録するようにしました。 記録しているのは、どのモードで動いていたか(Off / Observe / Enforce)、許可したのか制限したのか、制限判定の理由(グローバルバケットで弾いたのか、アクセス集中ショップ用バケットで弾いたのか)、処理にかかった所要時間、そしてfail-openが発生したかどうか、といった情報です。 特に Observe モードで記録した「もし遮断していたら弾いていたはずの量」は、Enforce へ昇格してよいかを判断するための一次データになります。また、レートリミッタが事前に遮断したものと、決済処理そのものが失敗したものとでは意味がまったく異なるので、ログには専用のタグを付けて、両者をはっきり区別できるようにしています。 おわりに 今回は、購入時のクレジットカード決済に流量制御を導入した話をご紹介しました。 やったことを一言でまとめると、 特定のアクセス集中ショップに購入が集中しても、その他のショップに影響が及ばないように、限られた決済の流量を能動的に制御することで受け止められるようにした 、ということになります。決済の流量を1つのグローバルカウンタで測って制御しつつ、アクセス集中ショップ用バケットによってアクセス集中ショップの流量に少しだけきつめの制限を設け、その他のショップの決済分を引き算で残す。そして3つのモードで安全に段階リリースし、万が一レートリミッタ自身が不調になってもfail-openで決済を止めない。 ひとつ補足しておきたいのは、これは「決済の処理能力がこれ以上伸ばせないから絞っている」という話ではない、ということです。実は、既存の実装を整理することで、レートリミッタを導入してもなお、単位時間あたりに捌ける決済の流量そのものはむしろ増えています。だからこそ今回、思い切ってアクセス集中ショップに制限を設けるという判断ができました。レートリミッタ導入前と比べても、アクセス集中ショップが単位時間あたりに購入できる流量はむしろ増えているのです。 そのうえで、限られたリソースのバランスを短期的に取るための手段として、レートリミッタによる制御を併用している、という位置づけです。一部のショップへのアクセス集中でシステム全体が不安定になれば、影響を受けるのはほかのショップだけでなく、アクセスが集中しているショップ自身も同じです。全体を安定して動かし続けることこそが、結果的にすべてのショップの購入機会を守ることにつながると考えています。 もちろん、ここで満足しているわけではありません。処理能力そのものの引き上げや、ほかの選択肢の検討も含めて、「どのショップでも、いつでも買いやすいカート」を目指して改善を続けていきます。 派手な機能ではありませんが、「どのショップでも、いつでも購入できる」という当たり前を裏側で支える仕組みとして、地味に効いてくれるものになったのではないかと思っています。今回のような改善はユーザーの目に直接見えるものではありませんが、快適な購入体験のために、Checkout Reliabilityチームではこういった細やかな改善を引き続き積み重ねていきます。 BASE では、「どのショップでも、いつでも購入できる」という当たり前を、こうした地道な設計と運用で支えていく仲間を募集しています。カートや決済まわりの信頼性に興味がある方は、ぜひお気軽に採用情報をご確認ください。 binc.jp

動画

書籍