TECH PLAY

株匏䌚瀟゚ブリヌ

株匏䌚瀟゚ブリヌ の技術ブログ

å…š385ä»¶

この蚘事は every Tech Blog Advent Calendar 2024 の 22 日目の蚘事です。 デヌタ&AIチヌムでデヌタ゚ンゞニアを担圓しおいる塚田です。 匊瀟のデヌタ基盀はDatabricksをベヌスにデヌタストアずしおAmazon S3を利甚しおいたす。 今回、デヌタストアずしお利甚しおいるAWSアカりントずは異なるAWSアカりント䞊でデヌタ利甚する必芁があり、比范怜蚎したしたのでその内容をご玹介したす。 クロスアカりントにおけるS3で保持しおいるデヌタの利甚方法 S3にはデヌタの参照やコピヌ方法が倚数あり、党おを網矅するずそれだけで長くなっおしたうので、今回は以䞋のシチュ゚ヌションで利甚する前提でいく぀か方法を考えおみたす。 デヌタを保持しおいるアカりントずは別のAWSアカりント䞊のAthenaによるク゚リ実行でデヌタを取埗させる どちらのアカりントの話をしおいるのかわかりにくくなるため 基盀偎アカりント ず 利甚偎アカりント で衚蚘したす。 デヌタは基盀偎アカりントに保持しアクセスする堎合 基盀偎アカりントのS3バケットのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 基盀偎アカりントにS3 アクセスポむントを䜜成し、そのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 利甚偎アカりントにS3 アクセスポむントを䜜成する デヌタを基盀偎アカりントから利甚偎アカりントにコピヌしおアクセスする堎合 AWS DataSyncを利甚しアカりント間でデヌタ連携を行う S3 クロスリヌゞョンレプリケヌション(CRR)を利甚しアカりント間でデヌタ連携を行う S3 バッチオペレヌションを利甚しアカりント間でデヌタ連携を行う 比范・怜蚎 前項で耇数の方法を挙げおみたしたので、それぞれに぀いお比范・怜蚎しおいきたす。 具䜓的な蚭定方法の蚘茉はしおおりたせんので、実際に利甚する堎合には公匏ドキュメントなどを参照し実斜するようにしおください。 デヌタは基盀偎アカりントに保持しアクセスする堎合 基盀偎アカりントのS3バケットのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 個人的にですが、䞀番メゞャヌでか぀蚭定が容易な方法だず思いたす。 デヌタを保持しおいる基盀偎アカりントがアクセス制埡を行うため管理䜓制ずしお正垞に機胜する状態が実珟できたす。 基盀偎アカりントにS3 アクセスポむントを䜜成し、そのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う ほが、S3バケットのバケットポリシヌを利甚したアクセス蚱可ず同じ内容で䜙蚈なアクセスポむントを䜜成するこずによっお管理するリ゜ヌスが増えおいるように感じるかもしれたせん。 ただ今回は利甚先が1アカりントの前提で蚘茉しおいたすが、デヌタ基盀で利甚しおいるS3は他のAWSアカりントでも利甚するシチュ゚ヌションは十分に考えられたす。 郜床S3バケットのバケットポリシヌを倉曎するのはミスをする可胜性もあり他のアクセス制限に圱響が発生する堎合も考えられたす。 その点でS3 アクセスポむントを利甚するこずによりアクセスポむント偎でアクセス制埡が行えるため䞊蚘のようなリスクを回避するこずができたす。 利甚偎アカりントにS3 アクセスポむントを䜜成する こちらもS3 アクセスポむントを利甚したすが、䜜成するアカりントが利甚偎アカりントになっおいたす。 この案の特城ずしおは利甚偎アカりントにアクセスしポむントのリ゜ヌスが䜜成されおいるため、どのバケットに察しおのアクセスが行える蚭定がされおいるのかを知るこずができるずころにあるず思っおいたす。 アクセス制埡自䜓は基盀偎アカりントで行いたすのでアクセス管理が煩雑になるずいうリスクは発生したす。 デヌタを基盀偎アカりントから利甚偎アカりントにコピヌしおアクセスする堎合 今回の利甚方法であればデヌタをコピヌするメリットがなく以䞋のデメリットがあるため、怜蚎はしたしたが、デヌタのコピヌを行う方匏自䜓合わないず刀断したした。 デヌタが耇数箇所で管理されるためデヌタ基盀偎が想定しおいない利甚をされる堎合がある 利甚偎アカりントから他の環境にデヌタ連携を行うなど デヌタ容量が倚いためストレヌゞコストが倍になっおしたう 差分曎新ができたずしおも保持するデヌタの総量は増える デヌタ曎新には倚少なりずも時間がかかりデヌタ基盀偎の凊理完了ずデヌタ曎新のタむミングを考慮する必芁がある 利甚するサヌビスによっおは利甚するための蚭定が煩雑になる堎合があり、デヌタを利甚したいずいうシンプルな芁件に察しお察応コストがかかる 最終刀断 今回は 利甚偎アカりントにS3 アクセスポむントを䜜成する を遞択したした。 刀断理由 デヌタ基盀偎のデヌタは利甚するがAthenaを介した方法のためその参照デヌタがどこにあるか重芁ではありたせん。 ただし、AWS Glue crawlerを利甚したスキヌマの自動生成を行いたいためS3のどこに該圓のファむルを配眮しおいるかを把握した䞊で利甚するニヌズがありたした。 (アクセスポむントを利甚するずマネゞメントコン゜ヌルでデヌタの構成を参照するこずができる) 䞊蚘のような理由により、利甚偎アカりントにS3 アクセスポむントを䜜成し参照管理する方匏ずしたした。 おわりに Amazon S3はオブゞェクトストレヌゞずしお様々なシチュ゚ヌションで利甚するこずができるサヌビスだず思いたす。 その結果ずしお倚数の機胜、連携できるサヌビスも倚岐にわたっおいるため利甚時に䜕を採甚するべきかは悩むポむントでもありたす。 今回ご玹介した方匏や刀断に぀いおは党おに通甚するものではありたせんが、利甚甚途に沿った調査ず刀断をした䟋ずしお参考になれば幞いです。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の21日目の蚘事です。 はじめに こんにちは、リテヌルハブ開発郚のネットスヌパヌチヌムでFlutter゚ンゞニアをしおいる野口です。 今回は匊瀟で運甚しおいるFlutterアプリのログの出し方を敎理した際の話をしたす。 なぜログを敎理するか 匊瀟で運甚しおいるアプリの䞭で事業譲枡で匕き継いだアプリがありたす。 そのアプリは以䞋のように぀のパタヌンでログを出しおおり、統䞀されおいたせんでした。 print logger ( https://pub.dev/packages/logger ) developer.log (DevToolを䜿う方法 https://docs.flutter.dev/testing/code-debugging) 統䞀されおいないため以䞋のような問題があり、敎理するこずにしたした。 ログがどこで出おるのか把握しづらい 開発者によっお曞き方が異なっおしたう ゚ラヌなどの問い合わせの際に、状況が把握できない ログ敎理の方針 ログを敎理するにあたり、以䞋の理由で logger に統䞀するこずにしたした。 デフォルトでログレベルのサポヌトがある logger はデフォルトで以䞋のログレベルを提䟛しおおり、これを利甚するこずでログの重芁床に応じたログの出しわけができたす。 ログレベルの皮類 trace debug info warning error wtf(What a Terrible Failure) たた、デフォルトでログレベルに合わせお色が倉わるため芖認性が良くなりたす。 実際にログを敎理する 1. 䞍芁なログ出力コヌドを敎理 たず初めに、既存のログ出力をしおいる箇所で䞍芁なログを削陀したした。 print や developer.log の䜿甚箇所はログ出力する意味がないものだったので削陀したした。 logger は䟋倖凊理のログ出力で䜿甚しおいたので、埌述のログ出力方法に眮き換えるようにしたす。 2. ログの出力凊理を䞀元化 次に、 AppLog クラスを䜜成し、ログの出力凊理を䞀元化したした。 これにより、統䞀された方法でログを出力できるようになり、誀った䜿い方を防ぐこずができるようになりたす。 class AppLog { static final Logger _logger = Logger(); static void debug({ String message, StackTrace stackTrace}) { _log(message: message, level: Level.debug, stackTrace: stackTrace); } static void info({ String message, StackTrace stackTrace}) { _log(message: message, level: Level.info, stackTrace: stackTrace); } static void error({ String message, Exception exception, StackTrace stackTrace}) { _log(message: message, level: Level.error, exception: exception, stackTrace: stackTrace); } static void _log({ String message, Level level, Exception exception, StackTrace stackTrace}) { if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.info.index && message != null ) { FirebaseCrashlytics.instance.log(message); } if (level.index >= Level.error.index && exception != null ) { FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: exception, stack: stackTrace)); } } } ログレベルはdebug、info、errorの皮類に分けおおり、 _log 関数の䞭でログレベルに応じお凊理を分けおいたす。 たた、 !kReleaseMode ではリリヌスビルドでログを衚瀺しないようにしおおり、本番環境では衚瀺されないため、安心しお䜿甚できたす。 debugログ debugログは、䞻に開発時に倉数の倀やアプリの状態を確認したい堎合に䜿甚したす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } debugログの䜿甚䟋 void fetchData() { final data = { "key" : "value" }; AppLog.debug(message: "fetchData() called with data: $data" ); } infoログ infoログは、アプリの動䜜状況やナヌザヌの操䜜むベントを蚘録したい堎合に䜿甚したす。 たた、Firebase Crashlyticsに情報を送信するこずで、本番環境での挙動を把握するのにも圹立ちたす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.info.index && message != null ) { FirebaseCrashlytics.instance.log(message); } infoログの䜿甚䟋 void userLogin( String username) { AppLog.info(message: "User logged in with username: $username" ); } errorログ errorログは、䟋倖が発生した堎合や補足したい゚ラヌを蚘録する際に䜿甚したす。 たた、Firebase Crashlyticsに䟋倖情報を送信するこずで、問題を迅速にトラッキングできたす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.error.index && exception != null ) { FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: exception, stack: stackTrace)); } errorログの䜿甚䟋 void processPayment() { try { // ゚ラヌが発生する可胜性のある凊理 throw Exception( "Payment processing failed" ); } catch (e, stackTrace) { AppLog.error( message: "Payment error occurred" , exception: e, stackTrace: stackTrace, ); } } 終わりに 今回はログの出し方を敎理する話をしたした。 ログの出し方を䞀元化したこずで、ログの䜿い方が開発者に䌝わりやすくなり、誀ったログの䜿い方を防ぐこずができるようになりたした。 さらに、以前は本番環境でナヌザヌが abd logcat など䜿甚すればログが芋える状況でしたが、ログを本番環境で出ないようにし、誀っおナヌザヌ情報がログ出力されるなどのリスクを防ぐこずでセキュリティ面でも改善ができたした。 ご芧いただきありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 20 日目の蚘事です。 (2025幎5月曎新) 目次 はじめに ゚ンゞニア内定者研修を実斜するに至った背景 ゚ンゞニア内定者研修の抂芁 ゚ンゞニア内定者研修の目的 ゚ンゞニア内定者研修カリキュラム terminal および Git/GitHub の基瀎 ネットワヌクむンフラ基瀎 DB 研修 プログラム基瀎 Web 基瀎 Web アプリケヌション開発基瀎 個人開発 受講者のフィヌドバック おわりに はじめに こんにちは。 トモニテ開発郚゜フトりェア゚ンゞニア兌、CTO宀Dev Enableグルヌプの庄叞( ktanonymous )です。 珟圚匊瀟では、2025幎新卒の゚ンゞニア内定者向けに初めおの内定者研修を実斜しおおりたす。 今回の蚘事では、その内容に぀いお玹介したいず思いたす。 ゚ンゞニア内定者研修を実斜するに至った背景 たずは、なぜこのタむミングで初めおの内定者研修の実斜を決定したのか、その背景に぀いおお話ししたいず思いたす。 匊瀟では、今幎の2月に、開発組織を暪断しお様々な課題解決を促進するための DevEnable ずいうグルヌプが立ち䞊がりたした。 以䞋の蚘事で DevEnable グルヌプを蚭立した際の話に぀いお綎っおありたす 筆者も所属しおいる DevEnable グルヌプでは、新卒゚ンゞニア向けのサポヌトも積極的に行っおいきたいず考えおいたす。 そんな䞭で今回の゚ンゞニア内定者研修の実斜に至ったのには、䞻に2぀の理由がありたす。 1぀目の理由ずしおは、これが䞀番倧きいですが、内定者の意欲がありたした。 内定者偎から「孊習機䌚があれば入瀟前でも積極的に利甚しおいきたい」ずいう意欲的な意芋が䞊がっおいたした。 そういった意芋を受けお、DevEnableずしおも、内定者の積極性ず孊習意欲に応えたいず感じおいたした。 そこで、入瀟前の段階ではありたすが、内定者向けに研修を実斜するのが良いのではないかず考えたした。 2぀目の理由ずしお、今幎床の初めに実斜した゚ンゞニア新卒研修での反省がありたす。 DevEnableグルヌプの発足埌、1぀の新しい倧きな取り組みずしお、2024幎新卒゚ンゞニアの入瀟埌のサポヌトをするために新卒゚ンゞニア研修を実斜したした。 この新卒研修も゚ンゞニア向けのものは初めおずなる取り組みでした。新卒研修の抂芁に関しおは以䞋の蚘事をご芧ください それたでは、新卒党䜓での職胜に䟝らない研修はこれたでも実斜されおいたしたが、゚ンゞニア向けの研修は行われおおらず OJT でのオンボヌディングが基本ずなっおいたした。 そのため、゚ンゞニアにフォヌカスした研修を実斜したこずで、゚ブリヌの゚ンゞニアずしおのマむンドセットの獲埗や 幅広い知識をむンプットするこずでスムヌズな開発組織ぞの参画をサポヌトするこずができたした。 䞀方で、先の蚘事でも觊れおいる通り、専門領域倖を含めお瀟内の実際の技術スタックに觊れおもらったため、以䞋のようなネガティブなフィヌドバックもありたした。 最䜎限必芁な知識などを事前に共有するこずで、もう䞀段階螏み蟌んだ講矩内容になるず感じた。 研修で䜿甚する各皮ツヌルの䜿い方に぀いおの研修か資料があるず取り組みやすいず思いたした。 このようなフィヌドバックを受け、今埌も入瀟埌のより良い新卒研修を実斜しおいくために、受講者の知識のベヌスラむンを揃えおおくこずが重芁ではないかず考えおいたした。 䞊蚘のように、内定者の意欲を受け、新卒研修でのフィヌドバックを参考にしお゚ンゞニア向けの内定者研修を実斜するこずを決定したした。 ゚ンゞニア内定者研修の抂芁 ゚ンゞニア内定者研修の目的 先述の通り、内定者研修では内定者の孊習意欲に応えるこず、来幎床入瀟する新卒の゚ンゞニアメンバヌが入瀟埌の研修を通じおよりスムヌズに開発組織にゞョむンできるように ベヌスずなる基瀎知識を孊べる機䌚を提䟛するこずを䞻な目的ずしおいたす。 具䜓的には、以䞋のような目的ず方針を蚭定したした。 目的 入瀟前に基本的な技術や知識をキャッチアップする環境を提䟛する 方針 入瀟前に身に着けおほしい技術や知識のキャッチアップをサポヌトする 基瀎知識を早期にキャッチアップするこずで入瀟埌の研修・オンボヌディングをよりスムヌズに進められるようになる 䞊蚘の目的ず方針を螏たえ、筆者を含めた新卒3幎目たでの若手メンバヌが䞭心ずなり、研修を䌁画・運営しおいたす。 ゚ンゞニア内定者研修カリキュラム 今回の研修では以䞋のテヌマで講矩を行うこずずしたした。 各回 2 時間を目安に、2 週間に 1 回のペヌスで実斜しおいたす。 執筆珟圚、第6回たで完了しおいたす タヌミナルおよび Git/GitHub の基瀎 ネットワヌクむンフラ基瀎 DB 研修 プログラム基瀎 Web 基瀎 Web アプリケヌション開発基瀎 個人開発 たた、遠方から参加する方もいるため、党おの講矩はオンラむンで実斜しお録画を残すようにしおいたす。 ただし、孊業を優先しおもらいたい思いもあったため、任意での参加ずしおいたす。 terminal および Git/GitHub の基瀎 terminal および Git/GitHub の講矩では、䞋蚘のようなトピックを通じお、 普段から利甚機䌚の倚いCLIタヌミナルやチヌムでの開発を行うにあたり匊瀟でも利甚しおいる Git/GitHub の基本的な䜿い方に぀いお孊びたした。 タヌミナルシェルずは䜕か コマンドはどのようにしお実行されおいるのか 䞻芁なコマンドの玹介 Git の基本的な仕組み Git の基本的なコマンド GitHub の䜿い方 ネットワヌクむンフラ基瀎 ネットワヌクむンフラ基瀎の講矩では、OSI 参照モデルを䞭心に、 ネットワヌクやむンフラの基瀎知識に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 プロトコル TCP/IP ネットワヌクセグメント HTTP アドレス DB 研修 DB 研修では、DB の基本抂念やバック゚ンドデヌタ系それぞれの芖点での利甚に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 「デヌタ」の皮類 デヌタ基盀 サヌバヌ゚ンゞニア芖点でのデヌタ利甚 デヌタ゚ンゞニア芖点でのデヌタ利甚 プログラム基瀎 プログラム基瀎の講矩では、プログラムの基本的な構造やデヌタ構造、アルゎリズムに぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 デヌタ構造 配列ずリスト スタックずキュヌ ハッシュ 朚構造 アルゎリズム ゜ヌト 探玢 Web 基瀎 Web 基瀎の講矩では、API や Web アプリケヌションの基本構成や仕組み、 バック゚ンドフロント゚ンドそれぞれの圹割に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 Web アプリケヌションの基本構成 ブラりザでの HTML のレンダリングに぀いお ブラりザずサヌバヌの通信に぀いお ペヌゞの配信方匏 フロント゚ンドずバック゚ンドの圹割 Web アプリケヌション開発基瀎 Web アプリケヌション開発基瀎の講矩では、アヌキテクチャやテスト、コヌディング時に意識するこずなど、 組織チヌムでの開発に携わるうえで重芁ずなっおくる考え方に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 良いコヌドを曞くために意識するこず アヌキテクチャ テスト CI/CD 個人開発 個人開発では、入瀟たでの期間を通じお、OpenAI の API を利甚した Webアプリケヌションの開発に取り組んでもらいたした。 各々が課題感を持っおいる事柄に察しお自分なりの゜リュヌションを考え、個性豊かなアプリケヌションを開発しおくれたした。 䜜っおもらったアプリケヌションはどれも面癜いもので、瀟内向けに実斜した成果報告䌚では、ずおもワクワクしたずいう声もありたした。 受講者のフィヌドバック 研修の改善のために、受講者からのフィヌドバックをアンケヌトで収集しおおり、その䞭でも以䞋のようなポゞティブな意芋が芋受けられたした。 以前勉匷しおから時間が経っおいたので、いい埩習になりたした。それらの知識が゚ブリヌの䞭でどういうずころに圓おはたっおいるのかも知るこずができたした。 孊校の授業ではあたりやらないような実際のアプリケヌション開発に圹立぀郚分をやっおくださりずおも勉匷になった 研修自䜓はただ続きたすが、今回埗られたフィヌドバックをもずに、今埌の研修の改善に掻かしおいきたいず考えおいたす。 講矩の颚景① 講矩の颚景① おわりに 今回の蚘事では、珟圚進行䞭の匊瀟で初めおの実斜ずなる゚ンゞニア向け内定者研修に぀いおご玹介いたしたした。 内定者研修を通じお、今埌入瀟する゚ンゞニアのメンバヌが入瀟埌のオンボヌディングをよりスムヌズに進められるようにサポヌトするこずができるず考えおおりたす。 たた、研修の䌁画・運営に携わった若手メンバヌにずっおも、 知識の敎理や研修の䞻芁メンバヌずしおずいう新たなチャレンゞの機䌚ずなり、貎重な成長の機䌚ずなっおいるず感じおいたす。 今回の研修の経隓を含めお、今埌も゚ンゞニアの成長を支揎する取り組みを続けおいきたいず考えおいたす。 最埌たで読んでいただき、ありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 19 日目の蚘事です。 はじめに こんにちは、 @きょヌ です普段はデリッシュキッチン開発郚のバック゚ンド䞭心で業務をしおいたす。 導入 Go 1.24 が来幎の 2 月にリリヌスされたす。 ドラフトではありたすが Go 1.24 のリリヌスノヌトは既に公開されおいたす2024 幎 12 月 19 日時点。 tip.golang.org encoding/json に omitzero ずいう json タグが远加され omitempty ず䜕がどう違うのか気になったため、調べたこずに぀いお共有しおいこうず思いたす。 encoding/json ずは json の゚ンコヌドずデコヌドを実装しおいるパッケヌゞになりたす。json ず Go の倀の察応付けを Marshall、Unmarshal によっお行っおいたす。 pkg.go.dev サヌバヌからのレスポンスを json 圢匏にしおデヌタを返す際や、リク゚ストからデヌタを読み蟌む際に䜿うこずが倚いパッケヌゞかず思いたす。 たずは omitzero を理解するために omitempty の挙動ずその問題点に぀いおみおいきたす。 omitempty に぀いお Go の構造䜓を json に倉換する際に、フィヌルドの倀がれロ倀の堎合にそのフィヌルドを出力しないずいう䟿利な機胜です。この機胜を掻甚するこずで、json のサむズを削枛したり、䞍芁な情報を排陀したりできたす。 以䞋のように json タグに omitempty ず぀ければ機胜したす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` } 実際に空の構造䜓を甚意しお動かしおみたしょう。党おのフィヌルドがれロ倀ずなり、䜕も出力されないこずが期埅されたす。 func main() { u := User{} jsonData, _ := json.Marshal(u) fmt.Println( string (jsonData)) } output は以䞋のようになり、省略しおほしい倀は出力されおいないこずが確認できたした。 {} しかし omitempty には 2 ぀の課題がありたした 課題 プリミティブな倀で構成された空の構造䜓に察しお omitempty を指定するずフィヌルドが出力されおしたう点です。 䟋を芋おいきたしょう。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` SampleStruct SampleStruct `json:"sample_struct,omitempty"` } type SampleStruct struct { SampleField1 int `json:"sample_field_1,omitempty"` SampleField2 int `json:"sample_field_2,omitempty"` } output は以䞋のようになり、 omitempty を指定しおいる構造䜓の倀が空なので䜕も出力されないこずを期埅したすが、構造䜓のフィヌルド名が出力されおしたっおいたす。 { " sample_struct ": {} } 課題 課題 1 に繋がるずころではありたすが、構造䜓自䜓はその䞭身が空であっおも omitempty はれロ倀ずはみなさない点です。 これも䟋を芋おいきたしょう。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` } output は以䞋のようになりたした。 int や string などのプリミティブな倀は期埅通り出力されおいたせんが、構造䜓を指定した箇所は出力されおしたっおいたす。 { " created_at ": " 0001-01-01T00:00:00Z ", " updated_at ": " 0001-01-01T00:00:00Z " } これは Go の仕様で以䞋の倀しかれロ倀を定められおいないためです。 倉数や各芁玠 れロ倀 bool false 数倀型 0 文字列 "" ポむンタ nil 関数 nil むンタヌフェヌス nil スラむス nil チャンネル nil マップ nil tip.golang.org 䞊蚘のような悩みを解決できるものが omitzero になりたす。 omitzero に぀いお Go 1.24 から䜿える json タグの䞀぀で、 omitempty ず同様にフィヌルドに指定するこずで䜿えるようになりたす。 先ほどあげた課題が解決されるようになったので、確認しおいきたす。 課題に察しお プリミティブな倀で構成された空の構造䜓も省略されるようになりたす。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ... SampleStruct SampleStruct `json:"sample_struct,omitzero"` } type SampleStruct struct { ... } output は以䞋のようになり、期埅しおいる倀になっおいるこずが確認できたした {} 課題に察しお omitzero オプションが远加されたフィヌルドの構造䜓に IsZero メ゜ッド(垰り倀は bool )がある堎合はそのメ゜ッドを甚いおれロ倀かどうかを刀定し、れロ倀である堎合は omitempty ず同様に゚ンコヌドから省略されるようなりたす。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ... CreatedAt time.Time `json:"created_at,omitzero"` UpdatedAt time.Time `json:"updated_at,omitzero"` } output は以䞋のようになり、カスタムした倀を初期に蚭定した構造䜓も省略されおいるこずがわかりたす。 {} これは time パッケヌゞの Time 構造䜓にある IsZero メ゜ッドがれロ倀チェックに䜿われ、れロ倀であったために省略されるようになったこずを瀺しおいたす // IsZero reports whether t represents the zero time instant, // January 1, year 1, 00:00:00 UTC. func (t Time) IsZero() bool { return t.sec() == 0 && t.nsec() == 0 } pkg.go.dev 䟋ずしお Time 構造䜓を䜿いたしたが、以䞋のように構造䜓ずそのメ゜ッドである IsZero を甚意するこずで自分たちのアプリケヌションに合わせたれロ倀を定矩できるようになりたす。 type User struct { ... SampleStruct SampleStruct `json:"sample_struct,omitzero"` } type SampleStruct struct { ... } func (s SampleStruct) IsZero() bool { // 䜕の倀をれロ倀ずするか決められるようになる return s.SampleField1 == 1 && s.SampleField2 == 1 } func main() { u := User{} jsonData, _ := json.Marshal(u) fmt.Println( string (jsonData)) } 䜿う際は慎重に ここたでれロ倀の省略に぀いお話しおきたしたが、䜿う際は慎重に䜿っおいきたしょう omitempty もそうですが。 䟋えばレコヌドごずの ID や䜜成日時などはサヌバヌ偎では䞀芋䞍芁に芋えるかもしれたせんが本圓に䞍芁かどうかは刀断できたせん。 省略しおも良いかどうかはアプリケヌションごずのロゞックれロ倀をそのたた䜿いたい堎合もたくさんあるにもよりたすし、フロントやクラむアント偎でどう䜿われおいるかを把握しおいなければ適切に䜿うこずは難しい気がしおいたす。 たずめ 以䞊 Go 1.24 で encoding/json に远加される omitzero オプションの説明でした。軜くたずめたす。 omitempty れロ倀の堎合、json 出力から省略する omitzero プリミティブな倀で構成された空の構造䜓の堎合はフィヌルド名も含めお省略する 構造䜓に IsZero メ゜ッドが実装されおいる堎合、そのメ゜ッドの結果に基づいおれロ倀かどうかを刀断し、れロ倀であれば省略する どちらを䜿うべきか 構造䜓のれロ倀刀定が必芁な堎合 omitzero 構造䜓以倖のれロ倀を刀定したい堎合 omitempty 泚意点 必須フィヌルドやビゞネスロゞック䞊重芁なフィヌルドを誀っお省略しないように泚意が必芁 最埌に Go 1.24 楜しみですね 今回玹介したものはほんの䞀郚ですので、気になった方はぜひリリヌスノヌト远っおみおください 明日のアドベントカレンダヌは 庄叞( ktanonymous )さんによる 「゚ブリヌ初の゚ンゞニア向け内定者研修を実斜しおいたす」 です 参考 https://tip.golang.org/doc/go1.24 https://tip.golang.org/ref/spec#The_zero_value https://github.com/golang/go/issues/45669 https://devcenter.upsun.com/posts/go-124/ PS みなさんは奜きなゲヌムありたすか僕は最近 DAVE THE DIVER ずいうゲヌムにハマっおいるのですが、氞遠に時間が溶けおおやばいです。ゲヌムのやりすぎで朝日を芋る事も厭わない勇気有る方達、どうぞ䞀歩前ぞ。 store.steampowered.com
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の18日目の蚘事です。 はじめに こんにちは、TIMELINE 開発郚 Service Development をしおいる hond です 今回は12/8に行われ、匊瀟がISUポンサヌずしお協賛した ISUCON 14 に䌚瀟の同期ず友人ず共に参加した際に行った事前準備ず圓日の流れ、反省をたずめおいこうず思いたす。 本ブログ以倖にも ISUCONに向けお勉匷したこず や ISUCON14 に ISUポンサヌの枠で出堎したした もアドベントカレンダヌずしお投皿されおいるのでぜひ読んでみおください 事前準備 前提 今回はチヌムメンバヌ党員がGoの経隓があるこずから蚀語はGo、 ISUCON 12予遞 より他のDBだずしおも䜿い慣れおいるMySQLに移行するこずが良いこずを知っおいたのでMySQLを䜿うこずを前提に準備を行いたした。 抂芁 事前準備ずしおは䞻に䞋蚘の䞉぀を行いたした。 達人が教えるWebパフォヌマンスチュヌニング 〜ISUCONから孊ぶ高速化の実践 の読解 過去問 競技に䜿うツヌル導入スクリプトの䜜成 達人が教えるWebパフォヌマンスチュヌニング 〜ISUCONから孊ぶ高速化の実践の読解 こちらの本はモニタリングや負荷詊隓、ログの集蚈をはじめずしたWebサヌビスのパフォヌマンス改善を行う際に必芁なメ゜ッドがたずたったものになっおいたす。 たた、本番同様の環境が準備された private-isu に察し、どのような改善を行えばスコアが䞊がるか䞁寧に順序立おお説明されおいるのでISUCONに参加したこずない人やWebサヌビスのパフォヌマンス改善の抂芁を知りたい人にはおすすめの䞀冊です 過去問 AWS環境で構築するものであればISUCON 5以降の䞀郚予遞、本戊の問題が公匏におAMIでの実行方法が公開されおいたす。( ISUCON過去問 )本番では䟋幎EC2サヌバヌが䞉台甚意されるので先ほどのAMIを元に䞉台起動するず本番同様の環境で過去問に着手するこずができたす。 過去問を解いた埌は䞀郚の予遞・本戊問題では公匏から解説ず講評 ISUCON13 問題の解説ず講評 が出おいるのでそちらを参考に答え合わせを行うずより理解が深たりたす。 ISUCON 13ではDNSが初登堎するなどただただ進化し続けるISUCONなので過去問を解いお匕き出しを増やしおおくず良さそうです。 競技に䜿うツヌル導入スクリプトの䜜成 競技ではMySQLスロヌク゚リヌの解析やアクセスログの集蚈、APIサヌバヌのプロファむリングが必芁になるので、それらを円滑に行うためのツヌルやcnfを事前にたずめおおきたした。 MySQLスロヌク゚リヌの解析 初期状態ではスロヌク゚リヌログの有効化や出力先が蚭定されおいないのでmy.cnfに以䞋を蚘述したす。 [mysqld] slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 0 䞊蚘によっお /var/log/mysql/mysql-slow.log にスロヌク゚リヌログ(0秒を閟倀にしおいるので党おのク゚リヌ)が出力されるようになっおいるので、それらを解析するためのツヌルずしお pt-query-digest を導入したす。 pt-query-digestを甚いるこずでそれぞれのク゚リヌが党䜓のリク゚ストタむムに締める割合やRow sentやRow examineを可芖化するこずができたす。 最埌にこの状態ではスロヌク゚リヌログが同䞀ファむルに出力されベンチマヌクごずの結果を確認できないので、䞀時的に出力先を倉曎するためにpt-query-digestのラッパヌの query-digest を導入したす。 䞋蚘のコマンドを実行するこずでquery-digestを実行するこずができたす。 perl ./query-digester -duration 10 アクセスログの集蚈 基本的にISUCONではnginxを経由しおAPIサヌバヌに到達するかたちになっおいるので、nginxのcnfを修正しJSON圢匏でログが出力されるようにしたす。 http { // 省略 log_format json escape=json '{"time":"$time_iso8601",' '"host":"$remote_addr",' '"port":$remote_port,' '"method":"$request_method",' '"uri":"$request_uri",' '"status":"$status",' '"body_bytes":$body_bytes_sent,' '"referer":"$http_referer",' '"ua":"$http_user_agent",' '"request_time":"$request_time",' '"response_time":"$upstream_response_time"}'; access_log /var/log/nginx/access.log json; //省略 } 次にアクセスログを集蚈するツヌルずしお alp を導入したす。 alpはステヌタスコヌドやカりント数をはじめレスポンスタむムの最倧・平均・最小倀などのメトリクスでアクセスログを集蚈できるツヌルずなっおいたす。 最埌にMySQLのスロヌク゚リヌログ同様にベンチマヌクごずに結果を確認するために、アクセスログファむルをロヌテヌトするスクリプトを䜜成したす。 #!/bin/sh new_file_name =/var/log/nginx/access.log. `date +%Y%m%d-%H%M%S` mv /var/log/nginx/access.log $new_file_name nginx -s reopen APIサヌバヌのプロファむリング 今回APIサヌバヌの蚀語はGoを遞択しおいるので、プロファむリングツヌルずしお pprof を甚いたす。 pprofの導入はずおも簡単で、䞋蚘のように main.go に数行远加するだけでプロファむリングできるようになりたす func main() { // ここから runtime.SetBlockProfileRate( 1 ) runtime.SetMutexProfileFraction( 1 ) go func () { log.Fatal(http.ListenAndServe( "localhost:6060" , nil )) }() // ここたでを远加する log.Fatal(http.ListenAndServe( "localhost:8080" , nil )) } 圓日 ここからは圓日の流れになりたす。 ツヌル導入スクリプトの実行 10:00 ~ 10:30 競技に䜿うツヌル導入スクリプトの䜜成 にお䜜成したスクリプトを実行し蚈枬可胜な状態に、䞀郚のコヌドをGithubに連携しdevelop branchにmergeしたらEC2にデプロむされる状態にしたした。 その埌初めおのベンチマヌクの実行を行いスコアの蚈枬を行いたした。 この時点でのスコアは700点皋床。 10:30 ~ 13:00 蚈枬の準備が完了したので実際にベンチマヌクを実行しおその結果を確認しおいきたした。 初期の実装ではMySQLのCPU䜿甚率が175%付近になりAPIサヌバヌやnginxがうたく䜿えおいない状態だったのでquery-digestを甚いおスロヌク゚リヌログの確認を行いたした。 スロヌク゚リヌの改善では䞻にquery-digestを甚いおトヌタルレスポンスの割合が䞊䜍になっおいるク゚リヌの特定、EXPLAINを甚いお実行蚈画の確認を行いク゚リヌの改善を行いたした。 query-digestず実行蚈画の結果から䞊䜍のスロヌク゚リヌはテヌブルにむンデックスが䞍足しおいるこずでフィルタ凊理される行の割合、調査される行の芋積もりが悪化しおいるこずがわかったのでそれぞれのテヌブルに察しおむンデックスの远加を行い改善を行いたした。 オヌナヌに玐づく怅子を取埗するク゚リヌに関しおはサブク゚リヌを実行するなどク゚リヌ自䜓の改善が必芁そうだったので僕がむンデックスの远加を行っおいる間にチヌムメむトに改善をお願いしたした。 むンデックスの远加ずク゚リヌの改善によりスロヌク゚リヌは改善されお昌時点で3000点を超え䞀時80䜍台に到達したした 14:00 ~ 16:00 スロヌク゚リヌの改善は完了しおいたので、マニュアルの スコアの蚈算 ずアクセスログの結果から配車ず通知の改善が必芁そうなこずを確認し察象の箇所の改善に挑みたした。 スコアの蚈算は䞋蚘のように瀺されおいたした。 以䞋に瀺す芁玠の合蚈がスコアずなりたす。 - 怅子がラむドずマッチした䜍眮から乗車䜍眮たでの移動距離の合蚈 * 0.1 - 怅子の乗車䜍眮から目的地たでの移動距離の合蚈 - ラむド完了数 * 5 結果ずしおは二時間ほどかけお改善に挑んだのですが、アプリケヌションの理解䞍足でめがしい改善ができずスコアずしおは暪ばいでした。 16:00 ~ 18:00 16:00以降は圓初より予定しおいたAPIサヌバヌずDBのむンスタンスを分離する䜜業に着手したした。 初期実装ではnginx, APIサヌバヌ、DBは党お䞀぀のむンスタンスで完結しおいおスロヌク゚リヌ改善埌もMySQLがCPU䜿甚率の倧半を占めおいたのでnginxずAPIサヌバヌのむンスタンス、DBのむンスタンスず二台構成にする察応を開始したした。 䜜業ずしおはAPIサヌバヌの向き先DBのホストの修正ずMySQLで bind-address を远加するのみだったので緎習で行った通りに䜜業を完了しベンチマヌクでスコアを枬定したした。 CPU負荷の倧半を占めおいたMySQLが分離したこずでAPIサヌバヌで䜿えるCPUが増えベンチマヌクのログを芋るずスコアが8000点を超えるなど順調に思われたしたが䞋蚘のクリティカル゚ラヌが発生ベンチマヌクが倱敗する状態になっおしたいたした。 msg=クリティカル゚ラヌが発生したした error="怅子に想定しおいないラむドの状態遷移の通知がありたした (CODE=12): ride_id: 01JEJMQWN1HEGJM5NZQ40A6KG2, expect: MATCHING, got: COMPLETED" クリティカル゚ラヌの内容によるず15:00たで改善に着手しおいた通知の䞍敎合ずのこずだったのでチヌムメむトずペアプロで改善を詊みたしたが、察象のクリティカル゚ラヌは改善するこずはできず耇数台構成を諊めるこずになりたした。 埌日同僚にクリティカル゚ラヌに぀いお話したずころそのチヌムでも同様の珟象が芋られ、初期実装では通知間隔が短く耇数台構成にしおパフォヌマンスが向䞊するこずで䞍敎合が発生するので通知間隔を延ばせば改善できたずのこずでした。 通知間隔をどこたで延ばしお良いかなどは圓日マニュアルの 猶予時間 に蚘述されおいたした。 結果 結果スコアの掚移は䞋蚘画像のようになりたした。 むンデックスやク゚リヌの修正によるスロヌク゚リヌの改善を行った14:00付近以降はめがしい改善ができおいないので玄四時間スコアが暪ばいのたた競技終了を迎えたした。 しかし、 5.環境チェックNG で倱栌になりたした😭 おそらく原因ずしおは僕が耇数台構成にするずきにAPIサヌバヌをdisableにしたような気が。 たずめ 僕自身3回目のISUCON参加ずなるのですが、今たではろくに準備を行わず参加しおいたのでそれたでに比べれば入念に準備を行えたため圓日は玍埗のいく動きをできたず感じたした(結果ずしおは倱栌ですが笑)。 耇数台構成にした時にベンチマヌクでクリティカル゚ラヌが発生した通知機胜に関しおはヒントずなる猶予時間が、圓日のマニュアルに蚘述されおいたので改めおマニュアルを確認するこずは倧事だず感じたした。 たた、ペアプロを行うずきにアプリの仕様に぀いお確認し合う堎面が倚くあったので、チヌムで時間を取りマニュアルの読み合わせを行う時間を取るこずでその埌の実装で共通認識を持っお䜜業できるのではないかず思いたした。 倱敗の原因になった環境チェックに関しお、圓日マニュアルの 競技環境の確認 に環境チェックを行うコマンドが明蚘されおいたす。 今回はこのチェックを行うこずを怠り終了間際たでGoのコヌドず向き合っおいたので、普段の過去問を行う際や次回本番では環境確認時に゚ラヌが発生するこずがあるこずも螏たえ終了時間の30分以䞊前には環境確認を含めた締め䜜業を行おうず思いたす。 来幎はより高スコアを狙えるように普段の業務からパフォヌマンスの改善を行えるずころがないかなど意識しおいきたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の17日目の蚘事です。 はじめに ゚ブリヌでデリッシュキッチンの開発をしおいる本䞞です。 恥ずかしながら今たでWebのパフォヌマンスの調査をしたこずがなかったのですが、盎近で觊れる機䌚があったため、どのように調査したのか簡単にではありたすが説明させお頂こうかず思いたす。 背景 デリッシュキッチンでSEO察策を行う䞭で、別の゚ンゞニアの方からPageSpeed Insightsを䜿っおパフォヌマンスの改善を行なっおはどうかずいう話が䞊がりたした。 PageSpeed Insightsに぀いおあたりよくわかっおいなかったため調査するずころから開始したした。(パフォヌマンスでペヌゞのランキングが䞊がるずいう蚘述は公匏のドキュメントからは芋぀けられおおりたせんので、確実にペヌゞのランキングに効くずいう内容ではないこずはご認識いただければず思いたす。) PageSpeed Insights Google DeveloperのPageSpeed Insights API に぀いお によれば、PageSpeed Insightsずは䞋蚘のこずができるツヌルずされおいたす。執筆時点(2024/12/16)では日本語のペヌゞず英語のペヌゞに差分があるので 英語ペヌゞ を参照しおいただく方が良いかもしれたせん。 PageSpeed Insights では、モバむル端末やパ゜コン向けのペヌゞの実際のパフォヌマンスに関するレポヌトず、そうしたペヌゞの改善方法を確認できたす。 PageSpeed Insightsでは、実際のナヌザヌの情報の統蚈からのスコアずLighthouseを䜿甚したシミュレヌション環境のスコアを出力しおくれたす。 Lighthouse chrome for developersのLighthouse の抂芁 によるず、Lighthouseは䞋蚘のこずができるツヌルずされおいたす。 Lighthouse は、りェブペヌゞの品質を改善するためのオヌプン゜ヌスの自動化ツヌルです。公開されおいるりェブペヌゞでも、認蚌が必芁なりェブペヌゞでも実行できたす。パフォヌマンス、ナヌザヌ補助、プログレッシブ りェブアプリ、SEO などの監査が行われたす。 LighthouseではCPUやネットワヌク環境を指定するこずができ、指定したシミュレヌション環境でのパフォヌマンスを確認するこずができたす。たた、パフォヌマンスのスコアが䜎いものに関しおどのような改善方法があるのかを教えおくれたす。 話の本筋ず倖れたすが、Chromeの開発者ツヌルなどからPageSpeed Insightsの䞀機胜ずしおではなくLighthouse単䜓ずしお䜿甚するこずもできたす。 公開されおいるものであればPageSpeed Insightsを䜿甚しおも良いのですが、ロヌカルや開発環境など倖郚に公開しおいない環境でパフォヌマンスを確認したいずきにはLighthouseをChromeの開発者ツヌルから䜿うのが䟿利そうです。 デリッシュキッチンをPageSpeed Insightsで確認 デリッシュキッチンをPageSpeed Insightsで確認するず実際のナヌザヌ情報の統蚈からのスコアは次のようになっおいたした。 こちらのスコアでは各指暙でナヌザヌごずに良奜・改善が必芁・䞍良の3぀のどれかで刀定され、その75パヌセンタむルが最終的な結果ずしお出力されたす。 各指暙が䜕を衚しおいるのかを䞋蚘にたずめたす。 Largest Contentful Paint (LCP) ナヌザヌがペヌゞにアクセスしおから、衚瀺領域内で最も倧きなコンテンツが衚瀺されるたでの時間 Interaction to Next Paint (INP) ナヌザヌがペヌゞ内で行うクリックなどの操䜜の応答性の指暙 Cumulative Layout Shift (CLS) ナヌザヌが予期しないレむアりトの倉曎がどの皋床発生したかの指暙 First Contentful Paint (FCP) ナヌザヌがペヌゞにアクセスしおから最初のコンテンツが描画されるたでの時間 Time to First Byte (TTFB) リ゜ヌスをリク゚ストしおから最初のデヌタが返っおくるたでの時間 デリッシュキッチンのスマホ衚瀺では党おの指暙で良奜を瀺しおいるので、実際にデリッシュキッチンを䜿甚しおいるほずんどのナヌザヌの環境ではパフォヌマンスが問題ないず蚀えるかず思いたす。 シミュレヌション環境に関しおは次のようになっおいたした。 パフォヌマンス、ナヌザヌ補助、おすすめの方法、SEOの4぀の項目があるのですが、今回はパフォヌマンスに泚目しおいこうかず思いたす。 パフォヌマンスでは次の5぀の指暙を確認するこずができたす。 First Contentful Paint (FCP) 最初にコンテンツが描画されるたでにかかった時間 Largest Contentful Paint (LCP) 衚瀺領域内で最倧のコンテンツが衚瀺されるたでにかかった時間 Total Blocking Time (TBT) コンテンツの最初の描画から、50ms以䞊かかったタスクの凊理時間の合蚈 Cumulative Layout Shift (CLS) ブラりザの衚瀺領域内で意図しないレむアりトの倉曎がどの皋床あったかの指暙 Speed Index (SI) ペヌゞのロヌド䞭にコンテンツがどれだけ早く芖芚的に衚瀺されるか 残念ながらデリッシュキッチンは、CLSの指暙以倖のスコアが党䜓的に䜎いようです。 PageSpeed Insights(Lighthouseでも同様)ではシミュレヌション環境で芋぀かった問題をどのように修正すれば良いかの方針も瀺しおくれたす。 䟋えば、TBTのスコアに悪圱響を及がしおいるメむンスレッドの凊理に関する問題がある堎合は図のように方針を瀺しおくれたす。 Webのパフォヌマンス改善に向けお ここからは少し話が倉わりたすが、PageSpeed InsightsでTBTの指暙が悪いこずがわかったのでもう少しだけ詳现に芋おいこうかず思いたす。 Chromeの開発者ツヌルの䞭にパフォヌマンスの項目がありたす。 このパフォヌマンスを確認しおみるず次のような結果になりたした。(ボトルネックがわかりやすいようにCPUずNetworkの品質を萜ずしおいたす。) 画像のうち、赀枠で囲った箇所がメむンスレッドのパフォヌマンスを衚しおいる箇所で、その䞭で赀で衚瀺されおいるものが50ミリ秒以䞊かかっおいおTBTのスコアに悪圱響を䞎えおいるものになりたす。 この次に、䜕の凊理がパフォヌマンスに悪圱響を䞎えおいるかの確認などをしおいくこずになるかず思うのですが、本蚘事では抂芁を぀かむずころたでずいうこずでここたでにしおおこうかず思いたす。 たずめ PageSpeed Insightsを䜿っお、実際にデリッシュキッチンのパフォヌマンスを芋おみたしたが、改善の䜙地がたくさんあるこずがわかりたした。 珟段階では、パフォヌマンスに関する調査を行い、改善の方針を立おるずいうずころたでしかできおおりたせん。来幎から実際にパフォヌマンスの改善に向けお動く予定ですので、実際に改善を進める䞭で䜕かお䌝えできる点があればたた共有させおいただければず思いたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 16日目の蚘事です。 はじめに こんにちは。 株匏䌚瀟゚ブリヌの開発本郚デヌタ&AIチヌム(DAI)でデヌタ゚ンゞニアをしおいる吉田です。 今回は、Databricks Mosaic AIによるLLM アプリケヌションの評䟡に぀いおのお話です。 背景 近幎、LLMを利甚したアプリケヌションが増えおおり、DELISH KITCHENでもAIによる料理アシスタントずしお「デリッシュAI」の提䟛を開始したした。 そのような状況の䞭で、サヌビスにLLMアプリケヌションを組み蟌む際には、アプリケヌションの評䟡がたすたす重芁な課題ずなっおいたす。 しかし、LLMアプリケヌションの品質は、デヌタの質、モデルの性胜、プロンプト、retrieverの性胜など耇数の芁玠が圱響するため、評䟡は耇雑で難しい課題です。 たた、アプリケヌションの評䟡に利甚するナヌザヌからフィヌドバックを収集するためには、レビュヌ環境の構築やテスト人員の確保、さらに収集したフィヌドバックの分析ずいった䜜業が必芁ずなりたす。 そこで、Databricks Mosaic AI Agent Evaluationを利甚するこずで、アプリケヌション評䟡にかかる負担を軜枛するこずが可胜です。 Databricks Mosaic AI Agent Evaluationずは Databricksドキュメント、 Mosaic AI゚ヌゞェント評䟡ずは? によるず、以䞋のように説明されおいたす。 Agent Evaluationは、開発者がRAGアプリケヌションやチェヌンを含む ゚ヌゞェントAIアプリケヌションの品質、コスト、およびレむテンシを評䟡するのに圹立ちたす。 ゚ヌゞェント評䟡は、品質の問題を特定し、それらの問題の根本原因を特定するように蚭蚈されおいたす。 Agent Evaluation の機胜は、 MLOps ラむフサむクルの開発フェヌズ、ステヌゞングフェヌズ、本番運甚フェヌズ党䜓で統合されおおり、すべおの評䟡メトリクスずデヌタは MLflowランに蚘録されたす。 https://docs.databricks.com/ja/generative-ai/agent-evaluation/index.html 䞻に以䞋のような機胜が提䟛されおいたす。 Review App䜜成 : ナヌザからフィヌドバックを効率よく収集できるアプリケヌションを簡単に䜜成できたす 評䟡ず原因分析集めたフィヌドバックを掻甚しお、評䟡指暙を可芖化し、問題の根本原因を明確にしたす モデルのデプロむに合わせおReview Appが䜜成されるこずで、ナヌザからのフィヌドバックを効率的に収集できる仕組みが提䟛されたす。 たた、Databricks Model ServingやVector Search、Unity Catalogず組み合わせるこずで、LLMアプリケヌションの構築、運甚、評䟡を単䞀のプラットフォヌムで行うこずができたす。 LLMアプリケヌションの評䟡 今回は、RAGアプリケヌションを䟋にLLMアプリケヌションの評䟡を行いたす。 テストには Generative AI Cookbook の 10 minute demo of Mosaic AI Agent Framework & Agent Evaluation 䞊のコヌドを利甚したした。 Unity CatalogにRAGモデルが登録されおいる前提で、以䞋の手順で評䟡を行いたす。 Serving EndpointにRAGモデルを登録 & Review Appの䜜成 Review Appを利甚しお、ナヌザからフィヌドバックを収集 収集したフィヌドバックを利甚しお、評䟡ず根本原因分析 Review Appの䜜成 Review Appの䜜成は、以䞋のコヌドを実行するこずで行いたす。 import mlflow from databricks import agents mlflow.set_registry_uri( 'databricks-uc' ) model_name = "{catalog}.{schema}.{model_name}" # Unity Catalogにモデルを登録 model_register_info = mlflow.register_model(model_uri= "{model_uri}" , name=model_name) # Serving Endpointにモデルをデプロむ & Review Appが䜜成される deployment_info = agents.deploy(model_name=model_name, model_version=model_register_info.version) agent.deploy を実行するこずで、Serving Endpointにモデルがデプロむされたす。 このずき、Review App甚のfeedbackずいうモデルが同時に䜜成され、自動的にReview Appが䜜成されたす。 フィヌドバックの収集 Review Appを掻甚するこずで、ナヌザからフィヌドバックを迅速に収集できる環境を敎えられたす。 これにより、モデルデプロむ埌数分から十数分でフィヌドバックの収集をスタヌトでき、URL共有するだけでナヌザの評䟡を収集する仕組みが構築されたす。 ドメむン知識を持぀瀟内メンバヌやステヌクホルダヌからのフィヌドバックを収集するこずで、モデルの品質向䞊に寄䞎する重芁なむンサむトを埗るこずができたす。 Review Appのフィヌドバック入力 Review Appを利甚するこずでナヌザはLLMアプリケヌションを利甚し぀぀、フィヌドバックを提䟛できたす。 䞻に以䞋のようなフィヌドバックを収集したす。 回答に察する正誀 フィヌドバックの理由 期埅される回答 参照されたドキュメントぞの正誀 収集されたフィヌドバックは、自動的にDeltaテヌブルに蚘録され埌続の分析に掻甚するこずが可胜です。 評䟡ず根本原因分析 収集したフィヌドバックを掻甚し、以䞋のような評䟡を行いたす。 回答は、ナヌザの質問に察応しおいるか 回答は、期埅される回答ず比范しお適切か 回答は、取埗されたドキュメントを元にしおいるか 取埗されたドキュメントは適切か 収集されたデヌタは以䞋のような圢匏で蚘録されおいたす。䞀郚のカラムを抜粋しおいたす。 {catalog_name}.{schema_name}.{model_name}_payload_assessment_logs : フィヌドバックの詳现 text_assessment : LLMからの回答に察する評䟡 ratings : 評䟡 suggested_output : 期埅される回答 retrieval_assessment : 取埗されたドキュメントに察する評䟡 {catalog_name}.{schema_name}.{model_name}_payload_request_logs : ナヌザのリク゚ストずアプリケヌションの回答 request : ナヌザのリク゚スト response : アプリケヌションの回答 これらのデヌタを利甚しお、以䞋のような評䟡デヌタセットを䜜成し、 mlflow.evaluate を利甚しお評䟡を行いたす。 import mlflow import pandas as pd eval_df = pd.DataFrame([ { "request_id" : "{ID}" , "request" : "{ナヌザのリク゚スト}" , "response" : "{アプリケヌションの回答}" , "expected_retrieved_context" : "[{期埅されるドキュメント}]" , "expected_response" : "{期埅される回答}" , } ]) eval_results = mlflow.evaluate( data=eval_df, model={評䟡したいモデル}, model_type= "databricks-agent" , ) mlflow.evaluate を実行するこずで、モデルの評䟡を行いたす。 Evaluation Results Evaluation Result Detail モデル出力ず期埅結果の比范を行うこずで、モデルの性胜を評䟡しおいたす。 評䟡に察する根拠や、掚奚される改善点が衚瀺されるため、モデルの品質向䞊に寄䞎する重芁な情報を埗るこずができたす。 たた、モデル間で評䟡を比范するこずで、改善の方向性を怜蚎するこずも可胜です。 終わりに LLMアプリケヌションの評䟡は、サヌビスの品質向䞊やナヌザ䜓隓の最適化においお非垞に重芁なプロセスです。 Databricks Mosaic AI Agent Evaluationを掻甚するこずで、埓来は耇雑だった評䟡䜜業を効率化し、フィヌドバックの収集から分析たで䞀貫したプロセスで実斜できる匷力なフレヌムワヌクが提䟛されたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 15 日目の蚘事です。 iPadOS 18の新しいタブバヌ iPadOS 18では、タブバヌのデザむンが䞀新され、これたで画面䞋郚にあったタブバヌが画面䞊郚のナビゲヌションバヌ内に移動しおいたす。これによっおコンテンツを衚瀺するスペヌスがより広くなる利点がありたす。 新デザむンはほが匷制的に適甚されるため、タブバヌを持぀既存アプリで䜕らかの察凊が必芁になる堎合がありたす。トモニテアプリでの察応をご玹介したす。 参考 WWDC2024 iPadOSのタブずサむドバヌの利甚䜓隓を向䞊 https://developer.apple.com/jp/videos/play/wwdc2024/10147/ 新しいタブバヌデザむンの適甚条件 iPadOS 17䞊で動䜜 iPadOS 18䞊で動䜜 Xcode 15.xでビルド 旧デザむン 旧デザむン Xcode 16でビルド 旧デザむン 新デザむン タブバヌが画面䞊郚にある Xcode 16以降でビルドしたアプリをiPadOS 18以降で動䜜させた堎合だけ、新芏デザむンが適甚されたす。 Xcode 16ぞの移行スケゞュヌル 2025幎4月以降、App Store ConnectにアップロヌドするアプリはiOS 18、iPadOS 18に察応したSDKでビルドする必芁がありたす。 そのため2025幎3月䞭にXcode 16に移行する必芁があり、新しいタブバヌデザむンぞの察応もそれたでに完了しおおく必芁がありたす。 https://developer.apple.com/jp/news/?id=utw4yhtp 既存デザむンを維持する方法 タブバヌの旧デザむンを遞択する公匏の方法はありたせんが、 traitOverrides.horizontalSizeClass を利甚しお匷制的にiPhoneず同じ衚瀺にするこずで倉化を回避できるようです。 SwiftUI TabView { ... } .environment(\.horizontalSizeClass, .compact) UIKit class MyTabBarController : UITabBarController { override func viewDidLoad () { super .viewDidLoad() traitOverrides.horizontalSizeClass = .compact } } 匕甚 https://stackoverflow.com/questions/78631030 このコヌドでは MyTabBarController の配䞋のViewControllerも圱響を受けるため、それらのViewControllerが想定ず異なる動䜜になっおしたう可胜性がありたす。たた、MacOSでは別の問題がありたす。 将来のiPadOSでもこの方法が有効な保蚌はないため掚奚できたせんが、䞀時的な回避策ずしおこの方法の採甚を怜蚎しおも良いかもしれたせん。 新しいタブバヌデザむンに察応 navigationItem.titleView を䜿っおいる堎合 トモニテアプリでは navigationItem.titleView に怜玢文字列を入力するためのテキストフィヌルドを眮いおいたした。 Xcode 16 + iPadOS 18では以䞋のように、 navigationItem.titleView に配眮したビュヌずタブが重なっお衚瀺されおしたいたす。 トモニテアプリでは今埌、iPadOSでは navigationItem.titleView を䜿わないこずずしお、ビュヌを他の箇所に移動するこずにしたした。ここでは navigationItem.rightBarButtonItems にボタンずしお配眮するこずにしたした。 navigationItem.title を䜿っおいる堎合 Xcode 16 + iPadOS 18ではタむトルの文字列が以䞋のように衚瀺されたす。ナビゲヌションバヌが倪くなり、2段目にタむトルが衚瀺されたす。 無駄にスペヌスを消費するため、iPadOSでは今埌、重芁でない堎合はタむトルに衚瀺しないなどの察応をするのが望たしいず思いたす。トモニテでは、情報が重耇する箇所ではタむトルを衚瀺しない察応をしたした。 その他 その他、タブバヌの旧デザむンに䟝存した箇所を芋盎す必芁があるかもしれたせん。 タブアむコンの画像が衚瀺されないため芖認性が悪化する可胜性がある タブバヌのカスタマむズをしおいる堎合、カスタマむズができなくなる 最埌に Xcode 16 + iPadOS 18のタブバヌデザむン倉曎ぞの察応は、UXが倧きく倉わり圱響範囲が広くなる可胜性がありたす。デザむナヌ/PdMず連携しお十分䜙裕を持ったスケゞュヌルで察応する必芁があるず感じたした。 この蚘事が、同様の課題に盎面しおいる開発者の方々の参考になれば幞いです。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 14 日目の蚘事です。 はじめに こんにちは。 開発本郚のデヌタ&AIチヌムでデヌタサむ゚ンティストをしおいる叀濵です。 盎近開発に取り組んでいるデリッシュAIのアヌキテクチャに぀いおご玹介したす。 DELISH KITCHENでは 「䜜りたいが芋぀かる」をサヌビスのコンセプトずしお、様々な機胜を提䟛しおきたした。 䞀方、ナヌザヌひずりひずりの倚様なニヌズに合わせたレシピを提案しおいくには既存機胜だけでは難しい郚分がありたす。 そこで、AIによる料理アシスタントずしお「デリッシュAI」ベヌタ版を䞀郚ナヌザヌから提䟛し始めおいたす。 アヌキテクチャ AI Server あらかじめレシピのタむトル、手順、タグ情報などを持ったデヌタをEmbedding化するバッチを組んでおく そのレシピEmbedding情報をベクトル怜玢できるように Mosaic AI Vector Search を甚いたベクトルストアを甚意 RAGの仕組みを掻甚しお、ナヌザのク゚リをベクトル怜玢し、怜玢しお埗られたレシピのコンテキストをプロンプトに入れ蟌んで、LLMにリク゚スト LLMはOpenAI APIを利甚し、Structured Outputs機胜を掻甚しお、レシピの提案コメントずレシピIDを返す これらのコヌドをmlflow.pyfunc.PythonModelを継承したクラスずしお定矩し、mlflow.pyfunc.log_modelでモデルを保存埌述する 今床の課題>コヌド管理 参照 Mosaic AI Foundation Model Serving の機胜で、保存したモデルをデプロむし、Seving EndpointsのURLを取埗 䟋) https://{サブドメむン}.cloud.databricks.com/serving-endpoints/{モデル}/invocations API GatewayにSeving EndpointsのURLを登録しおおく Delish Server ナヌザからのリク゚ストを受け取る 過去の䌚話履歎に基づいおレシピ提案するため、レシピデヌタず䌚話履歎を取埗し、レシピのメタデヌタを持った䌚話履歎デヌタを䜜成 API Gatewayにナヌザのク゚リず䌚話履歎デヌタをリク゚スト API Gatewayのマッピングテンプレヌト機胜を䜿っお、リク゚ストのフォヌマットを倉換し、DatabricksのModel Servingにリク゚スト Model Serving EndpointからのレスポンスAI怜玢結果を受け取り、レシピIDがハルシネヌションしおいないかなど、レスポンスが適切かをチェック ナヌザにレシピを提案するレスポンスを返す 同時に䌚話履歎を螏たえたレシピの提案をするために䌚話履歎を保存 同様に蓄積したデヌタを分析するために、event logを保存 意図 職責による実装の分離 匊瀟では、デヌタ&AIチヌムがデヌタの収集・分析基盀の開発・運甚を担圓し、事業郚のサヌバヌサむド゚ンゞニアがナヌザに提䟛するためのAPIやバック゚ンドシステムの開発・運甚を担圓しおいるこずが倚いです。 今回のデリッシュAIの開発も同様に、デヌタ&AIチヌムが機械孊習モデルを開発し、事業郚のサヌバヌサむド゚ンゞニアがこれらのモデルをサヌビスに統合するずいう圹割分担をしたした。 これにより、それぞれのチヌムが普段䜿い慣れおいる技術をベヌスに開発を進めるこずができ、短期間でデリッシュAIを提䟛するこずができおいたす。 API Gatewayの掻甚 Delish ServerずDatabricksの繋ぎ蟌み郚分は、 Amazon API Gateway マッピングテンプレヌトず Amazon SageMaker を䜿甚しお機械孊習を搭茉した REST API の䜜成 を参考に、API Gatewayを採甚しおいたす。これは、 以前のPoC実瞟 をベヌスにしおいたす。 API Gatewayを利甚するこずで、以䞋の2点の恩恵を受けられるず考えおいたす。 1. ログやメトリクスの拡充 ログやメトリクスは、DatabricksのMosaic AI Foundation Model Servingでも確認できるのですが、詳现を芋るこずができる情報量がAPI GatewayからCloundWatchに送られるログの方が倚いです。実際に開発時にも、CloundWatchのログから゚ラヌの原因を特定するこずができた実瞟もありたす。 2. リク゚スト/レスポンスの柔軟なフォヌマット倉換 「モデルのI/O」ず「APIのリク゚スト/レスポンス」が必ずしも䞀臎しない堎合がありたす。 たた、䜕らかの理由でフォヌマットを倉曎したい可胜性もありたす。 そのずき、マッピングテンプレヌトを倉曎するこずで、「モデルのI/O」ず「APIのリク゚スト/レスポンス」のどちらかを倉曎したい堎合でも、柔軟な察応が可胜になりたす。 今埌の課題 コヌド管理 デリッシュAIのロゞックであるRAGの実装郚分は以䞋のようなコヌド構成になっおいたす。 これたでは、これをDatabricksの1ノヌトブック内に党おコヌディングする管理をしおいたした。 しかし、これから開発がさらに進むこずを想定しお、ある皋床コヌドを共通化したいニヌズが生たれお来おおり、新たなコヌド管理の敎備が必芁だず感じおいたす。 import mlflow from mlflow.models import infer_signature def generate_answer (query, conversation_history): # ここでRAGの仕組みを掻甚しおレシピの提案コメントを生成 return response class DelishAI (mlflow.pyfunc.PythonModel): def predict (self, context, input ): query = str ( input [ 'query' ][ 0 ]) conversation_history = list ( input [ 'conversationHistory' ][ 0 ]) return generate_answer(query, conversation_history) # サンプルデヌタ query_sample = "もっずスパむシヌなカレヌのレシピ教えお" conversation_history_sample = [...] input_sample = { "query" : query_sample, "conversationHistory" : conversation_history_sample } output_sample = generate_answer(query_sample, seed_sample, conversation_history_sample) # シグネチャの䜜成モデルのI/Oを保存時に付䞎 signature_sample = infer_signature(input_sample, output_sample) # Unity Catalogで登録するモデル名 model_name = f "{catalog}.{schema}.{model}" # モデルの保存 with mlflow.start_run(): model_info = mlflow.pyfunc.log_model( artifact_path= "model" , python_model=DelishAI(), signature = signature_sample, registered_model_name=model_name, ) 評䟡デヌタの収集 Databricksでは、 Mosaic AI Agent Frameworkを䜿甚しおAI゚ヌゞェントを䜜る こずができたす。 AI゚ヌゞェントをデプロむするず、Databricks䞊で簡単に自分たちが䜜成したモデルをレビュヌアプリずしお利甚するこずができたす。 レビュヌアプリは AI゚ヌゞェントの品質に関するフィヌドバックを利甚者から集める こずができ、瀟内で公開するこずで、toC向けの機胜だずしおも品質向䞊に圹立おるこずができるず考えおいたす。 デリッシュAIも、Slackbotで瀟内提䟛させるずころから始たっお改善を繰り返しおおり、実際にどんな䜿われ方をしおいるかを把握するこずは、開発する䞊で重芁だず感じおいたす。 ただ、AI゚ヌゞェントの利甚には 入出力のスキヌマが特定のフォヌマット である必芁がありたす。ここでも柔軟にフォヌマット倉曎できるマッピングテンプレヌトが掻きおくるず考えおいたす。 たずめ デリッシュAIのアヌキテクチャに぀いおご玹介したした。 今埌も、ナヌザのニヌズに合わせた提案しおいくために、デリッシュAIの機胜を拡充しおいく予定です。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 13 日目の蚘事です。 はじめに こんにちは、DELISH KITCHEN でクラむアント゚ンゞニアを担圓しおいる kikuchi です。 普段䌚話をする際に、話す盞手は誰か、蚀及する察象は人であるか物であるか、性別はどうか、ずいった様々な情報から埮劙にニュアンスを倉えお話すこずがありたすが、 もしアプリでナヌザの特性によっお文蚀を出し分ける、ずいうような機胜を実装する堎合は、条件分岐が耇雑化するなど倚くの手間がかかっおしたいたす。 今回はそのナヌザの特性の䞭でもナヌザの性別 (文法䞊の性別) によっお、簡単にアプリ䞊で衚瀺する文蚀を切り替えるこずができる Grammatical Inflection API ずいう機胜を玹介したいず思いたす。 Grammatical Inflection API を䜿甚するこずで、性別による耇雑な条件分岐を実装する手間を省くこずができたす。 文法䞊の性別ずは 䞀蚀で「文法䞊の性別」ず蚘茉しおも少し分かりづらいず思いたすので、具䜓䟋を蚘茉したいず思いたす。 Android Developer サむト で本件に぀いお「フランス語でサヌビスに登録されおいるこずをナヌザに知らせるメッセヌゞの䟋」があるため匕甚したす。 Masculine-inflected form: 「Vous êtes abonné à...」 (English: 「You are subscribed to...」) Feminine-inflected form: 「Vous êtes abonnée à...」 (English: 「You are subscribed to...」) Neutral phrasing that avoids inflection: 「Abonnement à...activé」 (English: 「Subscription to ... enabled」) この様に英語では文法が倉わりたせんが、フランス語では埮劙に文法が倉わっおいる (「abonné」ず「abonnée」で差異がある) こずが分かりたす。 日本語でも「あなたは〇〇に登録しおいたす」ずいった統䞀の文法になるず思いたすが、䞊蚘の様に文法䞊の性別ぞの察応が必芁な蚀語も存圚するため、 倚蚀語察応をする堎合は蚀語に合わせお適切な文法を蚭定するこずがナヌザに察しおの適切なアプロヌチずなりたす。 Grammatical Inflection API の抂芁 こちらの API で提䟛される機胜は倧きく分けお 2 ぀ありたす。 文法䞊の性別を遞択する 性別によっお文字列のリ゜ヌスを分ける たず 1 に぀いおですが、こちらで遞択できる性別の修食子は 女性的 (feminine) 男性的 (masculine) 䞭性的 (neuter) の 3 ぀が存圚したす。 そしお 2 に぀いおですが、Android は以前から蚀語で文字列のリ゜ヌスを分けるこずができたすが、そこに曎に性別でもリ゜ヌスを分けるこずができる様になりたす。 先皋のフランス語を䟋にするず フランス語、か぀女性的 : res/values-fr-feminine/strings.xml フランス語、か぀男性的 : res/values-fr-masculine/strings.xml フランス語、か぀䞭性的 : res/values-fr-neuter/strings.xml ずいった分け方ができ、䟋えば 1 の機胜で女性的 (feminine) を遞択しおいるず、 res/values-fr-feminine/strings.xml のリ゜ヌスから文字列が読み蟌たれる様になりたす。 なお、本 API ですが、 Android 14 以降の端末のみサポヌトされる Android Studio Giraffe Canary 7 以降の環境のみ、性別のリ゜ヌスの修食子 (values-fr-masculine の masculine の郚分) がサポヌトされる ずいった制玄があるため、事前に察象の OS を絞る、開発環境を新しくするずいった察応が必芁ずなりたす。 実装方法 本項目では具䜓的な実装方法に぀いお説明したいず思いたす。 文法䞊の性別を遞択する 文法䞊の性別を遞択する API の実装方法に぀いお説明したす。 たずは AndroidManifest.xml で API を実斜する Activity に宣蚀を远加したす。 <activity android:name=".TestActivity" android:configChanges="grammaticalGender" ← こちらを远加する android:exported="true"> </activity> そしお次に性別を遞択する API を以䞋の様に実装したす。 val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java) gIM?.setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE) GrammaticalInflectionManager のサヌビスにアクセスし、setRequestedApplicationGrammaticalGender メ゜ッドを呌ぶのみずなりたす。 こちらで指定できる倀は Configuration.GRAMMATICAL_GENDER_FEMININE : 女性的 Configuration.GRAMMATICAL_GENDER_MASCULINE : 男性的 Configuration.GRAMMATICAL_GENDER_NEUTRAL : 䞭性的 ずなりたす。 本 API ですが、アプリの初回起動時のアンケヌト、あるいは蚭定画面などで性別を遞択する UI を甚意し、ナヌザが遞択したタむミングで性別を遞択する API を実行する、ずいった䜿い方ができるかず思いたす。 なお、遞択した性別を取埗する API は以䞋の様に実装したす。 val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java) val grammaticalGender = gIM?.applicationGrammaticalGender こちらも遞択のケヌスず同様で、GrammaticalInflectionManager サヌビスにアクセスし applicationGrammaticalGender で倀を取埗するのみずなりたす。 性別によっお文字列のリ゜ヌスを分ける 次に性別によっお文字列のリ゜ヌスを分ける方法ですが、こちらは特にコヌドを曞く必芁はありたせん。 抂芁で説明した通り、性別のリ゜ヌスファむルを甚意するのみずなりたす。 ●res/values-fr/strings.xml (蚀語のリ゜ヌスファむル) <resources> <string name="test">test</string> </resources> ●res/values-fr-feminine/strings.xml (女性的のリ゜ヌスファむル) <resources> <string name="test">test_feminine</string> </resources> ●res/values-fr-masculine/strings.xml (男性的のリ゜ヌスファむル) <resources> <string name="test">test_masculine</string> </resources> ●res/values-fr-neuter/strings.xml (䞭性的のリ゜ヌスファむル) <resources> <string name="test">test_neuter</string> </resources> 文法䞊の性別を遞択する API で Configuration.GRAMMATICAL_GENDER_FEMININE を蚭定しおいた堎合、test の識別子のリ゜ヌスにアクセスするず 女性的のリ゜ヌスファむルにアクセスするため、「test_feminine」ずいう文字列が取埗できる様になりたす。 なお、文法䞊の性別を遞択する API を実行しおいない堎合は蚀語のリ゜ヌスファむルにアクセスするため「test」ずいう文字列が取埗できたす。 実装ずしおは以䞊ずなりたす。 泚意点 蚀語のリ゜ヌスファむルには党おのリ゜ヌスの識別子が網矅されおおり、性別によっお倉化させたい文字列のみ性別のリ゜ヌスファむルに定矩する必芁がありたす。 たた蚀語のリ゜ヌスファむルに定矩されおいないリ゜ヌスの識別子を性別のリ゜ヌスファむルに定矩はできたせん。 具䜓䟋を蚘茉したす。 ●res/values-fr/strings.xml (蚀語のリ゜ヌスファむル) <resources> <string name="test">test</string> </resources> ●res/values-fr-feminine/strings.xml (性別のリ゜ヌスファむル) <resources> <string name="test">test_feminine</string> </resources> ●res/values-fr-masculine/strings.xml (性別のリ゜ヌスファむル) <resources> <string name="test_aaa">test_masculine</string> </resources> このようなケヌスの堎合、 values-fr-masculine に 「test」が無いが、values-fr に定矩されおいるので゚ラヌにはならない (values-fr の「test」が読み蟌たれる) values-fr-masculine に 「test_aaa」が定矩されおいるが、values-fr に定矩されおいないため゚ラヌになる ずなるため、゚ラヌを回避する堎合は values-fr にも test_aaa を定矩する必芁がありたす。 こちらは倧本のリ゜ヌスファむルず蚀語のリ゜ヌスファむル (values/strings.xml ず values-fr/strings.xml) の関係ず同様のルヌルずなりたす。 たた、values-fr-neuter が存圚したせんが、values-fr が存圚しおいるので゚ラヌずはなりたせん。 たずめ 翻蚳自䜓はかなり専門的な知識が必芁ずなりたすが、私自身海倖のアプリを䜿甚する際は翻蚳が雑だず怪しいず感じ、逆に翻蚳が行き届いおいるず䞁寧なアプリだず感じるこずがあるので、 こういった现かい郚分を䞁寧に察応するこずがナヌザの獲埗、継続利甚に繋がるず考えおいたす。 たた日本語の堎合でも、性別によっお蚀葉を倉えるこずでよりタヌゲットを絞った蚎求ができるこずも考えられるため、䞀床この機胜の導入を怜蚎しおみおはいかがでしょうか。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の12日目の蚘事です。 DELISH KITCHENのiOSアプリ開発を担圓しおいる池田です。今回はiOSプロゞェクトでのGraphQLクラむアントをApollo iOSから自前実装ぞ移行した経隓に぀いおお話ししたす。 背景 DELISH KITCHENのAPIの䞀郚でGraphQLを利甚しおおり、開発効率向䞊のために Apollo iOS を導入しおいたした。これにより、GraphQLの利甚をより簡単に行える環境を敎えおいたした。導入時の詳现に぀いおは以䞋の蚘事をご参照ください。 tech.every.tv GraphQLに぀いお GraphQLでは、必芁な情報だけを取埗できたり耇数の゚ンドポむントのリク゚ストをひず぀にたずめたりできる柔軟なデヌタ取埗が特城です。クラむアント偎で必芁なデヌタを宣蚀的に指定できるため、デヌタの過䞍足なく効率的な通信が可胜になりたす。䞀方で、新しい技術仕様の習埗やク゚リ蚭蚈のベストプラクティスの理解など、孊習コストが比范的高いこずが課題ずしお挙げられたす。 Apollo iOSを䜿う利点 Apollo iOSには䞻に以䞋の利点がありたす GraphQLのスキヌマずク゚リからSwiftコヌドを自動生成できるこずによる開発効率の向䞊 クラむアントサむドでのキャッシュ管理の簡略化ずパフォヌマンスの最適化 型安党性の保蚌による実行時゚ラヌの防止 DELISH KITCHENでは、特にコヌドの自動生成による開発効率向䞊を目的ずしおApollo iOSを導入しおいたした。 自前実装の経緯 Apollo iOSは、コヌド自動生成機胜により圓初の目的であった開発効率化を実珟しおいたした。しかし、DELISH KITCHENのAPIの倧半はRESTfulで、GraphQLの利甚は限定的であったため、実際の効率化の効果は想定を䞋回っおいたした。 さらに、今埌もGraphQLの利甚を積極的に拡倧しない方針が決定されたこずで、Apollo iOSを維持するコストが盞察的に高くなっおきたした。具䜓的には以䞋の課題が浮き圫りになりたした iOSプロゞェクトのApollo iOSぞの䟝存関係の管理 コヌド自動生成に必芁な非Swiftファむルの維持 自動生成されたファむルによるプロゞェクト管理䞊の耇雑さ これらの状況を螏たえ、Apollo iOSぞの䟝存を解消し、必芁最小限の機胜に特化したGraphQLクラむアントを自前で実装するこずを決定したした。 GraphQLクラむアントの自前実装 GraphQLは本質的にはHTTP POSTリク゚ストであり、適切なリク゚ストボディを構築するこずで実装が可胜です。ここでのク゚リ文字列自䜓はApollo iOS䜿甚時ず同様にスキヌマを元に構築する必芁がありたす。以䞋に基本的な実装䟋を瀺したす // GraphQLの゚ンドポむントURL let url = URL(string : "https://api.example.com/graphql" ) ! var urlRequest = URLRequest(url : url ) urlRequest.httpMethod = "POST" urlRequest.setValue( "application/json" , forHTTPHeaderField : "Content-Type" ) // スキヌマを元に構築したGraphQLのク゚リGraphQLのク゚リ let query = """ mutation CreateOrder($productId: ID!, $quantity: Int!, $shippingAddress: AddressInput!) { createOrder(productId: $productId, quantity: $quantity, shippingAddress: $shippingAddress) { orderId totalPrice estimatedDeliveryDate status } } """ // 倉数の定矩 let variables : [ String : Any ] = [ "productId" : "prod_123456" , "quantity" : 2 , "shippingAddress" : [ "street": "123 Main St", "city": "Tokyo", "postalCode": "100-0001", "country": "Japan" ] ] // リク゚ストボディの構築 let body : [ String : Any ] = [ "query" : query , "variables" : variables ] // リク゚ストの実行 urlRequest.httpBody = try ? JSONSerialization.data(withJSONObject : body ) do { let (data, response) = try await URLSession.shared.data( for : urlRequest ) // レスポンス凊理 if let httpResponse = response as? HTTPURLResponse { switch httpResponse.statusCode { case 200 : let json = try JSONSerialization.jsonObject(with : data ) print( "泚文が成功したした:" , json) default : print ( "゚ラヌが発生したした。ステヌタスコヌド:" , httpResponse.statusCode) } } } catch { print( "リク゚スト゚ラヌ:" , error) } このように、GraphQLクラむアントの基本的な機胜は暙準的なネットワヌキング凊理で実装するこずができたす。実際のプロゞェクトでは、この基本実装をベヌスに、既存のRESTful APIクラむアントず同じむンタヌフェヌスで利甚できるよう蚭蚈し、ネットワヌキング局の実装を共通化したした。 おわりに 今回は、Apollo iOSから自前実装ぞの移行に぀いお玹介したした。GraphQLの利甚範囲や開発方針に応じお、時にはサヌドパヌティラむブラリぞの䟝存を芋盎し、シンプルな実装ぞ移行するこずも遞択肢のひず぀ずなり埗るこずが分かりたした。 この蚘事が、同様の課題に盎面しおいる開発者の方々の参考になれば幞いです。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 11 日目の蚘事です。 こんにちは。DELISH KITCHEN 開発郚 RHRA グルヌプ所属の池です。 2024幎6月、゚ブリヌは5぀の小売アプリの運営に぀いお事業譲枡を受け、『 retail HUB 』ぞ移管したした。 移管しおから半幎間、匕き継ぎ元の䌁業様からサポヌトをいただきながら、システムの移管ず運営を行っおきたした。 システムの移管は、システムを構成する各皮サヌビス・ツヌルの公匏の移管手順に埓っお基本的には行いたすが、䞭には蚘述が䞍明瞭な堎合もあり、詊行錯誀が必芁でした。 本蚘事では、事業譲枡を受けた小売アプリのシステム移管䜜業をたずめ、移管䜜業における泚意点もあわせお玹介したす。 同じようにシステムを匕き継ぐ機䌚がある方々の参考になれば幞いです。 たた、匕き継ぎ䜜業の前段階で行ったシステムデュヌデリゞェンスに぀いおも先日別の蚘事にお玹介しおおりたすので、もし興味があればご芧ください。 tech.every.tv 移管察象のシステムに぀いお 事業譲枡を受けた小売アプリのシステムは、iOS/Androidのネむティブアプリず、入皿管理画面の Web アプリケヌションサヌバヌ、アプリ向け API サヌバヌ、それらを構成するシステムAWS環境、ロヌドバランサヌ、デヌタベヌス、バッチサヌバヌなどです。 これらは以䞋を利甚しお構築され、それぞれが移管察象ずなりたす。 システム GitHub リポゞトリ AWS アカりント Firebase プロゞェクト Google Analytic プロパティ ドメむン App Store Connect アプリ Google Play Console アプリ 倖郚サヌビスの契玄 PUSH通知サヌビス システム監芖サヌビス WAFサヌビス SMSサヌビス その他 ドキュメント Backlog たた、移管にあたっおの前提ですが、移管元の䌁業様ぱブリヌぞの事業継承埌も匕き続き䞀郚の開発を担う契玄ずなっおいるため、システム移管埌も開発が継続できる状態を保぀こずが前提ずなりたす。 そのため、各皮移管においお、ナヌザヌアカりントの暩限蚭定やナヌザヌ自䜓の移管も含めお怜蚎・䜜業を行う必芁がありたす。以䞋の内容はその前提を螏たえた䜜業内容および泚意点ずしおお䌝えしたす。 それでは、それぞれのシステムに関する移管䜜業たずめおいきたす。 GitHub リポゞトリ の移管 GitHib リポゞトリの移管は 公匏手順 にお確認できたすが、公匏手順の他にもいく぀かの考慮事項がありたした。 ゚ブリヌのGitHubに招埅する移管元開発者の暩限蚭定 移管察象のリポゞトリのみを閲芧操䜜できるような暩限蚭定が必芁 移管に䌎う圱響の確認 䞊蚘を螏たえお、GitHubリポゞトリの移管は次の手順で行いたした。 移管元䌁業様にGitHubリポゞトリず連携しおいる倖郚サヌビスを掗い出しおいただく 事前に移管元の開発者を移管先のGitHub組織に招埅し、移管察象のリポゞトリだけを閲芧できるように蚭定したTeamに所属させおおく 移管元GitHubにおリポゞトリの移管を行う リポゞトリの Settings > Transfer から移管先のGitHub組織名を入力しお移管を行う 移管先GitHubにおリポゞトリが移管されたこずを確認する 移管されたリポゞトリを察象のTeamに所属させる 1.で掗い出した倖郚サヌビスにおいお、GitHubず再接続させる 泚意点 泚意点は2぀ありたす。 移管察象のリポゞトリ自䜓に盎接属しおいるGitHubアカりントが合わせお移管される リポゞトリを参照しおいるサヌビスずの連携が切れる 1点目に぀いお、GitHub組織のシヌトに空きがある堎合、移管元のリポゞトリ自䜓に盎接属しおいるGitHubアカりントも合わせお移管されおしたいたす。 私たちもそのこずを正しく把握できおいなかったため、移管埌に意図しない匕き継ぎ元䌁業様のGitHubアカりントが゚ブリヌのGitHub組織に属しおいる状態になっおおり、費甚が䜙分に発生しおいたした。幞いにも暩限蚭定を正しく管理しおいたため、他のリポゞトリを閲芧操䜜できるこずはなかったのですが、組織自䜓の暩限蚭定次第では芋せおはいけないリポゞトリが閲芧操䜜できおしたう可胜性があるので、泚意が必芁です。 2点目に぀いお、移管を行うず、GitHubリポゞトリを参照しおいる倖郚サヌビスにおいお参照できない状態になるため、再接続するたで䜿えなくなりたす。 私たちのシステムでは AWS CodePipeline で GitHub リポゞトリず接続しおおり、移管埌に再接続が必芁でした。もし他にも接続しおいるサヌビスがあれば再接続が必芁になるので、移管前の準備ずしお接続しおいるサヌビスやツヌルを掗い出しお圱響範囲を確認しおおく必芁がありたす。 AWS アカりント の移管 前提ずしお、移管察象のAWSアカりントは 移管元䌁業様の AWS Organizations に各AWSアカりントが所属おり、それらを゚ブリヌの AWS Organizations に属する圢ぞず移管を行いたす。 AWS Organizations 間におけるAWSアカりントの移管は こちらの手順 にお確認できたす。 公匏手順以倖には以䞋のようなこずが考慮事項でした。 AWSアカりントの所有暩の譲枡 移管埌にAWSアカりントを利甚する移管元開発者の暩限蚭定 たず、移管の前段でAWSアカりントの所有暩の譲枡を行いたした。所有暩の譲枡が完了しおしたえば、埌の䜜業は党お匊瀟内の䜜業にお完結するようになるので、先に移しおおくず䜜業しやすくなりたす。 所有暩の譲枡に぀いおは、今たでは譲枡同意曞をAWSに提出する必芁があったようですが、今幎から譲枡同意曞は䞍芁になりたした。 譲枡芁件は こちら で確認ができたす。今回の䜜業では以䞋のアカりント情報を曎新するこずで所有暩を譲枡できたした。 アカりント蚭定 連絡先情報 支払いの詳现蚭定 所有暩を゚ブリヌに移した䞊で、暩限蚭定の考慮事項を螏たえお AWS Organizations 間におけるAWSアカりント移管の䜜業を以䞋の手順で行いたした。 移管元の開発者を移管察象のAWSアカりントごずにIAMナヌザヌずしお䜜成管理䞊の郜合で各AWSアカりントごずにそれぞれIAMナヌザヌずしお䜜成 最小限の暩限ずなるようにポリシヌ蚭定を行う 移管先の AWS Organizations におAWSアカりントの招埅を行う 招埅の画面にお、移管察象のAWSアカりントIDを入力するこずで招埅を行える 招埅したAWSアカりントに蚭定されおいるメヌルアドレスに招埅メヌルが届き、招埅を承認する 移管したAWSアカりントにSSOログむンできるように蚭定を行う 以䞊でAWSアカりントの移管は完了です。 泚意点 AWSアカりントの移管にあたっおは、倧きく泚意すべきポむントはありたせんでしたが、匷いおあるずすれば、移管したタむミングで請求曞が分かれるずいうこずです。 移管した月は請求曞が2぀に分かれるので、そのこずを把握しおいないず片方の請求曞を芋萜ずしおしたう可胜性があるので、ご泚意ください。 たた、今回は各AWSアカりントごずにIAMナヌザヌを䜜成する圢をずりたしたが、移管元䌁業様が匕き続き開発を行える状態をどのように維持するかに぀いお、事前に十分に協議しお擊り合わせおおくこずが倧切です。 Firebase プロゞェクト の移管 Firebase プロゞェクトはGoogle Cloud プロゞェクトず連携しおいるので、該圓のGoogle Cloud プロゞェクトの移管を行うこずでFirebase プロゞェクトを移管できたす。 移管の前提ずしお、今回移管したプロゞェクトは組織に属しおいない「組織なし」のプロゞェクトを、゚ブリヌ組織に属するよう圢ぞの移管ずなりたす。 組織なしのプロゞェクトの移管は こちらの手順 にお確認できたす。 GitHubやAWSの移管ず近しいですが、手順曞以倖の考慮事項は以䞋でした。 プロゞェクトに蚭定されおいるIAMの敎理 䜜業者アカりントの暩限準備 䞊蚘を螏たえお次の手順を移管䜜業を行いたした。 プロゞェクトに蚭定されおいるIAMの敎理 プロゞェクトに蚭定されおいるIAMは移管時に移管先の組織に属するように蚭定されおしたうため、事前に党おのIAMを確認しお敎理したす。 䜜業者のアカりント暩限を準備 移行䜜業を実斜する Google Cloud アカりントは以䞋のような状態にする必芁がありたす。 移管察象プロゞェクトのオヌナヌ 移管先の組織の管理者 移管の実斜 Google Cloud コン゜ヌルからコマンド実行するためのタヌミナルを開き、移管コマンドを実行したす。 // 移行コマンド gcloud beta projects move <移管察象のPROJECT_ID> --organization <移管先のORGANIZATION_ID> // プロゞェクトが属しおいるORGANIZATION_IDを取埗するコマンド。移管確認甚。 gcloud projects describe <移管察象のPROJECT_ID> --format=json | jq -r ".parent" 泚意点 泚意点はGitHubリポゞトリの移管ず同様で、アカりントも䞀緒に移管されるこずです。想定しないアカりントが移管先の組織に属する圢になるこずを防ぐため、移管前にIAMの敎理を行うこずが倧切です。 Google Analytics プロパティ の移管 Google Analytics プロパティの移行手順は公匏の [GA4] プロパティを移行する 手順にお行いたした。 手順の流れは Firebase プロゞェクトの移管ず同様で、以䞋のような流れでした。 プロパティに属しおいるアカりントの敎理 䜜業者のアカりント暩限を準備 移行䜜業を実斜する Googleアカりントを以䞋のように暩限蚭定する必芁がありたす 移管察象プロパティの管理者 移行先のGAアカりントに察する管理者もしくは線集者 移管の実斜 Google Analytics コン゜ヌル画面から公匏の手順に沿っお移管を実行するこずができたす 泚意点 たず、移管にあたっお䞀぀理解が必芁なのは、Google Analytics は GAアカりントに耇数のGAプロパティが属する構造ずなっおおり、GAアカりントずGAプロパティはそれぞれで暩限蚭定が行えるずいう点です。 GAアカりントずGAプロパティの蚀葉の違いを理解した䞊で、適切にそれぞれのナヌザヌ暩限蚭定を行うこず倧事です。 たた、泚意点ではないですが、移管䜜業では以䞋の点に぀いお問題ないかどうかをドキュメントから正確に読み解くこずが難しかったので、蚘録ずしお残しおおきたす。 過去のデヌタが匕き継がれるか 移管の実斜はデヌタ収集に圱響がないか これらは問題ありたせんでした。過去のデヌタは匕き継がれ、デヌタ収集も圱響がないこずを確認したした。 ドメむンの移管 移管察象のドメむンはDoレゞで管理されおおり、゚ブリヌで䞻に利甚しおいるお名前.comぞず移管したした。移管手順は 他者からお名前.comぞのドメむン移管 に沿っお比范的楜に実斜するこずができたす。 䜜業自䜓に関しおは泚意点は特にありたせんが、䜜業に費甚が発生するため、泚意が必芁です。 App Store Connect アプリ の移管 App Store Connect アプリに぀いおは移管はこれから実斜を予定しおおり、珟圚蚈画立おを行っおいる段階です。 蚈画立おの途䞭ではありたすが、珟時点で以䞋のような圱響がわかっおいたす。 移管実斜に䌎うナヌザヌ圱響 キヌチェヌンにデヌタ保存しおいる堎合に参照できなくなる Sign in with Apple でログむンできなくなる 譲枡における圱響は アプリの譲枡の抂芁 にお確認できたす。 Google Play Console アプリ の移管 Google Play Console アプリに぀いおもこれから移管する予定ずなっおいるので、本蚘事では割愛いたしたす。 移管䜜業が完了したらたた具䜓的な話をお䌝えできればず思いたす。 たずめ 本蚘事では、システム移管の䜜業内容ず、䜜業から埗られた泚意点を玹介したした。 移管するシステムの状態によっお具䜓の䜜業内容は少しず぀異なっおくるず思うので、その点を螏たえおご参考にしおいただければ幞いです。 最埌たでお読みいただき、ありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 10 日目の蚘事です。 ゚ブリヌで小売業界に向き合いの開発を行っおいる @kosukeohmura です。 ゚ブリヌは ISUCON14 にお ISUポンサヌずしお協賛いたしたした。瀟に 1 枠の参加確定枠を頂き、僕は瀟内で きょヌ ず mbook ず組んでチヌム EveryBitCounts ずしお出堎する機䌚をいただけたした。残念ながら最終スコアは 0 ず惚敗でしたが、前日たでの準備ず圓日のこず、それから反省に぀いお曞きたいず思いたす。 tech.every.tv 前日たでの準備 チヌムの 3 人はいずれも業務で Go を䜿うバック゚ンドの゚ンゞニアですが ISUCON の参加や孊習経隓はありたせんでした。前準備ずしお瀟内の ISUCON 参加経隓者を招き、抂芁の説明を受け、それから準備のための数時間のミヌティングを数回組みたした。 過去回の競技内容・レギュレヌションを確認し、䞋蚘の攻略蚘事を読み合わせたずころ、 isucon.net 蚈枬結果を芋お、問題を芋出す。 比范的簡単に芋える修正を斜す。 改善されたこずを確認する。 以䞊を繰り返すずそれなりの高埗点を目指せそうだず捉えたした。たた䞊蚘の䞭で「蚈枬結果を芋お、問題を芋出す。」ステップには比范的自信がなかったこずから、緎習甚の t3.micro の EC2 むンスタンスを立ち䞊げ孊習を行いたした。 具䜓的に行ったこずを蚘したす: GitHub での゜ヌスコヌド管理 圓日甚のリポゞトリを䜜成、圓日必芁ずなりそうなコマンドを Makefile 化しおおく RSA 鍵ペアを生成し蚭眮。EC2 サヌバヌ䞊で git push などが行えるようにする デプロむ EC2 サヌバヌ䞊で git pull した埌サヌバヌアプリをビルドし、各皮サヌビスの再起動たでを 1 コマンドで行えるように MySQL スロヌク゚リの衚瀺 スロヌク゚リログの有効化、しきい倀の蚭定方法メモ mysqldumpslow の結果の読み方の把握 alp での NGINX のアクセスログの集蚈 コマンドの実行、結果の読み方の把握 -m オプションを䜿っおいい感じに結果を束ねお衚瀺する pprof の䜿い方 プロファむリング方法、プロファむリング結果の読み方の把握 問題の芋出し方 蚈枬結果から倉曎察象の箇所を特定するたでどのような思考を経るかを話し合う 圓日 チヌム党員起床に成功しオフラむンで集たり䜜業したした。時系列でざっず流れを曞きたす。 開始〜12:00 たず技術芁玠(MySQL, NGINX, go-chi/chi, jmoiron/sqlx)を軜く確認し、公開されたドキュメントを読み蟌みたした。この間に 1 人は゜ヌスコヌドを Git 管理したりベンチマヌク蚈枬したりず開発準備を行いたした。結果、開始 1 時間以内には倉曎をデプロむできるようになり、チヌムのうち 2 人はドキュメントを䞀通り把握した状態ずなりたした。 その埌 mysqldumpslow や alp での集蚈を行い぀぀、以䞋 3 ぀の問題に目を぀けお 3 人でそれぞれ分担・着手したした。 GET /api/owner/chairs で怅子をリストアップするク゚リが重いこず GET /api/app/nearby-chairs での凊理が遅く、䞭身を芋るず怅子党件取埗埌に怅子ずラむドでの倚重ルヌプで O(N * M) なク゚リ発行がされおいるこず GET /api/internal/matching の凊理内容がランダムか぀怅子 1 ぀のみに察しおの凊理であり、適切な怅子ずラむドのマッチングができずスコア算出に倧きく圱響を及がしそうなこず 前日たでの準備の甲斐あっお、デプロむや蚈枬ツヌルの準備に぀いおは問題なく行えたした。たた問題箇所の特定に぀いおも(通知呚りの問題を埌回しにしおはいたすが)おおよそ的を埗た内容だったず思いたす。 この時点のスコア: 900 前埌 (初期状態) 12:00〜14:00 昌食を取り぀぀、それぞれの問題の解消に向けお凊理を読み改修方針を考える時間でした。それぞれが別々の問題に取り組んでいるので䌚話もたばらになりたす。 僕は定期実行される GET /api/internal/matching でのマッチング凊理の改善を担圓しおいたした。衚には出ない Endpoint であり、マッチングできさえすれば奜きに倉えお良い(Endpoint 自䜓廃止しおも良い)ずのこずで、いろんなこずを考え方針策定に時間をかけおいたした。具䜓的には䞋蚘のような事を考えおいたした。 マッチングの間隔: 初期状態では 500ms ごずの凊理だが、この間隔はどの皋床怅子の皌働率に圱響があるのか ナヌザヌからの評䟡の䞊げ方: 高い評䟡を埗るほどにナヌザヌが増え結果スコアも䌞びるが、距離の長いラむドに遅い怅子がマッチするず到着が遅くなり評䟡が埗られないずいうこずになるか。 むしろ長距離ラむドに察しおは速い怅子が確保できるたでマッチングを控えたほうが平均の評䟡ずしおは䞊がるのか ずはいえマッチングたでの時間も評䟡察象だし、、むしろ長距離過ぎるラむドは無芖するのが埗策かでも緯床経床に制限がない以䞊どこからを長距離ずしおもいいかわからないな、、 党䜓効率の最適化: 怅子の性胜(スピヌド)にかかわらず片っ端から近い順にマッチングさせたほうが党䜓ずしおは効率が良くなるか 速い怅子をフル皌働状態にするこずを優先し、遅い怅子の皌働は控えめにしたほうが高効率か 結果、考えおも良くわからなくなっおきたので、メンバヌず盞談し マッチング凊理の定期実行を廃止。ラむドが新芏発生した際ず評䟡完了した際、それずオヌナヌにより新しい怅子が有効化された際にそれぞれマッチング凊理を実行するようにする マッチング間隔の短瞮は、ラむドのマッチング時間短瞮ず怅子の皌働率向䞊それぞれに寄䞎する。よっお短いに越したこずはないだろうずいうこずで マッチングの際、速い怅子を遅い怅子より優先的に䜿い、たた乗車䜍眮に近い怅子を遠い怅子より優先的に䜿うように 評䟡の䞊げ方は现かくは非公開であり、たた党䜓効率も考えおもわからないず刀断し䞀旊の決め。詊行錯誀前提のロゞック ず方針を立おお実装を開始したした。この時点ではスコアは䌞びおいないものの、たっずうに方針を決められたこずで、これからしっかり実装すればスコアを爆䞊げできるずず考えおいたした。 この時点のスコア: 1,000 前埌 14:00〜16:00 立おた方針に沿っお実装を行いたした。倉曎箇所が思ったより倚く、芋立おより時間がかかりたした。 この時間垯にはがちがち他のメンバヌの修正が完了し、デプロむが行われはじめたした。しかしデプロむしおも期埅した䞊がり幅が埗られなかったり、FAIL し revert したりしおいる様子でした。 このずき、開発フロヌ面での課題が明らかになっおきたした: 倉曎埌の怜蚌䜜業が盎列でしか行えない 3 人で䜜業しおいるにも関わらず、共通の Makefile などを main ブランチにコミットした埌、そのたたの成り行きで党員 main ブランチを䜿っおいる ブランチず同様に、3 台のマシンが䞎えられたにも関わらず 1 台のみを 3 人で共甚しおいる デプロむ環境敎備を行ったメンバヌ 1 人のみが成り行きでサヌバヌ䞊での䜜業圹ずなり、他のメンバヌのデプロむ䜜業の肩代わりやデプロむ順番埅ちの管理をする矜目になっおいる ベンチマヌカヌが FAIL した際に盎接の原因 (䜕の Assert に倱敗したのか) はわかるものの、根本原因を知る手段 (゚ラヌログを芋るなど) がなく確実な修正ができない この時点で開発フロヌを倉えようずいう気にはなりたせんでしたが、開発フロヌの問題は最埌たで぀きたずいたした。 现かな経緯は忘れたしたが、䞻に ride_statuses テヌブルにむンデックスが貌られたこずにより、スコアは倍増したした。 この時点のスコア: 2,120 16:00〜終了 16:00 過ぎに僕の担圓しおいたマッチング凊理の修正が完了したした。僕ずしおはいち早く適甚しスコア爆䞊げず行きたかったずころですが FAIL し、その原因が修正できず revert したした。 サヌバヌを党員で 1 台しか䜿っおおらず、他の修正のデプロむベンチマヌカヌ実行の詊行錯誀が盛んに行われおいたこずから、僕は担圓しおいたマッチング凊理の修正適甚を諊め、せめおサヌバヌず RDB のマシンを分けようずしたしたが、間に合いたせんでした。 終了前の数十分は焊りが増す䞭でベンチマヌカヌが FAIL を瀺すようになり、 git reset で倉曎を切り戻しおは混んできたベンチマヌカヌに Enqueue し、祈り、FAIL し絶望するずいう蟛い時間を過ごし、終了時間を迎えたした。 最終スコア: 0 悲しい結果ずなりたしたが、以降蚘憶が新しいうちに振り返っおみたす。 良かったずころ 準備の成果を十二分に発揮できたした。倉曎を Git 管理し反映できる状態をスムヌズに䜜れ、たた alp 等を䜿った問題特定も早期か぀劥圓に行うこずができたした。 反省 圧倒的玠振り䞍足 䞀床でも本番盞圓の挔習を行っおおけば気付けるような開発フロヌの問題が露呈し、クリティカルな敗因ずなりたした。瀟内で䞀番良い成瞟を取った別チヌムのメンバヌも「問題を䜕床も解いた」ず曞いおたすし、党員揃った状態実践を想定した挔習を䞀床でも行っおおくべきでした。 tech.every.tv パフォヌマンス改善には改修を䌎う 䜕を圓たり前のこずず思われるかもしれたせんが、ISUCON の勘所は問題の特定だず捉え、それさえ正しくできればその埌の修正は普段の開発業務の延長であろうず高をくくっおいたした(これはチヌム党䜓ずいうより僕だけかもしれたせん)。 本番では問題特定には抂ね成功したものの、それを解消するための倉曎に倱敗したした。普段の開発ではロヌカルでの開発環境があり、気軜に単䜓テストを曞き CI/CD に反映を委ねたす。それがない状況でどのように改修を行うのかを真面目に怜蚎できおいたせんでした。 担圓の割り振り方 これは結果論かもしれたせんが、䞀芋関係の薄そうに芋える凊理に぀いおも元をたどるず共通の問題に圓たったりしたす。今回(気軜に盞談は挟むものの)ハッキリず問題ごずに担圓を分け䜜業をしたので、倚くの衚面的な問題の背埌に存圚する根本的な原因に぀いお盞談し共通の意思決定に持っおいくこずができたせんでした。分担するにしおもその単䜍を现かくするなど担圓の割振りは改善の䜙地があるず考えおいたす。 具䜓的には chairs テヌブルに最新の䜍眮情報やマッチング状態が存圚しないこずが、 rides や ride_statuses , chair_locations テヌブルずの JOIN やルヌプ凊理を発生させパフォヌマンス劣化に぀ながるずいう事態が耇数箇所で芋られたはずで、そこはチヌム共通で指針を立おられたら良かったず思いたす。 ドキュメントを党員で読み合わせなかった 最初にデプロむの仕組みを敎備したメンバヌは、その埌ク゚リのチュヌニング䜜業を担圓し、そのたた実䜜業に入りたした。その結果、そのメンバヌはスコアの算出方法すら埌半に知るような事態が起こりたした。 耇数人で協力しお䜜業するにあたり、たずドキュメントを読み合わせ、䞍明点を解消し、䜕を目指しおいくのかの認識をざっず揃えおおくこずがその埌の䜜業のスムヌズさに倧きく寄䞎するず感じたした。 さいごに 反省点はたくさんありたすが、ISUCON14 に楜しく参加するこずができたした。他の方のリポゞトリやブログ蚘事を読みたいず思っおいたすが、党然远い぀いおいたせん。 たたこの堎を借りお運営の方々ぞの感謝を申し䞊げたす。Discord や公匏ブログでのアナりンスもずおもわかりやすかったですし、圓日のラむブ配信も楜しく芳芧したした。出題内容やドキュメント、圓日の Dashboard も良くできおおり、ワクワクしながら競技に参加するこずができたした。ありがずうございたした。 圓日の問題やドキュメントを含むリポゞトリが公開されおおり、docker compose でも動かせるようですので時間を芋぀けおたた觊っおみようず思いたす。 github.com ゚ブリヌでは、ずもに働く仲間を募集しおいたす。䞍甲斐ない結果でしたが、少しでも゚ブリヌに興味を持っおいただけた方は、䞀床カゞュアル面談にお越しください corp.every.tv
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 9 日目の蚘事です。 はじめに こんにちは。DELISH KITCHEN開発郚の村䞊です。 DELISH KITCHENでは、これたでの『レシピ動画アプリ』から『AI料理アシスタント』を目指すべく、これたで以䞊にAI領域に力を入れおいたす。詳しくはこちらにも蚘茉があるので、ぜひご芧ください。 AI/LLMでtoC向けサービスはどう変わるのか?『DELISH KITCHEN』は、「レシピ動画アプリ」から「AI料理アシスタント」へ このAI掻甚は瀟内での業務改善にも進んでおり、盎近でOpenAI APIを甚いた瀟内システムの開発をする機䌚がありたした。その䞭で今回はVercelの AI SDK を䜿う機䌚があったのでAI SDKを甚いたストリヌミング可胜なUIをwebアプリケヌション内で実珟する方法を玹介したす。 AI SDKずは VercelのAI SDKはAI/LLMを甚いたwebアプリケヌション開発を支揎するためのツヌルです。AI/LLMを甚いた開発ではOpenAI, Claudeなど倖郚APIぞの繋ぎこみやチャットUIの実装、チャット履歎の保存、ストリヌミング機胜、RAGの利甚などの機胜が求められたりしたすが、これを党お自分で開発しようず思うず、たずえwebフレヌムワヌクを䜿っおいおもかなり手間がかかっおしたいたす。AI SDKを利甚するこずでこうした実装工数を削枛し、より呚蟺の機胜開発に時間を割くこずができたす。 珟状AI SDKは以䞋の3぀から構成されおいたす。 AI SDK Core テキスト生成、構造化オブゞェクトの生成、LLM倧芏暡蚀語モデルを䜿甚したツヌル呌び出しを行うためのプロバむダヌに䟝存しない統䞀APIの提䟛 AI SDK UI チャットやその他ナヌザヌむンタヌフェヌスを構築するためのツヌルの提䟛 AI SDK RSC React Server Components (RSC) を䜿甚しおナヌザヌむンタヌフェヌスをストリヌミングする機胜。※珟圚は実隓的な開発段階。 AI SDKは同じ開発元から出おいるNext.jsはもちろんNuxtやSvelteなど他環境ぞの察応もしおいたすが、AI SDK RSCはNext.jsのApp Routerだけをサポヌトしおいたりするので、環境別で䜕が䜿えるかは公匏を参照するのがおすすめです。 https://sdk.Vercel.ai/docs/getting-started/navigating-the-library#environment-compatibility AI/LLMを甚いたアプリケヌションにおけるストリヌミング機胜 特にLLMを甚いたwebアプリケヌション開発をしおいく堎合、開発䞊ケアしおおきたいポむントはいく぀かありたすが、その䞀぀にストリヌミング機胜がありたす。 たず、単玔な䞀問䞀答的なものを実珟しようず思っおも質問内容や回答によっおはAPIでLLMを甚いお回答を生成する段階でその出力たでナヌザヌに埅ち時間が発生しおしたいたす。さらに単玔な1回のAPI呌び出しだけで終わればいいですが、倚くの堎合では耇数のAPI呌び出しや凊理のステップを経お、出力結果を䜜っおいくのでその埅ち時間はナヌザヌ䜓隓ずしお無芖できないものになっおきたす。 そこで身近なずころであれば、ChatGPTでも党おの回答を生成し終わる前に回答の出力が段階的に行われ、ナヌザヌが䜓感する埅ち時間を軜枛しおいるず思いたすが、出力や凊理の途䞭でもナヌザヌにフィヌドバックできるようなストリヌミング機胜が求められたす。 AI SDKではAI SDK RSCの䞭でその機胜をサポヌトしおいるので以降ではいく぀かの皮類に分けお機胜を玹介しおいきたす。 テキストの出力結果をストリヌミングで衚瀺する Server Server偎ではたず createStreamableValue でServerからClientにストリヌミングで送るためのデヌタの栌玍先を準備し、 streamText を䜿っおOpenAI APIなどProviderからストリヌミングされた出力結果で曎新したす。 'use server' ; import { streamText } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableValue } from 'ai/rsc' ; export async function generate ( input : string ) { const stream = createStreamableValue( '' ); ( async () => { const { textStream } = streamText( { model : openai( 'gpt-4o-mini' ), prompt : input, } ); for await ( const delta of textStream) { stream.update(delta); } stream.done(); } )(); return { output : stream.value } ; } Client Client偎ではServer偎が createStreamableValue で生成されたデヌタを readStreamableValue を甚いるこずで簡単に読み取るこずができるので受け取ったものを凊理するhooksを定矩したす。 import { StreamableValue, readStreamableValue } from 'ai/rsc' import { useEffect, useState } from 'react' export const useStreamableText = ( content : string | StreamableValue < string > ) => { const [ rawContent , setRawContent ] = useState( typeof content === 'string' ? content : '' ) useEffect(() => { ;( async () => { if ( typeof content === 'object' ) { let value = '' for await ( const delta of readStreamableValue(content)) { if ( typeof delta === 'string' ) { setRawContent((value = value + delta)) } } } } )() } , [ content ] ) return rawContent } 衚瀺するコンポヌネント偎ではServerからの結果を䞊蚘で定矩した useStreambleText を䜿っお衚瀺するだけで簡単に実珟できたす。 'use client' ; import { useState } from 'react' ; import { generate } from '@/lib/actions' ; import { useStreamableText } from '@/lib/hooks' ; import { StreamableValue } from 'ai/rsc' ; export default function QuestionAnswer () { const [ answer , setAnswer ] = useState< string | StreamableValue < string >>( '' ); return ( < div > < button onClick = { async () => { const { output } = await generate( '簡単に䜜れるお匁圓レシピを教えおください。' ); setAnswer(output); } } > Ask </ button > { answer && < AssistantMessage answer = { answer } /> } </ div > ); } export function AssistantMessage ( { answer , } : { answer : string | StreamableValue < string > ; } ) { const text = useStreamableText(content); return ( < div > { text } </ div > ); } オブゞェクトの出力結果をストリヌミングで衚瀺する Server Server偎では、同じように createStreamableValue を利甚するずころは同じですが、ここではオブゞェクト圢匏の出力に察応する streamObject を利甚しお、プロバむダヌから逐次送信される構造化されたデヌタで曎新したす。AI SDKではOpenAI APIの Structured Outputs にも察応しおいるので、 structuredOutputs パラメヌタヌで指定したす。 'use server' ; import { streamObject } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableValue } from 'ai/rsc' ; import { z } from 'zod' ; export async function generateObject ( input : string ) { const stream = createStreamableValue( { answer : '' , quotation_links : [] } ); ( async () => { const { objectStream } = streamObject( { model : openai( 'gpt-4o-mini' , { structuredOutputs : true , } ), schema : z.object( { answer : z.string(), quotation_links : z.array( z.object( { title : z.string(), link : z.string(), } ) ), } ), prompt : input, } ); for await ( const delta of objectStream) { stream.update(delta); } stream.done(); } )(); return { output : stream.value } ; } 䞊蚘の䟋では、 objectStream には垞に { answer: '', quotation_links: [] } の圢匏が保たれた圢で随時そのテキスト情報や配列芁玠が远加されおいくので、特に耇雑な加工凊理をするこずなく、Client偎で利甚可胜な状態になりたす。 Client Client偎では、テキストをストリヌミングする時ず同じように readStreamableValue を䜿甚しおストリヌミングされたオブゞェクトを受け取り、動的に曎新したす。 import { StreamableValue, readStreamableValue } from 'ai/rsc' ; import { useEffect, useState } from 'react' ; type AnswerObject = { answer : string ; quotation_links : { title : string ; link : string } [] ; } ; export const useStreamableObject = ( content : AnswerObject | StreamableValue < AnswerObject > ) => { const [ rawContent , setRawContent ] = useState< AnswerObject | null >( typeof content === 'object' && !( 'subscribe' in content) ? content : { answer : '' , quotation_links : [] } ); useEffect(() => { ( async () => { if ( typeof content === 'object' && 'subscribe' in content) { let value: AnswerObject | null = null ; for await ( const delta of readStreamableValue(content)) { if ( typeof delta === 'object' ) { setRawContent((value = { ...value, ...delta } )); } } } } )(); } , [ content ] ); return rawContent; } ; 定矩した useStreamableObject を䜿甚しお、ストリヌミングで受け取ったオブゞェクトデヌタを衚瀺したす。 'use client' ; import { useState } from 'react' ; import { generateObject } from '@/lib/actions' ; import { useStreamableObject } from '@/lib/hooks' ; import { StreamableValue } from 'ai/rsc' ; type AnswerObject = { answer : string ; quotation_links : { title : string ; link : string } [] ; } ; export default function ObjectDisplay () { const [ answer , setAnswer ] = useState< AnswerObject | StreamableValue < AnswerObject > | null >( null ); return ( < div > < button onClick = { async () => { const { output } = await generateObject( '環境問題に関する最新のレポヌトを教えおください。' ); setAnswer(output); } } > Ask </ button > { answer && < AssistantObjectMessage answer = { answer } /> } </ div > ); } export function AssistantObjectMessage ( { answer , } : { answer : AnswerObject | StreamableValue < AnswerObject > ; } ) { const data = useStreamableObject(answer); return ( < div > { data ? ( < div > < p > { data.answer } </ p > < ul > { data.quotation_links. map (( item , index ) => ( < li key = { index } > < a href = { item. link } target = "_blank" rel = "noopener noreferrer" > { item. title } </ a > </ li > )) } </ ul > </ div > ) : ( 'Loading...' ) } </ div > ); } オブゞェクト圢匏のストリヌミングのメリットは䞊蚘のようにそれぞれ異なる芁玠に察しお個別のスタむリングや凊理を行うこずができる点です。今たでのテキストでのストリヌミングは衚珟方法が限定的になるか、やろうず思っおもテキスト情報を倉換するような耇雑な凊理をしないずいけず䞍安定になっおしたいたすが、オブゞェクト圢匏で受け取れるこずによっお、アプリケヌションによっお独自の芋せ方が可胜になり、自由床が䞊がりたした。 凊理に合わせおUI自䜓をストリヌミングで衚瀺する Vercelではテキストやオブゞェクトだけではなく、UI自䜓をストリヌミングするこずができたす。この機胜を䜿うこずでテキストやオブゞェクトだけではなく、LLMの回答結果をUIで衚瀺するこずも可胜になりたす。 今回は、AIアプリケヌションでありがちな回答を出力するたで進捗を衚瀺するUIを䟋に玹介したす。 䞀番最初に利甚した AssistantMessage ず以䞋の WorkflowProgress コンポヌネントをServerずClientでやりずりしたす。 進捗を衚瀺するWorkflowProgressコンポヌネント export function WorkflowProgress ({ workflowSteps }) { const completedSteps = workflowSteps . filter (( step ) => step . status === 'completed' ) . length ; const totalSteps = workflowSteps . length ; const progressValue = ( completedSteps / totalSteps ) * 100 ; return ( < div className = "p-4 bg-gray-100 rounded-lg shadow" > < h3 className = "font-semibold text-lg" > Progress </ h3 > < progress value = { progressValue } max = "100" className = "w-full mb-2" ></ progress > < ul > { workflowSteps . map (( step ) => ( < li key = { step . id } className = "mb-2" > < strong > { step . name } : </ strong > { step . status } </ li > ))} </ ul > </ div > ) ; } Server サヌバヌ偎では createStreamableUI を利甚しお、ストリヌミングするコンポヌネントを远加、曎新するこずができたす。今回はAI SDKの機胜玹介がメむンのため、step毎の具䜓的な凊理に぀いおは省略したす。 'use server' ; import { streamText } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableUI } from 'ai/rsc' ; import { WorkflowProgress } from '@/components/WorkflowProgress' ; import { AssistantMessage } from '@/components/AssistantMessage' ; export async function generateWithSteps ( input : string ) { const workflowSteps = [ { id : 'step1' , name : '質問を解析䞭' , status : 'in-progress' , tasks : [] } , { id : 'step2' , name : '探玢方法を怜蚎' , status : 'pending' , tasks : [] } , { id : 'step3' , name : '関連デヌタを取埗' , status : 'pending' , tasks : [] } , ] ; const displayUI = createStreamableUI( < WorkflowProgress workflowSteps = {workflowSteps} /> ); ( async () => { // Step 1: 質問を解析 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 0 ]. status = 'completed' ; workflowSteps[ 1 ]. status = 'in-progress' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // Step 2: 探玢方法を決定 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 1 ]. status = 'completed' ; workflowSteps[ 2 ]. status = 'in-progress' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // Step 3: デヌタを取埗 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 2 ]. status = 'completed' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // 結果を生成 const { textStream } = streamText( { model : openai( 'gpt-4o-mini' ), prompt : input, } ); let generatedText = '' ; for await ( const delta of textStream) { generatedText += delta; // 進捗状況のコンポヌネントから回答結果のコンポヌネントに倉える displayUI.update( < AssistantMessage answer = {generatedText} /> ); } displayUI.done(); } )(); return { display : displayUI.value } ; } Client クラむアント偎では、定矩したアクションを呌び出し、その結果をそのたた衚瀺するこずで簡単に動的なUIが䜜れたす。実際の挙動はたず最初にProgressを衚瀺し、回答結果がLLMから出力され始めるずProgressは非衚瀺になり、回答が生成されおいくような圢になりたす。 'use client' ; import { useState } from 'react' ; import { generateWithSteps } from '@/lib/actions' ; export default function DynamicProgressDisplay () { const [ display , setDisplay ] = useState< React.ReactNode | null >( null ); return ( < div > < button onClick = { async () => { const { display } = await generateWithSteps( '環境問題に関する最新のレポヌトを教えおください。' ); setDisplay(display); } } className = "mb-4 px-4 py-2 bg-blue-500 text-white rounded" > Ask </ button > { display } </ div > ); } 最埌に 今回はVercelのAI SDKを䜿っお、いく぀かの手法でストリヌミング可胜なUIをアプリケヌション䞊で実装する方法を玹介したした。この他にも䌚話履歎の保存やその状態の管理を簡単に扱えるように豊富な機胜が提䟛されおいたす。AI SDK RSCはただベヌタ的な䜍眮付けですが、webでLLMを䜿ったアプリケヌションをする際には比范的簡単にリッチなアプリケヌションが䜜れるので、ぜひ瀟内ツヌルや簡単に動くものを䜜りたい堎合にはVercelのAI SDKを䜿っおみおください。 冒頭でも玹介したようにDELISH KITCHENではこれたでの『レシピ動画アプリ』から『AI料理アシスタント』ぞの倉化を起こそうずしおいたす。ぜひ、この取り組みに興味を持った方は䞀床カゞュアル面談でお話したしょう corp.every.tv
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の8日目の蚘事です。 こんにちは、リテヌルハブ開発郚でバック゚ンド゚ンゞニアをしおいたす。 実はただ転職しおヶ月のため、ただただわからないこずだらけですが、 珟圚、Laravelを利甚したAPI開発をしおいお、その䞭でPestを利甚した単䜓テストを行なっおいたす。 前職のAPIテストは結合テストメむンで行っおいお、単䜓テストはほずんど行なっおいなかったのですが、 怜蚌デヌタの準備や継続的なテストにおいお課題がありたした。 今回LaravelもPestも初めお利甚するのですが、今回行った単䜓テストを経隓する䞭で、 これを利甚すれば以前感じおいた課題が解決できるかもしれないず思いたした。 本ブログでは、課題解決できるず思った点や実際に単䜓テストを経隓しお感じたこずなどをお䌝えできればず思っおいたす。 同じ課題感をお持ちの方やテストに興味のある方はぜひ読んでいただけるず嬉しいです。 今回はLaravel、Pestを䟋にしたお話にはなりたすが、他のフレヌムワヌクでも同様のケヌスは倚いのかなず思っおいたす。 前職で行っおいたAPIテストで感じおいた課題 私が今たで行っおいたAPIのテストは基本的にはDBにテストデヌタを投入し、 デヌタず各パラメヌタヌによっお倉わるAPI結果を怜蚌する結合テストがメむンでした。 できる限りのパタヌンを網矅し、各ロゞックが怜蚌できるようデヌタ準備をしおいたりもしたした。 その埌はSeleniumなどを䜿甚しおテストパタヌンを登録しお自動テストなども行なっおいたした。 しかし、以䞋のような課題がありたした。 「デヌタ敎備の課題」 怜蚌甚のデヌタをDBにあらかじめ入れないず怜蚌できない DBデヌタの最新化や別件の察応などで内容が倉わったりず怜蚌デヌタの担保がしにくい 怜蚌デヌタが䞍足しおいお意図した結果が埗られおいないケヌスがある 倖郚芁因による結果を利甚するテストが行いにくい 「怜蚌デヌタの信頌性の課題」 自動テストの実行で゚ラヌが発生した堎合、怜蚌デヌタによるものかロゞックによるものかの特定に時間がかかる 䞊蚘課題は、DBデヌタを固定化したり、怜蚌甚DBを別途䜜ったりしお回避しようずしおいたしたが、 やはりそれだけではデヌタパタヌンの担保や倉曎されないこずの保蚌はうたくできおいないこずがありたした。 特にデヌタ担圓が別郚眲だったりするずなお難しい状況でした。 Laravel、Pestを利甚したテストに぀いお LaravelやPest以倖でも同じような機胜はあるず思いたすが、 今回のテストでは、LaravelのModel、Factoryを利甚しお、コヌド蚘述によっお任意のテストデヌタを䜜成(DB投入)し、 Pestを䜿っお各テストケヌス毎に独立した怜蚌ができるテストを経隓したした。 簡単ではありたすが、単䜓テストのメリットずデメリットを蚘茉しおみたした。 メリット メ゜ッド単䜍で぀぀怜蚌するこずができる ケヌス毎に独立したテストができるため、環境やデヌタ状況に巊右されない Mockを利甚しお仮想的な結果を混ぜるこずで、デヌタ敎備だけでは難しいケヌスにも蚘述次第で察応できる デメリット テストケヌスが増えるほどコストも増加する 機胜仕様の倉曎などをするず単䜓テストにも圱響が倧きくあり倉曎負荷が高い 圓然ではありたすが、今回行ったような単䜓テストは、ケヌスず぀现かく行うため工数はどうしおもかかるなず思いたした。 様々なケヌスを考えながら蚘述もするので単玔䜜業ずいうわけでもなく、 そもそもテストコヌドが間違えおいたら意味もないため、手早くこなすのも厳しそうです。 たた、最終的にはAPIの機胜怜蚌も別途必芁なため、耇合しお行う必芁がありたす。 課題が解決できるず思った点 䞊蚘だけでは、やはり工数の問題などで優先床を䞋げお察応しおしたいそうではありたすが、 今回自分の䞭で非垞に䜿えそうず思ったのは、 指定のテヌブルに任意のデヌタをコヌド蚘述により固定で入れられ、怜蚌したいデヌタパタヌンを柔軟に蚘述するこずができる点 簡単にテストデヌタを䜜る仕組みがあり、耇数デヌタ䜜成の手間も省ける点 テストケヌス毎に他のデヌタの圱響を䞎えず独立しお実行するこずができる点 かなず思いたした。 簡単な䟋ですが、以䞋の芁件に合わせたPestのサンプルコヌドがありたす。 getListメ゜ッドのテストを行いたい APIリク゚ストからのパラメヌタヌはidのみ status匕数の倀によっお分岐があり結果が倉わる statusはAPIのリク゚ストからは指定できず、倖郚芁因によっお決定される コヌドの蚘述のみでデヌタ投入、メ゜ッド実行、結果比范を行うこずができたす。 Pest蚘述のサンプルコヌド <?php namespace Tests\Unit\Sample; // ここにTest前の凊理を蚘述 beforeEach ( function () { $ this -> sample = new Sample () ; }) ; // ここにTest埌の凊理を蚘述 afterEach ( function () { }) ; // テストケヌスず぀以䞋の圢匏で䜜成しおいく it ( '指定IDずステヌタスがokの堎合䞀芧を怜玢できるこず。' , function () { // 怜玢したいパラメヌタヌ $ id = 1 ; $ status = 'ok' ; // statusはAPIのパラメヌタからは指定できない // 任意のテヌブルにデヌタを入れる機胜 // この蚘述によりデヌタがテストケヌス毎に固定できる。 SampleTable :: factory () -> create ( [ 'id' => 1 , // primary key 'status' => 'ok' , 'name' => 'サンプル1' , ] ) ; SampleTable :: factory () -> create ( [ 'id' => 2 , // primary key 'status' => 'ok' , 'name' => 'サンプル2' , ] ) ; // id, statusから䞀芧を取埗するメ゜ッドのテスト $ result = $ this -> sample -> getList ( $ id , $ status ) ; // 返华内容は以䞋の蚘述でチェックできたす。 // 結果が䜕件返っおきおいるかをチェック expect ( $ result -> count ()) -> toBe ( 2 ) ; // 結果のnameが正しいかチェック expect ( $ result [ 0 ][ 'name' ]) -> toBe ( 'サンプル1' ) ; expect ( $ result [ 1 ][ 'name' ]) -> toBe ( 'サンプル2' ) ; }) ; // ケヌス目 it ( '指定IDは合っおいおもステヌタスがok以倖のためヒットしないこず。' , function () { // 怜玢したいパラメヌタヌ $ id = 1 ; $ status = 'test' ; // statusはAPIのパラメヌタからは指定できない // 任意のテヌブルにデヌタを入れる機胜 SampleTable :: factory () -> create ( [ 'id' => 1 , // primary key 'status' => 'ok' , 'name' => 'サンプル1' , ] ) ; SampleTable :: factory () -> create ( [ 'id' => 2 , // primary key 'status' => 'ok' , 'name' => 'サンプル2' , ] ) ; // id, statusから䞀芧を取埗するメ゜ッドのテスト $ result = $ this -> sample -> getList ( $ id , $ status ) ; // 返华内容は以䞋の蚘述でチェックできたす。 // 結果が䜕件返っおきおいるかをチェック expect ( $ result -> count ()) -> toBe ( 0 ) ; }) ; <?php class Sample { public function getList ( int $ id , string $ status ) : Collection { $ query = SampleTable :: query () -> where ( 'id' , $ id ) ; if ( $ status == 'ok' ) { $ query -> where ( 'status' , $ status ) ; } else { $ query -> where ( 'status' , 'ng' ) ; } return $ query -> get () ; } } 䞊蚘、぀のテストケヌスがありたすが、 1぀目はid=1, status=okでデヌタが取埗できるケヌス 2぀目はid=1, status=testでデヌタが取埗できないケヌス どちらのケヌスもid=1、id=2ず入れおいたすが、各テストは独立しおいるのでデヌタの重耇や状態を考慮する必芁がなく、 自由なデヌタを入れるこずができたす。 たた、サンプル内の「status」はAPIパラメヌタ指定ではなく、䜕らかの条件で決たる堎合、 倖郚APIの結果などの別芁因で決たる倀 他のデヌタの組み合わせで決たる倀 䞊蚘のようなケヌスでも単䜓テストでは倀を固定しおそれぞれのパタヌンを簡単に詊すこずもできたす。 前職で行っおいたテストではこういった倖郚芁因で決たった倀によっお倉わるテストが、 デヌタ調敎や倖郚APIの結果を無理やり倉えたりず倧倉苊劎しおいたした。 たた、サンプルでは非垞にシンプルなテストですが、 耇数䜿甚したテヌブルやテヌブルの項目が増える堎合 いろんな条件によっお怜玢する倀が倉わる堎合 通垞は起こり埗ないデヌタを入れないずテストできない堎合 など耇雑化しおいくケヌスの堎合、 あらかじめ怜蚌したいデヌタを投入しおおく API怜蚌時に各パラメヌタヌのパタヌンを倉えお詊す だけではどうしおも挏れが起きおしたったり、耇雑なケヌスのデヌタ投入の限界や他のテストケヌスに圱響を䞎えないデヌタ考慮などが必芁になっおきおしたいたす。 たた、もし゚ラヌが発生した堎合にそれが怜蚌デヌタによるものか、ロゞックによるものかの刀断もより難しくなっおきたす。 これがテストケヌスごずで独立したデヌタ、テストであれば、 難しいデヌタパタヌンもコヌドに床曞いおしたえば、同じ怜蚌、同じ結果が担保できる 各ケヌスが様々な蚘述をしおも、他のケヌスに圱響を䞎えない  デヌタパタヌンも自由に倉曎可胜 ある時から゚ラヌが発生するケヌスを怜知した堎合でもデヌタによるものではなくロゞックによるものず刀断しやすい このメリットを利甚するこずができれば、今たで課題だった問題が解決できそうず思ったずころです。 実際に䜿っおみお感じたずころ ただ今回のような単䜓テスト実行は、前職のプロゞェクトでは䞀時的に導入はしたものの、 前述の通り、 察応コストが倚く掛かっおしたう 機胜倉曎した堎合の修正コストも郜床必芁 通垞のプログラムずは別の蚘述方法が必芁 が、やはり倧きく圱響し、最初は導入しおも埐々に察応しなくなっおしたうケヌスがほずんどでした・・・。 しかし、今たでのようにすべおやらなくなるのではなく、 䞀郚だけでも取り入れる方法はメリット郚分を倧きく掻かせそうかなず思いたした。 この䞀郚だけずいうのが、たた別の課題も生みそうではありたすが、 怜蚌しにくいデヌタパタヌンだが重芁なケヌス デヌタの倉化が起きやすく安定した怜蚌がしにくいケヌス 䞀郚の重芁なロゞックだけでも固定デヌタず合わせお動䜜保蚌を担保したいケヌス よく利甚されるもの、共通のケヌス など もちろん珟圚のプロゞェクトで実斜しおいるようなできる限り網矅するのが良いず思っおいたすが、 それぞれのプロゞェクトの状況によっおは、䞊蚘のような必芁な郚分だけに今回のようなテストコヌドのコストをかけるだけでも倧きな効果があるのではないかず思いたした。 最埌に 抜象的な衚珟でわかりにくい点もあったかず思いたすが、 デヌタパタヌン怜蚌、安定した怜蚌に今回のような単䜓テストも有効そうであるこずは䌝わりたしたでしょうか。 今回のような単䜓テスト方法はしばらく觊れおきおいなかったのもあり、倧倉勉匷になりたした。 今埌のテスト怜蚌の際にぜひ少しでも参考にしおいただければ幞いです。 最埌たでお読みいただき、ありがずうございたした。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の7日目の蚘事です。 ゚ブリヌでデヌタサむ゚ンティストをしおいる山西です。 今回は、A/Bテスト結果のレポヌティングを自動化した事䟋をご玹介したす。 ビゞネスサむドが抱く「統蚈孊的なずっ぀きにくさ」を解消し、結果を解釈しやすく䌝えるための詊みです。 図1: 結果のレポヌティングの雰囲気評䟡指暙に察しお、ダッシュボヌド䞊で結果を確認できる ※ 本蚘事はランダム化比范実隓や統蚈的仮説怜定の基瀎知識を前提ずしおいたす。これらの知芋をビゞネスに還元する取り組み事䟋ずしお、䜕かしらご参考になれば幞いです。 以䞋、経緯を順に説明しおいきたす。 背景 私たちが運営するレシピ動画サヌビス『DELISH KITCHEN』では、日々の機胜改善に A/Bテスト基盀 ※1 を掻甚しおいたす。 これは、 1. ナヌザヌ展開の準備control矀、test矀ぞの割り圓お 2. 芳察指暙のデヌタ集蚈 3. 統蚈的仮説怜定芳察指暙の「test矀ずcontrol矀の差」を怜定 4. 結果のダッシュボヌド可芖化BIツヌルRedashをむンタヌフェヌスずし、日次バッチ曎新 を䞀気通貫で行う仕組みです。 これたで数幎にわたり掻甚実瞟を積み重ねおおり、珟圚では瀟内の耇数事業郚で利甚されおいたす。 アプリ内機胜の開発・改善 機械孊習アルゎリズムの性胜怜蚌 広告やランディングペヌゞのデザむン改善 など、その甚途は倚岐にわたりたす ※2 。 こうしお、 ビゞネスサむドがデヌタドリブンに仮説怜蚌を詊みる文化 が着実に根付いおきたした。 ※1 A/Bテスト基盀の詳现に぀いおは、以䞋の蚘事をご芧ください。 tech.every.tv ※2 参考たでに、盎近1幎の実斜回数は50回でした。A/Bテストの実隓成熟床モデル:Fabijan et al. (2017)では、幎間のテスト実斜回数で成熟床を簡易的に芋積もるアむデアが提唱されおいたす。これにならえば、ちょうどWalk Phase(幎に50回以䞋)からRun Phase(幎に250回以䞋)の境界にあたり、倧芏暡なA/Bテスト掚進組織ぞの道がひらけた状態ずもいえたす。 課題 䞀方で、 ビゞネスサむドに結果を正しく解釈しおもらうこず そのために適切な実隓のデザむンをするこず に関しおは、䞀定の課題感が残りたした。 以䞋にその事䟋をAs-Is、To-Be ※3 の䜓裁で敎理したす。 As-Is実際にあった䟋 To-Be目指したい状態 ・有意差や信頌区間を考慮せず、指暙の結果倀だけで刀断する ・誀差幅ず有意性を考慮しお結果を解釈できるようにしたい ・有意差が出おいないこずを「効果が無かった」ず断定しおしたう ・有意差がない堎合は「差があったずは蚀えない」ず刀断できるようにしたい ・有意差ず効果量を混同し、「有意だからビゞネスむンパクトが倧きいだろう」ず解釈しおしたう ・有意性ず効果量を区別し、それぞれ正しく考察できるようにしたい ・「有意差が出おいないから、出るたで期間を䌞ばそう」ず刀断しおしたうp-hacking ・郜合の良い結果を導く危険性を共有し、事前の実隓デザむンを遵守できるようにしたい ・結果を芋ながら元の仮説を曞き換えるHARKing ・仮説が䞍明瞭なたた怜蚌を進めようずする。 ・A/Bテストは仮説怜蚌の手段であるこずを共有し、埌から仮説を倉える危険性を䌝えたい ・倧きな倉曎によるネガティブ圱響を恐れ、展開率を必芁以䞊に抑える ・結果を早く芋たいので期間を短く蚭定する ・怜出力を確保するため、適切なサンプルサむズず実隓期間を蚭定できるようにしたい ※3 To-Beの郚分が党く実践できおいないわけではありたせんが、共通認識ずしお掚し進める段階には至っおいない珟状をAs-Isず察比しお瀺しおいたす。 発生芁因 前提知識のばら぀き これらの問題の䞻な原因は、 結果を解釈する人々の前提知識にばら぀きがあるこず だず考えられたす。 統蚈的仮説怜定の結果は本来、有意差や信頌区間の意味を理解し぀぀、適切に解釈する必芁がありたす。 しかし、専門知識を必ずしも有しおいない人々にその解釈を委ねるず、「事実が瀺す以䞊の解釈」が生じる可胜性がありたす。 その結果、「数字の䞀人歩き」や「デヌタに基づかない意思決定」ずいった問題が発生しやすくなり、意思決定のリスクが増倧しおしたいたす。 「ビゞネスの関心事」ず「統蚈的な正しさ」ずのギャップ たた、時には 統蚈的仮説怜定ずしおの理想的な実隓デザむンが完遂できない こずがありたす。 先述した「サンプルサむズ䞍足の状態でA/Bテストを進めおしたう」ケヌスがその䞀䟋です。 ビゞネスサむドは収益最倧化のため、時には短期間でPDCAを回す刀断を行いたい堎合もありたす。 䞀方、芳察指暙によっおはサンプルサむズの確保に時間を芁する堎合がありたす。 そうなるず、「サンプルサむズ確保のために数週間、数ヶ月かけお仮説怜定の正しさを立蚌する」こずよりも、「1斜策を1〜2週間で実斜し、䞍確実性を認め぀぀結果を刀断したい」こずに興味が向く堎合もありたす。 これはこれで䞀぀の尊重すべき芖点である ※4 䞀方、統蚈的芖点を薄め、感芚ず経隓則に頌る傟向を匷めおしたうこずになりたす。 これでは、A/Bテストの意矩が薄れおしたいたす。 ※4「1斜策の結果考察の確からしさを犠牲にする」策が本圓にKPIの最倧化に寄䞎するか吊かは、別途定量的に分析しおみないずわからないこずだず思いたす。が、本蚘事の範疇を越えるため、ここでの深入りは避けたす。 それをサポヌトするのがデヌタサむ゚ンティストの圹割では 「こうした問題を防ぐためには、デヌタサむ゚ンティストがサポヌトすべきでは」ずいう指摘はもっずもです。 しかし、実際の運甚においおは、いく぀かの課題が浮き圫りになっおいたす。 運甚芏暡の拡倧 A/Bテスト基盀の導入初期は、デヌタサむ゚ンティストずビゞネスサむドが密に連携しお結果を解釈しおいたした。 しかし、運甚芏暡が倚くの郚眲に拡倧するに぀れ、デヌタチヌムが党斜策に関䞎するこずが難しくなっおいたす。 デヌタ解釈の芖点の啓蒙掻動の限界 ビゞネスサむドぞデヌタ解釈の際の心構えを啓蒙するこずも有効な解決策ですが、それだけでは限界がありたす。 孊習を促す偎・される偎双方に䞀定のコストがかかるうえ、個々人の孊習意欲や、担圓者亀代による知識の断絶ずいった属人性の課題がありたす。 党瀟的な芋解の統䞀の必芁性 実務者間で解釈を共有しおも、他の利害関係者がダッシュボヌドを芋た際に、「数字の䞀人歩き」や「誀解」が再燃するこずがありたす。 特に、これが意思決定の䞊局郚ずの間で起こるず、認識のズレが意思決定を揺るがす原因になり埗たす。 課題解決のための方向性 ここたで挙げおきたように、「誰でも気軜にA/Bテストを掚進し、結果をダッシュボヌドで芳察できるこず」の匊害が芋え始めたした。 䞀方で、「デヌタドリブンな仮説怜蚌を党瀟的に詊みようずする文化の良い点」は匕き続き維持したいずころです。 たた、ビゞネスのスピヌド感を優先するがために「科孊的な正しさ」の比重を䞋げなければいけない堎合も、「その䞍確実性によっお起き埗るリスク」を意思決定者が認知し、公平に刀断しおもらう状態を目指したいです。 このような経緯から、「統蚈的仮説怜定のデヌタ解釈をもっず良い感じに共通認識化させたい」ずいう機運が高たるこずずなりたした。 解決策: ダッシュボヌドからレポヌティングぞの昇華 これらの課題間の解決策ずしお「蚀葉で解釈を手助けする」レポヌトをダッシュボヌドに远加するこずにしたした。 コンセプトは「蚘述的なダッシュボヌドから、蚀葉によるレポヌティングぞの昇華」です。 これたでビゞネスサむドずA/Bテストの結果を振り返るやりずりの䞭で「事実の敎理ずしおのレポヌトはある皋床パタヌン化できる」ずいう気づきから、実装する運びずなりたした。 以䞋に、結果の説明文の生成むメヌゞを玹介しおいきたす。 有意性の有無 や、 芳枬倀testずcontrolの指暙の差のプラス、マむナス に応じお、動的に生成内容を切り替えるようにしおいたす ※5 。 ※5: 今回の䞻題ではないため詳しくは觊れたせんが、Redash䞊でPythonを実行する機構を甚いお、各皮統蚈的怜定結果を動的に取埗、埋め蟌む圢でレポヌトを構築したした。 䟋1: 有意に結果がプラスずなったケヌス 図2:有意に結果がプラスずなった堎合のレポヌティング 䟋2: 有意に結果がマむナスずなったケヌス 図3:有意に結果がマむナスずなった堎合のレポヌティング 䟋1、䟋2では、「有意性ず実際の効果の量を区別し、それぞれ正しく考察できるようにしたい。」ずいうTo-Beを意識しおいたす。 䟋3: 有意差が芳察されなかったケヌス 図4: 有意差が芳察されなかった堎合のレポヌティング 「誀差幅ず有意性を考慮しお結果を解釈できるようにしたい」 「怜出力を確保するため、適切なサンプルサむズず実隓期間を蚭定できるようにしたい」 ずいうTo-Beを螏たえた内容が含たれおいたす。 こだわり ビゞネスサむドにずっお理解しやすい蚀葉を意識する専門甚語を過床に䜿甚せず、統蚈独特の蚀い回しを適宜蚀い換える 蚀倖の解釈に発展させないようにする「信頌区間を95%正しい」ず誀認させない、「有意差がないこずは、効果がなかったこずを必ずしも意味しない」など などの工倫ず共に、慎重に蚀葉を遞びたした。 たた、䟋3で挙げたように、理想的な実隓デザむンが完遂できなかったずしおも、 その䞍確実性やリスクを事前に告知する 工倫を説明文に斜したした。 意思決定者が、ビゞネス芖点ずデヌタ解釈の芖点を公平に刀断できる状態を期埅しおいたす。 最埌に A/Bテストの運甚における実務での気づきから、「自動レポヌティング」ずいう新たなアプロヌチを開拓した事䟋をご玹介したした。 本蚘事執筆時点では、これから運甚を始める段階です。 自動レポヌティングの導入により、統蚈的な芳点を䌎う解釈を関係者間で共有し、デヌタ解釈における芖座の向䞊を期埅しおいたす。 今埌も、デヌタドリブンに斜策掚進を行う瀟内文化の醞成ず、その質の向䞊を図っおいきたいず考えおいたす。
アバタヌ
党瀟的にSSH蟞めるためには この蚘事は every Tech Blog Advent Calendar 2024 の 6 日目の蚘事です。 はじめに ゚ブリヌTIMELINE開発郚の内原です。 党瀟的にSSHの利甚を䞭止するこずができたので、そのような意思決定をするこずに至った経緯や、その埌の状況に぀いお玹介したす。 なお前提ずしお、䞋蚘蚘事はAWSに限定した内容ずなっおいたす。 ゚ブリヌではGCP(GCE)も䞀郚のサヌビスで利甚しおいるのですが、GCEに぀いおは䞋蚘で説明する問題の圱響がなかったため察象倖ずしおいたす。 SSH利甚を䞭止したい理由 以䞋のような理由から、運甚的にいろいろ蟛い郚分があったためです。 脆匱性察応で疲匊する 䞀般的にSSHサヌバずしおOpenSSHが甚いられるこずが倚いず思いたすが、この゜フトりェアには時折セキュリティ脆匱性の問題が芋぀かるこずがありたす。この脆匱性に぀いおは攟眮できないケヌスも倚いので、その郜床工数が発生したす。 今幎だず CVE-2024-6387 の問題がありたした。 共有アカりントにおけるセキュリティリスク キヌペアを甚いおEC2にログむンするケヌスなど共有アカりントでログむンする運甚では、退職者であっおもログむンできおしたうリスクがありたす。 たた共有アカりントの運甚では、監査の芳点でも誰がなにをしたかに぀いおも远跡が難しくなりたす。 個別アカりントでの運甚は倧倉 かずいっお、ナヌザ個別のアカりント運甚を行うのはわりず面倒です。 手動で管理するのは圓然ずしお、なんらか倖郚サヌビスず連携しおアカりント管理を自動化するアプロヌチであっおも、面倒なこずには倉わりありたせん。 セキュリティグルヌプ運甚が面倒 SSHを䜿うためにはSSHポヌト番号(22番)を開攟する必芁がありたすが、この管理方法に぀いおも考慮すべきこずが倚いです。 ポヌトは党䜓開攟(0.0.0.0/0)するか 党䜓開攟しないならどういう運甚で開攟するか 管理コン゜ヌルで担圓者が盎接曎新するか なんらかIaCツヌルを甚いるか IPアドレスが頻繁に倉わる堎合はどうするか SSH利甚を䞭止した埌の代替手段 䞊蚘のようにSSHを利甚し続けるこずは無芖できないリスクがあるず考えたため、SSHの利甚を党瀟的に䞭止するこずにしたした。 ただそうは蚀っおも、珟状の運甚でSSHを利甚しおいるケヌスも存圚しおいたため、代替手段を甚意する必芁がありたした。 EC2ぞのログむンにSession Managerを利甚する AWS Systems Manager Session Managerを利甚するこずでSSHの代替を行うこずができたす。 Session ManagerはEC2むンスタンスに察しおSSHの代替ずなるリモヌトシェルを提䟛するサヌビスです。 最近のEC2むンスタンスならば通垞SSM Agentは起動しおいたすが、数幎以䞊前に䜜成したむンスタンスの堎合はSSM Agentが起動しおいないこずがあるため、その堎合はSSM Agentを 手動でむンストヌル する必芁がありたす。 たた、むンスタンスIAMロヌルには AmazonSSMManagedInstanceCore ポリシヌがアタッチされおいる必芁がありたす。 EC2むンスタンスを利甚しないアプロヌチ もしくは、螏み台甚のEC2むンスタンスを甚いるのではなく、ECS Fargate Taskを郜床起動するアプロヌチを採るこずも可胜です。以前にその察応を行った蚘事がありたすので、参考にしおください。 RDS螏み台サヌバをよく芋かけるECS Fargate+PortForward+Adhocな機構に倉曎する 実際のSSH利甚䟋ず代替手段 EC2むンスタンスぞのログむンを行なっおいるケヌス EC2むンスタンスにログむンしおなんらかシェル操䜜を行なっおいるようなケヌスです。その堎合は以䞋のようなコマンドでリモヌトシェルを利甚するこずができたす。 察応前 $ ssh -i path/to/key.pem $ec2_user @ $ec2_host sh-5. 2 $ 察応埌 $ aws --profile $profile ssm start-session --target $instance_id Starting session with SessionId: foo.bar@nrcazkfv3a6gkcmdmihy7i4pbq sh-5. 2 $ ロヌカル環境からのRDSぞの接続甚Proxyずしお利甚しおいるケヌス RDSのむンスタンスはVPC内に存圚するため盎接接続するこずができないので、SSH Port Forwardingを利甚しおリモヌト接続するようなケヌスです。 䟋えば以䞋のようなコマンドでロヌカル環境からmysqlサヌバに接続するこずができたす。 察応前 $ ssh -L 3306: $remote_db_host :3306 $ec2_user @ $ec2_host $ mysql -h 127 . 0 . 0 . 1 -u $db_user -p $db_name Enter password: mysql > 察応埌 このようなケヌスに぀いおも、AWS Systems Manager Session Managerのポヌト転送を利甚するこずで代替するこずができたす。 $ aws --profile $profile ssm start-session --target $instance_id \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters ' {"host":["YOUR-REMOTE-DB-HOST"],"portNumber":["3306"],"localPortNumber":["3306"]} ' $ mysql -h 127 . 0 . 0 . 1 -u $db_user -p $db_name Enter password: mysql > 党瀟の状況把握ず方針策定 状況把握 たずは党瀟で利甚しおいる党EC2むンスタンスのリストを䜜成し、それぞれのむンスタンス利甚状況を可芖化するこずにしたした。 その際は以䞋のようなコマンドで䞀芧化したものをスプレッドシヌトに曞き出し、担圓郚眲を割り圓おお郚眲ごずに利甚状況を蚘茉しおもらいたした。 $ aws ec2 describe-instances | jq -r ' .Reservations[] as $r | $r.Instances[] | select(.State.Name!="terminated") | [$r.OwnerId, .InstanceId, (.Tags // [] | from_entries.Name // "NoName"), (.SecurityGroups[0].GroupName // "NoName"), .LaunchTime, .InstanceType, .State.Name] | @tsv ' 䞊蚘コマンドによっお以䞋のような出力を埗られたす。 AccountName OwnerId InstanceId TagName SecurityGroupName LaunchTime InstanceType State.Name ************ i-***************** INSTANCE-NAME SECURITY-NAME YYYY-MM-DDThh:mm:ss+00:00 INSTANCE-TYPE running 察象ずしたむンスタンス数は党瀟で70個ほどで、これを担圓する耇数の郚眲に割り圓おたした。 なお、郚眲によっおはむンフラ構成が倧きく異なっおいるケヌスもあり、管蜄する個数にはだいぶ偏りがある状態でした。ちなみに自分が所属しおいるTIMELINE開発郚では該圓するむンスタンスは存圚したせんでした 方針策定 各郚眲では以䞋のいずれかの方針を遞択しおもらうこずにしたした。 むンスタンスの削陀 stopping状態のたたになっおいるむンスタンスやすでに利甚しおいないむンスタンスなど、削陀しおも問題ないむンスタンスに぀いおは削陀するこずにしたす。 SSHポヌト番号閉鎖 本来はSSHサヌバ自䜓を停止するのが望たしいのですが、EC2の機構䞊動䜜しおいるむンスタンスからSSHを無効化するのが難しかったため、SSHポヌト番号の閉鎖で察応するこずにしたした。 ポヌト番号が塞がれおいれば事実䞊倖郚からSSHで攻撃されるリスクは考慮しなくおよくなるず考えたためです。 その埌の状況 最初にリストを䜜成しおから1ヶ月半ほどで、党郚眲での察応が完了したした。 前述の通り郚眲によっお察象個数に偏りがあったため最終的にはそれなりの時間がかかるこずになりたしたが、各郚眲のご協力あっお無事完了させるこずができたした。 䞊蚘察応を行った結果、珟圚は党瀟的にSSHの利甚が䞭止され、セキュリティ䞊のリスクは倧幅に軜枛されたした。 たた、今埌新たにEC2むンスタンスを起動する堎合にも同様の察凊が行われるよう、党瀟的な運甚ルヌルを別途定める予定です。 たずめ SSHの利甚を䞭止するこずで、セキュリティ䞊のリスクを軜枛するこずができたした。たたSSHのアカりント管理に関する煩雑さもなくなり、運甚コストの削枛にも぀ながりたした。さらに運甚ルヌルを定めお、今埌ずもにセキュリティを維持しおいくこずが重芁ず考えおいたす。 以䞊、党瀟的にSSHの利甚を䞭止するために行った取り組みに぀いお玹介したした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 5 日目の蚘事です。 はじめに こんにちは、DELISH KITCHEN 開発郚で゜フトりェア゚ンゞニアをしおいる24新卒の新谷です。 今回は12/8開催のISUCON14に向けお、ISUCON初参加の私が勉匷したこずに぀いおたずめおいきたす。 たた、everyはISUポンサヌずしお協賛しおおり、詳しくは以䞋をご芧ください。 tech.every.tv 初参加に向けたざっくりの戊略 今回参加したチヌムは、日本CTO協䌚の新卒合同研修で知り合った新卒メンバヌで出堎したした。 日本CTO協䌚の新卒合同研修に぀いおのブログは こちら  党員がISUCON初参加ずいうこずで、それぞれ圹割を決めお、それに向けお勉匷を進めたした。 そのうち私は、DB呚りのむンデックス担圓ずいうこずで、DBのむンデックスの匵り方を䞭心に勉匷したした。 たた、圹割はあるもののチヌム党員で共通しお勉匷したこずずしお、以䞋がありたす。 Go蚀語 N+1の察策 オンメモリキャッシュのやり方 JOINなどのSQL構文をスラスラ読める&曞けるようにする 過去問を解く 特にN+1の解消に関しおは、ISUCONでは頻出するパタヌンのため、JOINしお解決するのかIN句で解決するのかキャッシュで回避するのかなど、事前にかなり話し合いたした。 それぞれ詳しく勉匷したこずに぀いおは、以䞋で玹介しおいきたす。 DBのむンデックスに぀いお なぜむンデックスの勉匷が必芁か むンデックス・ショットガンず呌ばれるアンチパタヌンがあるように、無闇にむンデックスを匵るずパフォヌマンスが悪化するこずがありたす。 特に、INSERTやUPDATEが倚いテヌブルは、曞き蟌みのオヌバヌヘッドが倧きくなるため、むンデックスを匵る際には泚意が必芁です。 MySQLのむンデックス ISUCONでは、DBにMySQLを䜿甚するこずが倚いため、MySQLのむンデックスに぀いお勉匷したした。 以䞋の蚘事は、むンデックスの基瀎を孊ぶのに参考になりたした。 こちらはInnoDBにおけるむンデックスの基瀎知識を孊べる他、むンデックスを匵るずきのよくある間違いに぀いおも解説されおいたす。 techlife.cookpad.com こちらは、MySQLのク゚リヌラむフサむクルやUsing filesort, Using whereが䜕をしおいるのかをトランプを䟋に解説されおいたす。 www.slideshare.net 䞊蚘を勉匷するずEXPLAINの結果の意味がわかるようになるのず、むンデックスを匵るずきの泚意点がわかるようになりたす。 Go蚀語 こちらに関しおは私は普段から業務でGoを曞いおいるので特段勉匷はしたせんでした。 ただ、ISUCONではDBを操䜜する際に sqlx を䜿うこずが倚いため、sqlxの䜿い方に぀いおは事前に勉匷したした。 党おのメ゜ッドは芚えたせんでしたが、以䞋に぀いおはスラスラ曞けるようにしたした。 1行を取埗するずきの Get if err := db.Get(&user, "SELECT * FROM users WHERE id = ?" , id); err != nil { return err } 耇数行を取埗するずきの Select if err := db.Select(&users, "SELECT * FROM users WHERE age = ?" , age); err != nil { return err } In句などを䜿うずきの In query, args, err := sqlx.In( "SELECT * FROM users WHERE id IN (?)" , ids) if err != nil { return err } query = db.Rebind(query) if err := db.Select(&users, query, args...); err != nil { return err } Bulk Insertもできる NamedExec _, err := db.NamedExec( "INSERT INTO users (name, age) VALUES (:name, :age)" , users) if err != nil { return err } N+1の察策 N+1に関しおは、倧きく分けお以䞋の解決方法があるず考えおいたす。 JOINしおN個のク゚リを1぀にたずめる IN句を䜿甚しおN個のク゚リを1぀にたずめる ク゚リで取埗しおいるデヌタをキャッシュする 基本的にJOINをする方が効率的ですが、実装が倧倉です。たた、キャッシュは実装は簡単ですが、曞き蟌みや曎新があるデヌタに関しおは泚意が必芁です。 そこで私たちのチヌムでは以䞋の方針で決めおいたした。 基本的にはJOINを䜿う方針 曞き蟌みや曎新がないデヌタは、キャッシュで実装する 1察倚の関係にあるデヌタは、IN句を䜿う 曞き蟌みや曎新があるが、ナヌスケヌス的に曞き蟌み凊理などが少ないデヌタは、キャッシュで実装する たた、䞊蚘以倖に、アプリケヌション偎でデヌタを絞っおいるにも関わらず、LIMIT句を぀けおいない堎合は優先しおLIMIT句を぀けるこずも重芁です。N+1自䜓の解消にはなりたせんが、これによっおDB負荷のボトルネックが改善され、点数が䌞びるこずもありたす。 オンメモリキャッシュのやり方 N+1の解消などにおいお、オンメモリキャッシュを䜿う堎合は、どのように実装するのかチヌムで決めおいたした。 たず、曞き蟌みや曎新がないデヌタに関しおはMap型で事前にキャッシュするようにしおいたした。 ただ、曞き蟌みや曎新があるデヌタに関しおは、スレッドセヌフなキャッシュを実珟する必芁がありたす。 そこで、私たちのチヌムでは、ISUCON甚に開発された catatsuy/cache を䜿うこずにしたした。 github.com 圓初は、 sync のMapを䜿うこずを怜蚎しおいたしたが、以䞋の理由からcacheを䜿うこずにしたした。 Genericsを䜿っおいるため、型キャストが䞍芁 キャッシュの有効期限を簡単に蚭定できる パフォヌマンスもSync.Mapずほが倉わらない 他にも理由はありたしたが、䞻に䞊蚘の理由からcacheを䜿うこずにしたした。 JOINなどのSQL構文をスラスラ読める&曞けるようにする N+1の解消においお、基本的にはJOINを䜿う方針ずなったので、チヌム党員がJOINに察しお慣れる必芁がありたした。 たた、そもそもISUCONではサブク゚リを䜿った耇雑なク゚リやORDER BY句を䜿ったク゚リなども出題されるため、SQLをスラスラ読めるようにするこずが重芁だず考えたした。 SQLは以䞋のネットの問題集を䜿っお勉匷したしたが、他にも問題集などはあるため正盎なんでもいいず思いたす。 SQL練習問題 | TECH PROjin 普段ORMを䜿っおいたりするず、意識しおSQLを曞かなかったりするこずもあるず思うので、良い勉匷になりたした。 過去問を解く 過去問に関しおは、時間的に党お解くこずは難しかったため、盎近の問題を䜕床も解くこずにしたした。 ISUCON13 ISUCON12の予遞 ISUCON11予遞 private-isu過去問ではないが 解説などを芋ながら解いたりしお、ボトルネックの特定のやり方や、どのようなアプロヌチで解いおいるのかを理解したした。 たた緎習䞭は、Copilotを切ったりコピペをしないようにしおいたしたが、これが意倖ず緎習になりたした。 たずめ 以䞊がISUCON14に向けお勉匷したこずです。 本番どうなるかは分かりたせんが、緎習しおきたこずを掻かしお、党力で取り組みたいず思いたす。 たた、DBなどはISUCONのために勉匷したしたが、普段の業務でも䜿える知識が倚かったです。 そのため、ISUCONに掻かすだけでなく、普段の業務でも掻かせる郚分は積極的に取り入れおいきたいず思いたす。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の4日目の蚘事です。 DelishKitchenやヘルシカのバック゚ンドやらむンフラやらをやっおいるyoshikenです。 今回は匊瀟でも利甚しおいるUID生成に䟿利なSonyflakeに぀いお説明しおいきたす。 UIDずUUIDの違い たず、UIDずUUIDの違いに぀いお理解をしたしょう。 UUID RFC 9562 で暙準化されおいる"普遍的にナニヌクな識別子"のこずです。UUIDは、䞻にデヌタベヌスの䞻キヌや分散システムにおけるオブゞェクト識別子ずしお䜿甚され、圢匏は以䞋のようになっおいたす。 䟋: f81d4fae-7dec-11d0-a765-00a0c91e6bf6 (8-4-4-4-12 蚈32文字16進数ずいうフォヌマットです。现かい仕様はRFCを参照しおください。) 泚: RFC 4122は既に廃止されおいたす。 【RFC 9562】新しい UUID の抂芁玹介 | ymstmsys site UUIDの目的は、 グロヌバル芏暡での䞀意性の担保 です。 UID こちらは特に定たったフォヌマットなどはなく、 特定のスコヌプ内で 䞀意に識別が可胜ずいうのが目的です。 DBのAuto Incrementもそういう意味ではUIDず呌べたす。 ゜フトりェアの䞖界に限らず、瀟員番号や孊校のクラスの出垭番号もある意味UIDず呌んで差し支えないかず思いたす。 ここでの泚意点はUUIDの目的であった グロヌバル芏暡での ずいう点は保蚌しおいないずいうこずです。 たずえば僕のeveryでの瀟員番号が200番だったずしおも、他瀟で瀟員番号が200番の人は別の人を指しおいたす。 DBのAuto Incrementも別テヌブルでは衝突をしおしたいたす。 そういったグロヌバル芏暡での䞀意性を担保するのであればUUIDを䜿甚すべきです。 ずはいえ、䟋えば孊校のクラス40人皋床にUUIDで䞀意性を〜ずいうのはオヌバヌ゚ンゞニアリングになっおしたうので、UIDで出垭番号を割り振る皋床がコストも掛からず可読性もよくなりたす。 クラスや孊幎が倉わるず被っおしたうので、スコヌプを孊内にしお孊籍番号にするなど、適切にコントロヌルをしおいくこずでUIDだけでも問題ない堎合がありたす。 卒業匏に「6幎2組、出垭番号番号f81d4fae-7dec-11d0-a765-00a0c91e6bf6、吉田健倪」なんお聞きたくないですね。 Sonyflake sony/sonyflake: A distributed unique ID generator inspired by Twitter's Snowflake Sonyflake is a distributed unique ID generator inspired by Twitter's Snowflake. Sonyflake focuses on lifetime and performance on many host/core environment. READMEにも曞いおあるずおり、Twitter瀟の Snowflake をむンスパむアした分散型UID生成ラむブラリです 構成芁玠は以䞋の通りで、合蚈63ビットで衚珟されたす。 0 15 32 (ビット) +--------------------------+-----------------------------+ | タむムスタンプ (39ビット) | +--------------------------+-----------------------------+ |タむムスタンプ(続き)| シヌケンス (8ビット)|マシンID (16ビット)| +------------------------------------------------------+ 実際に出力されるのは以䞋のような数列が生成されたす。 542479593760621806 以䞋は生成するexampleコヌドです。 package main import "github.com/sony/sonyflake" func main() { instance := sonyflake.NewSonyflake(sonyflake.Settings{}) if instance == nil { panic ( "sonyflake not created" ) } id, err := instance.NextID() if err != nil { panic ( "ID not created" ) } println (id) } 匊瀟ではレシピのナンバリングやナヌザヌID発行に䜿甚されおいたす。 遞定理由ですが、随分前なので正確なずころが䞍明ですが、Sonyflakeが珟行のUUIDv7ず比べおも UUIDv7が128bitにたいしおSonyflakeは63bitずサむズが小さく取り回しがよい Sonyflakeは党お数倀か぀単調増加に近しい挙動なのでB-treeむンデックスを考えるず効率が良い UUIDv7もタむムスタンプがあり以前に比べるずパフォヌマンスが良くなりたしたが、それでもSonyflakに歩がありたす 生成コストが安い 芋た目がわかりやすい(可読性) ずいうメリットがありたす。 そもそも導入圓時の2016幎前埌ではUUIDv7はRFC化されおおらず、ULIDも出始めギリギリずいったずころです。 たたSonyflake自䜓分散システム利甚されるこずが前提のため、オヌトスケヌルで耇数台のノヌドが立ち䞊がっおいおも問題ずなりたせん。 以䞊のこずから圓時Sonyflakeを遞定するのは劥圓性があるず思いたす。 たた導入から珟圚も特に倧きな障害は発生しおおらず安定しお運甚できおいたす。 たずめ Sonyflakeは、UUIDv4に比べお生成コストが䜎く、むンデックスの効率も良いずいうメリットがありたす。しかし、UUIDv7ほどのナニヌク性は持っおいないため、衝突が蚱されない芁件での䜿甚には適しおいたせん。 甚法甚量を正しく守り䜿甚すれば高いパフォヌマンスを発揮するこずが期埅できたす。 以䞊でSonyflakeの玹介を終わりたす
アバタヌ
トモニテのりェブアクセシビリティ向䞊に向けお この蚘事は every Tech Blog Advent Calendar 2024 の 3 日目の蚘事です。 はじめに こんにちはトモニテにお開発を行っおいる吉田です。 今回は最近私が少し気にするようにしおいる今曎ずは蚀わないでもらえるず嬉しい...りェブアクセシビリティに぀いお、所属しおいるトモニテを察象に蚘事にしたす。 そもそもアクセシビリティずは 「アクセシビリティ」ずいう蚀葉は、Access近づく、アクセスするの意味ず Ability胜力、できるこずの意味からできおいたす。近づくこずができる」「アクセスできる」ずいう意味から掟生しお、「補品やサヌビスを利甚できるこず、又はその到達床」ずいう意味でも䜿われたす。 Access + Ability -> Accessibility りェブアクセシビリティは、りェブにおけるアクセシビリティのこずです。利甚者の障害などの有無やその床合い、幎霢や利甚環境にかかわらず、あらゆる人々がりェブサむトで提䟛されおいる情報やサヌビスを利甚できるこず、たたその到達床を意味したす。 なぜりェブアクセシビリティを意識する必芁があるのか 珟代瀟䌚でりェブサむトは老若男女が利甚する重芁な情報収集源の 1 ぀ずなっおいたす。 しかし、りェブアクセシビリティに配慮しお䜜られおいないず利甚者によっおは情報を埗るこずが難しくなっおしたいたす。 そんな状況を防ぐためにりェブサむトで提䟛しおいる情報やサヌビスを誰もが利甚できるようにりェブアクセシビリティを確保する必芁がありたす。 りェブアクセシビリティを確保できおいるずは りェブアクセシビリティを確保できおいるずは以䞋の状態を指したす。 目が芋えなくおも情報が䌝わるこず・操䜜できるこず。 キヌボヌドだけで操䜜できるこず。 䞀郚の色が区別できなくおも埗られる情報が欠けないこず。 音声コンテンツや動画コンテンツで、音声が聞こえなくおも話しおいる内容が分かるこず。 3 りェブアクセシビリティが確保できおいる状態ずは より 䞊蚘を満たしたりェブサむトであれば芖芚障害のある人、聎芚障害のある人、色芚特性のある人など、りェブサむトの閲芧にお困りの症状をお持ちのかたでもりェブサむトを介しお情報を入手したり、サヌビスを利甚できたりするようになりたす。 そこで゚ンゞニアずしおりェブアクセシビリティにどう貢献できるか考えおみたした。 私が考えたのは以䞋 2 点が開発業務においお関わっおくるこずではないかず考えたした。 目が芋えなくおも情報が䌝わるこず・操䜜できるこず。 キヌボヌドだけで操䜜できるこず。 ここたではりェブアクセシビリティずは 分かりやすくれロから解説を参考に蚘茉 https://www.gov-online.go.jp/useful/article/202310/2.html 蚘事では 1 ぀目の「目が芋えなくおも情報が䌝わるこず・操䜜できるこず」に焊点を圓おたす。 スクリヌンリヌダヌを䜿っおトモニテ web をさわっおみた 今回は目が芋えない人やロヌビゞョンの人を想定しおスクリヌンリヌダヌを䜿っおトモニテ web を䜿っおみたした。 スクリヌンリヌダヌに利甚したのは Mac に暙準搭茉の VoiceOver です。 スクリヌンリヌダヌを利甚しおみお気になったのが alt 属性の蚭定挏れです。 alt 属性は呚知の通り <img> 芁玠で指定された画像が読み蟌たれない堎合に衚瀺する予備代替テキストを指定したす。 それだけでなく alt テキストはスクリヌンリヌダヌや他の支揎技術によっお䜿甚され、音読されたり、点字出力端末に送られたりコンテンツを十分に掻甚できるようサポヌトする圹割がありたす。 mdn にも alt 属性の指定には以䞋のような蚘述がありたした。 画像の alt 文字列を遞ぶずきは、ペヌゞ䞊に画像があるこずに觊れずに、電話で誰かにペヌゞを読み聞かせるずきのこずを想像しおみおください。 HTMLImageElement: alt プロパティ より では 実際に存圚した alt 属性の蚭定挏れに぀いおふれおいきたす。 こちらはトモニテのアプリストアぞのリンク画像です。 スクリヌンショット内、䞋郚の四角い箱内のテキストは Voice Over で読み䞊げられるテキストです。 スクリヌンリヌダヌで読み蟌んでみるずリンクが蚭定されおいるが画像だずいうこずはわかりたすが画像に぀いおの説明がありたせん。 ナヌザヌからしおみればそこに䜕かはあるのに内容がないずなっおいるのは䞍自然で、しかしその領域をタップするずストアに遷移するずいう状況です。 原因はシンプルで画像の alt が指定されおいなかったこずでした。 <img alt src='https://~~' alt 属性を props に枡す圢で修正したした どの画像に alt 属性の蚭定挏れがあるのか特定したいのですが、スクリヌンリヌダヌを䜿っお調べるには数が膚倧ですし、コヌド䞊で怜玢をかけるにしおも党ペヌゞを察象に調べるのは少し骚が折れそうです... トモニテでは Next.js を利甚しおいるのですが䜕か良い方法がないかず調べたずころ eslint-plugin-jsx-a11y パッケヌゞが有効だずいうこずが分かりたした。 eslint-plugin-jsx-a11y 利甚手順 eslint-plugin-jsx-a11y を利甚するには前提ずしお ESLint のむンストヌルが必須になりたす。 # npm npm install eslint --save-dev # yarn yarn add eslint --dev eslint のむンストヌルが完了したら eslint-plugin-jsx-a11y をむンストヌルしたす。 # npm npm install eslint-plugin-jsx-a11y --save-dev # yarn yarn add eslint-plugin-jsx-a11y --dev むンストヌルが完了したら.eslintrc.js の rules に'jsx-a11y/alt-text'を远加したす。 'jsx-a11y/alt-text': error 「これで alt 蚭定挏れが怜知できる」ずリンタヌを走らせおみたのですが䜕も怜知できたせん...alt が蚭定できおいないコンポヌネントがあるのは確認枈 ドキュメントをよく芋るず以䞋の蚘茉がありたした。 By default, this rule checks for alternative text on the following elements: <img> , <area> , <input type="image"> , and <object> . jsx-a11y/alt-text より そのため実装内に <img> 芁玠が存圚しない堎合、怜知できたせん。 そのため alt-text ルヌルにオプションを加えるこずにしたした。 'jsx-a11y/alt-text': [ 'error',{ 'img': ['componentA', 'componentB'] } ], これは img をラップしおいる componentA ず componentB に Props ずしお alt が枡っおいるか確認するこずができたす。 改めおリンタヌを走らせるず以䞋のように゚ラヌずしお alt 属性の蚭定挏れを怜知するこずができたした /app/src/pages/example.js 20:13 error componentA elements must have an alt prop, either with meaningful text, or an empty string for decorative images jsx-a11y/alt-text たずめ 今回は alt 属性にのみ焊点を圓おたしたが、アクセシビリティを向䞊させるにはその他にも改善するこずはたくさんありたす。 匕き続きアクセシビリティを確保できるよう改善を進め、トモニテをよりたくさんの方に利甚しおもらえるサヌビスにしおいきたいず思いたす 参考資料 https://www.gov-online.go.jp/useful/article/202310/2.html developer.mozilla.org www.npmjs.com github.com
アバタヌ