TECH PLAY

dely株式会社

dely株式会社 の技術ブログ

236

こんにちは!クラシル株式会社の岩崎です。 この度、弊社が下記のテックカンファレンスで実施したXポスト数に応じた寄附企画において、2025年10月30日に一般社団法人Kids Code Club様へ寄附をさせて頂きましたことを報告いたします。 DroidKaigi 2025 参加レポート - Kurashiru Tech Blog iOSDC2025 参加レポート - Kurashiru Tech Blog クラシル株式会社(旧: dely株式会社) は Kaigi on Rails 2025 に Ruby スポンサーとして参加しました! - Kurashiru Tech Blog Kids Code Club様のHPはこちらです。 kidscodeclub.jp 寄附企画の概要 改めて企画趣旨の振り返りになりますが、我々がサービス提供しているクラシルリワードというプロダクトでは、「Every Step, Every Reward」という事業ミッションを掲げており、これは「あなたの一歩が、あなたの幸せに」という世界を目指しています。 この世界観をテックカンファレンスを通して体験できるコンテンツとして本企画が発案され、テックカンファレンス中の自分なりの一歩(Every Step)が技術コミュニティへの還元(Every Reward)に繋がる、Xを活用したキャンペーンを実施しました。 実際に投稿されたXポストは下記で確認することができます。 https://x.com/search?q=%23every_step_every_reward なお、「クラシルリワード」は2025年11月1日より「レシチャレ」に名称変更されます。これに伴い事業ミッションも変化することになりますが、最終的に目指す世界観については上記を引き継いでいます。 prtimes.jp 寄附企画の結果 結果的には、全カンファレンスを通して期間中298人の方が本企画にご参加頂き、323件のXポストを募ることができました! 当初は、1ポストにつき100円を寄附するという企画でしたが、少しでも感謝と還元を最大化したいという気持ちから、1ポストにつき500円で換算し最終的には 161,500円 の寄附額となりました。 寄附先の選定について さまざま検討は重ねましたが、特定の技術ドメインに偏ることなく将来のエンジニアに向けた貢献ができればと考え、子どもたちにIT技術の教育や機会提供を行っている団体を探しました。 そんななか、来年に新卒入社を予定しているインターン生からの紹介があり、Kids Code Club様を知ることができました。その縁も大切にしつつ、実際の活動内容や掲げるVision/Missionを拝見させて頂いたとき、我々が掲げる「BE THE SUN」や「世界を照らす発明を続ける」というVision/Missionと共通する価値観があり、まさに探していたところだと直感しました。 Kids Code Clubについて | 一般社団法人Kids Code Club キッズコードクラブ KCCジャーナル | 一般社団法人Kids Code Club キッズコードクラブ その後、Kids Code Club様とご連絡を取り、今回の企画趣旨にご賛同頂き寄附をさせて頂くに至りました。 おわり テックカンファレンスを通じて本企画に参加して頂いた皆さまに改めて感謝を申し上げるとともに、弊社は今後も「世界を照らす発明を続ける」というMissionを体現していくため、引き続きエンジニアを積極採用中です。 興味がある方は、ぜひカジュアルにお話させて頂ければと思います! www.notion.so
アバター
こんにちは! クラシル株式会社(旧: dely株式会社) のバックエンドエンジニア、yozo です。 Kaigi on Rails 2025 に Ruby スポンサー として参加し、ブース出展とスポンサーLTを行いました。 当日は CTO を含む 8 名 のエンジニアが参加。この記事では、ブース企画、登壇資料、アフターイベント告知、そして採用情報までまとめてご紹介します。 Ruby スポンサーとして協賛 今年も Kaigi on Rails を応援すべく、dely は Ruby スポンサー として協賛しブース出展を行いました! ブース出展 AIでコード企画 出題に対して一発プロンプトを入力し AI でコード生成(aidecode) 。用意した 6 件のテスト をどこまで通せるかを競う企画を実施しました! 一発プロンプトでコード生成するという都合上少しずつ改善することができなかったり、同じプロンプトでも実行ごとに生成されるコードが変わってしまい再現性が完全でなかったりとイベント上の制約があったにも関わらず多くの方に挑戦いただき、 全問正解 の猛者も登場しました 🎉 Xポスト数に応じた募金企画 今弊社で注力しているプロダクトのクラシルリワードでは、「Every Step, Every Reward」という事業ミッションを掲げており、ハッシュタグ付きで投稿していただくことで、1ポスト100円で弊社から寄付をするという企画も行なっていました! Kaigi on Rails 2025 中に皆様にポストしていただいたことで、「iOSDC 2025」「DroidKaigi 2025」と合わせて32,200円の寄付金額となりました👏 スポンサーLT 紹介 弊社バックエンドエンジニア id:rakutek が 「クラシルを支える技術と組織」 をテーマに登壇。 クラシルの技術的な取り組みやAI活用を中心に紹介しました! - 登壇スライド: https://speakerdeck.com/rakutek/kurasiruwozhi-eruji-shu-tozu-zhi アフターイベントのご案内:Agentic Coding 勉強会(クラシルでの実践編) Kaigi on Rails の流れで、社内実践を共有する勉強会を開催します。ぜひご参加ください。 タイトル: Agentic Coding 勉強会 〜クラシルでの実践編〜 日時: 2025-10-27(月) 19:00〜 会場: クラシル株式会社 msb 田町ステーションタワー N 23F 申込: https://bethesun.connpass.com/event/370495/ 最後に AIにちなんだブース企画は盛況で、AIでコーディングする企画してます!というだけで多くの方に関心を持っていただけました。 回答していただいたプロンプトもロール設定や出力形式設定がされており、イベント企画でも自然に出るほどプロンプトのプラクティスが浸透していたのも印象的でした! 次回はもう少し再現性を持たせ、効果的に工夫すればするほど点数が上がるようにしていきたいです。 今回はたくさんの方にブースへお立ち寄りいただき、特に大きな問題もなく 盛況のうちに終了 できました。 運営・登壇者・来場者のみなさま、本当にありがとうございました。 クラシルではエンジニアを募集しています エンジニア採用情報まとめはこちらから! delyjp.notion.site バックエンドエンジニア herp.careers シニアバックエンドエンジニア herp.careers SRE herp.careers 新卒エンジニアの採用も強化しています!特設サイトはこちら dely.jp
アバター
iOSDCに初めてスポンサー協賛&ブース出展しました! こんにちは! dely株式会社でiOSエンジニアをしている takky です! 弊社としては初めてのiOSDC協賛・ブース出展側として初参加となるiOSDCで、初めて尽くしの3日間でした。 忙しさの中にも多くの方々と交流することができ、とても有意義な時間を過ごせました。 運営の皆様、参加者の皆様、本当にお疲れ様でした。そして、delyのブースにお立ち寄りいただいた皆様、ありがとうございました! そんなiOSDCの参加レポートを投稿したいと思います! iOSDCとは iOSDC Japan 2025はiOS関連技術をコアのテーマとした、ソフトウェア技術者のためのカンファレンスです。 会場は有明セントラルタワーホール&カンファレンスですが、トークセッションについてはオンライン配信も予定しています。 開催期間は、9月19日(金)〜 9月21日(日)の3日間です。 iosdc.jp また、iOSDC開催週の前週に行われていたDroidKaigi2025にも同じくスポンサーとしてブース出展しましたので、DroidKaigiの参加レポートに関してはこちらを見ていただけると幸いです!🙌 tech.dely.jp iOSDCに初めてスポンサー協賛&ブース出展しました! iOSDCとは コンテンツについて ポストに応じて寄付する企画 運営担当者の感想 funzin tattsun kaikai takky kei dai sei さいごに コンテンツについて 弊社のスポンサーブースでは、DroidKaigiで出展していたものとほぼ同じ内容で、唯一違うところというと、AIでコードの実装をAndroid実装→iOS実装に変更したのみです! モバイルアプリ内ではできる限り運営のコストや準備コストを削減するために、同じ企画にしてiOSDCに臨みました! 出展したコンテンツ クラシルリワード事業のミッション「Every Step, Every Reward」を元にしたXポスト数に応じた募金企画 クラシルリワードアプリのレシート検知処理の体験企画 ClaudeCodeを用いた一発プロンプトでUI実装で点数を競う、AIでコード企画 ポストに応じて寄付する企画 DroidKaigiからの共通企画である、「クラシルリワード事業のミッション「Every Step, Every Reward」を元にしたXポスト数に応じた募金企画」はDroidKaigi・iOSDC・Kaigi On Railsの合同企画で、 iOSDC終了時点で、DroidKaigiとの合計金額は25,400円まで貯まりました!🙌 iOSDC期間中のみで、15,400円の寄付額となりました!皆様のご参加、本当にありがとうございました! 引き続きKaigi on Railsへいいバトンをつなぐことができたと思います! Kaigi on Railsへご参加される方も是非、こちらの企画にご参加ください! 運営担当者の感想 このセクションでは、iOSDCに参加したメンバーの感想などを記載しておきたいと思います! funzin 良かったブース newtのブース 旅行が好きなのでガチャの商品で割引クーポンがもらえるのは魅力的に感じた 結果はD賞のtype-cケーブルをもらったが実用的なものをもらえて大満足 よかったこと 金の盾やブース企画の合間に、多くの方とお話しできたこと 久しぶりにお会いする方の仕事の近況を聞けたこと 参加してくれた方々のプロンプトの内容に創意工夫が見られたこと 改善できそうなこと 企画をもりもりしすぎたので、もう少しシンプルな内容でもよかったかもしれない 企画をシンプルにして、興味を持ってくれた方とお話の時間を増やしていきたい delyのサービス 開発体制 AI活用事例など tattsun 良かったブース Pixiv のブースが面白かったです!クイズ系のブースは多くありましたが、アプリとして体験出来るのは唯一無二でした!(iOSDC中にアプデしててすごいw) 良かったこと dely のブースで色々な人と交流できたこと! 他社の方や以前から繋がりがある方と近況やAIについて意見交換出来たのが良かったです。インターンの方も来てて嬉しかった! 改善できそうなこと プロンプトを考えている間に話しかけると体験されている方も手が止まってしまい、会話と作業のタイミングが掴みづらいなと思いました。 今回の反省を活かしてこういう点も考えて体験設計できると良さそうです! 今回はブースメインの参加になっていましたが、次回は登壇したいのとLTの方も満喫出来るようにしたい! kaikai 良かったブース Honba 実際にプロダクトを経験できるブースがあり、どんなプロダクトを作る会社なのかイメージできた 転職した後、どんなものを作るのかイメージできるようになり、転職の際の基準になりそう iOSDC用に作ったアプリを利用したゲームをできた 以下のイメージができた カンファレンスのために時間を割ける業務環境 技術力があることをイメージできる よかったこと 各社のAI利用状況について、ある程度知ることができた いろんなところに散らばった前職の知り合いと会うことできて同窓会みたいで楽しかった 金盾めっちゃ自慢できた AIdeコードで人が少し滞留したが、人が集まっているように見えて人気のブースっぽく見えたので意外にメリットもあったのでは!? 来年できそうなこと, 改善できそうなこと 良くも悪くも、企画の方向性にトークが持っていかれてしまうので、来年はどこを採用ターゲットに置くかで企画の趣旨を変えてもいいかも 今回は、AI文脈とプロダクト文脈メインでの会話がほとんどだった AIdeコード → AI文脈 ポスト募金 → クラシルリワードのプロダクト文脈 takky 良かったブース Luup 普段利用させていただいている企業さんでどのような技術を利用しているか軽くお話しさせていただけたこと Luup社のノベルティステッカーが一番可愛かった よかったこと Xでフォローさせていただいていた、個人的に話してみたかった方と対面で会うことができ、気になっていたことの質問ができて良かったと思います! 他のスポンサーブースを回ることで技術的な取り組みや、弊社でも試せそうなことを聞くことができ、刺激になりました! 普段の業務とは少し異なる形で、チームで1つの何かをするということもいい経験になりました!👍 (写真は、iOSDCに向かう前の 決起集会の様子 です) 改善したいこと 今年は初出展で弊社ブースの運営で手一杯だったので、来年はオペレーションをアップデートさせて効率よくブースの運営をしてdelyを知ってもらえたり、興味を持ってもらえるような機会を作っていきたいと思います! kei 良かったブース ホンダのブース バイク一台をそのまま展示していて圧倒的なインパクトがあった 良かったこと 他社のエンジニアとの交流ができた AIについて各社の利用状況を知ることができた AIdeコードでみんなが真剣に取り組んでいる姿が素晴らしかった 改善できそうなこと 来年はもっとブースを回りたい 今年のような企画(AIdeコード等)により積極的に参加したい dai 良かったブース Pixiv クイズ形式のブースがいくつかありましたが、長文の問題を読んで解答するタイプは少し読むのが面倒に感じました。 その点、Pixivのブースはゲーム感覚でアプリ内でクイズを解いていく形式だったので、面白かったです。 良かったこと 他社のプロジェクトや開発体制について知れたこと 他社のエンジニアや元々の知り合いと交流できたこと 改善できそうなこと 3日間という長時間ブースに滞在することになるので、体力を温存するためにも、ブースにいる人数は2〜3人にとどめ、全員が座った状態で対応できる仕組みづくりができると良いと思いました。 (ブースの前で立ったまま呼び込みをしている係の人がかなり大変そうだった) 来年できそうなこと 登壇 sei 良かったブース ZOZO 単純にZOZOMATがもらえるの嬉しかった 良かったこと ブースの企画がかなり好評だったこと。友達何人かに聞いてもクラシルのやつ結構面白かったっと言ってくれていた リワードiosチームの皆さんに対面出会えたこと 色んな人とのつながりが増えたこと 改善できそうなこと AIに修正をしてもらう時間や、挑戦者がプロンプトを考える時間など、かなり拘束時間が長かったなあという印象 来年できそうなこと 登壇チャレンジしたい! さいごに DroidKaigiに続きブース運営でバタつきはしましたが色んな方にdelyやクラシル・クラシルリワードについて認知していただけるいい機会にもなったかなと思います! これからも積極的にカンファレンスでの出展を行っていければと思います! 次は、Kaigi on Railsでお会いしましょう! 💎Kaigi on Rails スポンサー&ブース出展💎 明日 9/26(金) から2日間開催される Kaigi on Rails 2025 に、ゴールドスポンサーとして参加させていただきます! 弊社のRailsエンジニアも参加しブース出展もしますので、ぜひお立ち寄りください🙌 https://t.co/pUHGq489Ox — dely Developers (@dely_developers) 2025年9月25日
アバター
DroidKaigiに初めてスポンサー協賛&ブース出展しました! こんにちは! dely株式会社で Androidエンジニア iOSエンジニアをしている takky です! 弊社としては初めてのDroidKaigi協賛・ブース出展、そして私自身にとっても初参加となるDroidKaigiで、初めて尽くしの3日間でした。 忙しさの中にも多くの方々と交流することができ、とても有意義な時間を過ごせました。 運営の皆様、参加者の皆様、本当にお疲れ様でした。そして、delyのブースにお立ち寄りいただいた皆様、ありがとうございました! そんなDroidKaigiの参加レポートを投稿したいと思います! DroidKaigiとは DroidKaigiはエンジニアが主役の Androidカンファレンスです Android技術情報の共有とコミュニケーションを目的に 2025年9月10日(水)〜12日(金)の3日間開催します。 2025.droidkaigi.jp DroidKaigiに初めてスポンサー協賛&ブース出展しました! DroidKaigiとは 会場の雰囲気 ブース出展に関して クラシルリワード事業のミッション「Every Step, Every Reward」を元にしたXポスト数に応じた募金企画について ClaudeCodeを用いた一発プロンプトでUI実装で点数を競う、AIでコード企画について クラシルリワードアプリのレシート検知処理の体験企画について ブース運営に関しての反省点 セッションについて さいごに 会場の雰囲気 最初はこのような感じで開放感が溢れるフロア一面で出展ブーストメインステージがあります。2日目はここで交流会もあり、マグロの解体ショーや他の参加者と交流できるようなブースになりました。 弊社のブースの位置を見つけたので、こちらに設営していきます! 運営のための準備には1時間半ほどかけて設営を済ませて以下のような形にセッティングを済ませて、初日の準備は完了しました! ブースに立ち寄ってくださった皆さんYouTubeの登録者数100万人の金の盾を見て写真を撮ってシェアしてくださったり、とにかくインパクトがありました! ブース出展に関して 続いてブース出展の内容に関してで、弊社は「Kaigi on Rails 2024」や「RubyKaigi 2025」にスポンサーとして参加しましたが、モバイルアプリ関連のカンファレンスへの出展は初めてでブース出展の企画から行い、以下の3つのコンテンツを用意しました! クラシルリワード事業のミッション「Every Step, Every Reward」を元にしたXポスト数に応じた募金企画 クラシルリワードアプリのレシート検知処理の体験企画 ClaudeCodeを用いた一発プロンプトでUI実装で点数を競う、AIでコード企画 ブース運営は、iOSメンバーから私とAndroidメンバー2人、内定者インターンとして働いてくれている2人の計5名で行いました! 内定者インターン生として参加してくれた、dachoさん & timさん 本当にありがとうございました! クラシルリワード事業のミッション「Every Step, Every Reward」を元にしたXポスト数に応じた募金企画について 今弊社で注力しているプロダクトのクラシルリワードでは、「Every Step, Every Reward」という事業ミッションを掲げており、DroidKaigi中に何か自分なりに一歩踏み出したことがあれば、その内容をハッシュタグ付きで投稿していただくことで、1ポスト100円で弊社から寄付をさせていただくという企画を行なっていました。 DroidKaigi中にブースに立ち寄っていただき、皆様にポストしていただいて弊社から寄付をさせていただく金額は10,000円となりました!👏 こちらは引き続き「iOSDC2025」や「Kaigi on Rails 2025」などのカンファレンス出展時にも同じ企画を開催させていただくので、参加される方はポストしていただけると嬉しいです! ClaudeCodeを用いた一発プロンプトでUI実装で点数を競う、AIでコード企画について 直近、生成AIを用いてコードを書くことが多くなってきており、弊社でも月額10万円のAIエージェント利用補助するための開発者向け新制度「AI First」を導入し、AIの利活用が進められています。 dely.jp そんな状況下で「ClaudeCodeにUI実装をお願いするプロンプトを入れて完成度に応じて採点するのはどうか?」というアイデアが出て、こちらの企画を行うことになりました! 内容としてもそのままで、以下の画像のbefore・afterでできる限り完成形に近づけていくというゲームでAIならではの不完全さがゲーム性を生み出して非常に盛り上がった企画になりました! 参加者の中には、30分以上プロンプトを考える方、before・afterの写真を撮ってその写真をChat-GPTに投げてプロンプトを作成している方さらには、プロンプトの結果びっくりするようなUIを作り上げた方がいて周りにいる方も一緒に楽しむことができました! クラシルリワードアプリのレシート検知処理の体験企画について 最後にプロダクト内で直近力を入れている、レシート読み取り機能を疑似体験できるようなデモ端末を用意してブース来訪してくださった方にクラシルリワードの技術力とユーザー体験を直に経験していただけるようなコンテンツを用意しました。 端末を手に取って機能を使っていただき、「クラシルリワード・クラシルではどのようなアーキテクチャを導入して実装しているのか?」や「JetpackComposeの利用状況はどのくらいか?」などの質問をはじめ、さまざまな技術的な質問や会話も生まれプロダクトへの理解を深めていただけるいい機会になったと思います! ブース運営に関しての反省点 上記3つの全てのコンテンツを少人数で運用するのはかなり労力がかかってしまったので、より効率的なブース運営ができるように設計し直さないといけないなと反省しました! (2日目のAIでCodeの難易度を下げたのはここだけの話です。) セッションについて 主にインターン生二人には、メインでセッションへの参加や初日のワークシップに積極的に参加してくれたので、後日テックブログとして参加したセッションでの学びを共有いただける予定なので、乞うご期待ください!👍 さいごに 弊社として初めてのモバイルアプリのスポンサーとブース出展だったのでバタつきはしましたが色んな方にdelyやクラシル・クラシルリワードについて認知していただけるいい機会にもなったかなと思います! これからも積極的にカンファレンスでの出展を行っていければと思います! 前述の通りではありますが、本日から開催されるiOSDCにもスポンサーブースを出すので、iOSDCに参加される方はぜひdelyのブースにお立ち寄りください! iOSチームメンバー全員でお待ちしております!🍎
アバター
iOSDC Japan 2025 & DroidKaigi 2025 に協賛&初ブース出展します! こんにちは、iOSエンジニアのkaikaiです! 今年はなんと… iOSDC Japan 2025 ではプラチナスポンサー、DroidKaigi 2025 ではゴールドスポンサーとして 協賛&初のブース出展 をします 🎉 普段は「クラシル」や「クラシルリワード」を開発している私たちですが、 今回はエンジニアコミュニティの一員として、 みなさんと直接交流できる場を作れることをとても楽しみにしています。 弊社のモバイルアプリ紹介🐰 delyは「 世界を照らす発明を続ける 」をミッションに、2つのアプリを展開しています。 クラシル 日本最大級のレシピ動画サービス。毎日の「なに作ろう?」をサポートします。 👉 サービス紹介はこちら クラシルリワード 日常の買い物をもっとお得で楽しくするアプリ。 👉 サービス紹介はこちら 詳しい機能や取り組みについては、上記のリンクからぜひご覧ください! ブースコンテンツ delyのブースでは、来場者のみなさんに楽しんでいただける 2種類のコンテンツ を準備しています 🎉 どちらも気軽に参加できる内容なので、ぜひ立ち寄って体験してみてください! Every Step, Every Reward カンファレンスでの “ちょっとした一歩” をシェアするだけで参加できる企画です。 セッションを聞いた、ブースを訪れた、誰かに挨拶した──そんな小さなアクションを「#Every Step, Every Reward」を付けてポストすると、コミュニティへの寄付やノベルティに変わります。 ちょっとした参加でも楽しめるので、ぜひ気軽にご参加ください。 AIでCode AIを駆使し、“開発チャレンジ”に挑む企画です! あなたの“AIへの指示力”が試される! 制限時間の中でAIに指示を出し、参考UIの完成度を競うチャレンジ企画です。 どこまで理想に近づけられるかは、あなたの指示次第。 高い完成度を達成した方には、特別な景品が待っているかも…!? ぜひ普段の業務で磨いているAIスキルを活用してみてください!! ノベルティはこちら! 以下のノベルティ以外にも、魅力的なノベルティをまだまだご用意しています! どちらも「遊びながら挑戦できる」ような内容になっています。 ぜひ当日はブースに立ち寄って、私たちと交流しましょう 🚀 最後に delyでは、一緒に「世界を照らす発明を続ける」仲間を募集しています! イベントを通じて少しでも興味を持っていただけた方は、ぜひ気軽にチェックしてみてください。 採用情報 iOS / Android エンジニアをはじめ、プロダクトを支えるさまざまなポジションで採用を行っています。 👉 dely 採用ページ 開発者向け新制度「AI First」 私たちは開発者の成長と挑戦を全力でサポートしています。 2025年7月からは新制度 「AI First」 を導入しました。 エンジニア・デザイナー・PdMを対象に、Claude Code、Devin、CursorなどのAIエージェント利用料を 月額最大10万円まで補助 。 最先端のAIを自由に活用し、イノベーションを加速できる環境を整えています。 テックブログ delyのエンジニアチームでは、技術や開発文化について積極的に発信しています。 👉 dely Tech Blog イベント当日、みなさんにお会いできるのを楽しみにしています 🎉
アバター
先日、愛媛県松山市で開催されたRubyの国際カンファレンス「RubyKaigi 2025」に弊社バックエンドエンジニアが参加してまいりました。 本記事では、RubyKaigiの概要から、delyのブース出展の様子、注目したセッション、そして懇親会での交流まで、現地の熱気を余すことなくレポートします! rubykaigi.org みんなで集合写真 RubyKaigiとは RubyKaigiは、プログラミング言語Rubyに関する世界的な技術カンファレンスです。Ruby言語処理系の深い部分にまで踏み込んだ議論が交わされるのが特徴で、Rubyを徹底的に理解したいエンジニアにとってはまたとない機会となります。 Rubyの生みの親である、まつもとゆきひろ(Matz)氏をはじめ、国内外の著名なRuby開発者が集結し、最新の技術動向や今後の展望について熱い議論が繰り広げられます。 また、多くの企業がスポンサーとしてRubyKaigiを支えており、各社のブースでは、技術紹介やユニークな催し、魅力的なノベルティグッズの配布などが行われ、参加者同士の交流を深める場ともなっています。今年は地方都市である松山での開催ということもあり、地域ならではの魅力を感じることもできました。 参加の目的 弊社がRubyKaigi 2025に参加した目的は、大きく分けて以下の4点です。 1. テックカンパニーとしての認知向上 バックエンド開発にRuby on Railsを全面的に採用しているdelyとして、Rubyistが集う企業であることを強くアピールし、技術力を重視する企業としての認知度を高めます。 技術への投資姿勢を示すことで、エンジニアからの信頼感を醸成し、将来的な採用活動につなげるためのカジュアルな接点を作ることを目指します。 2. サービスやブランドの認知向上 「 クラシル 」や「 クラシルリワード 」といった自社サービスを、Rubyコミュニティに向けて広くアピールします。 ブースという直接的なコミュニケーションの場を通じて、delyという会社の良さや、サービスの魅力を効果的に伝えます。 3. 最新技術動向の情報収集 Rubyに関する最先端の技術トレンドを把握し、今後のサービス開発に活かすための知見を得ます。 他社のエンジニアとの交流を通じて、現場のリアルな声を聞き、自社サービスの現状を客観的に評価する機会とします。 4. エンジニアとの交流 他のIT企業のエンジニアと直接会話し、情報交換や意見交換を行うことで、Ruby業界におけるネットワークを構築します。 自社のブースについて delyのブースでは、多くの方に親しまれているレシピ動画サービス「クラシル」にちなみ、「旬の食材クイズ」をご用意しました。 旬の食材クイズ 難易度の高いクイズでしたが、多くの方に挑戦していただき、「面白い!」「意外と難しい!」といったポジティブな感想をいただくことができました。 参加者の方々が楽しみながら、旬の食材について知識を深める良い機会になったのではないかと思います。 多くの正解を獲得した方にはコースターやキャンドルをプレゼントしました。 アンケートに答えて旬の食材クイズにチャレンジ! YouTube 金の盾の展示 「クラシル」ではYouTube公式チャンネルを運営しております。 youtube.com ブースにはYouTube登録者数が100万人を突破した際に贈られる 「 金の盾 」 を展示しました。 「本物を初めて見た!人気のYouTuberがもらっているやつだよね」 「写真撮っても良いですか?触ってもいいですか?」 など、非常に多くの反響があり、多くの方に興味を持っていただけたようです。 ザ・存在感 AI関連技術やサービス概要の紹介 ブースのモニターでは、delyにおけるAIの取り組みや、提供しているサービスの概要を紹介しました。 特にAIに関する質問が多く 、参加者の皆様の関心の高さを肌で感じることができました。 開発チーム内で使っているAIツール 他社のブースについて 他のスポンサー企業のブースも非常に興味深く、各社が工夫を凝らした展示や企画を行っていました。 魅力的な企画の数々 SNSフォローでオリジナルグッズをプレゼント ボードにシールを貼って簡単なアンケートに答えるとグッズをプレゼント Rubyに関する知識を競うプログラミングクイズ 会場全体を使った宝探しゲーム 豊富なノベルティ 実用的なトートバッグやエコバッグ 持ち運びやすいキーホルダー コレクションしたくなる缶バッジやステッカー 自社ロゴ入りのお菓子 各社のブース運営から、参加者を楽しませ、自社の技術やサービスを効果的にアピールするための様々なアイデアを学ぶことができました。 Take Freeスペースには多くのノベルティが並んでいました セッション イベント中は多くのRubyistが登壇し、技術的な発表を行っていました。 今回、私が個人的に見たセッションを3つご紹介します。 Automatically generating types by running tests speakerdeck.com RBS::Trace というgemを用いて、テスト実行時に動的に関数の引数と戻り値の型情報を収集し、RBS型ファイルを自動生成するという内容でした。 現在delyでは、まだ積極的に型導入が進められていない状況ですが、IDEの補完精度の上昇などのメリットがあり、個人的には型推進派なので積極的に取り入れていきたいと思いました。 弊社ではRSpecテストを普段から書いているため、テストを実行するだけで型定義ファイルが作成されるというのは非常に魅力的です。 仮にこの仕組みで型情報をメンテナンスしていく場合、テストの品質をしっかりと担保する必要があるという点は留意しておきたいです。 Speeding up Class#new こちらはスライドを見つけられませんでした... 代わりにRubyKaigiのページを貼り付けておきます。 rubykaigi.org Rubyのインスタンス初期化( Class#new )の高速化に関するセッションでした。 new の引数を処理する際に、内部的にRubyからC言語のコードが呼び出され、さらにそこからRubyのコードが呼ばれるため、処理が遅くなっているという説明がありました。 inline cacheなどの技術を用いて速度改善を図るという話だったと理解していますが、低レイヤーな内容で完全に理解するには至りませんでした。 rb_class_alloc や rb_call といったキーワードが出てきた記憶があります。 もしこのセッションに関する参考文献などがあれば、ぜひ教えていただきたいです。 Analyzing Ruby Code in IRB drive.google.com irbのメンテナの方によるセッションでした。 Rubyistたちが普段何気なく利用しているirbですが、シンタックスハイライトやインデント機能などがありハイテクです。これらの機能がどのように実現されているのかという、irbの内部的な動きについて解説されていました。 コードの解析には Ripper::Lexer がこれらの処理に使われているとのことでしたが、これを Prism に置き換えたら… という話もあり、非常に興味深い内容でした。普段使っているツールの裏側の仕組みを知る良い機会になりました。 DrinkUpでの交流 RubyKaigi期間中には、多くのスポンサー企業が主催するDrinkUp(懇親会)が開催され、私もそのうちの一つに参加してきました。 どのようなDrinkUpイベントがあったかは公式サイトの Event ページにもまとめられています。 美味しい食事とお酒を堪能しながらのエンジニアとのコミュニケーションの場ということで、RubyKaigiの本編よりも、より踏み込んだ現場の話や、業界の動向について意見交換をすることができ、非常に有意義な時間となりました。やはりここでもAI時代にITエンジニアたちはどう戦っていくのかと言うような話題が上がっていました。 本編に負けないくらいDrinkUpの運営に注力しているという企業もいて、次回以降弊社でもRubyKaigiにどのような形で参画していくかを考える機会にもなりました。 豪勢な食事 最後に 今回のRubyKaigi 2025への参加を通して、最新のRuby技術に触れるだけでなく、多くのエンジニアの方々と交流することができ、非常に貴重な経験となりました。delyとしても、今回の経験を活かし、更なる技術力の向上とコミュニティへの貢献を目指していきたいと思います。 引き続き、delyのテックブログでは、技術に関する様々な情報を発信していきますので、ぜひご期待ください!
アバター
こんにちは。Androidエンジニアのkenzoです。 今回はAndroidチームでの取り組みや、その周辺環境の特徴をいくつかご紹介いたします。 ありがたいことに、クラシルリワードはローンチから2年程で大きなプロダクトに成長してきました。 www.advertimes.com それでもまだまだユーザーの皆様に早く届けたい機能や改善点があり、高速で開発サイクルを回していく必要があります。 また、より良いUXを目指し、アプリを意図通りに動作させることも大切です。 さらに、長期的に開発を続けるため、これらの取り組みを継続できるようチームで取り組んでいます。 速度・品質・継続の3つを達成するために、クラシルリワードのAndroidチームが取り組んでいることや環境をご紹介します。 クラシルリワードAndroidの開発環境 まず簡単に開発まわりの環境や特徴をご説明します。 フレームワーク・アーキテクチャ Full Jetpack Compose Google推奨のアーキテクチャ + Clean Architecture 開発を始めてから日が浅いため、ほぼ全ての画面をJetpack Composeで作成できています。 アーキテクチャに関してはAndroid Developersにあるガイドラインに沿った作りに加えてClean Architectureの概念も取り入れています。 developer.android.com プロダクトの特徴 位置情報・歩数機能や地図等の特徴的な機能を含むプロダクトを開発しています。位置情報や歩数を計測するためにバックグラウンド処理を制御したり、地図に独自のUIを表示させるために工夫したりしています。 また、リワード広告をはじめ、広告のSDKを組み込んだ開発も行っています。動画広告はメモリ消費量が多いため、その制御にも細心の注意を払っています。 開発体制 こちらの記事にあるようなスピード感のある開発体制となっています。 tech.dely.jp 速度を上げるための取り組み テスト・リリースの自動化 Github Actionsを用いて、PR作成時のテストやリリース作業を自動化しています。 チームでは週に複数回リリースを行うこともありますが、コマンドの実行のみでQAを始められるため、開発者の工数をかけずに安全にリリースを進めることができています。 小規模なブランチを素早くマージ ブランチを小さな単位で作成し、できるだけ早くマージすることで、コンフリクトの防止やレビューの効率化を図っています。 大規模な機能を開発する場合でも、レイヤーやUIのコンポーネント単位でブランチを分けることで、特定の箇所に集中してレビューできるため問題の発見が容易になり、レビュー着手への心理的負担を軽減することにもつながっています。 また、フラグを用いて開発途中の機能の公開を制御し、準備が整ったらフラグを外してリリース、といった方法も採用しています。 直近100件のPRの追加差分(insertions)を調べたところ、平均71.41行、中央値38.5行でした。全面的にJetpack Composeを使用できていることや整ったアーキテクチャで開発できていることも、ブランチを小さく保つことに寄与していると感じています。 AIを使用した開発の高速化 Copilot, ChatGPT等のAIを活用して開発の効率化をしています。 コードの生成、セルフレビュー、ドキュメント読解の手助け等、様々な場面で利用しています。 品質を高めるための取り組み E2Eテスト自動化 MagicPodを使用してE2Eテストを実施してアプリの基本的な動作を担保しています。 これによって改善や機能追加による既存機能への影響を検知して修正することができています。 tech.dely.jp 不具合や警告の通知 上記のようなテストの失敗やLint/Detekt等の警告に加え、その他、アプリの周辺で何らかの異常が発生したと思われる場合にSlackにアラートが送られるようになっています。 例えば、RemoteConfig等のアプリ外から設定値を送る仕組みからアプリに送られた値が仕様と異なっていた場合にはエラーとしてアラートが飛び、すぐに修正することができています。 気軽にQAを依頼できる環境 リリース前はもちろん、開発中においても動作検証を依頼する仕組みがあり、自身の作成した機能をメンバーに触ってもらうことができます。 コード生成 APIの仕様やイベントログの定義からコードを生成する仕組みを取り入れることで、手作業によるミスを減らし、その分のコードレビューの工数も削減できています。 生成するためのコードも同じリポジトリ内にあり、仕様が変わった場合にもプロダクトコードと同様に誰でも修正することができます。 継続的な開発を実現するための取り組み 将来を見据えた開発 長期的に開発を続けることを前提として開発を進めています。 コードの書き方や、レビュー観点、ドキュメント等それぞれにおいて長期的な視点を取り入れています。 レビューでは、仕様通りに動作しても長期の開発の妨げになり得る場合には指摘が入ります。 また、早めに作成しておけば様々な場面での使用が想定でき、長期的に効いてくるような改善も積極的に行っています。 仕様が複雑だったり特殊な実装でコードから容易に意図が読み取れない場合には、将来の開発者が背景を理解できるよう必ずコメントやドキュメントを残すようにしています。 最近では、技術的な意思決定を遡れるようADR(Architecture Decision Record)を残す取り組みも始めました。 エンジニアも仕様作成に関わる 各チームにおいてエンジニアが仕様作成段階から参加することで、ビジネス的な観点から達成したいことと、技術的な観点から将来的な負債や開発工数とのバランスをとった仕様を作成することができています。 これにより、ミニマムに価値のある機能を最短でリリースすることが可能になっています。 おわりに クラシルリワードにおけるAndroidチームの取り組みをご紹介いたしました。 分類してみたものの、複数の項目に関連している取り組みもありましたが、それぞれが良いプロダクトの開発に貢献していると感じています。 delyでは一緒にプロダクトを成長させてくれるAndroidエンジニアを募集しています。 本記事を読んで少しでも興味をお持ちいただいた方はぜひこちらからご応募ください。 www.wantedly.com
アバター
こんにちは。バックエンドエンジニアの松嶋です。 2024年10月25日から10月26日の2日間、Kaigi on Rails 2024が開催され、弊社はRubyスポンサーとしてスポンサーLTとブース出展の機会をいただきました。 初めてのブース出展でもあり、不慣れな部分もあったかもしれませんが、このような貴重な機会をいただけて、たくさんの方々が私たちのブースに遊びにきてくださったこと、とても感謝しています。 ブース出展の内容については、同じくバックエンドエンジニアの id:rakutek がブログを書いているので、こちらを御覧ください。 tech.dely.jp 私は、初日の25日にスポンサー枠で「クラシルの現在とこれから」というタイトルで発表させていただきました。500人以上の多くの方々の前で話すことは初めてだったのでとても緊張していましたが、無事何事もなく終えられたので安堵しました。登壇後には感想や質問もいただき、とても嬉しかったです。 今回の発表では、次の3つのテーマについてお話ししました: クラシルの規模感 クラシルのアーキテクチャやその背景 最近の技術的な取り組み これらのテーマを選んだ理由は、クラシルというサービスは広く知られているものの、実際にその裏側を支えるアプリケーションやシステムについては積極的に発信してこなかったため、あまり知られていないのではないかという思いがあったからです。 そこで、サービス開始から9年目を迎えたクラシルが現在どうなっているのか、歴史的背景を踏まえながら、参加者の皆さんに知ってもらうことを目的としました。 本ブログは、発表時間の都合上伝えきれなかった部分も補足しながら、改めてクラシルの「現在」について紹介させてください。 speakerdeck.com rails stats 発表ではspecの部分を省略していましたが、こちらが rails stats の全貌です。 総コード行数は、約41万の大規模なアプリケーションです。 Chirashi という接頭語のディレクトリがありますが、それらは Rails Engine が活用されている部分で、一部モジュール化しています。 Chirashi というのは、クラシルとは別にクラシルチラシというサービスを運営しており、それらはクラシルに強く依存していること、クラシルほど機能を多く必要としなかったことから、 Rails Engine で実装されています。 また、3年前の rails stats 結果が残っていたので比較してみると、約2.4倍に増加していることから、活発に開発が行われてきたことが分かるかと思います。 クラシルはネイティブアプリのイメージが強いかと思いますが、実はWeb版も存在しており、 Vue.js が使われています。そのため、 JavaScript のコード量が多くなっています。 ただ、Web版のクラシルは負債が結構溜まってきているため、現在リアーキテクチャを進めています。最終的には、Rails依存している部分をなくし、 Node.js + React で構成される状態を目指しています。こちらの詳細については、弊社のフロントエンドエンジニアがブログ化する予定なので、楽しみにしていてください。 +----------------------+--------+--------+---------+---------+-----+-------+ | Name | Lines | LOC | Classes | Methods | M/C | LOC/M | +----------------------+--------+--------+---------+---------+-----+-------+ | Controllers | 19799 | 16071 | 402 | 1607 | 3 | 8 | | Helpers | 623 | 489 | 1 | 78 | 78 | 4 | | Jobs | 2191 | 1932 | 39 | 108 | 2 | 15 | | Models | 26344 | 16450 | 353 | 1232 | 3 | 11 | | Mailers | 139 | 111 | 8 | 12 | 1 | 7 | | View | 1050 | 1004 | 0 | 0 | 0 | 0 | | Stylesheets | 27373 | 24170 | 0 | 0 | 0 | 0 | | JavaScript | 122266 | 109419 | 0 | 1 | 0 | 109417 | | Libraries | 14096 | 11500 | 194 | 727 | 3 | 13 | | Applibraries | 8043 | 6504 | 160 | 673 | 4 | 7 | | Batches | 3338 | 2767 | 53 | 229 | 4 | 10 | | Forms | 2478 | 2035 | 26 | 241 | 9 | 6 | | Services | 11143 | 9062 | 343 | 876 | 2 | 8 | | Serializers | 6452 | 5116 | 154 | 744 | 4 | 4 | | Uploaders | 1456 | 1163 | 45 | 141 | 3 | 6 | | Chirashi Controllers | 1734 | 1459 | 50 | 185 | 3 | 5 | | Chirashi Helpers | 337 | 266 | 1 | 58 | 58 | 2 | | Chirashi Mailers | 57 | 38 | 5 | 3 | 0 | 10 | | Chirashi Views | 58 | 50 | 0 | 0 | 0 | 0 | | Chirashi Stylesheets | 10185 | 8305 | 0 | 0 | 0 | 0 | | Chirashi JavaScript | 5057 | 4630 | 0 | 0 | 0 | 0 | | Chirashi Libraries | 66 | 57 | 4 | 5 | 1 | 9 | | Chirashi Forms | 274 | 221 | 5 | 29 | 5 | 5 | | Chirashi Services | 1738 | 1423 | 50 | 141 | 2 | 8 | | Chirashi Serializers | 612 | 521 | 23 | 53 | 2 | 7 | | Batch specs | 4509 | 3918 | 0 | 0 | 0 | 0 | | Controller specs | 6967 | 5873 | 0 | 7 | 0 | 837 | | Form specs | 4642 | 4143 | 1 | 0 | 0 | 0 | | Helper specs | 973 | 737 | 0 | 3 | 0 | 243 | | Job specs | 2782 | 2404 | 0 | 0 | 0 | 0 | | Library specs | 8944 | 7500 | 0 | 4 | 0 | 1873 | | Applibrary specs | 12988 | 11073 | 1 | 1 | 1 | 11071 | | Mailer specs | 438 | 397 | 1 | 1 | 1 | 395 | | Model specs | 24597 | 19720 | 7 | 7 | 1 | 2815 | | Request specs | 66331 | 54195 | 18 | 30 | 1 | 1804 | | Serializer specs | 3936 | 3269 | 6 | 2 | 0 | 1632 | | Service specs | 5090 | 4320 | 0 | 0 | 0 | 0 | | Chirashi Controller specs | 1460 | 1204 | 0 | 0 | 0 | 0 | | Chirashi Helper specs | 57 | 50 | 0 | 0 | 0 | 0 | | Chirashi Mailer specs | 200 | 176 | 0 | 0 | 0 | 0 | | Chirashi Library specs | 21 | 19 | 0 | 0 | 0 | 0 | | Chirashi Form specs | 312 | 274 | 0 | 0 | 0 | 0 | | Chirashi Service specs | 1300 | 1140 | 0 | 0 | 0 | 0 | | Chirashi Serializer specs | 38 | 32 | 0 | 0 | 0 | 0 | +----------------------+--------+--------+---------+---------+-----+-------+ | Total | 412494 | 345207 | 1950 | 7198 | 3 | 45 | +----------------------+--------+--------+---------+---------+-----+-------+ Code LOC: 224763 Test LOC: 120444 Code to Test Ratio: 1:0.5 クラシルを支えるアーキテクチャ クラシルはAWS上で構築されており、ID認証基盤とクラシル本体で分かれています。アカウント間はTransit Gateway経由で通信しています。 また、アプリケーションはECS Fargate上で動いています。 現在は、マネージドサービスを中心に利用していますが、以前は自前運用していたものが多く存在していました。しかし、それらが属人化したり、ブラックボックス化したことで、障害の温床になっていたため、ここ数年でマネージドサービスに移行しました。 LTでは詳しい説明を割愛しましたが、どのようなものを自前運用からマネージドサービスに移行したのか、代表的なものを4つ挙げて紹介したいと思います。 1. 動画変換基盤 以前、弊社の動画変換基盤は、ffmpegを用いた自社基盤(AWS Batch)が使われていました。 自社基盤の動画変換フローは以下のようなものでした。 S3に動画ファイルがアップロードされるとSNSに通知が飛び、Lambdaがフックされます。LambdaからAWS Batchのジョブが作成され、ECS上で動画変換が実行された後、再びS3に変換後のファイルがアップロードされます。 また、自社基盤はモニタリングが十分に行われておらず、対応できる動画フォーマットも限られているという課題もありました。これらを改善しながら保守していく選択肢もありましたが、非機能要件の面でMediaConvertの方が変換速度・拡張性・保守性の観点で優れていると判断し、移行しました。 現在は、動画が投稿されるとSQSにジョブがエンキューされ、shoryuken (ECS Fargate) によりMediaConvertのジョブが作成されて動画変換が行われています。 2. 検索基盤 以前はElasticsearchをEC2上にセルフホストして検索基盤を運用していたのですが、TV放送等で負荷が高まる場合は、事前にスケールアウトする設定を手動で行う必要がありました。例えば、Elasticsearchが動いているEC2にログインし、インデックスのレプリカ数の変更やシャードの配置確認等をコマンドラインで行っていました。検索機能はユーザーによく使われる機能の1つであり、手順を間違えて障害を起こしてしまうと影響が大きいため、かなり神経をすり減らす作業でもありました。 そのような背景から、Elastic Cloudを採択しました。Elastic Cloudの良い点は、運用負荷が軽減される以外にも、Elastic社の日本法人によるサポートが手厚かったり、最新バージョンへの追従が容易であるところです。 また、自前運用していた時より、レイテンシの悪化が懸念されましたが、Elastic CloudはAWS上で動かすことができ、Traffic Filterを使うことでプライベート接続が可能なため、移行後も安定しています。 3. バッチ処理 以前は専用のバッチ処理サーバー(EC2)でRailsアプリケーションが稼働し、wheneverでcron jobを管理していました。 しかし、データ量の増加に伴い、バッチ処理の需要が増加してきた一方で、サーバーの冗長化が難しく、単一障害点となっていました。また、処理が重いバッチが同時刻に実行されると、サーバーのCPUやメモリが逼迫し、他のバッチ処理に影響が出ることもあったため、開発者が実行タイミングを慎重に調整する必要がありました。 これらの課題を解決するために、EventbridgeとStep Functionsを組み合わせて、ECS run taskをスケジュール実行する方式に移行しました。また、Eventbridgeは at-least one 配信であるためバッチ処理の冪等性を担保する必要がありましたが、Eventbridgeのペイロードにコンテキスト属性である <aws.scheduler.execution-id> を渡すことで *1 、これを実現しています。 4. DBマイグレーション 以前のデプロイフローは、バッチサーバー上にマイグレーションコマンドが書かれたデプロイスクリプトが置いてあり、マイグレーションが必要な場合は先にバッチサーバーへデプロイすることで、DBのスキーマ変更に対応していました。 しかし、デプロイの順番を間違えたり忘れたりしてしまうと障害につながってしまうため、安全性が低いという課題がありました。 まずは、全てのデプロイパイプラインにマイグレーション工程を組み込み、どのデプロイでもマイグレーションが確実に実行されるようにしました。しかし、この方法では安全性は高まったものの、デプロイ時間が長くなり、開発生産性が低下しました。 本来であれば、スキーマ変更が必要な場合のみマイグレーションが実行されるべきなので、最終的にはGithub Actionsでリリースブランチとのdiffを取り、マイグレーションファイルが追加された時だけECS run taskでマイグレーションを実行するデプロイフローになりました。 その結果、安全性と開発生産性の両立が実現し、SREと開発者の両者の負担が軽減されました。 最近の技術的な取り組みについて 最後に、最近の技術的な取り組みの1つとして、AWS Bedrockを用いてUGCコンテンツの品質管理の仕組みを構築した話をしました。 クラシルは、2年前にブランドリニューアルを行い、ユーザーもレシピ投稿可能になりました。 これにより、ユーザーの多様な食の好みや要望に応えることが可能となりましたが、「品質管理」と「データ構造」の2つの大きな課題が浮き彫りとなりました。 一般投稿によりレシピの幅が広がる一方で、レシピではないものや、情報不足のコンテンツが投稿されることがあり、品質管理が難しくなっていました。 また、フリーフォーマットで投稿されるため、データが構造化されておらず、レシピの特徴を把握しづらいという問題もありました。 そこで私たちは、AWSのBedrock API(Anthropic社のClaude3 sonnetモデル)をRailsアプリケーションに組み込み、レシピ基準を満たしているかを自動判定し、レシピデータを構造化する仕組みを構築しました。 多くのLLMが存在する中でClaude3 sonnetを採用したのは、以下の3点です。 コスト・精度・パフォーマンス の観点で比較検証した結果、私たちの要件を最も満たしていたこと Bedrockを介してinvoke model APIを実行できるため、AWSのIAMによる細かな権限管理が可能であること マルチリージョンでリクエストを分散し可用性を担保できたり、AWS SDKに組み込まれているリトライ機構を利用することで自前実装やライブラリ導入が不要で、4xx or 5xxエラーは自動リトライ可能だったりと *2 、AWSの機能を最大限活用することで可用性を維持しながら素早く開発できること また、レシピ判定とデータ構造化の工程を1つにまとめず、別々のタスクに分割し、個別のLLMで処理する設計としました。これは、1つにまとめると指示が冗長になり精度が低下してしまうためです。 リクエストが2回に増えるためコスト増が懸念されましたが、レシピ判定に必要なテキストデータから余分な文字列(ハッシュタグやURLなど)を取り除き、消費トークン数を抑えることで、コストは許容範囲内に収まっています。 上記のような弊社の取り組みは、AWSの事例として取り上げていただきました。 AWSAIDay 最後に 改めてになりますが、Kaigi on Rails 2024という貴重な場でLTの機会をいただきまして、本当にありがとうございました。 弊社は、今後も積極的に開発者コミュニティに貢献していく予定です。 delyは開発者コミュニティに貢献し、今後更に強い開発チームを作りたいと考え、開発系のイベントに積極的にスポンサーになっていきます。delyチームにどんどんお声がけください🙌 delyはKaigi on Rails 2024にRubyスポンサーとして参加しました! - dely Tech Blog https://t.co/rm3Zp3tHVx — Yusuke Horie/dely (@yusuke_horie) 2024年10月28日 また、バックエンドエンジニアを含む各種エンジニアを大募集しています。 エンジニア採用情報まとめはこちらから! delyjp.notion.site バックエンドエンジニア herp.careers シニアバックエンドエンジニア herp.careers SRE herp.careers 26年度の新卒エンジニア用特設サイトはこちら dely.jp *1 : https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html *2 : リトライ回数は調整可能: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/BedrockRuntime/Client.html
アバター
こんにちは! dely株式会社のバックエンドエンジニアの  id:rakutek です dely株式会社はKaigi on Rails 2024にてRubyスポンサーとしてブース出展、スポンサーLTの登壇をさせていただきました。delyからは2日間でCTOを含む7名のエンジニアが参加しました。 rubyスポンサーとして協賛しました delyではKaigi on Rails 2024でRubyスポンサーとして協賛しました。delyではKaigi on Railsにスポンサーするのは今年度が初でした prtimes.jp https://dely.jp/news/update/pb3_k3eemn5g delyブースではクイズ正解でノベルティをプレゼントする企画を用意しました スポンサーブースでは食材の旬を当てるクイズアプリをiosで用意して、正解数に応じてクラシルのノベルティを獲得することができる企画を用意しました! #kaigionrails に delyブース出展してます! クラシルグッズが景品で貰えるのでぜひお越しください!🍎 https://t.co/SJAxjTjiVA pic.twitter.com/ByMZZGsz8h — Masato Otake / dely CTO (@masatootake) 2024年10月25日 9問全問正解数するのはかなり難しいのですが、見事に全問正解された方もいました 🎉 全問正解した様子 オフィスから持ってきたクラシルYoutubeチャンネルの金の盾も好評で、多くの方が金の盾と写真撮影したりブースに立ち寄っていただけました。 クイズの景品として用意したノベルティはマグカップ・moft、水筒、キャンドル、ピンバッチ、ステッカーなどで多くの種類を用意していたのも好評でした! 登壇者の紹介 delyからはクラシルのバックエンドエンジニアの id:akngo22 が 「クラシルの現在とこれから」というアツいタイトルでスポンサーLTとして登壇しました。 今年で9年目を迎えるクラシルの開発のダイジェスト、最近のAWS Bedrockを活用した技術的な取り組みなどについてお話ししました。 speakerdeck.com 最後に・・ 今回初のブース出展ということで、トラブルもありましたが、無事に終わり大成功でした! ブースに立ち寄ってくださったみなさま・運営のみなさまありがとうございました。 また来年も機会があればスポンサーとして参加できればと思います! delyではエンジニアを募集しています エンジニア採用情報まとめはこちらから! delyjp.notion.site バックエンドエンジニア herp.careers シニアバックエンドエンジニア herp.careers SRE herp.careers 26年度の新卒エンジニアの採用も強化しています!特設サイトはこちら dely.jp
アバター
始めまして!!クラシルリワードでiOSエンジニアをしているkaikaiと申します! 今回は、業務の時間を使って、8/23、8/24に開催された iOSDC 2024に 弊社のiOSエンジニア複数人でオフラインで参加してきました!(delyでは必要に応じて平日開催されるイベントへの参加もできます!) 今年の開催でも多くのセッションがあり、クラシルリワードのこれからの開発に役立ちそうなもの、もっとより良いものを作っていくために大事な学びがあったので、参加レポートという形でシェアできればと思います! セッション Day0 StoreKit 2によるモダンなアプリ内課金 新OSの機能を古いOSにバックポートする Day1 StoreKit 2によるモダンなアプリ内課金 複雑さに立ち向かうためのソフトウェア開発入門 座談会 「Strict ConcurrencyとSwift 6が開く新時代: 私たちはどう生きるか?」 複雑さに立ち向かうためのソフトウェア開発入門 Mergeable Libraryで高速なアプリ起動を実現しよう! Appleの新しいプライバシー要件対応:ノーコード アプリプラットフォームの実践事例 楽しく簡単に!QRコードの読み取り機能を実装しよう メインスレッドをブロックさせないためのSwift Concurrencyクイズ 開発を加速する共有Swift Package実践 iOSアプリらしさを紐解く 詳解UIWindow Day2 クロスプラットフォーム普及増加。SwiftでiOS開発はもうやらないのか....? 医療アプリ開発の最前線 - 安全性と生産性の両立への挑戦 さいごに セッション ここからは、参加した各セッションについての内容は、参加メンバーの感想を書いていきます! ※ 以下、使用している画像は登壇者様の登壇資料より引用させて頂いています。 Day0 StoreKit 2によるモダンなアプリ内課金 登壇者 : 蔀さん 資料 : StoreKit 2によるモダンなアプリ内課金 - Speaker Deck 概要 このセッションではAppStoreの課金から StoreKit V1, V2 の違いまでiOSアプリで課金を行うためにはどういったルール・方法があるのか知ることができました。特に外部決済でも手数料がかかることや、StoreKit2を導入する際の失敗談について言及されている点がとても良く、弊社でも移行時には参考にさせていただきたいと思いました! ポイント Appleは最近EUでの流れもあり外部決済が許可されつつあります。 しかし外部決済を利用する場合でも27%の手数料がかかることはキャッチアップが遅れていたこともあり非常に驚きでした(実際は売上の27%に加えてクレカ決済手数料の3−7%が上乗せされる) しかしAppStore自体の決済方法を利用するメリットも存在しており(OSレベルでの課金リクエスト対応、容易な返金リクエスト処理)まだまだ争いは収まりそうになりです。 そんな問題あるなかAppleはiOS15以上向けにStoreKit2と関連するサーバ向け機能を提供開始し、WWDC24ではStoreKit1の廃止が宣言されました。 StoreKit2はKit1に比べて以下の点が改善されました。 設計が分かりやすくなった Swift Concurrency ベースで実装可能 レシート検証が簡略化 ニアリアルタイムにユーザの課金状態を監視可能 また、StoreKit2では初回のみ課金アイテムの登録時に審査が必要かつ、アプリのアップデート(と審査)が必要というトリッキーな仕様が入っているようです。 失敗談として、課金アイテムは削除したら対象のIDはアカウント単位で利用することができなくなる点、課金アイテムの追加時に審査がありリジェクトになった話が紹介されていました。 以前に比べてテストも書きやすくなっており、検証などもやりやすくなったため新たに実装する際はStoreKit2を利用したいと思いました!(StoreKit1からv2へ移行するモチベーションは今のところ deprecated になったことだけで、提供されるサービスに変更はないとのこと) by uetyo 新OSの機能を古いOSにバックポートする 登壇者 : Mike Apurinさん 資料 : https://speakerdeck.com/auramagi/iosdc-2024 概要 このセッションではビルド方法の違い(StaticLink, DynamicLink)とライブラリの提供方法、具体的にバックポートをする方法について紹介されました。中でもバックポートできるものとできないものの違いが分かりやすく、クラシルのアプリでも知らず知らずのうちにバックポートされたAPIを利用していたんだと気がつくことができました! ポイント 著名なところだと TCA の PointFree が Observation をバックポートして話題になっていましたが、本来新しいOSでしか利用できない機能を古いOSでも利用できるにする技術がバックポートです。 いくつか方法があり、純正機能のバックポートやマクロを用いたもの、OSS(PointFreeのObservationとか)があります。 純正バックポートとしては iOS18でリリースした gestrue が iOS13までバックポートされていたり、 @backDeploied キーワードをつけることで実現できたりするようです。 @backDeployed to extend function availability to older OS releases https://www.avanderlee.com/swift/backdeployed-function-back-deployment/ バックポートできる基準は古いOSでもAPIが隠されているか、古いOSの機能を用いて実現できる場合のみで、OSに密接なWidgetKit等はできないとのことでした。 クラシルリワードは長くOSサポートを行わない(直近2年以内、新しいOSがリリースされて半年ほどで以前のバージョンをサポート解除)ためバックポートを行うメリット自体は少ないかもしれませんが、Swift/iOSエンジニアとして非常に興味深いセッションでした by uetyo Day1 StoreKit 2によるモダンなアプリ内課金 登壇者 :Ookaさん 資料 : Dropbox - 20240823_iosdc_binary_analysis.pdf - Simplify your life 概要 このセッションでは、SDKにarm64(シミュレーター)向けのバイナリがなく、Apple SiliconのMacでシミュレーターをビルドできない場合の対処法を紹介されていました。 ポイント SDK導入時に発生する「found architecture 'x86_64', required architecture 'arm64'」というエラーは、x86_64向けのバイナリしかなく、arm64向けのバイナリが必要な場合に表示されます。 SDKがどのアーキテクチャ向けのバイナリを持っているかは、.aファイルで確認可能です。 解決方法の手順: バイナリ解析で違いを発見 otoolを使用して、端末用とシミュレーター用のバイナリの違いを調査。 MashOを使用してバイナリを修正 見つけた差分を基に、MashOというSwiftのツールを使って、実機用のarm64バイナリをシミュレーター用に修正。 動作確認 修正後、シミュレーターで正常に動作することを確認! 感想 このセッションを通じて、普段あまり意識しない.oファイルと.aファイルの関連性や、バイナリ解析の有用性について学ぶことができました! 今後、同様のエラーが発生した際には、こんな方法もあるんだということを念頭に置いて対処できればと思います! by kaikai 複雑さに立ち向かうためのソフトウェア開発入門 登壇者 : shizさん 資料 : https://speakerdeck.com/auramagi/iosdc-2024 概要 このセッションでは、エンジニアが向き合っている複雑さの正体を「認知負荷」や「認知リソース」を焦点にしながら掘り下げ、複雑さに直面しているときの問題点を整理した上で、どのように対策するかについて紹介されていました。 ポイント すごく勉強になるお話しが多く、具体例もあり状況を想像しやすかったです。 その中でも個人的に大事だと思ったポイントは以下です。 複雑さの正体は、「認知負荷」が「認知リソース」または「ワーキングメモリの容量」を上回っている状態であるということです。 「認知リソース」は集中力や注意力のような、脳が情報を処理するためのエネルギーであり、ワーキングメモリは脳が一時的に情報を保持し操作する能力と紹介されていました。 これを1つのタスクを遂行するために必要な認知リソース、つまり「認知負荷」が上回ると人は複雑さを感じるようになるというものでした。 人が複雑さに直面している状況においては、つまらないミスを繰り返したり、忘れっぽくなったり、学んだことが身につかない、といった問題点があるため、如何にそのような状況を避けるかについても紹介されていました。 それが、「わける」、「だす」、「たよる」です。 ワーキングメモリの容量の限界を超えないように、向き合うタスクのボリュームを「わける」ことによって小さくしたり、メモなどに情報を「だす」ことによってワーキングメモリの容量を空けることができます。 また、チームに「たよる」ことで負荷を分散することができるというものでした。 まとめ 弊社iOSチームでは、PRを小さな粒度(目安100行前後)で作る開発文化がありますが、これはまさにレビュアー・レビュイー双方にとって認知負荷を下げることで、レビューサイクルを高速化するための効果的な運用であることに改めて気づきました。 そして、認知負荷が高い状態ではワーキングメモリから長期記憶に定着させる工程がうまく働かず、学んだことが身につかないという状況に陥ることは真剣に向き合わなければいけない問題だと思いました。 弊社には開発スピードを重要視したカルチャーもあるため、やることが多いという状況は簡単に生まれます。 このセッションで紹介されていた「わける」、「だす」、「たよる」という対策を、うまく仕組みとして落とし込み、エンジニアが学び成長できる組織にしていきたいと思いました。 by Seiya Iwasaki 座談会 「Strict ConcurrencyとSwift 6が開く新時代: 私たちはどう生きるか?」 登壇者 : shizさん kntkさん koherさん omochimetaruさん まつじさん 資料 : https://speakerdeck.com/shiz/zuo-tan-hui-strict-concurrencytoswift-6gakai-kuxin-shi-dai-si-tatihadousheng-kiruka 概要 このセッションは、iOSエンジニア5人でSwift 6で導入されるStrict Concurrencyとどのように向き合うかについて、土台となるSwift Concurrencyの理解から、Complete Strict Concurrencyへの移行戦略など様々な切り口で話し合うというものでした。 ポイント どのトピックも非常に示唆に富むものが多かったですが、中でも個人的に勉強になった部分を整理してみます。 これは必ず抑えておくべきことの一つで、Swift Concurrencyはデータ競合(data race)を防止してくれるもので、競合状態(race condition)を防止してくれるものではないということです。 データ競合(data race)は、複数のスレッド間で共有しているデータに対して、同時に読み書きが発生した際に、その結果が全く予想がつかなくなる事象を指します(読み込みだけの場合はデータ競合にはならない)。 対して競合状態(race condition)とは、各スレッド上で行われる操作の実行順序に依存して、その結果が変わってしまう事象を指します。 Swift ConcurrencyのActorを使うとデータを特定の領域に隔離することができ、その領域内で複数スレッドからデータに同時アクセスできないようにすることでデータ競合を防止しています。 データがそのような領域(隔離ドメイン)を超えられる条件が上記であり、基本的にはSendableに準拠して対応すれば良いという紹介がされていました。 また、今後データ競合を防止する際は、基本的にはスレッドをブロックしないActorを使い、どうしても同期アクセスしたいケースにおいてMutexやAtomicなどを使いましょうと紹介されていました。 iOSアプリにおけるSwift Concurrencyとの向き合い方についても話されており、iOSのようなGUIアプリの場合は基本的に隔離ドメインを超えるシーンは一部のModel内のロジックに限定されるだろうと紹介されていました。 弊社 クラシルリワード のiOSアプリでも実際上図のようにMainActorを使用しているため、余計に難しく考えるのではなくViewModelとModel間のデータ送信において、隔離ドメインを超えるものなのか、MainActorに隔離できないものなのかを着目できていれば基本的には良さそうです。 また、Swift6ではそもそもデータ競合リスクがあるコードの場合はコンパイルエラーになり、データ競合が作り込まれることは無いため、とりあえずActorにしておくというような考え方も不要になります。 余談ですが、fps60だとすると1/60秒で処理を全て終えることができればUIを固めないのだから、そのなかで終わる処理はMainActorで良いという話もされており、これは確かになと思いました。 まとめ 実は弊社クラシルリワードのiOSチームでは、すでにSwift6への移行を計画的に推進しています。 当然その中にはComplete Strict Concurrencyへの移行も含まれているのですが、我々の工夫として独自のSwift Concurrency Guideというものを作りながらこれを進めています。 このセッションでも最初に認識合わせや理解の確認から入ったように、Swift Concurrencyへの理解度はチームメンバーによってバラつきがある状態なので、基本的な理解や考え方から実際にプロジェクトで使用する際のプラクティスまでをまとめた一つのガイドとして構築しています。 このガイドはまだまだ作成途中で、上記でポイントとして挙げた点は聞いていて絶対ガイドに盛り込もうと思いました。 そして、このガイドも使いながらチームで勉強会を行い、チームとして向き合える体制を作ってComplete Strict Concurrencyへの移行を完遂させていこうと思います。 by Seiya Iwasaki 複雑さに立ち向かうためのソフトウェア開発入門 登壇者 : 小森英明さん 資料 : https://speakerdeck.com/techtver/20240826-iosdc-japan-2024 概要 このセッションでは、アプリダウンロード総数7,500万あるサービスのiOSアプリについて、現状のアーキテクチャが抱えている問題を整理した上で、如何に安全にリアーキテクチャを進めていくかについて紹介されていました。 ポイント 安全にリアーキテクチャを進めていくためのナレッジが詰まった発表で、非常に参考になるトピックを多く含んでいました。 個人的に特に大事だと思う部分を紹介してみます。 それが「マルチモジュール構成+モジュールごとのアップデート」です。 リアーキ後の一括リリースはどれだけテストしていても怖いですよね。 なので、マルチモジュール構成にしてモジュール単位でリアーキを推進していき、リアーキできたモジュールからリリースしていこうという戦略です。 このように進めることで、影響範囲を抑えながら段階的にリアーキを進めることができます。 さらに工夫として、リアーキモジュールのFeature Flagを用いた段階的な開放が紹介されていました。 リアーキしたモジュールをいきなり100%のユーザに公開するのではなく、クラッシュ発生率や問い合わせ件数などをモニタリングしながら、段階的に開放率を上げていくというものです。 このような工夫は、特にクリティカルな機能などを含むモジュールの公開においては、より安全に進めていくことができ非常に効果的だと思いました。 まとめ 数年前にかなり大規模なモバイルアプリのフルリプレイスを経験したことがありますが、そのときは1年くらいかけてリプレイスを実装しきり、一括リリースするという進め方を採用していました。 テストはその時考えられていた十分なレベルで行っていましたが、結果的には障害が群発しその対応に追われたのは苦い経験でした。 このセッションで紹介されている内容は、上記のような事態を効果的に防止することができると思います。 また、モジュールごとの段階的なリアーキは、プロダクトの施策と同時並行で開発を進めていくこともでき、施策を止めないという意味でもメリットが大きいです。 そもそもマルチモジュール構成になっていないプロジェクトの場合、進め方にはもうひと工夫必要だと思いますが、今後リアーキする機会があればぜひ取り入れていきたいと思います。 実は弊社が提供するクラシルも数年前にリアーキテクチャを行っているので、気になる方はチェックしてみてください。 Guide to "kurashiru android" app architecture vol.1 概要編 - dely Tech Blog なぜ MVVM + FRP は Elm Architecture に勝てないのか - dely Tech Blog by Seiya Iwasaki Mergeable Libraryで高速なアプリ起動を実現しよう! 登壇者 : giginetさん 資料 : https://speakerdeck.com/giginet/2024-08-23-mergeable-library 概要 このセッションでは、iOSアプリのStatic FrameworkとDynamic Frameworkについて、それらのリンクの実行タイミングの違いをおさらいしつつ、それぞれのメリデメを整理した上でその良いとこ取りを実現できるMergeable Libraryについて実際のパフォーマンス改善効果と合わせて紹介されていました。 ポイント Mergeable Libraryの利点を理解するために、大事だと思ったポイントを挙げてみます。 まずはおさらいになりますが、Dynamic FrameworkとStatic Frameworkのライブラリをリンクするタイミングの違いについて紹介されていました。 リンクとは、モジュール化(ライブラリ化)されたプログラムを一つに統合することを指しますが、Dynamic Frameworkはアプリ起動時にそれが行われ、Static Frameworkはビルド時に行われます。 これにより、Dynamic Frameworkはビルド時間が早くなる一方でアプリの起動時間が増加する性質があり、Static Frameworkは逆の性質を持つというものでした。 上記を踏まえて、Mergeable LibraryはDebugビルド時はDynamic Frameworkとして振る舞い、Releaseビルド時はStatic Frameworkとして振る舞うものだと紹介されていました。 まさに良いとこ取りという感じで、これまですべてのモジュールをStatic Frameworkにしていたプロジェクトでは、これを導入することでビルド時間を改善し開発者体験を向上させることができそうですし、逆の場合はアプリの起動時間を改善することができそうです。 スライドでは実際にベンチマークした結果も紹介されていましたので、ぜひご確認ください。 まとめ 弊社のiOSアプリはマルチモジュール構成を採用しており、多数のモジュールで一つのアプリが構成されています。 基本的にそれらはStaticリンクされているので、アプリの起動時間には影響しない一方でビルド時間は長くなってしまいます。 今回紹介されたMergeable Libraryを導入すると、アプリの起動時間はそのままにビルド時間を改善して開発者体験を向上させることができる可能性があり、その導入もそこまで工数がかかるものでは無いため、早速iOSチーム内で検証してみようと思います。 by Seiya Iwasaki Appleの新しいプライバシー要件対応:ノーコード アプリプラットフォームの実践事例 登壇者 : Nao-RandDさん 資料 : https://speakerdeck.com/nao_randd/applenoxin-siipuraibasiyao-jian-dui-ying-nokodoapuri-puratutohuomunoshi-jian-shi-li 概要 去年から今年の春にかけて大変話題となったプライバシーマニフェスト対応の要点、そしてどのような対応を行なったかの解説をされていました。 ポイント IDFAを利用する通信を行うドメインは、Privacy Tracking Domains に追加する必要があります。 そして、トラッキングを許可していないユーザーがそのドメインを利用した通信を行うと、エラーが発生します。 つまり、、、、 ⚠️同ドメインで、IDFAを必要としていない通信を行なっても、エラーが生じてしまう…. これに対し、yappliさんはトラッキングの許可状態に合わせて、domainを変更することで、トラッキングを許可していないユーザーでも、エラーが発生することがないように回避をしていました。 以前、自分がプライバシーマニフェスト対応したアプリでは、トラッキング機能を利用していなかったため、こんな落とし穴があったのか、、、ととても勉強になりました! まとめ プライバシーマニフェスト対応に関する自分の知らなかった対応内容、 そしてノーコードアプリプラットフォーマーとしてのyapliさんならではの対応をされており、 手法を知ることができて大変勉強になりました。 by kaikai 楽しく簡単に!QRコードの読み取り機能を実装しよう 登壇者 : ぺんぎんさんさん 資料 : https://speakerdeck.com/penguinsan_pg/le-sikujian-dan-ni-qrkodonodu-miqu-riji-neng-woshi-zhuang-siyou 概要 以前までのAVFoundationを使用していたQRコード読み取り機能を、 新たに誕生したAPI、VisionKitを利用して快適に開発した内容を解説していただきました。 ポイント VisionKitが登場するまでは、AVCaptureDevice、AVCaptureDeviceInput、AVCaptureConnection。。。。。。など、多くのクラスを巻き込んで実装する必要がありました。 VisionKitのDataScannerViewControllerの登場によって、AVFoundationを使うよりとても少ないコードかつ、高品質なQR読み取りを実行することができるようになったとのことです! 実際に、ソースコードも映していただきましたが、目に見えてコード量が削減されていました。 (半分以下に….!!) また、QRコード読み取り以外にも、バーコード読み取りなどにも利用できるそうです。 ⚠️注意点として、2018年以降のNeuralEngineを搭載したiOS16以上の端末しか動作しないので、サポートOSは注意する必要がありそうでした! まとめ 発表内容の学びも深く、ユーモアがある発表方法でとても面白かったです! クラシルリワードアプリ内でも、バーコード読み取りの機能は存在するので、 いち早く導入し、コードのリファクタ、および機能の高品質化を図りたいと思いました! by kaikai メインスレッドをブロックさせないためのSwift Concurrencyクイズ 登壇者 : tokizoさん 資料 : https://speakerdeck.com/tokizuoh/swift-cocurrency-quiz 概要 Swift Concurrencyを使った非同期処理におけるメインスレッドのブロックリスクを見極めるため、登壇者が用意したコードを用いてクイズ形式で知識を深めることが目的でした。 ポイント 完全な理解が難しいSwift Concurrencyの実装について5つのケースを用いて、理解度を測ることができ、本セッションでは、主に以下のポイントについて再度おさらいしました。 Detached Taskについて Actor隔離されていない非同期関数 プロパティラッパーからの推論 Global Atorの規則 また、「メインスレッドを適切に管理する」とは以下のことを指しており、適切に管理できるようになることが狙いでした。 「メインスレッドを適切に管理する」とは「UI更新などではメインスレッドで処理を実行する」・「メインスレッドでは行わなくてもいい処理はバックグラウンドスレッドに逃す」ということを定義されています。 具体的なクイズの例としては、以下のようなコードを例題を含め6つ用意されており、それぞれなぜMainActor.assertIsolated()が通るor通らないのかを確認しました。 感想 現在弊社のiOSチームでは、Swift6以降に伴い、Swift Concurrencyの利用のガイドラインが設けられているところでもあるので、今回こちらのセッションで学んだことも念頭においてガイドラインの精度に寄与できればと感じました。 個人的には、クイズ形式で確認することで、ケースごとの対処法を学ぶことがあったので、大変有意義なセッションとなりました! また、自分で実装していて自信の無い箇所は丁寧にMainActor.assertIsolated()などを活用しながら、プログラムの実行が停止されないかを確認しながら、実装したいと思いました! by takky 開発を加速する共有Swift Package実践 登壇者 : el_metalさん 資料 : https://speakerdeck.com/elmetal/our-case-studies-of-shared-swift-packages-to-accelerate-development 概要 サイボウズの3つのプロダクトで共有利用する社内OSSを作成したことや、その経緯と内容についてを発表がメインで、質疑応答でも運用についてを話しされていました。 ポイント アプリ内の認証機能等の共通部分は同じロジックで実装したいニーズがあったが、それぞれのアプリで利用されている技術スタックが異なりロジック部分の実装で他アプリの参考をして散逸してしまうことがあったので、負債が拡散する問題があったそうです。 上記も含め共有Swift Packageを実装する際に「SwiftUIに馴染むAPI」「高いtestability」「ドキュメントの徹底」を大事にしてより利用しやすいシステムにされました。 また、Owner・Contributor・Userの3つのロールに分かれてパッケージを成長させていく仕組みに関しても、より効率良くプロダクト開発において参考になるポイントかと思いました! 弊社のプロダクトである、クラシルとクラシルリワード内のユーザー認証機能等が統一されているので、発表されていたことを参考にして共有Packageを作ることで当社にもメリットはかなりあり、新たにプロダクトを立ち上げる際の立ち上げコストがかなり圧縮されると感じたので、機会があれば、社内共有するOSSの作成は大変魅力的だと感じました! 感想 上記にもある通りで、各企業で同じような悩みを持っていると思うので、参考にさせていただきやすい内容で、運用方法や辛かった部分等についても機会があれば詳しく伺いたいと感じました! また、導入した効果として、「コード整理」「ロジックのSSoT」「置き換えの加速」「アプリコードの品質向上」など大きなメリットも挙げられていたので、複数アプリを運用する企業や組織は社内で共有利用するPackageの導入は検討してみてはいかがでしょうか! by takky iOSアプリらしさを紐解く 登壇者 : Natsuho Ideさん 資料 : https://www.figma.com/deck/SdnNYyqxF80BpyvQLt5Pof/iOSDC-Japan-2024-%E7%99%BB%E5%A3%87%E8%B3%87%E6%96%99?embed_host=twitter&kind=deck&node-id=30-413&page-id=0:1&scaling=min-zoom 概要 「iOSアプリらしいデザイン」とは、直感的で使いやすいデザインであることを再認識し、登壇者が調査した結果を共有するセッションでした。 ポイント 上記のように、「直感的で使いやすいデザイン」を「iOSアプリらしいデザイン」と定義されたのは、HIG(Human Interface Guidelines)やApple純正のアプリに触れることで、ヒントを得たそうです! その「直感的で使いやすいデザイン」を実現するためのデザインを実現するためには、特に以下の4つのデザイン原則が重要とのことでした! 直感的な操作性 :ユーザーが直接的に操作できるインターフェースを提供すること。 フィードバック :ユーザーが操作の結果を視覚的・聴覚的・触覚的に理解できるようにすること。 現実世界の比喩 :現実世界のオブジェクトや動作を模倣することで、ユーザーが操作を直感的に理解できるようにすること。 ユーザー主導 :ユーザーが自由に操作を行い、その結果を制御できるようにすること。 具体例として、カードコンポーネントとお気に入りのUIの例にとって説明されており、従来のpush遷移をズーム遷移に変更することで、より直感的な操作性を実現されていました。 また、お気に入りUIでは、アイテムが実際にお気に入り一覧に吸い込まれるようなアニメーションを追加し、ユーザーにとっての操作と結果の一貫性を強調し捜査の一貫性が大事であることを学べました! このような改善は、WWDC24でも紹介された手法を参考にしており、今後のプロジェクトでも積極的に取り入れていくために、デザイナーやPMにも積極的に提案したいですね! 感想 直感的なデザインを実現するためには、HIGを確認したりAppleのデザイン原則を理解するだけでなく、デザインの背景にある考え方や実際にアプリを触ってみることで理解できると改めて、学ぶことができました。 特に、ユーザーが操作を通じて自然に結果を得られるようなデザインを心がけることで、アプリの使いやすさが大幅に向上することを実感しました。 by takky 詳解UIWindow 登壇者 : atsuyanさん 資料 : https://speakerdeck.com/elmetal/our-case-studies-of-shared-swift-packages-to-accelerate-development 概要 UIWindowについて再度のおさらいと、SwiftUI時代ではどのようにUIWindowと付き合っていくべきなのかを共有するセッションでした。 また、UI構成の際によくやりがちな画面サイズ取得の例などを挙げながら、UIViewやUIWindowView、UISreenの特徴を解説されていました。 ポイント 画面サイズを取得する際に検討される、UIWindow等の上記のクラスがあるが、どのように取得すればいいかをUIの表示レイヤーについて解説されていました。 また、UIWindowの活用方法としてアプリ内で、最前面にUIを表示したい際に操作するのが良さそうであり、具体的には、画面のローディングやトースト、デバックツールの表示などを挙げられていました。 当社では、リワードアプリのDebug版ではイベントの取得をリアルタイムで確認するためのツールは最前面に表示するように設計されているため、登壇内容を聞きながら思わず頷いてしまいました。(笑) SwiftUIを利用して、画面サイズを取得する際の方法として、「App内のViewからから取得する場合」と「App外で作成したUIWindowに紐付くSwiftUIのViewから取得する場合」の2つに分けられるとしておられました。 リワードアプリの開発組織的にもGeometoryReaderで毎回画面サイズを取得することよりも、どこかで画面のサイズを取得して、その値を利用することで、わざわざ画面横幅を取得するために大量の差分を発生させずにするとの話になっていました! すごくタイムリーな話だったので、持ち帰って議論もして検証後よければそのような仕組みでUIを組めるようにしたいと考えています! 感想 上記でも述べていますが、画面の横幅を取得してUIを組みたいニーズがある時に、毎回GeometoryReaderを書いてしまっていたので、変更差分の圧縮やボイラーコードになりつつ箇所を削ることができそうなので、すぐにでもプロジェクトに適応できそうです! by takky Day2 クロスプラットフォーム普及増加。SwiftでiOS開発はもうやらないのか....? 登壇者 : 清水翔貴さん 資料 : https://speakerdeck.com/teamlab/iosdc-2024-kurosupuratutohuomupu-ji-zeng-jia-swiftdeioskai-fa-hamouyaranainoka-dot-dot-dot 概要 昨今、多く話題に上がるクラスプラットフォームによる開発と、それに対するネイティブ開発、 それぞれのメリットデメリットをわかりやすくまとめ、言語化されていました。 ポイント 普段、なんとなくクロスプラットフォーム流行りだよなぁ、と思いつつ、自分はクラスプラットフォームのメリットデメリットをちゃんと理解していませんでした。 そんなクラスプラットフォーム開発のメリットデメリットを言語化してわかりやすく解説していました。 挙げられているメリットの中で、特に印象深かったのは、以下です。 iOSとAndroidの実装差分が軽減される AndroidでできてるのにiOSではできていないんだけど! ということは今までたくさん言われてきた経験があり、いい思い出はありません、、、、、 また、差分を出さないために両OSのエンジニアの認識を合わせるのも、少ない工数ではありません。 認識合わせの工数を削減できるのは、とても強いメリットだと思いました。 また、デメリットで気になったのは以下です。 サードパーティのSDKが対応していない場合がある 超コアな機能の開発に使用するSDKが対応していない場合とか、どうするんだ、、、ととても 気になりました。 (そもそもそんなことが起きないように技術選定しっかりしようね!という話なのかも) 感想 改めて、クラスプラットフォーム開発に対する考え方を知るいい機会になりました! それと同時に、クラスプラットフォーム、少し触ってみたいな!と思いました。 今後、クラスプラットフォーム開発が選択肢に上がった際は、こちらのセッションを思い返して、 技術選定の参考にさせていただきたいと思います。 by kaikai 医療アプリ開発の最前線 - 安全性と生産性の両立への挑戦 登壇者 : Shogo Yoshidaさん 資料 : https://speakerdeck.com/medley/yi-liao-apurikai-fa-nozui-qian-xian-an-quan-xing-tosheng-chan-xing-noliang-li-henotiao-zhan 概要 リリースから8年を超える医療系のアプリを継続的にリファクタリングし、生産性を高めつつ、自動化できる部分は積極的に自動化している点が非常に参考になりました。特に、ADR(ArchitectureDecisionRecord)については、このセクションを聞いた翌週にすぐにクラシルリワードにも取り入れ運用を開始しています。 Magicpodについてもクラシルリワードは導入済みですが、なかなか安定しないため100%の運用率を保っていることは非常にモチベーションにも繋がりました! ポイント オンライン診療アプリはリリースから8年を経過しているが、継続的なリファクタリングによりSPMベースのマルチモジュールに対応しMVP開発を支えている。 また、クラシルリワードでも先日導入したADR(ArchitectureDecisionRecord)を残すことでアーキテクチャ設計の思想背景などを後世に残すドキュメンテーションの取り組みを積極的に行っている点が我々のチーム、組織でも学べる点だった。 例えばクラシルリワードではリリース速度を優先するあまり、中途半端な設計のまま積み上がってしまうケースがあり、改修するタイミングもずれるため作成した本人もどういった思想でその設計にしたのかうろ覚えなケースがあり、改善する必要があると認識している。 Magicpodに関しても運用スコア100%を保持しており、QAチームと並んで改善を行っている。特にQAチームは企画説明時から一緒に参加することでスムーズに対応できる話などが印象的でした。 by uetyo   さいごに iOSDCの参加を通じて、普段業務で取り扱っている以外の技術や、業務に応用できそうな技術など、たくさんの知識を吸収することができました! また、他企業のエンジニアさんとも多く話す機会があり、とても刺激をいただけことも今回オフラインで参加した大きなメリットに思います。 iOSDCで得た知識、経験を活かして、さらに弊社で展開しているiOSアプリをもっと良いものにしていければと思います! delyでは、iOSエンジニアを大募集しています! 開発ではスピードを大切にしており、最新技術を積極的に取り入れ、より良いアプリを作るために、チーム一丸で切磋琢磨しております。 気になる方は、ぜひカジュアル面談でお話ししましょう! www.wantedly.com
アバター
こんにちは、クラシルリワードでSREエンジニアをしているkatotakです。 6/20・21に幕張メッセで開催されたAWS Summit Japan 2024に参加してきました! aws.amazon.com 今回は業務の時間を使ってイベントに参加することができたので弊社メンバー数人とDay1・Day2に分かれて参加しました! (delyでは必要に応じて平日開催されるイベントへの参加もできます!) 参加の目的・モチベーションとして、弊社ではクラシルリワードだけでなくクラシルやその他のサービスにおいてAWSを主要なクラウドサービスとして活用してサービスを提供しています。 そこで普段利用しているサービスへの知見を深めることや昨今注目されている生成AI関連のサービスなどトレンドを追いかける機会を設けて開発者自身のスキルアップや今後の開発に活かしていくことを目的・モチベーションとしています。 個人的に今回参加して普段利用しているサービスの内部のアーキテクチャにDeep diveしたり、まだ利用したことがないサービスの使い所について知見を深められたことで普段の開発へのモチベーションやアーキテクチャを考えることが楽しみになりました! セッション ここからは参加したセッションについての内容や参加メンバーの感想を書いていきます! Day1 インシデント対応を 10 倍速くする方法、教えます - PagerDuty と AWS で爆速障害対応 資料は こちら このセッションでは、サービス運用をする上では目を逸らすことのできない「インシデント」において、より早く気づき、より早く直すために重要な観点を紹介されました。 ◇ PagerDuty インシデント対応においては 検知 影響範囲の把握 ステークホルダーとのコミュニケーション 様々な調査や復旧における作業 など、様々な作業を行う必要がありますが、それをサポートしてくれるPagerDutyの紹介がありました。 インシデントにより早く気づくことを目的として、データの収集や膨大なデータの中からインシデントを検知し、然るべき通知を行う仕組み インシデント対応に必要な、コミュニケーションの場の自動提供、対応ステータスの可視化 ◇ インシデントコマンダーの重要性 そして、セッションを通じてお話しされていたのは、インシデントコマンダーの重要性でした。 インシデント対応においては前述の通り、様々な作業を複数の人がそれぞれの役割を持って対応することが必要となってきます、そんな状況の中、インシデント解決のために「全体の交通整理」「意思決定」を行うことがインシデントコマンダーの役割と紹介されています。 ◇ インシデントコマンダーになれる人 インシデントコマンダーは特別な技術や知識必要が必要なのではなく、下記の点が重要であると紹介されていました。 ◇ よぎった考え このセッションを拝聴する前にAWSのブースにおいて障害対応におけるLLM利用のサンプルを拝見していたため、インシデントコマンダーの役割を一部でもLLMで担うことはできないかと想像しました。 資料は こちら 特に前述のインシデントコマンダーになれる人の サービスがどのように連携しているかの理解 状況を判断して、行動方針に対する迅速な意思決定ができる 上記の点について特にサービスに長く携わっていることが必要となり属人性が発生してしまうこと、インシデントが頻繁に発生しないためインシデントコマンダーの経験を積む機会が少ないことが課題です。その結果、いざという時の意思決定が難しく何から交通整理をすべきかの判断に迷ってしまうことが多い印象があります。 そこで、LLMにおいて状況の把握をすることができれば、意思決定や交通整理のサポートの役割を担うことができるのではと感じました。 ただし、企業やサービスによって同じ状況でも意思決定の方向性が異なるので、LLMをサービスに特化させる必要があるので、道のりは遠いですが、今からその未来に向けてアウトプットを整理することから始めていこうかなと思いました。 Day2 AWS NoSQL コスト削減大全 資料は こちら データベースのコスト削減と聞くと、「高い可用性が求められ、専門性が高く、とにかく難しい領域」というイメージを持たれている方は多いのではないでしょうか? かく言う私も、そのように捉えていました。 このセッションでは、データベースにおけるコスト削減は通常のデータベースよりも比較的簡単だと紹介されており、具体的なコスト削減のためのアクションに繋がる考えや知見を得ることができました。 本トピックでは、まずNoSQLデータべースのコスト削減についての大枠から入り、具体的なデータベースサービスでは、弊社のサービスにも大きく関わってくるAmazon DynamoDBを中心に、コストの構造と効果的なコスト削減方法について、セッションの内容をもとにした学びと感想を共有します。 ◇ コストとは? まず、プロダクトにおけるコストとは、以下のような要素に分解することができると紹介されています。 インフラコスト:サーバーやストレージの使用に伴うコスト ライセンスコスト:商用サービスなどの使用料 オペレーションコスト:メンテナンスや人材の時間的コスト 開発コスト:開発のしやすさに関わるコスト ビジネスコスト:メンテナンスや障害による機会損失 ◇ データベースのコスト削減の難しさ 先にも紹介した通り、「データベースのコスト削減=難しい領域」という先入観にとらわれがちです。しかし、ことNoSQLデータベースにおけるコスト削減は通常のデータベースよりも比較的簡単だと紹介されていました。 AWSのデータベースサービスはPurpose-built Databaseという目的別に特化されたデータベースが複数用意されています。 なので、「目的に沿って適切に使用することでベストプラクティスになりやすく、このベストプラクティスに従うことで自然とコストが最適化される」という点が通常のデータべースと違って扱いやすい点だと言えます。 ◇ コスト削減のポイントは NoSQLデータベースのコスト削減ポイントは以下だと紹介されています。 サイジング:適切なコンピューティングリソースの選定 スケジューリング:リソースの適切なタイミングでの割り当て 価格体系:適切な価格モードの選定 データモデリング:効果的なデータモデリング ◇ DynamoDBにおけるコスト削減のポイント では、上記のポイントを踏まえて、具体的にDynamoDBにおいてはどのようなコスト削減のベストプラクティスがあるのかについてセッションで紹介された内容を一言でまとめると、「価格体系の選定とキャパシティユニットの理解」と結論づけました。 前提としてサーバーレスなDynamoDBには「プロビジョンドキャパシティ」と「オンデマンドキャパシティ」という二つの価格体系があります。そしてこれらがキャパシティユニットという単位で計測されています。 ◇ 価格体系の選定 「プロビジョンドキャパシティ」と「オンデマンドキャパシティ」のどちらを選ぶことが最適なのかについては、二つのモードをフルで使って均一なスループットが来た時の比較をイメージするのが手っ取り早いです。 もしプロビジョンドモードである程度の値でピークを設定した時に、実際の使用率が14.4%だった場合、「プロビジョンドモード」の方が7倍もコストが安くなります。 安全率は大体50%(スパイクしやすいものならもう少し上)なので、プロビジョンドモードで設定して安全率50%なら、オンデマンドモードでたまに来るスパイクを処理しているよりもコストは低くすることができます。 また、プロビジョンドモードはリザーブドキャパシティが使えるため、計算上約3.2%の使用率でもプロビジョンドモードの効率が良いとされています。 では、オンデマンドモードどのような時に最適な選択肢とされるのかについては、 オートスケーリングの設定やピーク時の処理やスロットリングエラーをどうしても出したくない。 その瞬間のオペレーションコストやビジネスコストを減らしたい といった時に有効とされています。 「プロビジョンドモード」と「オンデマンドモード」は切り替えが可能なので、現状の波形やビジネス的な観点から切り替えることも有効言えます。 ◇ キャパシティユニットの理解 キャパシティユニットは読み込み時と書き込み時でコストが異なり、読み込みは書き込みの5倍安い設定になっています。「1WCU=5〜40RCU」 この読み込みと書き込みのコスト差分を意識した設計を行うことで、全体コストを大きく下げることが可能と言えます。 例えば、indexを作る時に発生する書き込みにおいて、もしindexを作らず5回未満の読み込みと、絞り込みによってデータが取得可能であるのならば、セカンダリインデックスを作るよりもコストを抑えることができるなど。 感想 今回のセッションで、データベースのコスト削減は一度の対策で終わるものではなく、継続的な見直しと改善が必要であることを学びました。特に、DynamoDBのようなNoSQLデータベースでは、適切な価格体系の選定や効果的なデータモデリングがコスト削減の鍵となります。 また、コスト削減の取り組みはビジネスの直接的な利益に繋がるため、組織全体での取り組みが重要です。今後もコスト削減に向けた最適化を続けていくことで、より効果的なシステム運用を目指していきたいと思います。 Amazon Aurora の技術とイノベーションDeep dive 資料は こちら このセッションではAmazon Auroraのアーキテクチャ・グローバルデータベースの説明から始まり、Serverless v2やBlue/Greenデプロイ・Zero ETLなど運用上で役立つ機能やストレージタイプ・pgvectorなどの新機能の紹介が行われました。 アーキテクチャ Auroraストレージは3AZに分散して配置されることで可用性や耐久性を担保していることや コンピュートとストレージが分離されているアーキテクチャであるため、リードレプリカの追加がストレージのアタッチのみだけで完結する点がアニメーションで説明されわかりやすかったです。 管理性 – Aurora Serverless Amazon Aurora Serverless v2の使いどころについてあるワークロードをもとにプロビジョンされたインスタンスとServerlessで比較を行いCPU・メモリがスケールアウトされることによるレイテンシーへ効果が説明されました。 Serverlessを使用することでワークロードの負荷に合わせて自動でCPU・メモリをスケールすることができるので一定時間に集中して使用負荷が高まるような場合には非常に有効な手段になることがわかりました。クラシルリワードにおいても日次で一時的に多くのデータを取得するようなワークロードがあり、Serverlessインスタンスを使用することで適切なリソースで処理を実行できパフォーマンスの維持・向上が見込めそうだなと思いました。 docs.aws.amazon.com 新しい Aurora ストレージ タイプ 新しいAuroraストレージタイプとしてAmazon Aurora I/O-Optimizedが紹介されました。 I/O-Optimizedではコンピューティングとストレージコストがスタンダードと比較して増加しますがI/Oが多く発生するようなワークロードにおいては最大40%減のコストパフォーマンスが発揮できたりパフォーマンスの向上が見込めます。 実際にクラシルリワードでのI/O支出についてCost Explorerで確認をしてみましたが現状では支出に占めるI/Oの割合は高くない状態であったため、今後のコストを確認する中で最適化できそうなタイミングで導入を検討したいと思います。 Amazon Aurora Limitless Database 内部アーキテクチャ詳解 〜 スケーラビリティと高可用性の秘密 〜 資料は こちら このセッションではAmazon Aurora Limitless Databaseについての開発背景や内部アーキテクチャ・開発にあたり大事にしていることが紹介されました。 AWS re:Invent 2023にてAmazon Aurora Limitless Databaseが発表されたことは知っていましたが内部のアーキテクチャについてかなり踏み込んで説明されておりボリューミーなセッションでした。 Amazon Aurora Limitless Databaseとは 現在Limited Preview中でPostgreSQLエンジンでのみ提供されているマネージドなシャーディングサービスです。 開発背景としては事業を運用していく中で扱うデータサイズが増えたり、機能追加やイベント時のスパイク等によってライターのスケーラビリティが要求される場合にデータをシャーディング(水平分割)を行いテーブルを複数のチャンクに分割して保存するのが一般的です。 ですが、トレードオフとしてデータがどのシャードにあるかや複数のシャード内のデータの結合やトランザクションの一貫性の担保やデータのバックアップをどうするかなどメンテナンスコストが上がってしまいます。これらのシャーディングにおける問題を解決することを目指して作られたものがAmazon Aurora Limitless Databaseです。 Amazon Aurora Limitless Databaseは1つのエンドポイントに繋いで使うだけでよくアプリケーション上での接続切り替え処理が不要で1 秒あたり数百万件を超える書き込みトランザクションにスケーリングしペタバイト単位のストレージを管理可能することができるそうです。分散トランザクション・クエリについてもサポートされます。 Shard management Sharded table シャーディングが行われるテーブルで各シャードにデータが配置される。 シングルシャードでクエリ実行されるとパフォーマンスが良いので関連するレコード(ex. user_id)などは同じシャード内に配置することをコロケーションと読んでいる Reference table 更新量が少なくデータサイズが小さいテーブルのことで、Reference tableは各シャードへ配置を行いできる限りシングルシャードクエリで実行できるように自動で配置される シャーディング対象にしたいテーブルを作りたい場合はセッション変数でテーブル対応を指定することでテーブル作成ができるのでセッション変数を使わなければローカルやCI/CDの際のテーブル作成も容易になる設計にしているとのことです。 Internal Architecture Auroraクラスタ内にシャードグループというものが作成されてその中に必要機能が内包されており単一のエンドポイントが提供され、指定しているキャパシティで自動でスケーリングを行う。 Distributed transaction routers エンドポイントを提供してどのシャードにどのデータがあるかのメタデータを管理、時間ベースのトランザクションで使用するための時間払い出し・クエリの実行プラン解析・分散トランザクションやクエリの場合は集計等を行うレイヤー Data access shards メタデータだけを保持している。 ルータから時間情報とクエリの実行計画を受け取りクエリを実行してルータに結果を返すレイヤー。シングルシャードトランザクションである場合はこのレイヤーでコミット・ロールバックを行う。 シングルシャードのクエリをうまく使うと並列化できるのでスループットが良くなる。 Challenges in a distributed database 分離レベルを守るためにBounded clocksという仕組みを使ってクエリの整合性や実行順序を保証している。 Current time Earliest possible time Latest possible time EC2 TimeSync serviceから時刻情報の信頼区間を配信して使用することでPostgreSQLの分離レベルを守るために連携している。実際にどのようなフローでトランザクションが実行されるか資料では説明されています。 1つのエンドポイントに繋いで普段通りのクエリを実行するだけで書き込みのスケーリングや分散トランザクション・クエリを裏側で良しなにやってくれるのは開発者側からすると事業ドメインの開発により時間を使えるので嬉しいですね! 使いどころとしては書き込み性能を上げたい・MySQL/PostgreSQLインターフェイスを使いたい・分散トランザクションやクエリをマネージドに処理したい場合に有用とのことでした。 アーキテクチャ道場! アーキテクチャ道場は、AWSのSAが実際に設計したアーキテクチャを題材に、チーフテクノロジストがレビューを行う実践的なセッションです。今回はレジリエンス向上をテーマに2つのケースが用意されていました。 ケース1:クレジットカード会社のシステム 一つ目のお題はクレジットカードの決済システムに関するものでした。A社は全てのシステムをAWS上で運用しており、特に決済関連のアプリケーションは高い可用性が求められるため、以下の対策を講じていました 全てのリソースを複数のAZに冗長化 セカンダリーリージョンへのデータレプリケーション この構成は完全な障害(バイナリー障害)やブラックアウトには有効でしたが、グレー障害やブラウンアウトには十分に対応できていませんでした。 グレー障害:観測される状態が視点によって変化する障害。例えば、インフラの死活監視は成功するがアプリケーションが正常に応答しない状態。 ブラウンアウト:機能は完全には失われていないが品質が低下している状態。例えば、パフォーマンスが徐々に悪化したり、エラーレートが上昇したりする状態。 (グレー障害、ブラウンアウトという単語はこのセッションで初めて名前がついていることを知りました...!) 改善の方針 セッションでは、以下の2つの主要な方針が示されました 障害のあるAZを検知し、ワークロードから切り離す 検知:グレー障害・ブラウンアウトを考慮した障害検知 隔離:障害のあるAZへのリクエスト流入を遮断 AZ間の独立性を高める AZを跨ぐ通信・処理をアーキテクチャから極力排除 具体的な改善策 これらの方針に基づき、以下の具体的な改善策が示されました: Application Load Balancer(ALB)の設定変更 クロスゾーン負荷分散をオフにし、LBからフロントサービス間の通信がAZを跨がないようにする Amazon EKSの構成変更 Fargate profileを各AZに個別に作成 各AZごとにDeploymentを作成し、各AZに確実にPodが存在するようにする フロントサービスからバックサービスへの通信改善 バックサービスをAZごとに作成 各AZのフロントサービスからのエンドポイントを環境変数に個別に指定 データベース層(Amazon Aurora)の改善 各AZのインスタンスを指すカスタムエンドポイントを作成 各AZのバックサービスからのDBエンドポイントとして指定 ただし、更新系クエリについてはAZを跨ぐ通信を許容(プライマリーインスタンスは1つのみのため) 障害検知の方法 障害を正確に検知し、不要な隔離を避けるために、CloudWatchで以下の複合的な条件を設定していました AZ-1へのリクエスト全体でエラー率・レイテンシが増加 AZ-1からプライマリーDBへのアクセスでエラー率・レイテンシが増加 上記の現象がリージョン全体の障害ではないこと 上記の現象がDBプライマリーインスタンスの障害ではないこと これらの条件を監視するために、以下の実装が示されました: ALBのCloudWatchメトリクスをAZ単位でアラーム化 各AZからプライマリーDBへのヘルスチェックを実装(Amazon CloudWatch Syntheticsを利用) 上記のアラームを組み合わせた複合アラームの作成 障害検知後の対応 障害を検知した際の隔離方法として、Amazon Route 53 Application Recovery Controllerを使用することによって、障害が検知されたAZへのトラフィック流入をデータプレーンレベルで制御することが可能になります。 ケース2:外部サービス依存のあるシステム お題2では、ECサイトを運営するB社が依存するサードパーティのサービスの障害への対処方法が検討されました。 このサードパーティサービスは信頼性が非常に低く、リクエストが集中するとすぐにエラーレスポンスの返却やパフォーマンスの悪化が始まります。現在のアーキテクチャでは、サードパーティサービスに問題が発生するとアプリケーション全体にその影響が広がってしまうため、B社はサードパーティサービスとアプリケーションの依存関係を緩和することで解決を図ろうとしていました。 この問題に対する解決策として、プロキシサービスの導入が示されました。 プロキシサービスは、アプリケーションとサードパーティサービスの間に位置し、以下の主要な機能を提供します: 負荷の緩和 レートリミット キャッシュ 障害影響の遮断 サーキットブレーカー バックオフリトライ 非同期処理 プロキシサービスは、サードパーティサービスの処理要件に応じて、以下の4つのアクセス方式を提供します: アプリケーションは、リクエスト時にこれらのアクセス方式を選択します。 プロキシサービスの主要コンポーネントとその役割は以下の通りです: リクエストハンドラー:アプリケーションからのリクエストを受け付け、適切な処理を行う レートリミット:サードパーティサービスへのリクエスト数を制限する サーキットブレーカー:サードパーティサービスの障害を検知し、一時的にリクエストを遮断する バックオフリトライ:エラー時に一定の間隔を空けて再試行する キャッシュ:読み込みリクエストの結果をキャッシュし、サードパーティサービスへのアクセスを減らす 処理キューとコンシューマー:非同期処理のためのキューとその処理を行うコンポーネント ステートウォッチャー:非同期処理の状態を監視する これらのコンポーネントにより、サードパーティサービスの障害や負荷増大時にも、アプリケーションへの影響を最小限に抑えることができます。 このアーキテクチャにより、B社はサードパーティサービスの信頼性の低さやパフォーマンスの問題から自社のアプリケーションを保護し、より安定したサービスを提供できるようになります。 さいごに AWS Summitへの参加を通じて普段とは別の視点から現在のアーキテクチャを考える機会になりクラシルリワードでどのように活かせるかを考えるいい機会になりました! 今後もこのような機会を活かしてスキルアップを図りサービス成長に貢献していきたいと思います! delyではサーバサイドエンジニアを大募集しています! フルサイクルエンジニアリングを推進していてサーバサイドエンジニアもAWSサービスを使ってプロダクト開発・運用などを行うシーンが多くあります! 気になる方はぜひカジュアル面談でお話しましょう!!! herp.careers その他のポジションも大募集中です! dely.jp
アバター
こんにちは!delyのクラシルリワードでデータエンジニアをしている harry( @gappy50 )です〜。 これまで、クラシルやクラシルリワードのデータエンジニアリングに関する発信をこのブログでしてきました。 tech.dely.jp tech.dely.jp tech.dely.jp が、最近ブログのネタに困っていたのと、社内でキーボードにまつわる宗教戦争(?)が勃発したこともあって、 様々な企業ブログで散々こすりにこすられた企画である「突撃!隣のキーボード! 」のdely版をやってみようと思います。 ただただ、エンジニアメンバーたちのキーボードを中心としたデスク環境を紹介するというだけの企画です。 あとは、個人的にキーボード買い替えたい気持ちがあるので勉強させてもらおうと思っている裏企画も同時並行で走っています。 これを期にdelyのエンジニアの楽しげな人となり(?)を知ってもらえればよいなと思いつつも、書き始めたらそれぞれの思いや信仰心(?)がかなり強いので知ってもらわなくてもよさそうだな?と思い始めています。。 …それではスタート!!! funzinさん(EM)の場合 いま使っているキーボード HHKB Professional HYBRID Type-S 英語配列 /墨 このキーボードのおすすめポイント(JIS/US、軸などのこだわりもあれば) 打鍵感が一番しっくりくるため使ってます その他デスクまわりのこだわりポイント FLEXSPOTの電動昇降デスクを使ってます(最近は昇ってすらなく固定) 広々とした机で物を極力置かないようにしている 最後にひとこと もう少し大きめのディスプレイが欲しい funzinさんのキーボードのこだわりはやっぱり打鍵感!HHBKを語るうえでは外せないポイントですね🥸 デスクまわりでいうとトラックボールは絶対に外せないというこだわりがあるみたいで、 親指だけでカーソル移動が完結する 親指は疲労に強い(自己認識) とおっしゃってました! トラックボール持っても首振ってクリックしちゃって使いこなせずいつもmagic tracpadに戻ってきてしまう私も、トラックボールに再チャレンジしてみたいなと心に誓いました👏 次行ってみましょう!! takamatsuさん(サーバーサイドエンジニア)の場合 いま使っているキーボード Keychron Q10 Pro このキーボードのおすすめポイント(JIS/US、軸などのこだわりもあれば) Aliceレイアウト ホットスワップ対応 VIAでキーリマップができる 光る その他デスクまわりのこだわりポイント FlexiSpot kvmスイッチを使って仕事用macと個人PCの切り替えをワンタッチでできるようにしてある デスク下のフットレスト 最後にひとこと デスクツアーに載せられるようなこだわりの作業環境にしたい でました!!かっこよい〜〜〜!!! Aliceレイアウトのキーボードやっぱり打ちやすそうですよね🤔 個人的にはkvm確かにいいなと思ったので、私も最高の作業環境にしてデスクツアーに載せられるようにしていきたいと思いました! 次! rakuさん(サーバーサイドエンジニア)の場合 いま使っているキーボード HHKBの墨・雪、choco60、Keychron Q60、HyperX Alloy Origins 60 このキーボードのおすすめポイント(JIS/US、軸などのこだわりもあれば) HHKB配列と60%が大好きです choco60はHHKB配列の分割キーボード、Keychron Q60はKeychron製のHHKB配列です。オフィスではこの二つを気分で使っています 自宅では自作PC用のHyperxのキーボードと、HHKB2つを無理やり分割キーボードとして使っています 雪の方のHHKBはdely入社時の5万円で用意してもらいました!宣伝! その他デスクまわりのこだわりポイント 自宅でちらっと写ってるAbleton Push3のような音楽の機材をデスクに置くとスペースが全然なくなるのでもっと大きい机が欲しい! 最後にひとこと 家にはあと3つキーボードが眠っています でました!HHBK2台で分割キーボードニキ! わたしも60%とHHKB配列好きなのでchoco60よさそうだなと思い始めました! delyに入社すると支給PC・周辺機器の購入補助が最大5万円まであるので、キーボードにこだわったりできるのはいいですよね! わたしはこの制度でモニター買ったのですが、度重なる座席移動でどこかにドナドナされてしまったので、そんなことならキーボード買っておけばよかった… dely.jp Nikoさん(データエンジニア)の場合 いま使っているキーボード Remote : Keychron K3 (low profile), US Office : Normal Macbook Keyboard このキーボードのおすすめポイント(JIS/US、軸などのこだわりもあれば) Keychron K3 デザインがシンプルでもTockの音がほぼいい感じ Macbook Keyboard 入社する時にKeyboardを持つことは面倒なので、これを使ってます その他デスクまわりのこだわりポイント MinimalistのStyleが好きなので、dely Guidebook(会社のVMVなどがまとまっている手帳)とモニターしかない 最後にひとこと USレイアウトのキーボードを使用すると、初期設定がほとんど不要でとても簡単だと思います。 NikoさんはKeychron K3のロープロファイル!!! わたしの最近の作業環境も尊師スタイルになっているので、ロープロファイルのキーボードを結構狙っていたりします。 あと、打鍵感いいキーボードだったりUS配列の設定のメリットも色々ありますよね。 我々のCTOのたけさん ( @masatootake ) も打鍵音好きらしくKlackというデジタルにタイピング音を出すアプリを一時期使ってたみたいです。 tryklack.com それと、たけさんは今回の企画に際して第一声でこんなことをおっしゃっていました。 思想強めですね。 この信仰心に反旗を翻した一人の男の戦いも次にご紹介します。 maruさん(PdM)の場合 いま使っているキーボード MacBookに標準搭載のキーボード。 JIS配列。 このキーボードのおすすめポイント(JIS/US、軸などのこだわりもあれば) 「キーボードを用意する」という無駄な行動をしなくても良いところ。 ハッキングで世界を救わなければいけないとなった時に、「キーボードが違うからパソコン使いづらい」とか言い訳しなくて済むところ。 その他デスクまわりのこだわりポイント 全て純正。あるのはモニターくらい。弘法筆を選ばず。 最後にひとこと 一周回ってJISが至高です。みんなここまで来て欲しい。 彼のこの投稿に対してメンバーから以下のような反応がありました。 oさん < dely内で思想の揺れが生じてるな fさん < 一周まわってないだろw jさん < ハッキングで世界を救うとかまでは考えてなかったですが、maruさんの思想に一票 hさん < 大体世界救わないといけないときにサーバーラックから登場するキーボードってUNIXキーボードのはずで、JIS配列のキーボードが世界救うこと少なさそうだしControlキーもどこだかわからない間に世界滅びてそう oさん < でかすぎるreturnキー間違って押しちゃって世界救うためのミサイル誤発射しそう 圧倒的US配列派の声の大きさ\(^o^)/ そんな中、彼は一言言い放って去っていきました。 maruさん < 自分の思想持たないとダメだと思います!!!他者依存から抜け出しましょう!! 〜完〜 最後に いかがでしょうか? こんなアットホームな職場のdelyではエンジニアを大大大大大募集しています!!! dely.jp 特にサーバーサイドエンジニアの方を重点的に募集しているので、US/JISキーボードどちらをお使いでも結構です! ご応募お待ちしております!
アバター
こんにちは。 クラシルリワードのPMをしている小川です。 クラシルリワードに携わってから半年ほどですが、 純粋なプロダクト開発以外にもできることがあると気づきがありました。 最近は、"コントローラブルな領域"を増やすマネジメントを心がけています。 心変わりのきっかけ クラシルリワードのアフィリエイト事業を例に話を進めていきます。 この事業は下記のようなKPIツリーだったとします。 アフィリエイトのコンバージョン数(CV数)と、その平均単価が売上になります。 「🚫」がついているKPIは、プロダクト開発ではアンコントローラブルだとします。 プロダクトの機能開発等で、ユーザー数は直接上げることはできないものとします。 今まで、「プロダクト開発でコントローラブルな数値を改善に向かわせる」スタンスでプロダクトマネジメントしていました。 仮説を持って、速く細かくPDCAを回していくイメージです。 しかし、そういったプロダクト開発の延長では、 会社から期待されている事業の水準に達せない可能性が出てきました。 達成のために、下記の2点を自分のテーマに開発を進めています。 アンコトローラブルなものをコントローラブルにすること 運用努力ができる余地を作ること アンコトローラブルなものをコントローラブルにすること 解像度を高めたり、プロダクト開発の外に目を向けると、 アンコントローラブルだと思われる部分がコントローラブルになることがありました。 先程は「ユーザー数」がアンコントローラブルと設定しました。 しかし「ユーザー数」をもう少し分解すると以下のようになります。 新規獲得数はマーケチームに依存、 リテンションレートはプロダクト開発チームに依存します。 プロダクト開発チームから見ると、新規獲得数はアンコトローラブルですが、 ここの数値が大きく改善できると、事業には大きな影響を及ぼすことができます。 何とかできないか動き回った結果、 マーケチームが見られるデータを、より高度かつ詳細に見られるように整備することで、打てる施策運用の幅を大幅に広げられることが分かりました。 マーケチームとデータチームを巻き込んで推進し、 結果として、アンコトローラブルだと思われた「新規獲得数」を、プロダクト開発のアプローチでもコントローラブルにすることができました。 また、セールスチームと話していくと、どうやら売上ももう少し細分化できそうで、 アフィリエイトの成果報酬だけでなく、純広告としての売上も作ることができています。 開発とセールスの協働次第で、売上も直接最大化できそうでした。 営業活動をしやすくなる開発をすることで、コントローラブルな領域を生み出すことができそうです。 自分の携わっている事業の全体把握に努めることで打ち手の幅が大きくなり、結果としてコントロールできる領域が大きくなっていきます。 運用努力ができる余地を作ること コントローラブルな領域を広げた後、その領域で運用を頑張れば目標達成できるような状況を作れるとGoodです。 「目標達成のためにやれる運用や検証手段がないです。。」という状況を作ったら負けだと思っています。 既存の仕組み以上にやれることを増やし、頑張れば伸びる領域を見つけにいき、 各チームの努力・アクション数に比例するような設計ができると、 人材開発・採用でも大きく事業を前進させられる 仕組みも作れます。 こういった領域・仕組みを見つけられることが、腕の見せどころの一つかもしれません。 「コントローラブルな領域を作り、そこの伸び代を大きくする仕組みも作る」といった、「運用を最大化できる伸び代を作る」という視点は、個人的には新しいアプローチでした。 各チームの人材はその道のプロフェッショナルなので、そんな彼らの運用がもたらすパワーは強力です。 さいごに プロダクトマネージャーとして働いていると、目の前ユーザーに目を向けがちですが、事業部全体にも目を向けるとやれることが転がってます。 ユーザーが一番大切ですが、他の可能性も潰すのは勿体無いので、ふと周りを見渡してみることも良いと思った経験でした。 そんなdelyではエンジニアを大募集しています!今は特にバックエンドエンジニアを募集しています。 実際にメンバーと話してみたいと思ってくださった方にはカジュアルにお話しする場も設けられますので、どうぞお気軽にお声かけください。 herp.careers dely.jp
アバター
はじめに 立夏も過ぎて気温が上がってくるのを感じる中、少しずつ夏が近づいて来ているのを感じますね。 このテックブログを読んでいただいている皆さんいかがお過ごしでしょうか? 近所でセミがもう鳴いてるのに気付いて温暖化をひしひしと感じている、みうらです。 皆さんAGSLをご存知でしょうか?Androidでシェーダープログラミングが行えるものです。 2022年に公開されてから少し経ちますが、要求されるAndroidバージョンが13と高いこともあり使ったことがなく、どこかで試してみたいと思っていました。 そんな中、少し前ですがGoogleのRebeccaさんから以下の記事が共有されているのを拝見しました。 この記事でAGSLの概要とパーリンノイズについて知ったので、試していきたいと思います。 medium.com パーリンノイズとは? かなり簡単に言うと、ノイズとはランダム値ですが、パーリンノイズは完全ランダムではなく連続性を持っている自然的なランダム値のことです。 雲や水、波といった自然的なものを表現するのに使ったりします。 AGSLでパーリンノイズを使ってみよう では早速AGSLを使ってみましょう。 以下のクラシウサギ画像にAGSLでシェーダーを適用します。 ここでは記事で紹介されているパーリンノイズをそのまま使ってみます。 AGSL用のパーリンノイズは以下のGistを利用しています。 [part] Jellyfish perlin noise · GitHub 次にAGSLを適用するImageを定義します。 @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable private fun UsagiPerlinNoiseImage( modifier: Modifier = Modifier ) { val time by produceState(0f) { while (true) { withInfiniteAnimationFrameMillis { value = it / 1000f } } } val shader = RuntimeShader(PERLIN_NOISE) Image( painter = painterResource(id = R.drawable.kurashi_normal_bow), contentDescription = null, modifier = modifier .onSizeChanged { size -> shader.setFloatUniform( "resolution", size.width.toFloat(), size.height.toFloat() ) } .graphicsLayer { shader.setFloatUniform("time", time) renderEffect = android.graphics.RenderEffect .createRuntimeShaderEffect( shader, "contents" ) .asComposeRenderEffect() } ) } このパーリンノイズが適用されたAGSLを実行してみましょう。 シェーダーとして波のようなアニメーション効果が発生しました。 時間の経過と共に変化するノイズを画像に適用することで、波のような表現が発生しています。 ポジション値に対してノイズを適用してみよう パーリンノイズは他のものにも適用可能です。今度はポジション値に対して適用してみましょう。 画像は以下のクラシウサギ画像を使います。 AGSLのままではポジション値に対して適用することが難しいと思ったため、ここでは疑似的な2次元ノイズ生成メソッドを定義します。 このメソッドはChatGPTで生成したものを一部加工しています。 private fun perlinNoise(x: Float, y: Float): Float { val n = floor(x) + floor(y) * 57 // 57はオフセット値 return sin(n) * 43758.547f } このノイズ生成メソッドからポジション値にノイズを適用させることでアニメーションさせてみます。 Imageにoffsetを適用することでランダムにアニメーションさせます。 @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable private fun UsagiImage( modifier: Modifier = Modifier ) { val time by produceState(0f) { while (true) { withInfiniteAnimationFrameMillis { value = it / 10f } } } val noiseOffsetX = remember { mutableFloatStateOf(0f) } val noiseOffsetY = remember { mutableFloatStateOf(0f) } LaunchedEffect(time) { noiseOffsetX.floatValue = perlinNoise(time, 0f) % 0.5f * 100 noiseOffsetY.floatValue = perlinNoise(0f, time) % 0.5f * 100 } Image( painter = painterResource(id = R.drawable.kurashi_normal_cry), contentDescription = null, modifier = modifier .offset { IntOffset(noiseOffsetX.floatValue.toInt(), noiseOffsetY.floatValue.toInt()) } ) } ポジション値に設定することでガクブル震えるようなアニメーションを作ることができました。 ※GIFだとわかりづらいですが、激しく震えるアニメーションをさせています まとめ 今回はAndroid上でパーリンノイズを使って画像を変化させてみました。 使ってみるととてもシンプルに使える割に、いつもとは違った自然的な印象を与えることができるので、表現の一つとして覚えておくと使えそうです。 最後に delyではエンジニアを大募集しています!今は特にバックエンドエンジニアを募集しています💡 実際にメンバーと話してみたいと思ってくださった方にはカジュアルにお話しする場も設けられますので、どうぞお気軽にお声かけください 🙌 Androidエンジニアも募集しています!下記の採用リンクから応募いただけますのでお願いします! herp.careers dely.jp
アバター
こんにちは、クラシルリワードのiOSエンジニア uetyo です! クラシルリワードでは、くらしうさぎをはじめとする魅力的なキャラクターたちと共に、「使っていて楽しいアプリ」を目指して日々開発に取り組んでいます。私たちのアプリがユーザーに愛される理由の一つは、これらのキャラクターを生き生きと動かすアニメーションにあると考えています。 アプリのリリース初期から、私たちはAirbnbが開発したアニメーションツールであるLottieを活用してきました。Lottieは使いやすさと高い互換性で知られていますが、複雑なモーションを実行しようとすると読み込みに時間がかかってしまうなど、アプリ体験が低下する問題に直面しました。 この問題を解決するために、私たちは「インタラクティブ」アニメーションを実現可能にする新しいツール『 Rive 』を導入しました。 今回はRive導入の良かった点・微妙な点、実際の利用方法などをまとめていきます。 そもそも Rive とは Riveはアメリカシリコンバレーのベンチャー企業である Rive, inc. が開発しているアニメーションツールです。 アニメーションに関することであれば基本的に何でもできます。他のアニメーションツールとは異なりファイル内で条件分岐や外部からの引数引き渡し、 サウンド再生 などができることが特徴です。 シンプルなロゴアニメーション(開くとアニメーションします) https://rive.app/community/files/3157-6670-notion-animation-concept/ 状態によって変化するアニメーション(開くとアニメーションします) https://rive.app/community/files/1030-2020-liquid-download/ アニメーションに合わせてサウンドを再生するアニメーション www.youtube.com クラシルリワードでの利用例(移動タブのお知らせアイコンやオンボーディングなどで利用) 規模の大きいところだと Duolingo がマルチ言語に対応したアニメーションを再生するため積極的に利用しています。Duoをよりスムーズに高い品質で再生するには不可欠とのことです。 www.youtube.com Rive を導入して良かった点 Lottie に比べて動作が軽く、パフォーマンスが高く、できることが多いです。これまでコードで分岐させて実装していたところをデザイナーに依頼することで解決できるためモバイルエンジニアの負担を大きく減らすことができました。 Riveによる比較記事: rive.app 再生時のCPU使用率やメモリ使用量が段違いに違うためアプリパフォーマンスの向上に繋がりました。Riveファイルはバイナリ形式なので読み込み的にも◎ アニメーション作成に必要なツールも大幅に削減できました。 マルチプラットフォームに対応しているのでiOS, Android, Webすべての環境で同一のリソースを何も変更することなく利用できています。 Rive の微妙な点 Riveを利用している中で以下の点が難しいなと感じました。 デザイナーの学習コスト 導入事例の少なさ 公式が提供しているドキュメントの不足 Riveの更新頻度が高くついていくのが難しい Figmaやイラストレーターのようなデザインツールとしての側面とAfterEffectsのようなアニメーションを設定する側面の両方存在するためデザイナーの学習コストが高いです(リワード開発チームでも導入から2ヶ月ほど経過するまではRiveのLottieファイル読み込み機能を用いて変換していました) また、日本で導入している企業が少なく、公式が提供しているドキュメントも完全には網羅されていないため、不具合などはRiveコミュニティやSDKのGitHubリポジトリで相談する必要があります。 https://github.com/rive-app/rive-ios/issues/286 https://github.com/rive-app/rive-ios/issues/284 ここ数ヶ月で追加された新しい機能(アニメーションにサウンドを追加する機能、独自フォントを埋め込んで利用)のドキュメントや知見が不足しているためファーストペンギンの気持ちで望む必要がありました。 最近になり料金形態が変更になり、より柔軟な契約ができるようになったため、日本でももっと流行らせて行きたいですね。 クラシルリワードでの RiveFile 管理方法 アプリでアニメーションを実行するにはRiveから出力したバイナリファイル(以下、RiveFile)のファイル名を指定します。 public struct RiveResource { /// リソースファイル名 let name : String /// リソースのバンドル let bundle : Bundle? public init ( name : String , bundle : Bundle? = nil ) { self .name = name self .bundle = bundle } } extension RiveResource { static let loading = RiveResource(name : Files.loadingRiv.name , bundle : .module) } ファイル名を直で設定しまうと、RiveFileを名前ごと差し替えた際に参照できず、ランタイムクラッシュしてしまうため SwiftGen を用いて定数化しています。 // swiftgen.yml files : - inputs : - AppPackage / Sources / HogeFeature / Resources / Rive filter : - . + \.riv$ outputs : - templateName : structured - swift5 output : AppPackage / Sources / HogeFeature / Generated / Files.swift クラシルリワードはマルチモジュール構成なので、以下のようにアニメーションを一箇所で利用する場合は特定のFeatureに配置、複数のFeatureで使い回す場合は共有のモジュールに配置するようにしています。 . └── Sources / ├── HogeFeature / │ ├── Resources / │ │ ├── super_great_animation.riv │ │ └── sugoku_ii_animation.rive │ └── Generated / │ └── Files.swift └── CommonView / ├── Resources / │ ├── common_loading_animation.riv │ └── minna_tsukau_animation.riv └── Generated / └── Files.swift クラシルリワードでの RiveView 利用方法 Rive公式が提供しているSDKの実装方法ではViewに @StateObject RiveViewModel というものを保持する必要があります。しかし、クラシルリワードのコード規約では極力ViewでStateを持たないようにする必要があるため、簡単なWapperを作成しています。 @MainActor public struct RiveView : View { private let viewModel : RiveViewModel private var animationDidFinish : (() -> Void ) ? public init ( riveResource : RiveResource , fit : RiveFit = .contain, alignment : RiveAlignment = .center, autoPlay : Bool = true ) { self .viewModel = RiveViewModel( fileName : riveResource.name , in : riveResource.bundle ?? .module, fit : fit , alignment : alignment , autoPlay : autoPlay , loadCdn : false , // CDN の読み込みが遅い & 今はローカルリソースで完結しているため customLoader : { _, _, _ in } ) } public var body : some View { RiveViewRepresentable( viewModel : viewModel , animationDidFinish : animationDidFinish ) } } extension RiveView { public func animationDidFinish (_ animationDidFinish : @escaping () -> Void ) -> Self { var copy = self copy.animationDidFinish = animationDidFinish return copy } } private struct RiveViewRepresentable : UIViewRepresentable { private let viewModel : RiveViewModel private let animationDidFinish : (() -> Void ) ? init ( viewModel : RiveViewModel , animationDidFinish : (() -> Void ) ? ) { self .viewModel = viewModel self .animationDidFinish = animationDidFinish } func makeUIView (context : Context ) -> RiveRuntime.RiveView { let view = viewModel.createRiveView() view.playerDelegate = context.coordinator view.stateMachineDelegate = context.coordinator return view } func updateUIView (_ view : RiveRuntime.RiveView , context : Context ) { viewModel.update(view : view ) } func makeCoordinator () -> Coordinator { Coordinator( self ) } class Coordinator : NSObject , RivePlayerDelegate, RiveStateMachineDelegate { var parent : RiveViewRepresentable init (_ parent : RiveViewRepresentable ) { self .parent = parent } func player (playedWithModel riveModel : RiveRuntime.RiveModel? ) {} func player (pausedWithModel riveModel : RiveRuntime.RiveModel? ) { // アニメーションの再生完了時はこのdelegateメソッドが呼ばれるので、再生完了イベントとして扱う parent.animationDidFinish?() } func player (loopedWithModel riveModel : RiveRuntime.RiveModel? , type : Int ) {} func player (stoppedWithModel riveModel : RiveRuntime.RiveModel? ) {} func player (didAdvanceby seconds : Double , riveModel : RiveRuntime.RiveModel? ) {} } } 必要最低限のみ記載するとこのようなViewを用意することで別途StateObjectを定義することなく利用できます。(Wrapper を生やしている関係で新しい機能が追加されたら対応する必要があるのが難点です) VStack(spacing : 0 ) { RiveView(riveResource : .loading) .frame(width : 210 , height : 160 , alignment : .center) } 以上のような形でクラシルリワードではアニメーションを再生する実装をおこなっています。 まとめ クラシルリワードではインタラクティブアニメーションツールの Rive を導入しました Rive は学習コストが高いですが軽量で様々なことができます 今後もクラシルリワードではRiveを用いた使っていて楽しいアプリを開発していきます 🔥
アバター
こんにちは。delyでクラシルリワードのサーバーサイドの開発をおこなっている高松です。 はじめに 今私が開発を担当している「クラシルリワード」は、日々我々が行う購買行動をよりお得に行なってもらうことを目的に作られたサービスです。 dely.jp 私は2023年10月からクラシルからこのクラシルリワードの開発担当として異動してきました。 クラシルリワードにおいては、プロダクトの急成長段階にあり、KPIを伸ばす上で何が適切なのかの解像度が低い状況にあります。 そのため、仮説検証をなるべく早く繰り返すことで、解像度を上げて施策の効果を最大化したい思いがあります。 この記事はそんな中で、開発ルールは現状のもので良いのかを改めて考えてみた経緯です。 トレードオンとは 弊社にはトレードオフと対比する言葉としてトレードオンというどちらのメリットも享受できる方法を考えようという文化があります。 今回はその文化のもと、開発における攻めと守りの両方を意識してみたというお話です。 はじめに トレードオンとは 1. 究極の理想状態を考える どうして不具合が発生してはいけないのか どうして開発者以外要件がわからなければいけないのか 現実的な理想をまとめる 2. 現実的な理想を実現する方法について考える 「全ての機能が不具合なく動くことを担保する」ためには 3. トレードオン状態になるポイントをチームで探る プロダクトとして絶対に担保しないといけないところはどこか それぞれ担保するために必要になりそうなことは何か 4. 現実的な理想と現状の差分を可視化する delyの開発体制とクラシルの開発ルール クラシルリワードにおいて今の開発ルールは適切か 5. 開発ルールを変更する 変更による効果 6. まとめ 1. 究極の理想状態を考える スタートはいろんな制約を考えず、どういった状態であったら最高なのかを考えるようにしています。 仮説検証をなるべく早く繰り返したいプロダクトの状態においてサーバーサイドの開発に関しては、「リリースしたい機能を誰でもすぐにリリースできる状態」かなと考えました。 この状態は技術的に実現できるか否かでいうとどうでしょうか。 実現できなくはなさそうですが、同時に下記のような問題が発生することが想像できます。 個人で気が付かない不具合が含まれている可能性がある 開発者以外要件と目的がわからなくなる どうして不具合が発生してはいけないのか システムの不具合 = プロダクトが意図通り動いていない状態 = 利用者に想定の価値を提供できていない つまり 利用者の目線で言えば、「プロダクトを使っている目的を果たせなくなる」 我々サービス提供者目線で言えば、「達成したい目標を達成できない」 と、当然ながら誰も幸せにならない状態になってしまうため、不具合は基本的には発生させてはいけません。 どうして開発者以外要件がわからなければいけないのか こちらはどうでしょう。 チームで継続して開発をする上で既存の要件を制約として満たさなければいけないため さらに深ぼると 既存の要件を無視して開発を継続することで不具合が発生する可能性があるため つまり、こちらも結局は不具合の発生の温床になるためという結論になりました。 現実的な理想をまとめる 結論我々の理想は「リリースしたい機能を "全ての機能が不具合なく動くことを担保した上で" 誰でもすぐにリリースできる状態」という制約付きのものであるという考えに至りました。 2. 現実的な理想を実現する方法について考える ひとまず現実的な理想が リリースしたい機能を誰でもすぐにリリースできる状態であること 全ての機能が不具合なく動くこと であることとしたので、それぞれどうすれば実現できそうかを考えます。前者に関しては冒頭で考えたので、後者に関して考えてみます。 「全ての機能が不具合なく動くことを担保する」ためには そもそも開発者全員不具合を出さないように開発をしているはずなのに、個人で気が付かない不具合が含まれてしまうのはなぜでしょうか。 正常動作の確認は簡単であるが、異常動作の全てを確認することが容易でないため。 開発箇所の影響範囲が不明確なため、開発者の意図しない箇所の挙動が変わる可能性があるため。 本番のトラフィック量やデータ量など環境依存で発生する要因の確認が難しいため。 まだまだあるかと思いますが、この辺りを要因として挙げました。 それぞれの要因に対して防ぐ方法はどんなことが考えられるでしょうか。 正常動作の確認は簡単であるが、異常動作の全てを確認することが容易でないため。 各開発者のノウハウの共有 = 技術力の底上げ? 開発箇所の影響範囲が不明確なため、開発者の意図しない箇所の挙動が変わる可能性があるため。 全ての機能において網羅的なテストケースの作成? 本番のトラフィック量やデータ量など環境依存で発生する要因の確認が難しいため。 リリース前の負荷テストの実行? 深ぼってみると、開発者個人の頑張りよりは仕組みや状況で解決する方がよさそうでした。 どのような仕組みや状況用意することでを「全ての機能が不具合なく動くことを担保する」ことができるでしょう 厳密なQA体制ができていること? 強固なシステムテストがつくれていること? 実装ガイドラインが細かくできていること? PRレビューのapproveガイドラインが細かくできていること? 完全なドキュメントが整備されていること? 本番環境相当の検証環境が用意されていること? と、俯瞰してみてみると 不具合の発生確率 と 開発速度 においてトレードオフが発生してしまいそうです。 つまり、現実的な理想である「全ての機能が不具合なく動くことを担保する」ことと、「リリースしたい機能を誰でもすぐにリリースできる状態にする」ことはトレードオフの関係であることが改めてわかりました。 3. トレードオン状態になるポイントをチームで探る ここからは認識をそろえるために、クラシルリワードのサーバーサイドチームメンバーや適宜プロダクト開発の意思決定者も交えて話し合いをしていきました。 プロダクトとして絶対に担保しないといけないところはどこか コインやガチャチケットなど売り上げに直結する情報が正しく更新・表示されること = 利用者が受け取れるはずの報酬をちゃんと配布できること = 不正利用によって報酬が配布されないようにすること 利用者がクラシルリワードに来た目的を安全に果たせること = アプリのどのページもいつでも開けること = 個人情報などセキュアな情報がちゃんと取り扱えていること 担保しなければいけない点はたくさんありますが、プロダクトの特性上大まかに「金銭に関わること」「個人情報に関わること」「アクセスできない状態を避けること」のポイントに絞られました。 それぞれ担保するために必要になりそうなことは何か 結論としては、「用意するテストケース」「監視体制」「レビュー観点」という原始的な部分の再認識となりました。 この段階では開発ルールの変更は何もされていないものの、チーム内で「守るべきこと」「攻めていい箇所」の感覚が擦り合って来たと思います。  また、開発ルール変更とは別軸ですが、弊社のエンジニアjoeさんが用意してくれた、SLIによってまだ攻められるか、攻めすぎかを定量的に見えるような工夫もできました。 tech.dely.jp 4. 現実的な理想と現状の差分を可視化する 当時クラシルリワードでは、先輩プロダクトであるクラシルでの知見や反省を活かすために、開発ルールも基本踏襲した形をとっていました。 delyの開発体制とクラシルの開発ルール delyではプロダクトを開発する際、プロダクトの主要KPIから作られるKPIツリーにおいて、注力するべきKPI毎にプロダクト開発チームを作り自走して開発ができるようにしています。 そんなプロダクト開発チームにおいて、サーバーサイドのエンジニアはこんなフローで開発をしています。 プロダクト開発チームが解決するべき課題と仮説をもとに次にリリースする施策を決定する プロダクト開発チームで、施策の実現方法を検討する 施策実現のために必要なサーバーサイドの開発内容を設計書としてまとめる 他のサーバーサイドメンバーが設計書をレビューする(3名ランダムアサイン。2名のapprove必須) 設計書のレビューが完了したら、設計書に則った形で実装をする 他のサーバーサイドメンバーが実装をレビューする(1名ランダムアサイン。approve必須) 実装のレビューが完了したら、リリースする 特色としては、実装前に仕様書・設計書という名目でドキュメントを作成し展開することで、 この設計で目的を達成できるか、問題になるところはないか が議論でき、「別のプロダクト開発チームに属するサーバーサイドエンジニア」「将来ジョインするサーバーサイドエンジニア」「将来の自分」に向けて、 開発をする目的は何か この開発をするに至った経緯 が残せることがメリットとして大きくあります。 実装時のレビューにおいても、コーディングにフォーカスできるため、実装レビューの負担を少なくすることができています。 クラシルの開発ルールのメリットをまとめると下記です 各箇所の開発の意図や目的がドキュメントとして残る 実装前にレビューが入るので、実装の手戻りが発生しにくい クラシルリワードにおいて今の開発ルールは適切か ここは個人的な感覚として、クラシルリワードにおいてクラシルの開発ルールを踏襲するのは、下記の理由からオーバーキルであると感じました。 設計と実装で2回のレビューが入ることは開発者もレビューする側もかかる時間が大きい かかる時間に対して、チームで取り決めた「守るべきこと」を担保できる効果が薄い 全メンバーが設計に目を通すわけではないので、設計の段階では一部のメンバーにしか開発経緯や目的が伝わらない 5. 開発ルールを変更する 上記の理由から、下記の通り開発ルールを変更しました。 実装前のドキュメント作成の義務は撤廃 理由は上記の通り、これにかかる時間に対して「守るべきこと」を担保できる効果が薄いため 開発量が膨大だったり、事前に伝えておきたいと開発者が感じた場合はドキュメントの作成・レビュー依頼共にOK 実装レビュー依頼時のランダムアサインを撤廃 開発者以外で経緯を把握しているメンバーや過去に同じ領域を開発していたメンバーによってレビューされた方が「守るべきこと」は担保されやすいため ドキュメントは仕様書という役割で、実装後に「どんな経緯で」「何を目的に」実装されているかをまとめるようにする 将来の自分達のために価値のあるドキュメントを残しておくことの重要性は、クラシルの経験から引き続き踏襲したいため 変更による効果 「1日のデプロイ頻度の向上」「変更障害率の低下」が数値としてはみられています。 定性的には、ひとまず施策リリースのために#FIXMEとして残しておいた部分を後から改修しやすくなったため、デプロイに対するハードルが下がり施策リリースサイクルは上がっていると感じます。 6. まとめ 今回は開発効率の改善をテーマに考えたこと、行ったアクションをまとめてみました。 一概に開発効率の改善といっても、こんな開発ルールにすれば必ず効率が上がります!という所謂銀の弾丸と呼ばれるものは無いのかなと思った一方で、 フレームワーク的な見方として、組織の文化や今のプロジェクトにおける開発で重要視される点を具体的にしていくと、現状に対して何を改善する必要があるのかは見えてきたりするのかなという学びがありました。 また、個人的に今回の件に限らず抽象度の高い問題に対しては 究極的な理想を考える 究極的な理想に対して、発生してしまう制約を洗い出して現実的な理想を定義する 現実的な理想と現状の差分を可視化する 差分を埋める方法を考える やってみる 効果を見る こんな感じのステップで考えるようにしています。 また、他の人も巻き込みながら何かを進める際は下記のことが大事かなと思っています。 当たり前と思われることも言語化する 原理原則から認識をそろえる この考え方が必ずいいというわけではないと思いますが、どなたかの参考になれば嬉しいです。
アバター
こんにちは。AndroidエンジニアのJです。 今回はFirebase Device StreamingがOpen Alphaになったようなので実際に使ってみようと思います! firebase.blog Firebase Device Streamingとは developer.android.com Android Studio のデバイス ストリーミングを使用すると、Google の安全なデータセンターでホストされているリモートの物理的な Android デバイスに安全に接続できます。Google Pixel 8、Google Pixel 8 Pro、Google Pixel Fold、一部の Samsung デバイスなど、最新の Android デバイスの物理ユニットに対して、アプリを迅速かつ簡単にテストできます。 Android Studioで物理デバイスを使用しない場合、emulatorを使っての開発となりますが、Device Streamingを使うと実際に物理デバイスに接続してテストをできるとのこと。 個人的にemulatorは物理デバイスよりもっさりしていてあまり使う機会がないのですが、動作のスムーズ感も気になるところですね 👀 さっそく試してみる 前掲の記事 の内容に従って、 最新のCanaryバージョンをインストール してみます。 しかし ダウンロードできたので、手順に従ってDevice Managerを開いてみます。 ワイヤレスデバッグのアイコン横にFirebaseのアイコンがでるはずなのですが、出てこない...? 🤔 どうやら最新のCanaryバージョンでないと使えないようでした...。Check for Updatesを実行してKoala🐨にアップデートした *1 ところ無事表示されました! 実際に使ってみる ようやく準備ができたので触っていきます。 Android Studio上でFirebaseとの連携を完了させると以下のように使用可能なデバイス一覧が表示されます。 Google / Samsung端末がメインで、APIレベルは27から対応されているようです。 まずは現在の最新Google端末であるPixel8を使ってみましょう。 端末を▶で起動させるとReservingのステータスになり、1分ほどで使用可能な状態になりました。 まだOpen Alphaのためか、使用時間は15分程に制限されているようですね。 *2 使用感について 気になる使用感ですが、やはり実機と比べると少し動きがもっさり *3 しますね 🤔 (遠隔で物理デバイスを操作するという形なので、ある程度仕方のない部分かもしれません) また、すべての端末を確認したわけではないのですが、カメラ機能についてはPixel Fold(Camera-enabled)という端末でのみ使用できるようでした。 他の端末でカメラが使えないわけではないのですが、カメラを起動しても真っ暗な画面のままなので実用はできないかなといった印象です。 おわりに さて、今回サクッとDevice Streamingをお試ししてみました。 使っみての感想ですが、現状の機能だとあまり適切な使い所が思い浮かばないというのが正直な感想でした😅 動作のレスポンスは仕方ないにしても、ウリにしている機種依存のバグ調査も現状のデバイスラインナップだとメジャーどころが多くてあまり強みを活かせなさそうでした。 とはいえ、実際に物理デバイスを使用出ること自体は嬉しいポイントだとは思っていて、例えば個人開発者やスタートアップ企業など検証端末が十分に利用できない状況だと、Device Streamingを使った実機検証が役に立ちそうです。 まだOpen Alphaの段階なので、これからの進化に期待しようと思います 💪 *1 : https://firebase.blog/posts/2024/02/device-streaming-android こちらを見るとJellyfishであれば使えるかと思っていたのですが、思わぬ落とし穴でした *2 : タイムリミットの5分前になると30分延長ができるようでした *3 : キャプチャを載せたかったのですが、容量の関係で省略しました🙏
アバター
クラシルリワード プロダクトデザイナーのredです。 クラシルリワードでは、新規事業の立ち上げから担当しており、現在はデザインとプロダクトマネジメントの兼務で開発に携わっています。 クラシルリワードではアプリのローンチ時点でデザインシステムを構築しており、グロースフェーズである現時点でもローンチ時点で設計したものを運用してデザイン・開発を効率的に進めることができています。 そこで今回は、0→1段階でプロダクト・事業のスケールを見越してデザインシステムを構築することのメリット等について書いていきます。 ローンチ時点でデザインシステムを構築するメリット この記事では、プロダクト開発においては「ローンチ→PMFを目指す→グロース」の3つのフェーズがあるという整理で話を進めます。 ローンチ時点でスケールに備えることが必要だと考える理由とそのメリットについては、以下のようなものがあると考えています。 1.PMF達成へのスピード感のある開発 プロダクトのローンチからPMFを達成するまでの道のりでは、大なり小なりアップデートを重ねて試行錯誤する開発が必要になるかと思います。この過程で開発サイクルのスピードを上げるために、効率的な開発の土台となるデザインシステムがローンチ時点で存在することは重要だと考えています。ローンチ時点から生産性の高い状態にしておくことで、PMF到達に向けた開発サイクルのスピードを上げることにも繋がります。 2.チーム拡大への対応 PMFを達成すると、多くの場合、プロダクトのさらなる成長を目指してリソースが増えチームが拡大するかと思います。人が増えると、生産性と品質の維持向上のためデザインシステムの重要性は更に増していくため、0のタイミングから組織のスケールに対応しやすい状態にしておくことは有用であると考えています。 3.グロースへの集中 グロースフェーズに突入すると、プロダクトの成長スピードを更に上げるために、機能追加やKPI改善に全集中する組織力学が働くケースが多々あるかと思います。そういったケースが発生している場合や、機能追加・改善を重ねて複雑性が増した状態で、いざ負債を返済しましょう、デザインシステムを作りましょうという話になると、ローンチ前に構築するよりもコストがかさみ、重めの意思決定になることが予想されます。「ローンチ後にやろう」「いつかやろう」はなかなか来ないものだと考えて、先んじて基盤を用意しておいた方が生産性向上の恩恵を受ける面が大きいのではないかと考えています。 意識したいこと 上記の考え方を取り入れる上で意識しておきたいことは以下になります。 当然、0→1を成功させるためローンチ→PMF達成が至上命題であり第1優先事項。0→1時点でデザインシステムを構築すること自体はPMF達成の必要条件ではない。1→10以降のデザイン・開発効率をスムーズに進めるための手段であること。 再利用性が低い要素をコンポーネントとして不必要に定義しないなど、オーバーエンジニアリングにならないように注意を払うこと。また、再利用性の低いユニークな要素が生まれることを許容し寛容になること。 0→1デザイン段階で見えているプロダクトのビジョン・青写真を可能な限りインプットして、再利用性が高いもの・高くなりそうなものを予測する。プロダクトをこれから長く運用することを想定して、デザインシステムに一貫性や柔軟性をもたせる。 デザインシステムを最初から完璧な設計にするのは不可能であり、プロダクトと同様にリリース後の改善・運用が必要なものと捉える。 これらの点を意識することで、0→1の段階でデザインシステムを構築する際のバランスを見極め、長期的な視点でプロダクトの成長をサポートする土台を築くことを目指すのが理想です。 まとめ クラシルリワードはリリースから1~2年ではありますが、頭をかかえる程のデザイン的負債はなく、生産性高くデザインを進めることができてきました。 ローンチ以降、機能追加・改善を順調に積み重ねてきており、0のタイミングで長期を見据えたデザインシステムを構築することによる恩恵を受けることができました。 無論、最も重要なのは仮説検証を進めるための速やかなローンチではあるのですが、作る前から未来のスケールに備えることには一定の価値があると考えています。 これから0→1をやるプロダクトデザイナーの方にとって何かしらの参考になれば幸いです。
アバター
こんにちは、クラシルリワードというサービスでSREときどきサーバーサイドを担当しているjoooee000です。 外の空気がほんのりと暖かくなり、春の訪れを感じさせる今日この頃、心まで軽やかになってきました。冬の長い間、凛とした寒さに耐えていた私たちにとって、この季節の変わり目はまさに待ちに待った瞬間です。 ということで、みなさんのチームではSLOを導入していますか?クラシルリワードのサーバーサイドチームでは、SLOを導入しています。 SLOの設定や浸透、導入ってとても難しいですよね。リワードサーバーサイドチームも、SLO導入までの道のりで最初の成果を実感するまでは何回ももやっと感覚を味わってきました。 この記事では、SLO導入の軌跡と、導入方法や改善したことなどを書いていきたいと思います!一つのケースとして、参考になれば幸いです。 導入当時の課題感 SLOの導入は、もともとサーバーサイドチームで下記のような課題感を持っていたことが発端でした。 アラート設定の閾値がオレオレ 今設定している閾値より、もっと早い段階でユーザーが不便と感じるかもしれない パフォーマンスが下がっていても気づくことができない (明らかに障害というレベルでしか気付けない) パフォーマンス改善に対する優先順位の意思決定ができない クラシルリワードはiOS / Android / webでサービス利用できるようになっていますが、まずはサーバー側の計測とスコープを区切り、まずいことが起こったときにアクションが取れるようにしようという意思でSLOの導入をすることにしました。 SLOを導入した結果 まずSLOを導入した結果どうなったかを先にいうと、 大幅なレイテンシ悪化(障害までにはならない)に気づくことができた いくつかの機能でレイテンシを改善し、SLOを満たせるようなパフォーマンス改善ができた🎉 継続的にパフォーマンスがウォッチできる様になった やらないこともSLOを基準に決められるようになった 頻繁にtimeoutが発生しているようにみえていた外部サービスも、計測したらSLOの範囲内だった 等 改善前後の1ヶ月間のSLO(before) 改善後の直近1週間のSLO(after) SLO導入の軌跡 結果的に改善事例が出来ましたが、そこまでにはいろいろな試行錯誤があった(まだまだ試行錯誤中)なので、導入の軌跡を紹介できればと思います。 導入初期 ~ SLO設定編 ~ まずはじめに、上で書いた課題感からSLIと、SLO基準値を決定することにしました。 SLIの決定は、まずはシンプルにAPIのレイテンシと可用性(5xxエラー)にしました。 この2つの指標は、以前サーバーサイドチームのメンバーで「クラシルリワードを開発するうえで担保すべきこと」を話し合った結果のうちの2つを指標にしています。 下記が、話し合いの結果チームで決定した 「サービスを開発するうえで担保すべきこと」 です。 コインやチケットなど、売上に直結する情報が正しく更新・表示される ユーザーがサービスに来た目的を安全に果たせる 動作が快適 ⇒ レイテンシ エラーが起きない ⇒ 可用性 個人情報などセキュリティ面の担保 次にSLOの設定ですが、下記の2項目を考慮しながら最初は最低限のSLOを引くことにしました。 他社の指標などの参考値 「今のレイテンシであれば、許容できる」というEM(兼PDM)のセリフ しかし、いざごく最低限と思われるラインのSLOを設定してみると、レイテンシのSLOがすでにガッツリ抵触していました。深堀りしていくと、一部の外部サービス連携のある機能のレイテンシに引っ張られているという事がわかりました。その中で下記のような疑問が湧いてきました。 どの機能も同じSLOでいいのか? 外部サービスとの依存が激しい機能で、一部外部サービスの基準に合わせてレイテンシを許容する意思決定をしている箇所が存在する 不正防止チェックが重要で、一部レイテンシを許容している箇所が存在する レイテンシを許容しなければならない箇所が混ざっている状態で、一律でSLOを設定することに意味はあるのか、SLOが下がったときにどこを改善するかの判断が難しくないか、などを考えました。 機能ごとにSLOを分割することに 上記のもやもやがあり、機能ごとにSLOを分割してみることにしました。外部サービスへのリアルタイムのリクエストが発生する機能も一部だったため、機能ごとに分割することでより明快なSLOを設定できると思いました。また、機能ごとに開発メンバーの責任者が決まっていたため、各メンバーやチームがオーナーシップをもって改善しやすいこと、チームによっては、一時的にレイテンシを犠牲にしてスピード開発するPoCを行っているパターンも存在していたため、機能ごとのメリットがあると考えました そこで、エンドポイントのpathごとに意味のある機能にまとめて、SLOを設定しました。 機能ごとに分割することで、一律ですべて同じSLOを設定しているときよりは納得感のあるSLOが設定できました。また、どのメンバーが修正するべきかの責任範囲と、どこの機能でレイテンシが悪化しているか明確になりました。 導入中期 ~ 可視化編 ~ 次に、SLOダッシュボードを作成してサービスの健康状態を可視化しました。 1ページで各機能のSLOを見れるようにし、SLOがどういう動きをするか観察できるようにしました。 ダッシュボード作成やSLOやエラーバジェットのモニタリングはNewRelicのServiceLevel機能を用いて行いました。 また、下記terraformで設定することでNewRelicのSLO機能の作成と、サービスごとのSLOをまとめて閲覧できるダッシュボード(機能ごとのSLOをタブ化)とを自動生成できるようにしました。 対象機能 機能ごとの対象エンドポイント 例) /api/v1/surveys/* SLO 機能ごとのSLOダッシュボード この時点では、サーバーサイドチームにダッシュボードを共有し、SLOの活用方法として5xxアラートがきたときに修正対応すべきかどうかという基準にのみ利用していました。例えば、外部サービスとのつなぎ込みがある機能のtimeoutをどの程度気にして改善するかなどです。SLOに抵触していない限りは、対応不要という意思決定ができるようになりました。 しかし、SLOが下がったときにはどういうアクションをとるかということに関しては、仕組み化出来ていませんでした。SLOを普段目にすることがなくそのうちSLOの存在を忘れてしまいそうな状況でした。 現在 ~ アラート設定編 ~ 上で述べた通り、新しいフィーチャーのデプロイなどによりSLOに抵触した時、対応するフックやルールが何も無いため、そのうち見なくなるだろうなという感覚がありました。 SLOに抵触した際に気づけるよう、アラートの設定をしました。 まずは、SLOのバーンレートアラートを設定し、slackのwarnチャンネルに流せるように、さきほどのダッシュボードの作成時に、terraformによって同時にNewRelicアラートが設定されるようにしました。 (リワードのサーバーサイドチームでは、アラートチャンネルがcritical / warn / noticeチャンネルに分類されており、warnチャンネルは、「オンコール担当者(サーバーサイドメンバー含む)が業務時間内に見て対応が必要かどうか判断する必要がある」という基準を設けています。) SLO slackアラート しかし、アラート化するとメンバーには絶対に対応するべきもの、または対応が必要かどうか都度判断するべきものとして今まで以上に認識されるようになります。ここで、SLO設定時の「本当に追うべき指標になっているのか」「メンバーの時間をとって改善すべきと強く言えるか」という問題と再度向き合う必要がありました。 そのときのもやもやポイントとして、設定したSLOの値の妥当性がわからないというところにありました。 そこで、機能ごとに分割してあるレイテンシのSLOをさらに、GET / POSTごとのリクエストメソッドでも分割しました。このことで、なぜその値に設定しているかわからないSLOよりは、GETで外部サービスとのつなぎこみもなくこの閾値を下回っているのは、なにか実装面で問題がありそう。というように、より指標を一般化できると思いました。例えば、GETとPOST関係なく500ms、99%を目指しましょう。というと、GETだと妥当かな、POSTだとちょっと厳しい、というようにより共通の感覚が持てます。 また、サービスの性質上、POSTリクエストに不正リクエストを防ぐための重めの処理が入っている箇所もあり、サービスによってはPOSTのレイテンシに引っ張られてGET系のレイテンシの低下を正しく判断できない箇所もありました。そのため、確認したところで「POSTなので許容します」みたいなコミュニケーションになってしまうのではと思っていたこともあります。 実際にやってみると、わりとしっくり来るSLOを設定することができました。基準として、 各サービスごとに一律でGETとPOSTリクエストで基準となるSLOを作成 その上で、外部サービスの連携があり一部レイテンシを許容する意思決定をしている機能に関しては、外部サービスが提示している目標レスポンスタイムを基準となるSLOに足す これで無理なくSLOを守っていける、アラートが上がったらかなりの高確率で実装上の問題があるという感覚のSLOを設定できました。 また、対応ルールは最初は修正を強制にせず、各機能チームの意思決定に委ねるという運用から始めました。 アラート化することで、SLOのチーム内の認識度が上がった ちょうどそのタイミングで、チームが注力している機能のレイテンシが大幅に下がる事象が発生しました。 (計測してて良かった!) この頃には、当初SLOを計測し始めた頃よりも大きくサービスが成長し、些細な実装がレイテンシに影響するようになってきており、SLOの必要性も当初より上がってきたように思います。 そして、SLOに抵触したサービスに関わっていたメンバーが、迅速に対応してくれて、SLOに基づいてパフォーマンス改善をすることが出来ました。 パフォーマンス改善によってレイテンシをV字回復してくれたメンバー チームでSLOを意識するようになってきた 最近では、サーバーサイドチームでSLOを意識することが出来ています。 アラートにしたことでチームに認識されて改善点の意見もでてきたり 開発速度の計測を担当してくれているメンバーが「意識すること」で言及してくれたり 開発速度計測レポートの一部 レイテンシを改善したことによって事業にどういうインパクトがあったかを計測してくれたりしました。 (このときの改善は残念ながらあまりKPIには影響がなかったので、SLOを下げた) また、パフォーマンス改善などによって技術的知見を得る機会も増えたため、サーバーサイドチームのMTGに技術シェアというコーナーができました 🎉 チームミーティングの議事録の一部 まとめや今後の展望 クラシルリワードのサーバーサイドチームでは、機能ごと、リクエストメソッドごとにSLOを計測しています。 また、まだまだSLOの運用を始めたばかりなので、今後は下記にも注力できたらなどと妄想しています。 バックエンド側に閉じないSLOの設定やモニタリング SLOの値の調整 今も、パフォーマンス改善した際にKPI周りをみて変化がなければSLOを緩めてみたりしています SLOと離脱率の相関の分析 最後まで目を通していただき、ありがとうございました。
アバター
はじめに こんにちは、クラシルリワードのサーバーサイドエンジニアのrakuです! 今回は趣味でRemixを使用した複雑なフォームの実装をする際に便利だった、React向けのtype-safeなフォームライブラリであるConformについてご紹介します。 Conformは、RemixやNext.jsでFormDataの検証をサーバーサイドでも簡単に実装できるため、これらのフレームワークとの相性が抜群です。そのため、Remix Resourcesでも紹介されています。 remix.run またConformの特徴を公式ドキュメントから引用すると↓と書かれています. Progressive enhancement first APIs. Type-safe field inference. Fine-grained subscription. Built-in accessibility helpers. Automatic type coercion with Zod. conform.guide Remixとの連携 一般的にフロントエンドとバックエンドのあるシステムでは、入力値のバリデーションをフロントエンドとバックエンドの両方で行うことが望ましいです。 ConformはRemixの action 関数と useActionData フックを使ってサーバーサイドとクライアントサイドの連携を実現し、zodで定義した型スキーマを使ってデータのバリデーションを行います。 Conformを使うことでRemixのサーバーサイドとクライアントサイドの処理をシームレスに連携できます。 サンプルコード 以下は、RemixとConformを使った動的なフォームのサンプルコードです。 UIコンポーネントにshadcn/uiを使用しています ui.shadcn.com import { getFormProps , getInputProps , useForm } from "@conform-to/react" ; import { getZodConstraint , parseWithZod } from "@conform-to/zod" ; import { ActionFunctionArgs , json } from "@remix-run/node" ; import { Form , useActionData } from "@remix-run/react" ; import { FC , useEffect } from "react" ; import { z } from "zod" ; import { Button } from "~/components/ui/button" ; import { Input } from "~/components/ui/input" ; import { Label } from "~/components/ui/label" ; const schema = z. object ( { title: z. string ( { required_error: "タイトルは必須です" } ), lists: z . object ( { name: z. string (), location: z. string () } ) .array () .nonempty ( "アイテムを追加してください" ), } ); export const action = async ( { request } : ActionFunctionArgs ) => { const submission = parseWithZod (await request.formData (), { schema } ); console .log ( submission.reply ()); return json ( { message: "エラー" , submission: submission.reply ( { formErrors: [ "エラーメッセージ" ] } ), } ); } ; const ErrorMessage: FC < { error?: string [] } > = ( { error } ) => { return < div className = "text-red-500" > { error } < /div >; } ; export default function TestPage () { const actionData = useActionData <typeof action >(); const [ form , fields ] = useForm ( { lastResult: actionData?.submission , constraint: getZodConstraint ( schema ), onValidate: ( { formData } ) => { return parseWithZod ( formData , { schema } ); } , } ); const lists = fields.lists.getFieldList (); useEffect (() => { if ( ! actionData?.submission ) return; console .log ( actionData ); if ( actionData.submission. status == "error" ) { alert( actionData.message ); } } , [ actionData ] ); return ( < Form method = "POST" className = "flex flex-col gap-4 p-4" { ...getFormProps ( form ) } > < ErrorMessage error = { fields.title.errors } / > < Label htmlFor = { fields.title.id } className = "block text-sm font-medium text-gray-700" > 買い物リスト < /Label > < Input { ...getInputProps ( fields.title , { type : "text" } ) } placeholder = "Title" / > < ErrorMessage error = { fields.lists.errors } / > { lists.map (( item , index ) => { const itemFields = item.getFieldset (); return ( < div key = { index } className = "grid grid-cols-2 gap-4" > < div > < Label htmlFor = { itemFields.name.id } className = "block text-sm font-medium text-gray-700" > 名前 < /Label > < ErrorMessage error = { itemFields.name.errors } / > < Input className = "border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-500" { ...getInputProps ( itemFields.name , { type : "text" } ) } / > < /div > < div > < Label htmlFor = { itemFields.location.id } className = "block text-sm font-medium text-gray-700" > 場所 < /Label > < ErrorMessage error = { itemFields.location.errors } / > < Input className = "border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-500" { ...getInputProps ( itemFields.location , { type : "text" } ) } / > < /div > < /div > ); } ) } < Button type= "button" onClick = { () => form.insert ( { name: fields.lists.name , } ) } > 追加 < /Button > < Button type= "submit" > 登録 < /Button > < /Form > ); } ZodとConformの連携 Conformは、Zodと組み合わせることで、型安全なフォームとバリデーションを実現します。 まず、Zodを使ってフォームのスキーマを定義します。ここでは、 title と lists の2つのフィールドを定義しています。 lists フィールドは、 name と location を持つオブジェクトの配列で、 nonempty をつけることで空の配列を許容しないようにしています。 const schema = z. object ( { title: z. string ( { required_error: "タイトルは必須です" } ), lists: z . object ( { name: z. string (), location: z. string () } ) .array () .nonempty ( "アイテムを追加してください" ), } ); またformのvalidate結果のエラーメッセージもここで定義します。 次に、フォームの送信時に実行されるremixのaction関数を作成します。ここではConformの parseWithZod 関数を使って、フォームの値をZodのスキーマに従ってパースします。 export const action = async ( { request } : ActionFunctionArgs ) => { const submission = parseWithZod (await request.formData (), { schema } ); console .log ( submission.reply ()); if ( submission. status !== "success" ) { return submission.reply (); } return submission.reply ( { formErrors: [ "エラーメッセージ" ] , } ); } ; Zodを用いた入力値の検証結果は、parseWithZod関数が返すオブジェクトの中に格納されています。submission.statusプロパティの値が"success"である場合、入力値が定義されたスキーマ通りであることを示しています。一方、検証でエラーが発生した場合は、submission.reply()を呼び出すことで、エラー情報とユーザーが入力したデータをレスポンスとして返すことができます。 submission.valueからは、フォームの各フィールドの値を取得できます。ここで得られる値は、Zodのスキーマに基づいて適切な型に変換済みとなっています。 useForm useActionData フックを使うことで、actionからの戻り値を取得することができます。この結果を useForm フックの lastResult に渡すことで、サーバーサイドのバリデーション結果をクライアントサイドで反映できます。 const lastResult = useActionData <typeof action >(); const [ form , fields ] = useForm ( { lastResult , constraint: getZodConstraint ( schema ), onValidate: ( { formData } ) => { return parseWithZod ( formData , { schema } ); } , } ); ただし、 lastResult は省略可能なので、フォーム側でactionの状態を扱う必要がなければ省略することもできます。 また、onValidateにparseWithZod関数を使うことで、サーバーサイドと同じバリデーション処理をクライアントサイドでも1行で記述できます。 動的なフォームの実装 Conformを使うと、動的にフィールドを追加・削除できるフォームを簡単に実装できます。サンプルコードでは、 lists フィールドが動的なフィールドになっています。 const [ form , fields ] = useForm ( { lastResult , constraint: getZodConstraint ( schema ), onValidate: ( { formData } ) => { const res = parseWithZod ( formData , { schema } ); return res ; } , } ); 次に、 useForm フックを使ってフォームの状態を管理します。 lastResult には、サーバーサイドから返ってきたバリデーション結果を渡します。 constraint には、先ほど定義したスキーマを渡します。 onValidate には、フォームのバリデーション処理を記述します。ここでは、 parseWithZod 関数を使って、フォームのデータをスキーマに基づいてバリデーションしています。 const lists = fields.lists.getFieldList (); getFieldList メソッドを使って、 lists フィールドの動的なフィールドリストを取得します。これにより、 lists フィールドの要素を動的に追加・削除できるようになります。 inputではgetInputPropsヘルパーを利用することによってa11yや冗長な記述を自動で追加することができます。 conform.guide { lists.map (( item , index ) => { const itemFields = item.getFieldset (); return ( < div key = { index } className = "grid grid-cols-2 gap-4" > < div > < Label htmlFor = { itemFields.name.id } className = "block text-sm font-medium text-gray-700" > 名前 < /Label > < ErrorMessage error = { itemFields.name.errors } / > < Input className = "border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-500" { ...getInputProps ( itemFields.name , { type : "text" } ) } / > < /div > < div > < Label htmlFor = { itemFields.location.id } className = "block text-sm font-medium text-gray-700" > 場所 < /Label > < ErrorMessage error = { itemFields.location.errors } / > < Input className = "border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-500" { ...getInputProps ( itemFields.location , { type : "text" } ) } / > < /div > < /div > ); } ) } lists フィールドの要素をマップして、動的なフィールドを描画します。 getFieldset メソッドを使って、各要素のフィールドセットを取得し、 name と location のフィールドを描画します。 < Button type= "button" onClick = { () => form.insert ( { name: fields.lists.name , } ) } > 追加 < /Button > 最後に、 form.insert メソッドを使って、 lists フィールドに要素を追加するためのボタンを追加します。 name 属性には、 fields.lists.name を指定します。これはConformのIntent Buttonと呼ばれる機能で、これにより簡単に動的なフォームの実装をすることができます。 conform.guide まとめ RemixとConformを組み合わせることで、型安全で動的なフォームを簡単に実装でき、Intent Buttonの機能を使えば複雑なフォームの実装を大幅に簡略化してくれます。 ぜひRemixとConformを使って、効率的にWebアプリケーションを開発してみてください。
アバター