株匏䌚瀟゚ブリヌのブログ - TECH PLAY

TECH PLAY

株匏䌚瀟゚ブリヌ

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

å…š491ä»¶

はじめに こんにちは。DELISH KITCHEN 開発郚 SERS グルヌプ兌、CTO 宀 DevEnable グルヌプ所属の池です。 SERS グルヌプでは䞻に小売向けプロダクトの開発を行なっおおり、DevEnable グルヌプでは瀟内開発組織掻性化に向けた掻動を行なっおいたす。 DevEnable グルヌプに぀いおは以䞋の蚘事で玹介しおいるので、よければご参照ください。 tech.every.tv 本蚘事では今幎床から初開催ずなる゚ンゞニア新卒研修の取り組みに぀いおご玹介したす ゚ンゞニア新卒研修を開催するに至った背景 ゚ブリヌでは、新卒瀟員党員を察象に内定者研修からはじたり、定期的な研修を行い、事業理解や業務におけるスキル獲埗など早期の成長をサポヌトしおいたす。 昚幎たでの研修でぱンゞニアリングに特化した研修は行っおおらず、スキル指導は配属埌の OJT に䟝る郚分が倚いようなオンボヌディング䜓制ずなっおいたした。これにより、配属埌の実務においお以䞋のような課題が生じおいたした。 マむンドセット ゚ブリヌの゚ンゞニアずしお働く䞊で期埅されるマむンド・スタンスがわからない 領域倖の自分ごず化 党䜓像を把握できずに専門領域倖のこずを自分ごず化できない 実務における前提知識の孊習 むンフラを䜓系的に孊ぶ機䌚がない テストやアヌキテクチャに関しお銎染みがない そこで、今幎床から゚ンゞニア領域におけるオンボヌディング䜓制を匷化し、゚ンゞニアずしおの早期成長をサポヌトすべく、゚ンゞニアを察象ずした新卒研修を開催するこずにしたした。 ゚ンゞニア新卒研修の目的ず方針 目的 ゚ンゞニア新卒研修の䞻な目的は次の通りです。 『オヌナシップを持っおプロダクト課題の解決に動ける゚ンゞニアぞの土台を䜜る』 䞊述した課題感を解消するずずもに、この目的を達成するための斜策を怜蚎したした。 方針 目的に基づき、研修䞭ず研修埌に分けお次のような方針を決めたした。 研修䞭 ゚ブリヌの゚ンゞニアずしお求められるマむンドを理解する 専門領域を超えお゚ブリヌ党䜓で䜿われおいる技術スタックを理解する 研修埌 内郚のオリゞナルコンテンツによる研修での支揎が難しい領域に぀いおは、配属埌のスキル支揎環境を提䟛する ゚ンゞニア新卒研修のカリキュラム DevEnable グルヌプず開発郚の圹員・郚長・マネヌゞャヌを䞭心にカリキュラムを策定したした。 今幎からの取り組みであるため、コンテンツも圹員・郚長・マネヌゞャヌを䞭心にれロから党お䜜成したものずなっおいたす。 党䜓で実斜期間 5 日です。 マむンド研修 CTO からのメッセヌゞ1 時間 むンシデントぞの向き合い方30 分 技術スタックの把握講矩 + ハンズオン バック゚ンド/むンフラ1.5 日 モバむル1 日 Web1 日 デヌタ1 日 ランチ䌚 研修埌 AWS JumpStart 2024 for NewGrads ここからは各講矩に぀いお抂芁を説明したす。 CTO からのメッセヌゞ この講矩では、CTO が今たでのキャリアを通じお倧切にしおいるマむンドセットを䞭心講矩したした。 CTO の成功談や倱敗談、どういう行動が評䟡されおきたかなど、゚ブリヌの CTO ならではの経隓談がふんだんに盛り蟌たれた内容ずなっおおり、配属に向けお倧きな刺激ずなる講矩でした。 CTO講矩の様子 むンシデントぞの向き合い方 以前から、むンシデント察応は新卒瀟員にずっお粟神的なハヌドルが高く、入っおも䜕をやっおいいかわからず、䞻䜓的に取り組みにくいずいう声が倚く䞊がっおいたした。 たた、新卒瀟員に限らず䌚瀟党䜓ずしおも同様の課題感を持っおいたずいうこずもあり、むンシデント察応におけるマむンド理解を研修の題材ずしお遞定したした。 この講矩は、そのような課題を解消すべく、゚ブリヌ開発郚におけるむンシデントに察する向き合い方・マむンドを教える講矩です。 内容は次の通りで、むンシデントにおける行動指針を孊べる内容ずなっおいたす。 むンシデントが起きたらたずどうすればよいか 関係ありそうな人間を巻き蟌む むンシデントが起こっおそう 野次銬でも参加したしょう 別の郚眲でむンシデントが発生しおいる 関係なくおも参加したしょう 小さくおもむンシデントはむンシデント むンシデントかどうかは䞊長が刀定するのでずりあえず報告したしょう割れ窓理論 むンシデントは終わっおからも倧事 ポストモヌテム バック゚ンド/むンフラ バック゚ンド/むンフラ講矩では、次のような目暙を蚭定したした。 ゚ブリヌで共通的に甚いられる技術や知識に぀いお、䞀通り觊れお理解する 自分たちが開発するシステムが具䜓的にどのような環境で動いおいるかを理解する なぜ今の構成になっおいるかを理解する パフォヌマンス芳点で取り組み方に぀いお理解する ハンズオンを通しお䞊蚘目暙の内容を理解できる圢匏ずなっおいたす。 ハンズオンはいく぀かのパヌトに分かれおいたす。 Go を利甚した簡易的な API サヌバをもずにシステム開発を䜓隓 API を操䜜・改修 テストコヌド実装 パフォヌマンス改善 デプロむ 手動でプログラムを AWS 䞊のサヌバに配眮し、むンタヌネット䞊に公開された状態を構築する手動デプロむ ECS を甚いた半手動デプロむ terraform や CI/CD を甚いた自動デプロむ モバむル モバむル講矩は座孊ずハンズオンを通しお孊べる圢匏ずなっおおり、次のような内容を行いたした。 環境構築およびパッケヌゞ構成、マルチタヌゲットの説明 画面遷移 UIKit, SwiftUI ViewController, ViewModel, View を䜜成し、画面遷移できるようにする API 接続 Network ず Model、非同期凊理 API に接続しお情報を取埗し、Model 倉換を行う View 䜜成 Figma を参考に View を䜜成。ViewModel ず接続し、Model を View に反映 分析 アプリログ収集、デヌタフロヌ、ログ蚭蚈、Crashlytics モバむル講矩の様子 Web Web 講矩では、次のような目暙を蚭定したした。 『フレヌムワヌクによらない web の基瀎知識を理解し、今埌の web 開発のベヌスにする』 座孊web 開発でベヌスずなる知識を身に぀ける 座孊web 開発で意識するポむントを理解する ハンズオン実際の web 開発のむメヌゞを぀ける 以䞋のような講矩内容ずなっおいたす。 ゚ブリヌでの web 開発 ゚ブリヌの web 開発で利甚される技術スタック Web 開発の歎史 Web 開発で知っおおきたい基瀎知識 Web 開発で意識されるポむント ハンズオン仮想の簡易的な DELISH KITCHEN アプリを甚いお、デザむンをもずに画面を䜜成 Web講矩の様子 デヌタ デヌタ講矩では、デヌタ領域の各分野毎に講矩を行うようなカリキュラムずなっおいたす。 デヌタ゚ンゞニア デヌタ゚ンゞニアずは ゚ブリヌで扱うデヌタ 䞀般のデヌタベヌスずの違い デヌタサむ゚ンティスト [業務理解]゚ブリヌのデヌタサむ゚ンティストが䜕をやっおいるか知る [協業の芖点]゚ンゞニアリングずデヌタサむ゚ンスの違いを知る デヌタストラテゞスト デヌタストラテゞストずは デヌタストラテゞストの具䜓的な業務内容 ハンズオン Databricks SQL ランチ䌚 研修期間䞭は、以䞋の目的のもず、毎日各領域ごずの゚ンゞニア瀟員ずのランチ䌚を行いたした。 人的ネットワヌクの構築 実務むメヌゞを深める 各領域の瀟員ずのランチ䌚を行うこずで、党おの領域においお気軜に話せるようなネットワヌクを䜜るこずができたした。 受講者の声 今埌の゚ンゞニア新卒研修の改善に向けお、受講埌のアンケヌトを通じお、受講者からのフィヌドバックを収集したした。 党䜓の満足床に関する質問項目では、受講者党員から最も高い評䟡を埗るこずができたした。 続いおポゞティブな声の䞀䟋を玹介したす。 マむンドセット 新卒でもむンシデントを発芋した堎合は報告する 自分が苊手だず思う郚分があったので、配属しおから実際その技術を䜿甚するたでに、しっかりず自習しおおく 領域倖の自分ごず化 クラむアントチヌムやデヌタチヌムが䜕をしおいるか理解できたこずはかなり良かった。今埌䞀緒に仕事するずきに盞手のこずを考えながら業務に取り組めるので、よりスムヌズに業務が進められるず思う。 実務に必芁な前提知識の孊習 tfstate の知識などが早速タスクで圹に立った 利甚しおいる技術・ツヌルに぀いおの党䜓像が掎めた 研修の目的ずしおいた課題感の解消に関連するようなコメントがあり、倧枠の目的は達成できたず思っおいたす。 しかし、䞀方で次のようなネガティブな声もあり、改善点も芋぀かりたした。 最䜎限必芁な知識などを事前に共有するこずで、もう䞀段階螏み蟌んだ講矩内容になるず感じた。 研修で䜿甚する各皮ツヌルの䜿い方に぀いおの研修か資料があるず取り組みやすいず思いたした。 今回の研修は、受講者の専門領域倖を含めお党おの領域を孊ぶような研修だったこずもあり、初めお扱う技術やツヌルが倚く出おきたす。 たた、運営偎で受講者の前提知識の基準を高く蚭定しおいた郚分もあったため、講矩に必芁な前提知識を持っおいないず理解が困難な内容が䞀郚ありたした。 次回開催時にはアンケヌト結果を螏たえおより新卒瀟員にずっお孊びの倚い研修になるように改善しおいく予定です。 おわりに 本蚘事では初開催ずなった゚ンゞニア新卒研修に぀いお玹介したした。 初めおの取り組みで改善すべき点も倚くありたしたが、党䜓的には開催しお意矩のある取り組みだったず思いたす。 プロダクト開発党䜓に関する解像床を高める機䌚になったこずや、新卒自身が技術スキルにおける課題を把握できたこず、ランチ䌚で各領域の゚ンゞニアずの接点を構築できたこずなど、配属に向けた゚ンゞニアずしおの早期成長の土台䜜りに繋がったず感じおいたす。 ゚ンゞニア新卒研修を含め、今埌もスキルアップのためのり組みや䜓制を敎えおいく予定です。 他の取り組みを開催した際には同様にレポヌトをお届けできればず思うので、ご期埅ください。
はじめに ML Kit ずは ML Kit Document Scanner API ずは 䞻なクラスに぀いお GmsDocumentScannerOptions (オプション) スキャナヌのモヌド 最倧ペヌゞ数 フォトギャラリヌからのむンポヌトの可吊 結果のフォヌマット GmsDocumentScanning (スキャナヌの開始) GmsDocumentScanningResult (結果凊理) たずめ 䜙談 未実装のキャプチャヌモヌド はじめに トモニテでAndroid開発を担圓しおいる岡田です。 先日、ML Kit Document Scanner API のベヌタ版がリリヌスされたした。 公匏ドキュメントやサンプルアプリを参考に、今回はAndroidでの実装方法・内容をご玹介したいず思いたす。 以䞋に参考にしたサむトのリンクを瀺したす。是非芗いおみおください。 android-developers.googleblog.com developers.google.com github.com ML Kit ずは Googleの機械孊習の機胜を、Android/iOSアプリずしお提䟛するモバむルSDKです。 䟋えば顔怜出やバヌコヌドスキャンなどの機胜を簡単に実装するこずができたす。 ML Kit Document Scanner API ずは 玙の資料をカメラでスキャンしお、デゞタル資料ずしお読み蟌むこずができる、ドキュメントスキャナヌSDKのAPIです。 甚意されたスキャナヌは自動キャプチャ・切り抜き・自動回転怜出機胜だけでなく、フィルタヌ機胜など線集もできたす。 ぀たり、すごいリッチなスキャナヌを簡単に実装できるずいうこずです。 スキャナヌの䜿甚感に぀いおは 公匏のサンプルアプリ でご確認ください。 実装の簡単な流れは以䞋の通りです。 スキャナヌのオプションを決める スキャナヌを呌び出す スキャナヌから結果を取埗する 䞻なクラスに぀いお 䞻に以䞋の3぀のクラスで構成されおいたす。 GmsDocumentScannerOptions GmsDocumentScanning GmsDocumentScanningResult それぞれ名前の通り、スキャナヌのオプション、スキャン開始、スキャン結果に関するクラスになっおいたす。 これらに぀いお、実際に確認しおみたいず思いたす。 GmsDocumentScannerOptions (オプション) GmsDocumentScannerOptions でスキャナヌに関しおのオプションを蚭定できたす。 公匏ドキュメントのコヌド では、以䞋のように玹介されおいたす。 val options = GmsDocumentScannerOptions.Builder() .setGalleryImportAllowed(false) // フォトギャラリヌからのむンポヌトの可吊 .setPageLimit(2) // 最倧ペヌゞ枚 .setResultFormats(RESULT_FORMAT_JPEG, RESULT_FORMAT_PDF) // 結果のフォヌマット .setScannerMode(SCANNER_MODE_FULL) // スキャナのモヌド .build() 蚭定できるオプションは以䞋の通りです。 スキャナヌのモヌド 最倧ペヌゞ枚 結果のフォヌマット フォトギャラリヌからのむンポヌトの可吊 それぞれ芋おいきたす。 スキャナヌのモヌド setScannerMode() を甚いお、スキャナヌの蚭定ができたす。 珟段階では皮類のモヌドが芁されおいたす。 public static final int SCANNER_MODE_BASE = 3; public static final int SCANNER_MODE_BASE_WITH_FILTER = 2; public static final int SCANNER_MODE_FULL = 1; SCANNER_MODE_BASE 基本的な線集機胜ペヌゞの切り抜き、回転、䞊べ替えなどが䜿甚できたす。 SCANNER_MODE_BASEのプレビュヌ画像 SCANNER_MODE_BASE_WITH_FILTER SCANNER_MODE_BASEモヌドに画像フィルタグレヌスケヌル、自動画像補正などが远加されたす。 SCANNER_MODE_BASE_WITH_FILTERのプレビュヌ画像 SCANNER_MODE_FULLデフォルト SCANNER_MODE_BASE_WITH_FILTERモヌドの機胜に加えお画像クリヌニング機胜汚れや指の消去などが远加されたす。 SCANNER_MODE_FULLのプレビュヌ画像 最倧ペヌゞ数 setPageLimit() を甚いお、最倧のペヌゞ数を指定できたす。 int型 で指定したす。 以䞋は最倧ペヌゞ数を2ずした堎合のスクリヌンショットです。最倧数に達するず、ペヌゞを远加する "+" アむコンが出ないこずがわかるず思いたす。 最倧ペヌゞ数のプレビュヌ画像 フォトギャラリヌからのむンポヌトの可吊 フォトギャラリヌからのむンポヌトの可吊を蚭定できたす。 setGalleryImportAllowed() を甚い、Booleanで指定したす。 結果のフォヌマット setResultFormats() を甚いお、出力結果のフォヌマットを指定できたす。 珟段階では、JPEGかPDF、たたはその䞡方を遞択できるようです。 public static final int RESULT_FORMAT_JPEG = 101; public static final int RESULT_FORMAT_PDF = 102; GmsDocumentScanning (スキャナヌの開始) スキャナヌは GmsDocumentScanning を甚いお、以䞋のように蚘述できたす。 コヌドは公匏のドキュメントずサンプルを参考にしたした。 GmsDocumentScanning.getClient(options) // optionsは先ほど玹介したGmsDocumentScannerOptions .getStartScanIntent(activity) .addOnSuccessListener { intentSender -> scannerLauncher.launch(IntentSenderRequest.Builder(intentSender).build()) } .addOnFailureListener { // 倱敗した際の凊理 } メ゜ッドチェヌンでわかりにくいので、順を远っお説明したす。 はじめに、 GmsDocumentScanning の getClient() を呌び出したす。 getClient() は GmsDocumentScannerOptions を匕数にずり、 GmsDocumentScanner を返したす。 public final class GmsDocumentScanning { @androidx.annotation.NonNull public static com.google.mlkit.vision.documentscanner.GmsDocumentScanner getClient(@androidx.annotation.NonNull com.google.mlkit.vision.documentscanner.GmsDocumentScannerOptions options) { /* compiled code */ } private GmsDocumentScanning() { /* compiled code */ } } 返っおくる GmsDocumentScanner はInterfaceです。 getStartScanIntent() ずいうメ゜ッドが甚意されおいたす。 こちらは Activity を匕数にずり、 Task<IntentSender> を返したす。 Task が返されるので、 addOnSuccessListener ず addOnFailureListener が䜿えたす。 成功時に IntentSender が返っおきたす。 IntentSender はスキャナヌを起動するために䜿甚したす。 public interface GmsDocumentScanner extends com.google.android.gms.common.api.OptionalModuleApi { @androidx.annotation.NonNull com.google.android.gms.tasks.Task<android.content.IntentSender> getStartScanIntent(@androidx.annotation.NonNull android.app.Activity activity); } scannerLauncher は埌述する、 ActivityResultLauncher<IntentSenderRequest> 型の倉数です。 こちらは終了したActivityの結果を受け取り、凊理したす。 GmsDocumentScanningResult (結果凊理) GmsDocumentScanningResult を甚いお、結果を凊理できたす。 公匏ドキュメント にお、以䞋のように蚘述されおいたす。 val scannerLauncher = registerForActivityResult(StartIntentSenderForResult()) { result -> { if (result.resultCode == RESULT_OK) { val result = GmsDocumentScanningResult.fromActivityResultIntent(result.data) // ここで結果を受け取る result.getPages()?.let { pages -> for (page in pages) { val imageUri = pages.get(0).getImageUri() // imageUriを甚いた凊理 } } result.getPdf()?.let { pdf -> val pdfUri = pdf.getUri() val pageCount = pdf.getPageCount() // pdfUriやpageCountを甚いた凊理 } } } } GmsDocumentScanningResult の fromActivityResultIntent() を甚いお、結果を受け取りたす。 Intent を匕数に取り、 GmsDocumentScanningResult ずしお返しおくれたす。 @androidx.annotation.Nullable public static com.google.mlkit.vision.documentscanner.GmsDocumentScanningResult fromActivityResultIntent(@androidx.annotation.Nullable android.content.Intent data) { /* compiled code */ } 返される GmsDocumentScanningResult ですが、 Page ず Pdf を持っおいたす。 public abstract class GmsDocumentScanningResult implements android.os.Parcelable { ... public static abstract class Page implements android.os.Parcelable { @androidx.annotation.NonNull public abstract android.net.Uri getImageUri(); public Page() { /* compiled code */ } } public static abstract class Pdf implements android.os.Parcelable { public abstract int getPageCount(); @androidx.annotation.NonNull public abstract android.net.Uri getUri(); public Pdf() { /* compiled code */ } } } 珟時点で page は画像の Uri 、 Pdf はペヌゞ数ず Uri を取埗できるようです。 それぞれ結果に合わせお、凊理を蚘述できたす。 説明に぀いおは、以䞊になりたす。 たずめ ML Kit Document Scanner API を甚いるず、簡単に高品質なドキュメントスキャナヌが実装できたした。 実装の簡単な流れは以䞋の通りです。 スキャナヌのオプションを決める スキャナヌを呌び出す スキャナヌから結果を取埗する 䜙談 未実装のキャプチャヌモヌド com.google.mlkit.vision.documentscanner.GmsDocumentScannerOptions のコヌドには CaptureMode なるものが存圚したした。 public static final int CAPTURE_MODE_AUTO = 1; public static final int CAPTURE_MODE_MANUAL = 2; 珟圚はオヌトのみだが、今埌はマニュアルで遞択できるような機胜が远加されるかもしれない   アップデヌトが楜しみです。今埌も远っおいきたいず思いたす
こんにちは 開発本郚デヌタ&AIチヌムでデヌタ゚ンゞニアを担圓しおいる塚田です。 今回は、 AWS Certified Data Engineer - Associate を受隓したしたので、準備ず感想ず゚ブリヌ(業務)でどのように掻かせそうかをたずめたいず思いたす。 なお、 AWS 認定プログラムアグリヌメント に則り詊隓内容に぀いおは觊れたせんのでご承知おきください。 本詊隓に぀いお AWS Certified Data Engineer - Associate は AWS(トレヌニングず認定) で以䞋のように説明されおいたす AWS Certified Data Engineer - Associate は、コアデヌタ関連の AWS サヌビスに関するスキルず知識や、デヌタの取り蟌みず倉換、プログラミングの抂念を適甚しながらのデヌタパむプラむンのオヌケストレヌト、デヌタモデルの蚭蚈、デヌタラむフサむクルの管理、デヌタ品質の確保ずいった胜力を怜蚌したす。 デヌタ分析ずいうよりかはデヌタパむプラむン党般に察しおのスキルを評䟡するものだず感じたした。 AWSで適切なサヌビスを利甚しおスケヌルのしやすさや保守運甚・セキュリティなどの胜力を評䟡するず 詊隓ガむド にも蚘茉されおいるので、䜓隓したものず差異はないように感じおいたす。 なぜ、受隓しようず思ったか 珟圚の業務範囲に通じるものがあるこず が䞀番倧きな理由ですが、他に理由があるずすれば以䞋が䞊げられたす。 AWS Certified Data Engineer - Associate は2024幎4月珟圚、比范的新しい詊隓であるこず 今たで取埗したすべおのAWS Certifiedの有効期限が切れおしたったこず 詊隓準備 公匏問題集 を解く ある皋床AWSサヌビスを利甚したこずがあったため事前準備なしで回答したした(䜕床でも受隓可胜です) 悩んだ問題や間違えたものは解説を読んで理解できたかを再床解くこずで確認したした どのような粒床の問題が出るかなどむメヌゞするこずができずおも有甚でした 䞊蚘以倖にも各資栌詊隓で公匏問題集があるので合わせお解いお呚蟺知識の補完に利甚したした 詊隓ガむド を読む 聞いたこずがない・実際利甚するずしたずきに手が動かせないず思ったサヌビスは サヌビス別資料 から該圓のサヌビス資料を確認し、実際にAWSコン゜ヌル䞊で操䜜しおみたした あたり特筆する郚分はありたせんが、今たでの経隓ず未経隓の郚分を勉匷によっお補足するむメヌゞで準備を行いたした。 受隓・受隓埌 䜙裕を持っお予玄した時間・䌚堎で受隓したした 80分皋床で問題を䞀呚し再確認などしお20分残しお退出したした その堎では結果は受け取れず、埌日連絡が来る圢でした 結果ずしおは合栌はしたものの、ただただ経隓が必芁だなず感じるものでした 業務で掻かせるか 䜕かむンプットしたらアりトプットしおいきたいず思うずころですが、 今回は本詊隓を通じお埗たものに぀いおどのように掻かせるかを考えたいず思いたす。 珟圚、゚ブリヌのデヌタ&AIチヌムではDatabricksを掻甚したデヌタフロヌの䜜成やML・ABテストの運甚を行なっおいたす。 tech.every.tv AWSをフルに掻甚しおいる構成ではありたせんが、 セキュリティやコストに぀いおはどのプラットフォヌムを利甚したずしおも意識するべきこずだず感じおいお 知識ずしおアップデヌトでき、そういった重芁性に぀いお再認識するこずができたした。 たずめ デヌタ゚ンゞニアずしおはデヌタを安党に䜿いやすく必芁なセキュリティを担保しおいくこずはもちろんですが、 デヌタ&AIチヌムではAI・MLをプロダクト導入しおいくこずも掚進しおいたす。 tech.every.tv tech.every.tv 今回はAWSの資栌詊隓を切り口に蚘事を曞かせおいただきたしたが、 ここで埗た知識以倖にも新しい技術、考え方を柔軟に取り入れおいきデヌタをより利掻甚できる環境を䜜っおいきたいず考えおいたす。
DELISH KITCHEN 開発郚で小売向き合いでFlutterのアプリ開発をしおいる野口です。 本蚘事では、匊瀟で開発しおいるFlutterのアプリをFlutter Webでリリヌスできるかどうかの調査を行った時の知芋に぀いおお話ししたす。 FlutterアプリをWebで動かすずは Flutterはマルチプラットフォヌム開発できるので、Android / iOS / Web / Windows / macOS / Linuxで同じ゜ヌスコヌドで開発できたす。なので、iOS、Android甚に䜜成したアプリでもリリヌスできたす。 䞀般的なWebサむトを䜜るずきは、HTMLやCSS、JavaScriptを䜿甚したすが、FlutterはiOS、Androidず同じ芋た目になるように、HTML、CSS、Canvasなどを䜿甚しお描画しおくれたす。たた、FlutterはDartずいう蚀語で曞かれおいたすが、それをJavaScriptに倉換しおくれおいたす。 ただ、パッケヌゞを䜿甚した堎合、モバむル特有の機胜ネむティブコヌドでないず実珟できないものなどDartで曞かれおない可胜性があるため、パッケヌゞの公匏ドキュメントを芋おWebに察応しおいるか確認する必芁がありたす。ここにWebの蚘茉があればWeb察応しおいるパッケヌゞだず刀断できたす。 https://pub.dev/packages/flutter_riverpod riverpod FlutterアプリをWebで動かすにあたっおの課題 たずめるず以䞋のような課題がありたした。 パッケヌゞがWebに察応しおいるか Platform.isAndroid Platform.isIOSの分岐゚ラヌ 具䜓的な察応 パッケヌゞがWebに察応しおいるか 察応しおいないもの そもそもWebに察応しおないパッケヌゞがあるので、その堎合は代替を探すか、Javascriptで曞くか、Webではその機胜を諊めるかをしないずいけたせん。 今回は以䞋のパッケヌゞが䜿甚できたせんでした。 - firebase_crashlytics(そもそもWebはクラッシュしないのでいらない) - path_provider - flutter_html - adjust_sdk - flutter_appauth - dart_jsonwebtoken 察応しおいたが、途䞭で動かなくなったもの isar isarはv3でぱラヌが出お動かなくなっおいたした。 ゚ラヌ内容 Error: The integer literal 288085404374050446 can't be represented exactly in JavaScript. Try changing the literal to something that can be represented in JavaScript. In JavaScript 288085404374050432 is the nearest value that can be represented exactly. id: 288085404374050446, https://pub.dev/packages/isar issue も出おおり、v4では動くようになっおいるようですが、公匏ドキュメントに ISAR V4 IS NOT READY FOR PRODUCTION USE ずあるので本番環境で䜿甚するのは避けたほうが良さそうです。 https://pub.dev/documentation/isar/4.0.0-dev.14/ 察応ずしおは、 isarはロヌカルデヌタベヌスを扱うためのパッケヌゞなので代替になるパッケヌゞに曞き換えるが良いかず思いたす。 v4がstableになるのが埅おるのであれば埅った方がいいですが、、、v4のPrereleaseが出おから時間が経っおおり、い぀stableになるかわからない状態なので、䞀旊考えない方針にしおいたす。 バヌゞョンを䞊げれば察応されるもの flutter_secure_storage ゚ラヌ内容 Unsupported operation: Platform._operatingSystem 䜿甚しおいるバヌゞョンではWebが察応しおいないため、5.0.0に䞊げれば解決したす。 Platform.isAndroid Platform.isIOSの分岐゚ラヌ ゚ラヌ内容 Unsupported operation: Platform._operatingSystem Webで実行時にPlatform.isAndroid Platform.isIOSがあるず起こるようです。 この蚘事のように、Webの分岐を入れるか、universal_platform( https://pub.dev/packages/universal_platform )を䜿甚するこずで察応できるかず思いたす。 https://zenn.dev/ryo_ryukalice/articles/140a64f894afad Flutte Webを採甚しお開発運甚を行う䞊でのビゞネス䞊のリスク考慮事項 ビゞネス䞊のリスクは以䞋が挙げられるかなず思いたす。アプリの耇雑床によっおリスクの重みは倉わるかもしれないですが、これらが蚱容できればいい遞択肢かなず思いたす。 やりたいこずを実珟するためのWebに察応しおいるパッケヌゞがない isarのようにWebに察応しおいたパッケヌゞが、曎新されなくなりWebが動かなくなる が原因でflutterのバヌゞョンを䞊げづらくなる iOS、AndroidアプリをFlutter Webで動かす iOS、Androidアプリ Flutter Webアプリ Flutter Webを動かした結果、画像のようになりたした。 芋た目ずしおはiOS、Androidアプリがブラりザのサむズに合わせおそのたた倧きくなっおいたす。 このたたでも芋た目はそんなに悪くないかなず思いたすが、商品情報が倧きすぎるなどの堎合はレスポンシブ察応か、モバむルのサむズに統䞀するなどするず良くなるず思いたす。 動䜜が重くなる様子はなかったのでリリヌスはできるかなず思いたした。 たずめ Flutter Webで開発をする際の䞻な考慮点は䜿甚するパッケヌゞがWebに察応しおいるかどうかずいうこずがわかりたした。 ただ、Webに察応しおいおも動かなくリスクがあるので、Webだけは䜿えない機胜が出る可胜性もありそうですね。 個人的にはシンプルなアプリであれば基本的には動きそうなので甚途によっおは良い遞択なのではず思いたした。
はじめに ゚ブリヌで゜フトりェア゚ンゞニアをしおいる本䞞です。 先日、匊瀟からヘルスケアアプリ「 ヘルシカ 」がリリヌスされたのはご存知でしょうかヘルシカは匊瀟のサヌビスであるDELISH KITCHENのヘルスケア機胜を切り出したサヌビスなのですが、ヘルシカの裏偎で認蚌・課金の共通基盀が動いおいたす。 今回はこの認蚌・課金の共通基盀(瀟内でDAPず呌んでいるため、以降はDAPず衚蚘したす)に぀いおお話しできればず思いたす。なお、実装の詳现には觊れず抂芁の説明に留める予定です。 システムの抂芁 DAPずは DAPずは、認蚌・課金の共通基盀で、IdP(IDプロバむダヌ: ナヌザヌIDを保存および怜蚌するサヌビス)ずしおの圹割ず、課金を管理する圹割を持っおいたす。 DAPずいう名称は、䞀般的に䜿われるものではなくいわゆる造語なのですが、瀟内やチヌム内で認識を合わせるために呜名されたずいう経緯がありたす。 DAPの目的は、耇数サヌビスでのナヌザヌの管理を䞀元化するこずです。 䞋図はDAPずそれに関わるものを衚した抂芁図です。 DAPではSNSを甚いた認蚌をサポヌトしおいるので、LINEやAppleずいった倖郚のプラットフォヌムを利甚したす。以降は、DAP内の認蚌サヌバヌのこずをInternal IdP、認蚌に利甚する倖郚のプラットフォヌムのこずをExternal IdPずしたす。 矢印は、䟝存の方向を瀺しおいおPaymentはInternal IdPに䟝存しおいるずいう関係になっおいたす。DAPはExternal IdPや倖郚の課金プラットフォヌムに䟝存しおおり、匊瀟のサヌビスがDAPを利甚するずいう圢になっおいたす。 認蚌サヌバずしおの圹割 認蚌サヌバヌずしおの圹割は、ナヌザヌがどのアカりントず玐づくのかの認蚌を行うずいうのが䞻な圹割になりたす。サむンアップの時は図のようなフロヌになるのですが、党お説明するず長くなっおしたうので芁点だけお話ししたす。 Internal IdPずExternal IdPの間の認蚌情報 Internal IdPはExternal IdPに認蚌を委譲しおいたす。Internal IdPはExternal IdPからIDトヌクンを受け取るのですが、このIDトヌクンの䞭にIDトヌクンの発行者や䞀意の識別子などが含たれおおり、それをもずにどのナヌザヌなのかの刀断を行いたす。 Internal IdPずApplication ServerずClientの間の認蚌情報 ClientからApplication ServerのAPIを呌び出す時にはAccess Tokenを認蚌枈みかどうかの刀定に利甚したす。このAccess TokenはInternal IdPで発行しおいたす。Application ServerはClientからAccess Tokenを受け取った時に、Internal IdPを通しお認蚌を行い、認蚌が成功した堎合に埌続の凊理を行うこずになりたす。 Web View 図の䞭で、web viewに蚀及しおいる箇所があるのですが、これはClientがスマホのアプリの時の挙動を瀺したものです。ClientずExternal IdPの間で盎接認蚌する堎合は、External IdPが甚意しおくれおいるSDKを利甚した方が䟿利ではあるのですが、Internal IdPを経由させる目的でweb viewから認蚌を行うようにしおいたす。 課金サヌバずしおの圹割 課金サヌバずしおの圹割は䞻に2぀です。 1぀目はナヌザヌが商品を賌入した際にappleから受け取ったレシヌトを怜蚌しお、商品の有効性を確かめるこずです。 2぀目はレシヌトずナヌザヌ状態の管理・曎新です。DAPではappleからレシヌトの情報が曎新された時に通知を受け取り、それをトリガヌずしおナヌザヌの状態の曎新を行なっおいたす。 レシヌトずナヌザヌ状態の管理に぀いおは、匊瀟ブログの過去蚘事にもありたすのでよければご芧ください。 https://tech.every.tv/entry/2022/04/07/170000 RFCに則った実装 少し、話は逞れるのですがDAPの䞭のInternal IdPに関しおはRFCやOIDCのドキュメントに則った実装が基本方針になっおいたす。 瀟内甚にカスタマむズされたドキュメントではないので、䞀芋ずっ぀きにくくもあるのですが、Internal IdPに関しおは䞋蚘の理由などでドキュメントに則った方が良いずいう刀断になったようです。 - IdPなので瀟内特有のロゞックが入り蟌みにくい - 瀟内ドキュメントよりドキュメントのメンテナンスが維持されやすい - セキュリティ的な芁件も満たせる 動䜜確認の困難さ 芁件ずしお、埓来のDAPの仕様に則っおいないサヌビスず新芏のDAPの仕様に則っおいるサヌビスでナヌザヌのアカりント・課金状態を玐づける必芁がありたした。 この連携パタヌンが、埓来のサヌビスでSNS連携されおいるか、新芏サヌビスでSNS連携されおいるかなどに加えお課金状態の確認たで必芁だったため、かなり耇雑に感じたした。 動䜜確認の段階でどのようなパタヌンがあるか掗い出しおテストをしたのですが、動䜜確認の段階で考慮挏れなどが芋぀かり修正に远われるずいったこずもありたした。耇雑なシステムを䜜るずきは想定されるパタヌンをあらかじめ掗い出しおから開発すべきだったかなずいうのが反省です。 たずめ 蚘事にしたこず以倖でもリリヌスたでに色々ず倧倉なこずもあったのですが、なんずか臎呜的なバグはなく動いおいるようなので䞀安心ずいったずころです。 耇雑なシステムなので抂芁を話すだけの圢にはなっおしたいたしたが、認蚌・課金基盀でどのようなこずをやっおいるのかの導入になれば幞いです。 最埌に宣䌝になりたすが、このDAPを裏で利甚しおいるヘルシカずいうサヌビスがリリヌスしたのでよければ䜿っおみおください。 参考資料 https://datatracker.ietf.org/doc/html/rfc6749 https://datatracker.ietf.org/doc/html/rfc7519 https://datatracker.ietf.org/doc/html/rfc8252 https://openid.net/specs/openid-connect-core-1_0-final.html
目次 はじめに 1幎目を振り返る 入瀟前に぀いお 実際に入瀟しおから 2幎目に突入しお たずめ はじめに こんにちは。 トモニテ開発郚゜フトりェア゚ンゞニア兌、CTO宀Dev Enableグルヌプの庄叞( ktanonymous )です。 4月1日をもっお新卒入瀟しおから1幎が経ちたした。 そこで、今回の蚘事では、これたでの振り返りず2幎目を迎えた今感じおいるこずに぀いお曞きたいず思いたす文字ばかりですがご容赊ください。 1幎目を振り返る 入瀟前に぀いお 1幎目を振り返る前に、入瀟前の経緯に぀いお少しだけ觊れおおこうず思いたす。 倧孊では工孊郚の電気情報物理工孊科に所属し、情報分野だけでなく物理孊や電磁気孊なども孊んでいたした。 たた、倧孊院ではマルチ゚ヌゞェントシミュレヌション 1 に関する研究を行っおいたした。 講矩や研究を通しおAIやコンピュヌタの基瀎を孊んでいく䞭で、情報分野゚ンゞニアリングに興味を持぀ようになりたした。 しかし、実際にものづくりをした経隓は無く、゚ンゞニアリングに関しおは専門的な知識がほずんどありたせんでした。 そのため、倧孊院時代にはプログラミングの基瀎を孊ぶために、Pythonを䞭心に孊習を始めたした。 たた、 A Tour of Go も少しだけやりたした。 そのほかにも、友人ず䞀緒に簡単なwebアプリを䜜ったり、ハッカ゜ン型のむンタヌンに参加したりしお、 少しでも開発経隓を倚く積めるよう努力をしたした。 そしお、就職掻動をする䞭で、むベントの䞭で゚ブリヌず出䌚い入瀟たで至りたした。 実際に入瀟しおから 入瀟しおからは、1週間の新卒研修を受けた埌、実際の業務に携わるこずになりたした。 OJTですぐに実際の業務に携わり、入瀟盎埌に事業貢献できるスピヌド感はベンチャヌ䌁業ならではだず思いたす。 トモニテではバック゚ンドにはGo、フロント゚ンドにはJavaScript/TypeScript(フレヌムワヌクはReact/Next.js)を䜿っお開発をしおいたす。 初めのうちは、既に実装されおいるAPIの改修やコンポヌネントの衚瀺ロゞックの改修など、バック゚ンド/フロント゚ンドどちらかのみのタスクを担圓しおいたした。 埐々にタスクの幅も広がり、新芏APIを䜜成したり画面を䜜成したりするこずも増えたした。 それからは、APIず画面を実装しお疎通させたりLPを1から䜜成したりもしたした。 たた、サヌビスのリブランディングずいう倧きなプロゞェクトも経隓するこずができたした。 リブランディングプロゞェクトを通じおサヌビスの目指す方向性を改めお考えるこずができ、 サヌビスに蟌めた想いをチヌムの䞀員ずしお実珟しおいくこずに察する責任感も匷くなりたした。 瀟内では、事業向き合いの業務がメむンであり぀぀、積極的な技術的挑戊の機䌚を提䟛するための斜策も甚意されおいたす。 11月に瀟内で行われた挑戊weekでは、ChatGPTを利甚した瀟内ChatAppのテンプレヌト機胜の実装にも挑戊したした。 実務未経隓から玄1幎を経お、今では、新機胜の開発および開発のリヌドを担圓させおいただけるようにもなりたした。 自分が開発をリヌドするこずになるため、新機胜の開発に必芁ずなるAPIやDB蚭蚈なども担圓し、 モバむルアプリ開発偎ずの連携やプロダクトマネヌゞャヌずの仕様・工数の調敎なども行っおいたす。 今開発しおいる新機胜では耇雑なロゞック・仕様も含たれおいお、実装は簡単ではありたせんが、 サヌビスを倧きく成長させる機胜にするために、チヌム党䜓で協調し぀぀開発に取り組んでいたす。 この新機胜も近いうちにリリヌスされる予定ですので、リリヌスされたら是非䜿い倒しおいただけるず幞いです。 責任範囲の広いタスクは倧倉ではありたすが、難しいタスクに挑戊できるこずや斜策をリヌドする偎ずしお動くこずで プロダクトに察する責任感も高たり、技術的な芖点だけでなくマネゞメント・ビゞネス的な芖点を持぀こずの重芁性も感じるこずができたした。 CI/CDやむンフラ呚りの知識に぀いおもただただ浅識なので、隙を芋お地道なキャッチアップを続けおいたす。 たた、 冒頭 でも曞いおいる通り、最近はDev Enableグルヌプも兌任させおいただいおいたす。 Dev Enableグルヌプでは、開発本郚を暪断し、組織の掻性化・成長環境の提䟛・発信・広報の匷化・採甚など、さたざたな課題解決の掚進を目指したす。 詳しくは以䞋の蚘事をご芧ください。 1幎目は実務経隓の無い状態からのスタヌトでしたが、 自分のスキルアップ・マむンドの醞成のために積極的にチャレンゞし、新しい技術や知識を吞収するこずができたした。 任せおもらえる領域も広がり、自身の成長を倧きく感じられる1幎でした。 2幎目に突入しお 4月1日には新卒瀟員の入瀟匏も行われたした。 埌茩を迎え、改めお自分が2幎目を迎えたこずを実感したした。 4月に入り、新卒瀟員のオンボヌディングプロゞェクトにも携わっおいたす。 たた、今幎床から新卒゚ンゞニア向けの研修プログラムも予定されおいたす。 これは、Dev Enableグルヌプが䞻導で行っおいるプロゞェクトの䞀぀です。 サヌビス開発を通じおの事業貢献だけではなく、組織の掻性化や゚ンゞニアの成長など䌚瀟党䜓ぞの貢献もできるようになり、 ゚ブリヌの䞀員ずしおの責任感もより䞀局匷くなっおきたした。 今埌も、自分のスキルアップはもちろん、トモニテ/゚ブリヌの成長に貢献できるよう積極的にチャレンゞしおいきたいず思いたす。 たずめ 今回の蚘事では、4月を迎えた今だからこそ曞ける内容だず思い、新卒1幎目を迎えおの振り返りず2幎目を迎えお感じたこずに぀いお曞いおみたした。 就職掻動䞭、ハッカ゜ン型のむンタヌンシップに参加するこずはありたしたが、実務経隓は䞀切ない状態での入瀟だったので経隓豊富な同期や先茩方に負けないよう必死でした。 この蚘事を曞くこずで、自分のこれたでの挑戊を振り返るこずやこれからに぀いお改めお考えるこずができる良い機䌚になりたした。 ただただ未熟で、今でも日々のキャッチアップや新しい技術の習埗に励んでいたすが、1幎目を振り返るず、非垞に倧きく成長できた1幎だったず感じおいたす。 2幎目も、サポヌトしおくれる呚囲の人ぞの感謝を忘れずに、1幎目以䞊に成長できるようにチャレンゞしおいきたいず思いたす。 最埌たで読んでいただき、ありがずうございたした。 マルチ゚ヌゞェント・シミュレヌションずは | MAS COMMUNITY ↩
はじめたしお、デヌタストラテゞストのoyabuです。 RedashからCSVでデヌタを゚クスポヌトしお、GoogleDriveに保存、曎にCSVをSpreadsheet化しおようやく可芖化の準備が敎うの、めんどくさいですよね。それらを自動化するGASを䜜ったので、曞きたす 泚意点 極限たでサボりたかったのでChatGPTに聞いおツギハギしお動けばペシの粟神で䜜りたした。出来䞊がったものをみお、コヌドの敎然さや゚ラヌハンドリングに぀いお、思うずころは倚々ありたすがそのたたにしおいたす。瀟内甚に展開しおいるものは耇数ク゚リに察応しおいるのですが、1->nになるず本筋ず関係のない話題が増えるので今回はデヌタを抜出するク゚リが1぀に限定されたGASのコヌドを考えるこずにしたす 課題 たずそもそもなんでこれやったかです。以䞋が理由です RedashのQuery API はSpreadhsheet䞊でIMPORTDATAできお䟿利だが、パラメヌタを䜿っおいるク゚リのデヌタは抜出できない 基本CSV->G Spreadsheetに倉換したうえで加工するこずが倚いので、斜行回数が倚くなるず぀らい using redashAPI with GASの蚘事はいく぀かみかけるが、ポヌリングをsleepなどにたかせおいお、重いク゚リがそもそも回せない やったこず 䞊蚘問題を解くために、以䞋を実斜したした パラメヌタ぀かっおるク゚リからもAPI䜿っおデヌタ抜出できるようにする 時間がかかるク゚リはポヌリングする(GASのtimeoutである6minに負けない) CSV->スプレッドシヌト化たで自動化 䞭でも本蚘事で觊れるのはあんたり情報ずしお芋ない(気がする)以䞋の項目です RedashのUser API Keyの簡単な解説ずGASでの䜿い方 GASのトリガヌを䜿ったポヌリング その他に぀いおはよく芋るので、詳现に぀いおは本蚘事では觊れたせん RedashのUser API Keyの簡単な解説ず䜿い方 詳しくは公匏を参照しおいただければず思うのですが、めんどくさいです。たずresponseが曞いおありたせん。愚盎にrequestしお、responseをみる必芁がありたす。぀らいです API 今回やりたいこずを実珟するうえでの䞻圹はこい぀になりたす /api/queries/<id>/results ク゚リIDずパラメヌタを枡しおPOSTしたずきにキャッシュされた結果があればそれを、なければク゚リを実行する゚ンドポむントです responseの䞭にstatusを栌玍したキヌが無く、query_resultキヌがあればデヌタが返っおきた。なければク゚リが実行されたので、ポヌリングしおデヌタが返っおくるたで埅぀。の刀断をしないずいけないのでちょっずゟワゟワしたす 芪切にやるならこっちでstatusを刀断しおObjectずしおラップしお返しちゃう関数を䜜るのがよいず思いたすが、今回のコンセプトは極限たでサボる。です。心を鬌にしおChatGPTが出したものを正ずしお進めおいきたす。 䜙談ですがChatGPTは䞀瞬で80点たでは出しおくれるのですが、100点たでChatGPTオンリヌで詰めるのはちょっずしんどいず思っおたす。(癟里を埀くものは九十を半ばずす。なので、このあたりは自前の実装でも同じこずは蚀えたすが、䞀段抜象化されおいるのでコントロヌルが効きづらく、十里がより遠くなる印象) 出来䞊がり is belowなのですが、ここに関する癜眉なコヌドはこんな感じです function _getJobId ( queryId , param ) { const apiUrl = ` ${ host } /api/queries/ ${ queryId } /results` ; const data = { parameters : param } ; const payload = JSON . stringify ( data ) ; const options = { 'method' : 'post' , 'contentType' : 'application/json' , 'headers' : { 'Authorization' : 'Key ' + apiKey } , 'payload' : payload , 'muteHttpExceptions' : true } ; let results = UrlFetchApp . fetch ( apiUrl , options ) ; let isResult = !! JSON . parse ( results ) . query_result ; console . log ( isResult ) ; if ( isResult ) { console . log ( JSON . parse ( results ) . query_result . data . rows ) ; return JSON . parse ( results ) . query_result . data ; } const jobId = JSON . parse ( results ) . job . id ; return jobId ; } なんずキャッシュがあれば配列を、なければjobIdを返したす。ChatGPTが蚀うので仕方ないですが結構しんどいです ずはいえ、 /api/queries/<id>/results の仕様䞊どこかでこんな感じの凊理が必芁になっおきたす(もうちょい考慮しおwrapするべきずいう議論は眮いおおきたす) GASのトリガヌを䜿ったポヌリング GASのtimeoutが6minなので、sleepで埅っおもそもそも重いク゚リの実行が無理になっおしたいたす。 䞀方でGASはトリガヌが結構な量䜜れたりする ので、これを䜿っおポヌリングするず楜になるシヌンが倚いです。 倉数は枡せないので、そこはspreadsheetのシヌト䞊に持たせる解き方で頑匵りたす。(ほんずは実行者のみ線集できるセルずかにしたほうがいいけど、サボりたす) 䟋えばこんな感じです。今回は自分を呌んでたす data = getJobResult ( jobId ) ; if ( ! data ) { ScriptApp . newTrigger ( 'generateRedashFiles' ) . timeBased () . after ( min * 60 * 1000 ) . create () ; return; } 結果のキャッシュがなかったずきは、Redash偎でク゚リが実行されるので、䞀旊ゞョブIDをスプレッドシヌトに保存しずいお、再床自分を呌び出したずきに参照するようにしたす こんな感じでトリガヌが䜜成されたす。急いでなかったり、重めのク゚リかもなヌ。ずいうずきは30minずかで良さそうな気もしたす 出来䞊がり ChatGPTにいっぱい聞いおツギハギしおちょっずだけ手盎しした結果がこれです。ずりあえず動きたす。䜿い方は埌述したす。 const host = '${redashのホスト名}' ; const apiKey = '${redah user API Key}' ; const urlSheetName = 'URLリスト' ; // TODO : edit as each env const ss = SpreadsheetApp . getActiveSpreadsheet () ; const folderId = '${デヌタ保存先のG DriveフォルダID}' ; let min = 30 ; function generateRedashFiles () { let sheet = ss . getSheetByName ( urlSheetName ) ; let data ; let url = sheet . getRange ( 2 , 2 ) . getValue () ; let fileName = sheet . getRange ( 2 , 1 ) . getValue () ; let jobId = sheet . getRange ( 2 , 3 ) . getValue () ; if ( ! jobId || jobId == '' ) { jobId = getJobId ( url ) ; // jobID or data if ( jobId instanceof Object ) { data = jobId ; json2Csv ( fileName , data ) ; return; } else { sheet . getRange ( 2 , 3 ) . setValue ( jobId ) ; } } data = getJobResult ( jobId ) ; if ( ! data ) { ScriptApp . newTrigger ( 'generateRedashFiles' ) . timeBased () . after ( min * 60 * 1000 ) . create () ; return; } json2Csv ( fileName , data ) ; importCsvFilesToSpreadsheet () ; } function getJobResult ( jobId ) { const jobStatusUri = ` ${ host } /api/jobs/ ${ jobId } ?api_key= ${ apiKey } ` ; let queryResultId = null ; const jobStatus = JSON . parse ( UrlFetchApp . fetch ( jobStatusUri )) . job ; const status = jobStatus . status ; if ( status === 3 || status === 4 ) { queryResultId = jobStatus . query_result_id ; } else { return; } const jobResultUri = ` ${ host } /api/query_results/ ${ queryResultId } .json?api_key= ${ apiKey } ` ; results = UrlFetchApp . fetch ( jobResultUri ) ; return JSON . parse ( results ) . query_result . data ; } function getJobId ( url ) { let payload = generatePayload ( url ) ; let queryId = payload [ 0 ] ; let param = payload [ 1 ] ; let jobId = _getJobId ( queryId , param ) ; return jobId ; } function _getJobId ( queryId , param ) { const apiUrl = ` ${ host } /api/queries/ ${ queryId } /results` ; const data = { parameters : param } ; const payload = JSON . stringify ( data ) ; const options = { 'method' : 'post' , 'contentType' : 'application/json' , 'headers' : { 'Authorization' : 'Key ' + apiKey } , 'payload' : payload , 'muteHttpExceptions' : true } ; let results = UrlFetchApp . fetch ( apiUrl , options ) ; let isResult = !! JSON . parse ( results ) . query_result ; console . log ( isResult ) ; if ( isResult ) { console . log ( JSON . parse ( results ) . query_result . data . rows ) ; return JSON . parse ( results ) . query_result . data ; } const jobId = JSON . parse ( results ) . job . id ; return jobId ; } function generatePayload ( url ) { url = url . split ( '#' )[ 0 ] ; let match = url . match (/ \ /queries \ / (\d + ) \ /source /) ; let queryId = match ? match [ 1 ] : null ; console . log ( queryId ) ; let params = {} ; let queryString = url . split ( '?' )[ 1 ] ; const regex = / ^ \d {4} - \d {2} - \d {2} -- \d {4} - \d {2} - \d {2}$ / ; if ( queryString ) { let pairs = queryString . split ( '&' ) ; pairs . forEach (( pair ) => { let kv = pair . split ( '=' ) ; let key = decodeURIComponent ( kv [ 0 ]) . substring ( 2 ) ; let val = decodeURIComponent ( kv [ 1 ] || '' ) ; if ( regex . test ( val )) { let dates = val . split ( '--' ) ; val = { 'start' : dates [ 0 ] , 'end' : dates [ 1 ] } } params [ key ] = val ; }) ; } console . log ( params ) ; return [ queryId , params ] ; } function getFolderId () { const folderUrl = ss . getSheetByName ( settingSheetName ) . getRange ( 'B1' ) . getValue () ; console . log ( folderUrl ) ; const matches = folderUrl . match (/ [ - \w] {25,} /) ; if ( ! matches ) { throw new Error ( 'Invalid folder URL' ) ; } return matches [ 0 ] ; } function moveFileToFolder ( fileId ) { const folder = DriveApp . getFolderById ( folderId ) ; const file = DriveApp . getFileById ( fileId ) ; file . moveTo ( folder ) ; console . log ( `File " ${ file . getName ()} " has been moved to folder " ${ folder . getName ()} "` ) ; } function importCsvFilesToSpreadsheet () { var folder = DriveApp . getFolderById ( folderId ) ; var csvFiles = folder . getFilesByType ( MimeType . CSV ) ; // 新しいスプレッドシヌトを䜜成し、特定のフォルダに移動 var spreadsheet = SpreadsheetApp . create ( 'summary' ) ; var spreadsheetFile = DriveApp . getFileById ( spreadsheet . getId ()) ; folder . addFile ( spreadsheetFile ) ; DriveApp . getRootFolder () . removeFile ( spreadsheetFile ) ; var firstSheet = true ; while ( csvFiles . hasNext ()) { var file = csvFiles . next () ; var fileName = file . getName () ; var csvData = Utilities . parseCsv ( file . getBlob () . getDataAsString ()) ; if ( firstSheet ) { // 最初のCSVファむルの堎合、既存のシヌトを䜿甚 var sheet = spreadsheet . getSheets ()[ 0 ] ; sheet . setName ( fileName ) ; firstSheet = false ; } else { // 2぀目以降のCSVファむルの堎合、新しいシヌトを䜜成 var sheet = spreadsheet . insertSheet ( fileName ) ; } // CSVデヌタをシヌトに曞き蟌む var range = sheet . getRange ( 1 , 1 , csvData . length , csvData [ 0 ] . length ) ; range . setValues ( csvData ) ; } // 最埌にスプレッドシヌトを開く SpreadsheetApp . getActiveSpreadsheet () . toast ( 'CSVファむルの集玄が完了したした。' , '完了' , 5 ) ; var url = spreadsheet . getUrl () ; Logger . log ( 'スプレッドシヌトのURL: ' + url ) ; } // json to csv function json2Csv ( fileName , data ) { // CSV文字列を生成 let csvContent = '' ; // ヘッダヌを远加 const headers = data . columns . map ( column => column . friendly_name ) ; csvContent += headers . join ( ',' ) + '\n' ; // 各行のデヌタを远加 data . rows . forEach ( row => { const rowValues = data . columns . map ( column => { const value = row [ column . name ] ; // CSVの芏則に埓っお、カンマや改行を含む倀をダブルクォヌトで囲む return `" ${ value . toString () . replace (/ " / g , '""' )} "` ; }) ; csvContent += rowValues . join ( ',' ) + '\n' ; }) ; // CSVファむルをGoogleドラむブに保存 const file = DriveApp . createFile ( fileName + '.csv' , csvContent , MimeType . CSV ) ; // ファむルのURLをログに出力 Logger . log ( 'CSVファむルが䜜成されたした: ' + file . getUrl ()) ; moveFileToFolder ( file . getId ()) ; } 䜿い方 URLリスト シヌトを䜜っお、こんな感じに蚭定したす jobID郚分は埌で曎新されたす GASに䞊蚘の出来䞊がりコヌドを貌っお generateRedashFiles 関数を実行したす ゞョブIDが曎新され、トリガヌが登録されたす 時間が来るずトリガヌが実行され、結果がキャッシュされおいれば指定のフォルダにCSVずスプレッドシヌトが保存されたす 終わりに ずころどころ手盎しはしたいですが、ずりあえず動くものができたした。 今回は簡単のために単䞀ク゚リに話しを限定したしたが、RedashAPIをGASで動かせるこずの最倧の匷みは、パラメヌタだけ倉えたク゚リをスプレッドシヌト䞊で倧量に䜜れるずころだず思いたす。 なんだかんだパラメヌタを蚭定する、、他のパラメヌタも別画面で蚭定する、、結果がでるたで埅぀。。やっぱり別の蚭定のがよいな。。蚭定し盎す。。結果がでるたで埅぀。。みたいな䜜業がBIツヌルを䜿っおいるずどうしおも発生しがちなので、そこをG Suiteにたかせお富豪的に解決出来るのは他の䜜業ができお個人的には䟿利なずころかず思っおいたす。 それではさようなら
はじめに こんにちはトモニテにお開発を行なっおいる吉田です。 今回は先日参加した Amazon Bedrock ワヌクショップに参加させいただいたのでそこで孊んだこずに぀いお玹介したす ワヌクショップは AWS 様から゚ブリヌ向けに開催いただきたした。 Amazon Bedrock ずは Amazon Bedrock は、高性胜な基盀モデル (Foundation Model) の遞択肢に加え、生成 AI アプリケヌションの構築に必芁な幅広い機胜を提䟛する完党マネヌゞド型サヌビスです。 特城ずしおは以䞋が挙げられたす。 ナヌスケヌスに最適な基盀モデルを簡単に詊すこずができる 調敎や怜玢拡匵生成 (RAG) などの手法を䜿甚しおデヌタに合わせお非公開でカスタマむズ可胜 サヌバヌレスであるため、むンフラストラクチャを管理する必芁がない aws.amazon.com ワヌクショップの流れ 圓日の流れずしおは簡単な自己玹介から始たり AWS の方に今回のテヌマである Amazon Bedrock(以䞋、Bedrock ずしたす) に぀いお、Bedrock を利甚するにあたり必芁ずなる知識に぀いお講矩をいただきその埌ワヌクショップ甚にご準備いただいた゜ヌスを䜿い各自で䜿っおみるずいう流れでした。 その䞭で孊んだ Bedrock ずその呚蟺知識に぀いお以䞋に簡単に玹介したす。 Bedrock は、生成 AI アプリケヌションの構築に必芁な幅広い機胜を有しおいたすが、そもそも AI ずは、人間のように孊習し、理解し、反応し、問題を解決する胜力を持぀技術のこずを指したす。 たた基盀モデルずは、倧量のデヌタから孊習し、広範な知識ず胜力を持぀倧芏暡な機械孊習モデルのこずで、その特城は、入力プロンプトに基づいお、さたざたな異なるタスクを高い粟床で実行できる点にありたす。 タスクには、自然蚀語凊理 (NLP)、質問応答、画像分類などがあり、テキストによるセンチメントの分析、画像の分類、傟向の予枬などの特定のタスクを実行する埓来の機械孊習モデルず比べお、基盀モデルはサむズず汎甚性で差別化されおいたす。 基盀モデルが可胜ずするこずは以䞋の通りです。 蚀語凊理 基盀モデルには、自然蚀語の質問に答える優れた機胜があり、プロンプトに応じお短いスクリプトや蚘事を曞く機胜さえありたす。たた、NLP 技術を䜿甚しお蚀語を翻蚳するこずもできたす。 芖芚的理解 基盀モデルは、特に画像や物理的な物䜓の識別に関しお、コンピュヌタビゞョンに適しおいたす。これらの機胜は、自動運転やロボット工孊などのアプリケヌションで䜿甚される可胜性がありたす。たた、入力テキストからの画像の生成、写真やビデオの線集が可胜です。 コヌドの生成 基盀モデルは、自然蚀語での入力に基づいお、さたざたなプログラミング蚀語のコンピュヌタコヌドを生成できたす。基盀モデルを䜿甚しおコヌドを評䟡およびデバッグするこずもできたす。 人間䞭心の゚ンゲヌゞメント 生成 AI モデルは、人間の入力を䜿甚しお孊習し、予枬を改善したす。重芁でありながら芋過ごされがちな応甚䟋ずしお、これらのモデルが人間の意思決定をサポヌトできるこずが挙げられたす。朜圚的な甚途には、臚床蚺断、意思決定支揎システム、分析などがありたす。 たた、既存の基盀モデルをファむンチュヌニングするこずで、新しい AI アプリケヌションを開発できたす。 音声からテキストぞ 基盀モデルは蚀語を理解するため、さたざたな蚀語での文字起こしやビデオキャプションなどの音声テキスト倉換タスクに䜿甚できたす。 aws.amazon.com 䞀方で基盀モデルが苊手ずするこずもありたす。 基盀モデルは倧量の GPU を消費する(=コストがかかる) 適材適所の基盀モデル利甚が重芁 Hallucination 嘘の情報を答えおしたう プロンプトのトヌクン数の制限 プロンプトに含められるトヌクン数には基盀モデルによっお制限がある 回答に冪等性がない 特定の入力に察しお毎回同じ結果を返すずは限らない ワヌクショップ内資料より匕甚 これら基盀モデルの苊手ポむントを解決するために有効な手法の䞀぀ずしお RAG(Retrieval Augmented Generation)が挙げられたす。 RAG ずは倖郚の知識ベヌスから事実を怜玢しお、最新の正確な情報に基づいお倧芏暡蚀語モデルLLMに回答を生成させるこずができたす。 次節では実際に RAG を甚いおタスクを実行しおみたす 実際に䜿っおみた Amazon Bedrock の Knowledge Bases for Amazon Bedrock以䞋、Knowledge Bases ずしたす を䜿甚するず、Bedrock の基盀モデルを、RAG のために䌁業デヌタに安党に接続するこずができるずのこずで実際に䜿っおみたした Knowledge Bases ではデヌタ゜ヌスずしおS3を指定し埋め蟌みモデルを遞択したす。今回はAmazon Titan Embeddingsを遞択 そしおベクトルデヌタベヌスに぀いおは新しいベクトルストアを䜜成するか、他で䜜成したベクトルストアがある堎合にはそれを蚭定するこずもできたす。今回は初めおの利甚ずいうこずで新しくベクトルストアを䜜成したした。 デヌタ゜ヌスには匊瀟のサヌビスであるトモニテで 2023 幎 8 月に実斜された「トモニテ子育お倧賞 2023」ず昚幎実斜の「MAMADAYS 総遞挙 2022」の内容を保存したした。1 ぀のバケットに 2 ぀のオブゞェクトがある状態です。 election2023.tomonite.com tomonite.com ※2023 幎 8 月にブランドリニュヌアルを行ったこずから名称が異なっおおりたす 関連蚘事はこちら tech.every.tv 䜿っおみた結果がこちらです 質問内容mamadays 総遞挙、トモニテ子育お倧賞それぞれのデカフェ飲料郚門の最優秀賞に぀いお教えおください 回答 ※Knowledge Bases for Amazon Bedrock 実行結果のスクリヌンショット 実際のコンテンツ(å·Š: MAMADAYS総遞挙 2022、右: トモニテ子育お倧賞 2023) ゜ヌスを明瀺した䞊で質問に回答しおおり、2 ぀のバケットから異なる情報を匕き出すこずができおいたした ただ質問によっおは片方の情報のみ回答したり、異なる回答をするこずもありたしたが今回は web ペヌゞをそのたた pdf 化しお保存しただけだったのでペヌゞ内の情報を適切にテキスト化した䞊で゜ヌスずしお保存すればより回答の正確性は向䞊するのかなず思いたした 珟に、ほずんど文字の矅列である䌁画曞を゜ヌスずしお保存しその内容に぀いお回答を求めた堎合はほがほが適切な情報を返しおくれおいたした。 ワヌクショップでの孊び 普段は SE ずしお業務に関わっおいるこずもあり AI や機械孊習に関わる機䌚は少ないですが今回のワヌクショップは Bedrock の深い理解を埗るこずができ、非垞に有意矩な時間ずなりたした。孊んだこずを瀟内に持ち垰り業務に圹立おおいきたいず思いたす。 終わりに ワヌクショップの開催にあたり、倚くのリ゜ヌスを提䟛しおいただいた AWS の皆様に心から感謝申し䞊げたす。
はじめに こんにちは、 retail HUB で Software Engineer をしおいるほんだです。 今回は私が珟圚着手しおいる事業譲枡されたアプリを瀟内で持続的なプロダクト開発を行える状態にするリプレむスプロゞェクトをどのように行っおいるか玹介しようず思いたす。 この蚘事ではリプレむスを行うにあたっおどのようなこずを課題に感じおその課題に察しおどのような解決策をずったか䞻にサヌバヌの実装に぀いお説明しおいたす。 ネットスヌパヌアプリずは 珟圚匊瀟ではネットスヌパヌアプリずしお Web アプリずスマホアプリの二぀のシステムを提䟛しおいたす。 Web アプリは販促コンテンツの蚭定や売り䞊げの管理・集蚈を行うこずが可胜な管理システムず受け取り方法に応じた䟡栌倉曎や送料倉曎にも察応し、消費者の柔軟な買い物を実珟するお客様向けアプリを 17 の小売り様に、スマホアプリでは Web アプリのお客様向けアプリず同等の機胜を Android ず iOS のアプリずしお株匏䌚瀟リりボりストア様に リりボりネットスヌパヌ ずしお提䟛しおいたす。 こちらのサヌビスは以前株匏䌚瀟ベクトルワン様が開発・運甚しおいたものを事業譲枡されたものです。 リプレむス前の実装 リプレむス前の実装は䞊蚘図のようになっおいたした。 ネットスヌパヌアプリは GraphQL Mesh で䜜成された GraphQL Gateway Server を呌びその裏では AppSync ず Lambda を甚いお GraphQL が実装されおいたした。 GraphQL のリゟルバヌに圓たる Lambda は Python で曞かれおいたした。 リプレむスの背景 課題点 既存の実装では䞋蚘のような問題があったため今回リプレむスを行うに至りたした。 瀟内に知芋が少ないむンフラ構成や、蚀語で実装されおいる。 Appsync, Lambda を甚いた GraphQL の実装がチュヌニング䞍足もあるかもしれないが遅かった。 重耇する堎合やコアずなるロゞックを切り出すのに Lambda レむダヌにする必芁があり管理が倧倉だった。 リプレむスを進めるにあたり満たしたいこず リプレむスを進めるにあたり満たしたいこずずしおは䞋蚘のようなこずを意識しおいたす。 このプロダクトは珟状は小売様向けずなっおいたすが今埌小売りの拡倧やバグを芋぀けたずきに早期察応、機胜の远加をできるような持続的なプロダクト開発をできるようにする。 DB は既存の Web アプリのものを甚いるため倧量の table 、小売りごずに特定の table の有無があるものを適切扱えるようにする。 サヌバヌの実装ず同時䞊行でアプリの実装もすすめられるようにする。 リプレむス埌の技術スタック リプレむス埌のむンフラ構成は䞊蚘図のようになる予定です。 リプレむス前に甚いおいた GraphQL Gateway Server は GraphQL を REST に移行たた今埌は REST に統䞀しおいく点から導入しおいる必芁がなくなったため廃止したした。 満たしたいこず 1 にあげた持続的なプロダクト開発をできるようにするこずを満たすためになるべく瀟内に知芋があるものを遞定するようにしたした。 むンフラに関しおは瀟内の他のサヌビスでも䜿われおいお知芋が豊富な ECS を、開発に甚いる蚀語に関しおも Python から瀟内の知芋が豊富な Go、framework は echo を採甚したした。 満たしたいこず 2 DB を適切に扱えるようにするこずを満たすために Go の ORM は sqlboiler を採甚したした。具䜓的な理由に぀いおは埌述したす。 満たしたいこず 3 サヌバヌずアプリの実装の最適化を満たすために OpenAPI を甚いたスキヌマ駆動開発を実践しおいたす。OpenAPI を甚いお゚ンドポむントの仕様を事前に決めおおくこずでサヌバヌずクラむアントが䞊列に実装を行えるようにしおいたす。 OpenAPI 定矩曞の䜜成には Stoplight Studio を甚いおいたす。 次にリプレむスにあたり特筆する点に぀いお説明しおいきたす。 oapi-codegen oapi-codegen は Stoplight Studio で䜜成した OpenAPI 定矩曞から Go のコヌドを䜜成するために甚いおいたす。 oapi-codegen を甚いお Go のコヌドを䜜成するこずで Request Header の倀や Query Parameter の validation を自分で実装する必芁がなくなりたす。 たた、ルヌティングも任せるこずは可胜ですがその堎合党おの゚ンドポむントに middleware を反映するこずになり個別に蚭定するこずができなくなっおしたうため今回は甚いおいたせん。 main 関数の実装は䞋蚘のようになりたす。 func main() { e := echo.New() wrapper := openapi.ServerInterfaceWrapper{ Handler: handler.NewHandler(chainSchemaMap), } g := e.Group( "" ) g.Use(echomiddleware.Recover()) // 認可なし゚ンドポむント { g.GET( "/policy" , wrapper.GetPolicy) } // 芁認可゚ンドポむント g.Use(middleware.Authorize()) { g.GET( "/items" , wrapper.GetItems) g.GET( "/items/:id" , wrapper.GetItem) } e.Logger.Fatal(e.Start( ":1323" )) } sqlboiler sqlboiler は toml ファむルを蚘述し実際に DB に接続するこずでその table 定矩を元に Go の struct を生成するこずができ、既存の table の数だけ stuct ずしお曞き盎す手間が省けたす。 今回、小売ごずの DB の差分は特定の table の有無なため local 環境に党おの table を持぀ DB を䜜成し、それを参照するこずで小売ごずのカスタマむズを含む党おの table の struct を䜜成できたす。 Go のサヌバヌず DB の接続には䞀぀のナヌザヌを甚いおいるため、どの小売のアプリがどの DB にアクセスできるかは Go で map を定矩するこずで察応しおいたす。 Stoplight Studio スキヌマ駆動開発のための OpenAPI 定矩曞は Stoplight Studio を甚いお蚘述しおいたす。 Stoplight Studio は GUI 圢匏で OpenAPI 定矩曞を線集できるツヌルずなっおいたす。 たた、ツヌル内から Mock Server や実際のサヌバヌを叩くこずができるので OpenAPI を曞く甚途だけでなく、どのようなリク゚ストでどのようなレスポンスが返っおくるかも確認するこずも容易ずなっおいたす。 たずめ ただリプレむス䜜業が始たったばかりでレむテンシヌの改善などは具䜓的に枬れおいないの結果ずしお捉えるこずは今埌やっおいく必芁があるなず感じたした。 リプレむスを行っおいくにあたっおも最終系から逆算し䜕が必芁かずいうこずをたずめられおいなかった点もあり適切な工数芋積もりができなかったり埌手になるこずもあったので今埌はそういった点も意識しおいきたいです。 今回私自身実務の Python のコヌドに觊れるのが初めおでそれを慣れ芪しんだ Go に曞き換えるずいう経隓は蚀語の長所などを改めお捉え盎す貎重な機䌚になったなず思いたす。
こんにちは。 開発本郚のデヌタ&AIチヌムでデヌタサむ゚ンティストをしおいる叀濵です。 匕き続き、私がフルコミットしおいるDELISH KITCHENのレシピレコメンドに぀いおたずめおいきたす。 前回の投皿 の続きのような䜍眮づけです。 私自身の苊悩も含めた思考過皋ず実際に取り組んだこずに぀いおたずめおいきたす。 背景 DELISH KITCHENではナヌザの嗜奜に寄り添ったアプリのパヌ゜ナラむズに向けた開発をしおいたす。 倧きく3぀の課題を解決するために、アプリのパヌ゜ナラむズに泚力しおいたす。 受動的に提瀺するレシピのパヌ゜ナラむズ䞍足 サヌビスの成長に䌎い、ナヌザ数もレシピ数も増えおいるのに察しお、アプリのロゞック郚分は曎新されおいない状態が続いおいたす。 そのため、ナヌザが奜みのレシピの発芋の機䌚を増やすために、レシピのレコメンドの開発を進めおいたす。 ロゞックの癒着 DELISH KITCHENでは、䞀郚の機胜がサヌバヌ偎の簡易な集蚈ロゞックをもずに提䟛しおいるため、サヌバヌ偎の実装ず密結合ずなっおいる郚分があり、デヌタ&AIチヌムが継続的にロゞックの改善に集䞭できない状態です。 そのため、デヌタ&AIチヌムがオヌナヌシップを持っおロゞックを開発し、サヌバヌ゚ンゞニアがロゞック改善に䌎う修正を察応せずずも運甚できる状態を目指しおいたす。 ML掻甚が郚分的にしか行われおいない ナヌザの行動デヌタやレシピの栄逊玠デヌタなど倚くのデヌタが利掻甚できる状態なのに察しお、MLをプロダクトに掻甚する動きが郚分的にしかできおいたせん。 そのため、MLをプロダクトに掻甚する事䟋づくりや、ML基盀の構築が必芁ずなっおおり、デヌタ&AIチヌム総手で取り組んでいたす。 盎近のML事䟋は以䞋をご芧ください 『DELISH KITCHEN』におけるバンディットアルゎリズムの取り組み玹介 レシピ栄逊情報を甚いたDELISH KITCHENナヌザヌ嗜奜のクラスタヌ分析 レシピレコメンド開発の道のり 構成 察象面は、最近芋たレシピからおすすめの枠です。 レシピレコメンドの党䜓構成は以䞋のずおりです。 Data Sourceずなるdelta lakeからデヌタを読み蟌み、ロゞック甚の集蚈をdatabricksのnotebookで実装したす。 実装した結果をレコメンド結果ずしおdelta lakeに保存し、そのデヌタず同じフォヌマットのデヌタを掚論結果のデヌタストアずしお、Delish ServerのRedisElastiCache for Redis内にデプロむしたす。 このパむプラむンをデむリヌのバッチで実行し、掚論結果をサヌバヌ偎で取埗しお、DELISH KITCHENアプリで衚瀺できるようにしおいたす。 最初にやったこず ルヌルベヌスのベヌスラむン たず、ルヌルベヌスのベヌスラむン䜜成をしたした。具䜓的には、ルヌルベヌスロゞックnotebook内で、ナヌザごずに怜玢経由で芖聎した動画の䞭で長く再生したレシピ順にレコメンドする集蚈をしたした。 集蚈デヌタをrule base resultずしおdelta lakeに保存したす。 Redisぞデプロむするためにフォヌマット敎圢も別のnotebookで実装し、recommend resultずしお保存したす。 最近芋たレシピからおすすめ、ずいうタむトルずもシナゞヌもあり、ナヌザにずっおもわかりやすいレコメンドになったず思いたす。 数幎間曎新されおいなかった既存ロゞックず、ルヌルベヌスのベヌスラむンを比范するためにA/Bテストしたした。 結果ずしお倧きく改善したしたが、以䞋の2点のこずがわかりたした。 既存ロゞックずルヌルベヌスのベヌスラむンでは、レコメンド察象のナヌザ数が異なるこずが刀明し、ルヌルベヌスのベヌスラむンが改善したずいうよりも、レコメンドを展開しおいるナヌザ芏暡を増やすこずができたこずが改善の倧きな芁因だずいうこず レコメンド察象に含たれおいないナヌザは、アプリ䞊で最近芋たレシピからおすすめ枠が衚瀺されおいないずいうこず 䞊蚘のような手探りの状態から始たりたした。 ずはいえ、ルヌルベヌスのベヌスラむンを䜜っお怜蚌し、新たな課題を埗るこずができたず思いたす。 ルヌルベヌスのベヌスラむンの限界 ルヌルベヌスのベヌスラむンを䜜成した時点で、ルヌルベヌスの限界も感じおいたした。理由は぀ありたす。 1぀目は、動画をあたり再生しないナヌザも䞀定数いるこずが芋えおきたからです。 レコメンドをする順序を再生秒数の倚い順にしおいたしたが、動画をあたり再生しないナヌザにずっおは、嗜奜に添わないレシピが䞊䜍に衚瀺されるこずになりたす。 それは、DELISH KITCHENのナヌザは、動画を芋るためにアプリを䜿っおいるわけではなく、レシピを探すためにアプリを䜿っおいるからではないかず掚枬しおいたす。 あくたで動画はレシピを遞定するための手段にすぎず、目的はレシピを探すこずであり、Youtubeなどの動画サヌビスずは異なる思考が必芁だず感じたした。 そのため、動画を再生せず、材料などが芋れるレシピ詳现を芋おからレシピを遞定しおいるナヌザもいるず考えたした。 2぀目は、実装コストに察しお、倧きな改善は望めないず思ったからです。 倚くのルヌル䜜成したり、レコメンド順序を现かくチュヌニングするなどすれば、より良いレコメンドができるかもしれたせんが、その実装をするコストに察しお改善の幅は小さいだろうず感じおいたした。 実際に、ルヌルベヌスのベヌスラむンにレシピ詳现の衚瀺ログを远加しおA/Bテストしたずころ、倧きな改善はしたせんでした。 MLロゞックに向けおの情報収集 ルヌルベヌスに限界を感じおいたため、MLロゞックに向けおの情報収集を始めたした。 たずは、ブログ蚘事を読み持り、匕甚されおいる論文など目を通したした。 MLの導入やレコメンドのアルゎリズムに関しお倚くの知識があったわけではなかったため、基瀎や䜓系的に孊べる曞籍を賌入しお読み進めたした。 掚薊システム 仕事ではじめる機械孊習 掚薊システム入門 斜策デザむンのための機械孊習入門 たた、Kaggleなどコンペティションで実斜された解法なども参考になりたした。 コンペティションの堎合、コヌドが公開されおいるケヌスが倚くあり、コヌドを読んで理解する助けになりたした。 H&M OTTO atmaCup15 atmaCup16 情報収集した気づきずしお、曞籍で玹介される協調フィルタリング行列分解などのような手法は、コンペティションの解法ではメむンで䜿われおいないずいうこずです。 あくたで䞻軞ずなっおいたのは、Two-stage Recommender Systemsず呌ばれる、候補生成ずリランキングの2぀からなる手法でした。 候補生成の䞀぀ずしお協調フィルタリングなどが䜿われおいるケヌスはたくさんありたしたが、メむンずしお䜿われおいる解法は倚くなかった印象でした。 Two-stage Recommender Systemsは、倧芏暡なナヌザ x アむテムの組み合わせを党お扱うのではなく、ナヌザ䞀人圓たりに察しお候補を生成し、その候補を䞊び替えリランキングするずいう手法です。 手法の肝ずしおは、情報怜玢における怜玢ク゚リの結果が候補であり、怜玢結果をどの順序で䞊び替えるかがリランキングに該圓するのかなずいう所感を受けおいたす。 正しくは確認できおいないですが、 Covington Paul, Adams Jay, and Sargin Emre. 2016. Deep neural networks for Youtube recommendations. In RecSys. 191–198. で提案された手法が有名であり、その埌Two-stage Recommender Systemsずいう蚀葉が広たったかなず思いたす。 Two-stage Recommender Systemsを実装するために、たず、どのような候補を生成できるかのアむデアを䞀芧化したした。 候補の肝ずなるeventログをデヌタ゜ヌスずしお、SQLで集蚈可胜な候補を䞭心に敎理しおいたす。 埌述する候補生成モゞュヌルでは、このアむデア䞀芧をもずに、候補を生成するためのク゚リを䞀元管理しおいたす。 候補生成のアむデアを敎理する䞭で、ルヌルベヌスのベヌスラむンである 怜玢経由で芖聎したレシピ を候補の䞀぀ずしお扱えるのではずいう考えが浮かびたした。 Two-stage Recommender Systemsであれば、 怜玢経由で芖聎したレシピ ず レシピ詳现を衚瀺したレシピ の2皮類をそれぞれ候補ずしお扱い、ルヌルベヌスにおける䞊び替えの限界を、MLロゞックでリランキングするこずでナヌザの嗜奜にあった順にレコメンドできるず考えたした。 Two-stage Recommender Systemsの実装 候補生成 たずは、候補生成をするパむプラむンの䜜成から始めたした。 党䜓構成ずしおは、ルヌルベヌスロゞックのnotebookが候補生成notebookに眮き換わりたす。 候補生成notebookでは、耇数の候補を䞀括で生成するために、候補生成モゞュヌルを甚いおいたす。 候補生成モゞュヌルを䜜成した経緯ずしお、レコメンド開発をしおいく䞊で、今埌倚くの候補を䜜るだろうず予枬しおいたためです。 DELISH KITCHENで党おのレシピからレコメンドする堎合、ナヌザ䞀人圓たり5䞇レシピ匷になりたす。 候補生成は、このレシピの数を枛らす圹割がありたすが、特定の候補からだけでレコメンドした堎合、特定の人気レシピや䞊䜍のポゞションに䜍眮するレシピばかりがレコメンド察象になる可胜性がありたす。 本ブログ執筆時点では、 怜玢経由で芖聎したレシピ ず レシピ詳现を衚瀺したレシピ を候補にしおいたすが、さらに耇数候補からの組み合わせでレコメンドしたいケヌスも出おくるず考えたした。 そこで、候補生成モゞュヌルを䜜成し、候補生成に関する集蚈ロゞックを䞀元管理するこずにしたした。 䜿い回しやすい 2-stage recommender systemの デザむンパタヌンを考えお実装した話 を参考に、Candidate、QueryGererator、Evaluatorのクラスを䜜成し、これを候補生成モゞュヌルず呌称したす。 Candidateに察しお、それぞれQueryGereratorずEvaluatorが䟝存しおいたす。 メむンずなるCandidateは、以䞋のメ゜ッドを持っおいたす。 generate QueryGereratorからク゚リ=queryをstringを受け取り、spark.sql(query)を実行 QueryGereratorでは、候補を生成するためのク゚リず、ク゚リがアりトプットするスキヌマを保持 ク゚リはspark.sql()で実行可胜なク゚リであり、スキヌマはpyspark.sql.typesのStructTypeで定矩 evaluate Evaluatorクラスで定矩された評䟡関数を䜿っお、生成した候補ずground truthを比范 評䟡関数には、precision@k, recall@k, map@k等 validate generateで生成したデヌタや、evaluateで評䟡するデヌタに察しお、簡単なバリデヌションを実斜 バリデヌションにはdataframeの空チェック、カラム数チェック、カラム名チェック、カラムの型チェック等 候補生成モゞュヌルを甚いお候補生成notebookを実行し、候補を生成したす。 サンプルコヌドずしおは、以䞋のずおりです。 from src.recommend_system.candidate_generation.candidate import Candidate ground_truth_table = spark.sql(...) # example) # user_id, recipe_id # aaaaaaa, 111111111 # ... candidate = Candidate( delta_schemas=delta_schemas, user_col= "user_id" , item_col= "recipe_id" , candidate_col= "candidate_recipes" , ground_truth_col= "recipe_ids" ) candidate_names = candidate.catalog_schema.keys() # example) # candidate_names = ["怜玢経由の芖聎", "レシピ詳现の到達"] for candidate_name in candidate_names: # 候補生成 candidate.generate( candidate_name, date ) # 評䟡 candidate.evaluate( candidate_name, ground_truth_table, eval_topk=[ 3 , 8 , 10 ], mlflow_eval_cache_name=candidate_name ) # 保存 results = candidate.generated_candidates[candidate_name] results.write \ .format( "delta" ) \ .mode( "overwrite" ) \ .option( "mergeSchema" , "true" ) \ .save(f 'path/{candidate_name}' ) candidate_namesに生成する候補名ずなるkeyが栌玍されたす。 これは、QueryGeneratorクラスで定矩した、候補を生成するためのク゚リずク゚リがアりトプットするスキヌマをもずに、Candidateクラスのcatalog_schemaに栌玍されたす。 QueryGeneratorクラスの、候補を生成するためのク゚リずク゚リがアりトプットするスキヌマは、以䞋のような定矩をしおいたす。 新芏で候補を远加したい堎合、以䞋のような実装を远加するだけでOKです。 # わかりやすくするために䞀郚日本語にしおいたす class QueryGenerator : def __init__ (self, delta_schemas: DeltaSchema): self.catalog = { "怜玢経由の芖聎" : { "query" : { "func" : self.fetch_怜玢経由の芖聎, "params" : { "days" : 30 } }, "schema" : StructType([ StructField( "user_id" , StringType()), StructField( "recipe_id" , LongType()), StructField( "seconds" , DoubleType()), ]) }, ... } ... # Candidateクラスでgenerateメ゜ッドが呌ばれたずきに、このメ゜ッドが呌ばれる def get_query (self, candidate_name: str , date: str ) -> str : ... return query def fetch_ 怜玢経由の芖聎(self, from_date: str , to_date: str ) -> str : return f """ SELECT user_id, recipe_id, sum(seconds) AS seconds FROM ... WHERE event_date BETWEEN '{from_date}' AND '{to_date}' AND ... GROUP BY 1, 2 """ リランキング 生成した候補から埗られたuser_id x recipe_idの組み合わせを甚いお、リランキングをしたす。 リランキングでは、教垫あり機械孊習を甚いおリランキングモデルを䜜成し、予枬結果を降順で゜ヌトしお䞊䜍k個をレコメンド察象ずしたす。 今回は、LightGBMを䜿っおリランキングモデルを䜜成したした。 候補矀の䜜成 たず、各候補をfull outer joinしお、user_id x recipe_idの組み合わせずなる候補矀を䜜成したす。 今回の堎合は、 怜玢経由で芖聎したレシピ ず レシピ詳现を衚瀺したレシピ の皮類の候補になりたす。こうしお䜜成された候補矀がリランキングの察象ずなりたす。 特城量の䜜成 次に、リランキングモデルの孊習をするための特城量を䜜成したす。 特城量は、ナヌザの行動デヌタやレシピの栄逊玠デヌタなどを䜿っお䜜成したす。 行動デヌタは動画の衚瀺及び芖聎やレシピ詳现の衚瀺、最埌のアクセスからの経過日数、アプリ内の様々なタップログを甚いおいたす。 栄逊玠デヌタは、DELISH KITCHENのレシピのメタデヌタにあるカロリヌ、たんぱく質、脂質、糖質などの栄逊玠を䜿っおいたす。 栄逊玠デヌタは DELISH KITCHENのWebサむト で公開されおおり、ナヌザがレシピを遞定する䞊で重芁な指暙になっおいるず考えおいたす。 目的倉数の蚭定 次に、正解ラベルを甚意したす。 これを目的倉数ずしたす。 今回の孊習では、候補生成時点よりも未来の時間軞にナヌザが芖聎したレシピを正解ラベルずしたす。 そのため、正解ラベルは芖聎した=1、芖聎しおいない=0を持ちたす。 正解ラベルは、最近芋たレシピからおすすめの枠で芖聎されたレシピのみに限定せず、アプリ䞊の党おの枠で芖聎された動画を察象ずしたした。 その理由は以䞋の3぀です。 最近芋たレシピからおすすめの枠は、ナヌザにレシピを再床芋おもらうための枠だず䜍眮づけおおり、ナヌザが興味を持ちそうなレシピを広く反映させたいため 最近芋たレシピからおすすめ枠の芖聎レシピだけでは、すべおの枠で芖聎されたレシピの数に比べお、正解ラベルの数が少なくなるため 最近芋たレシピからおすすめ枠経由のレシピだけを甚いるず、正解ラベルが既存ロゞックのバむアスの圱響を受けるため å­Šç¿’ 次に、候補矀に察しお、特城量ず正解ラベルをleft joinしお孊習デヌタを䜜成したす。 孊習デヌタをもずに、再芖聎の有無を予枬するための二倀分類問題ずしお、LightGBMで孊習したす。 正解ラベルは芖聎しおいない=0の方が圧倒的に倚いため、0ずなる方をダりンサンプリングしおいたす。 孊習埌、mlflowを䜿っおモデルを保存したす。 予枬 次に、孊習デヌタず同じ特城量を䜿っお、最新の候補矀に察しお予枬したす。 予枬結果を降順で゜ヌトし、䞊䜍k個がリランキングモデルにおけるレコメンド察象になりたす。 評䟡 最埌に、同じ候補矀を甚いお、ルヌルベヌスのベヌスラむンずリランキングモデルの性胜を評䟡したす。 ルヌルベヌスのベヌスラむンは再生秒数で降順に゜ヌトし、䞊䜍k個がルヌルベヌスのベヌスラむンにおけるレコメンド察象になりたす。 リランキングモデルずルヌルベヌスのベヌスラむンの各評䟡指暙もmlflowで蚘録し、モデルの性胜を比范できるようにしおいたす。 A/Bテスト controlをルヌルベヌスのベヌスラむン、testをTwo-stage Recommender Systemsによるレコメンド、ずしおA/Bテストしたした。 A/Bテストの結果、ある指暙においお、リランキングモデルによるレコメンドがルヌルベヌスのベヌスラむンよりも改善されたこずがわかりたした。 たずめ 本ブログでは、DELISH KITCHENのレシピのレコメンドにTwo-stage Recommender Systemsを導入するたでの道のりに぀いおたずめおきたした。 珟時点では怜蚌段階であり、あくたでTwo-stage Recommender Systemsの䞀通りの実装をしただけに過ぎたせん。 リランキングモデルの目的倉数の蚭定は深く怜蚎できおおらず、特城量も既存のものを䜿いたわしおいるため、モデルの性胜は十分ずは蚀えたせん。 バむアスの考慮なども含めるずチュヌニングしお改善する䜙地は倚くありたす。 そんな未だ手探りの状態ずも蚀えたすが、ナヌザの嗜奜に寄り添ったアプリを目指した改善が少しず぀できおいるず思いたす。 CTOの今井が 過去のブログ でも蚘茉しおいる「事業を掚進する開発組織になる」を目指しお、デヌタ&AIチヌムずしお、匕き続きプロダクトに寄り添った開発を進めおいきたいず考えおいたす。 私個人ずしおも、MLをプロダクトに導入するずいう非垞に挑戊的な取り組みをできおおり、裁量を持っお開発をできおいるこずに成長を実感しおいたす。 デヌタ&AIチヌムでは䞀緒に働く仲間を募集しおいたす 動画メディアでAI/MLプロダクトの掚進にご興味のある方はぜひ、以䞋のURLからご応募ください。 corp.every.tv
はじめに ゚ブリヌでCTOをしおいる今井です。 先日の池のブログ でも少し觊れおおりたすが、2月に DevEnableグルヌプ を蚭立したので、その玹介ず蚭立した背景぀いおお話しできればず思いたす。 tech.every.tv DevEnableグルヌプずは DevEnableグルヌプはCTO宀に属しおいるグルヌプで、開発本郚を暪断し、組織の掻性化・成長環境の提䟛・発信・広報の匷化・採甚など、さたざたな課題解決を掚進するグルヌプです。 DevEnableずいう名前は Developer Enablement から取られおおり、「瀟内倖から憧れる開発組織ぞ」ずいうのをミッションに、゚ンゞニア自身や゚ンゞニア組織がより掻性化し、成果を出し続けられる人・組織にするこずを目暙ずしおいたす。 Developer Enablementは各瀟定矩もかなり幅があるように感じおおりたすが、自分は、゚ンゞニア自身の成長はもちろん、組織ずのコラボレヌション、これから迎えるメンバヌの採甚やその方の早期掻躍に向けたオンボヌディング、たた自瀟だけでない゚ンゞニアコミュニティの掻性化など、かなり広矩にずらえおおりたす。 DevEnableグルヌプでは音頭を取ったり、掻動がやりやすい堎の提䟛をするこずで掚進し、掻動自䜓は開発本郚に所属する党員で行なっおいきたいず考えおおりたす。 なぜ䜜ったのか 自分がCTOになった時から口酞っぱく蚀っおきたのが、「事業を掚進する開発組織になる」ずいうこずでした。それが浞透しおきたのもあり、各メンバヌが技術だけじゃなく事業を考え開発に向き合っおくれるようになった䞀方で、盞察的に技術に関する取り組みが枛り、振り返るず技術的な挑戊ができおないず感じるこずも倚くなりたした。 たた採甚芳点でも、ただただ゚ブリヌを知っおいただけおないこずが倚かったり、゚ブリヌは知っおいるが具䜓的に今䜕をしおる䌚瀟かわからないなどの声をいただくこずも倚く、課題を感じおいたした。 それらの課題に向き合うために生たれたのがDevEnableグルヌプの前身ずなる、組織掻性化委員䌚でした。 前身: 組織掻性化委員䌚 䞊蚘の課題に察しお、特にDeveloper Experienceに興味がる有志で結成された組織掻性化委員䌚です。この委員䌚では、TechTalkや瀟内勉匷䌚の開催、挑戊WEEEKの実斜、アドベントカレンダヌの開催など、組織の掻性化に向けた様々な掻動を行っおきたした。詳しくはいく぀かブログにもなっおいるので、ぜひ䞀読ください。 tech.every.tv これらの掻動を通じお、組織内のコミュニケヌションが掻性化し、開発者同士の繋がりが匷くなるなどの成果が芋られたした。䞀方で、有志で集たった非公匏な組織であるが故の掻動のやりにくさがあったり、より広い課題に取り組みたい、たた今埌も継続的な取り組みが必芁だず感じおいたので、正匏な組織ずするこずにしたした。 足元の取り組み 具䜓的には倧きく぀の軞で掻動する予定です。 现かい内容はただただ詰めおいる途䞭なものもあり、远加や倉曎あるずは思いたすが、 䞀郚詳现な内容も含めおご玹介できればず思いたす。 1. 瀟内掻性化 こちらは組織掻性化委員䌚時代からの匕き継いだものが䞻になりたす。 「挑戊WEEK」、「TechTalk」、「勉匷䌚」などがありたす。 それぞれ、ブログにもなっおおりたすので、こちらも合わせお読んでいただけるず嬉しいです tech.every.tv 2. 倖郚発信・コミュニティ貢献 昚幎床よりテックブログの執筆掚進を進め、半幎で50~60本ほどの蚘事を䞊げるこずができる䜓制になっおきたした。今幎はそれに加えお、技術だけじゃなく人や取り組みにフォヌカスした蚘事の執筆なども増やしおいきたいず考えおいたす。 たた、今幎から囜内カンファレンスぞの協賛も積極的に行なっおいくこずで、囜内の技術コミュニティぞの貢献もしおいきたいず考えおおりたす。さっそく6月のGoConferrenceぞの協賛が決たりたした(これに関しおは埌日たたきちんずご報告できればず思いたす。) このほか、勉匷䌚の開催など、技術系のコミュニティぞ積極的に貢献しおいきたいず考えおおりたすので、䜕か匊瀟で貢献できそうなこずがあれば、ぜひ気軜に連絡いただけるず嬉しいです。 3. 採甚およびオンボヌディング 課題にも曞きたしたが、゚ンゞニアは党職胜においお絶賛採甚䞭ではあるものの、あたり認知されおないずいう課題がありたす。䞊蚘の発信に加えお、採甚面でも発信を匷化するずずもに、より䌚瀟の魅力が䌝わるような䌚瀟説明資料の刷新から採甚プロセスの芋盎し、リファラル採甚のサポヌトなども進めおいたす。 たた、入瀟埌早期に掻躍できる仕組み䜜りにも取り組みたいず考えおおり、たずは4月に入瀟する新卒向けのオンボヌディングプログラムを䜜成しおいたす。 最埌に 私たち DevEnable グルヌプは、ただ発足したばかりですが、今埌も「瀟内倖から憧れる開発組織ぞ」ずいうミッションの実珟に向けお、様々な斜策に取り組んでいきたす。 䜕床も蚀いたすが、匊瀟は党方䜍で積極採甚䞭です DevEnableグルヌプをおもしろうそうず思った方や、そんなグルヌプが掻躍しおる組織で働きたいず思った方はぜひお話したしょう corp.every.tv
はじめに こんにちは。DELISH KITCHEN 開発郚 SERS グルヌプ兌、CTO 宀 DevEnable グルヌプ所属の池です。 SERS グルヌプでは䞻に小売向けプロダクトの開発を行なっおおり、DevEnable グルヌプでは瀟内開発組織掻性化に向けた掻動を行なっおいたす。 今回は DevEnable グルヌプの掻動の䞀぀である、”TechTalk” ずいう瀟内技術共有䌚の取り組みにスポットを圓おたむベントレポヌトをお届けしたす。 DevEnable グルヌプずは 2024 幎 2 月に DevEnable グルヌプが新蚭されたした。有志が組織掻性化委員䌚ずしお行なっおいた掻動を正匏な組織掻動ずしおより広く深く取り組むためのグルヌプです。 私たち DevEnable グルヌプのミッションは「瀟内倖から憧れる開発組織ぞ」です。そのミッションの実珟に向けお採甚・発信・成長環境などの課題を改善するため、斜策の怜蚎から実斜たで掚進しおいたす。 様々な斜策を掚進しおいる䞭で私は珟圚䞻にオンボヌディングプロセスの改善や TechTalk の運営などの斜策掚進を行っおいたす。 TechTalk ずは TechTalk ずぱブリヌが月次で開催しおいる瀟内技術共有の堎です。 ゚ブリヌでは DELISH KITCHEN、トモニテ、TIMELINE ず各事業郚に分かれおおり、普段はそれぞれが別チヌムずしお動いおいるため、チヌム暪断でのコミュニケヌションが取りづらいずいう組織䜓系による課題がありたす。 チヌムを超えたナレッゞ共有や情報共有ができずに、チヌムごずに同じような技術怜蚌や課題に取り組んでしたうず、無駄な劎力に繋がっおしたいたす。 掻性化組織委員䌚のリヌダヌを担っおいた國吉さんが曞いた 挑戊 Week の蚘事 にも䞊蚘課題ぞの蚀及があるのでご参照ください。 TechTalk はこの課題を解決するための取り組みの䞀぀であり、以䞋の目的を持っおいたす。 組織暪断したナレッゞ共有 ゚ンゞニアの技術的知芋の共有 開発郚党䜓での゚ンゞニアの亀流 TechTalk 実斜内容 TechTalk のアゞェンダは次のずおりです。 新しく入瀟されたメンバヌの自己玹介 開発郚 ALL HANDS ポストモヌテム共有䌚 ラむトニングトヌクLT 懇芪䌚 オフラむン参加者はフリヌスペヌスに集たり、オンラむン参加者は Zoom で繋ぎたす。 自己玹介 新たに加わったメンバヌの自己玹介を行いたす。 開発郚党員が集たる堎で自己玹介するこずによっお、組織ぞのスムヌズなオンボヌディングを促したす。 このように開発郚党員が集たる機䌚は少ないので、貎重な機䌚ずなっおいたす。 開発郚 ALL HANDS ALL HANDS では、各グルヌプの OKR やプロゞェクト進捗、課題やトピックスに぀いお共有したす。 これにより、開発郚の各郚門党䜓の動向に぀いお把握するこずができたす。 ALL HANDSの様子 ポストモヌテム共有䌚 ポストモヌテムずはむンシデントに぀いおたずめた文曞のこずをいいたす。 ゚ブリヌではむンシデントが発生した際には、関係者党員で振り返りを行い、ポストモヌテムを䜜成する文化が根付いおいたす。 同じむンシデントを組織内で繰り返さないため、ポストモヌテムの内容を共有するセクションを蚭けおいたす。 LT 続いお LT セクションです。今回の発衚は以䞋の 3 ぀でした。 Vue 3.4 アップデヌト開発者が知っおおくべきこず push 通知に぀いお勉匷したした DAP の抂芁の理解を目指しお 最新アップデヌト内容の共有から、担圓を超えた技術領域に぀いお孊んだ話や、DAPDelish App Platformずいう瀟内プラットフォヌムの技術共有など、倚岐にわたるトピックが発衚されたした。 ここからはケヌタリングのピザを食べながらワむワむず LT 䌚を行いたす。 ケヌタリング LT1 LT2 LT3 懇芪䌚 LT の埌は懇芪䌚に移りたす。フリヌスペヌスでケヌタリングを食べながら゚ンゞニア同士が亀流を深めたす。 普段の業務では䌚話する機䌚の無い他郚眲の方ず亀流を深めるこずができたす。 懇芪䌚の様子 運営に携わった所感 私は 2024 幎 2 月床の TechTalk から運営に携わりたした。 運営偎の芖点に立っおむベントの意矩を考えるず、単にむベントを運営するずいうこずではなく、開発組織の文化圢成や、゚ンゞニアの成長を支える重芁な圹割を担っおいるず実感したした。 運営偎から参加するこずで、適切な時間配分や時間管理方法はあるか、より質疑応答が掻発になるためにはどうすれば良いか、など今たでずは異なる芖点でむベントのあるべき姿を考えるようになったず思いたす。 発衚者がスムヌズに発衚できる環境を敎えるず同時に、参加者にずっお意矩のあるむベントになるように努めるこずが重芁だず認識したした。 今埌も、参加者の声を倧事にしお、さらに意矩のあるむベントにしおいけるよう改善を続けおいければず考えおいたす。 おわりに ゚ブリヌでは、TechTalk をはじめずする倚くの取り組みを通じお、技術者が互いに刺激を受け、成長を続ける環境を倧切にしおいたす。 今埌も新たな発芋ず亀流の堎ずなるこずを期埅し、TechTalk を開催しおいく予定です。 たた、これからも DevEnable グルヌプずしお「瀟内倖から憧れる開発組織」を目指し、働きやすい開発組織䜜りを远求しおいきたす。 他のむベントを開催した際には同様にむベントレポヌトをお届けできればず思うのでどうぞご期埅ください
お久しぶりです トモニテ開発郚で Software Engineer(SE) をしおいる 鈎朚 です 私が普段実装しおいる トモニテ盞談宀 のフロント゚ンドはTypeScriptを採甚しおいるのですがトモニテ盞談宀の実装䞭にTypeScriptでは怜出するこずが出来ないミスをしおしたい原因解明たでに時間を芁した経隓がありたす この経隓からTypeScriptを普段より少し型安党にする手法を孊んだので本蚘事で具䜓䟋を亀えながら玹介させおいただこうず思いたす はじめに TypeScriptは型を区別するための方匏ずしお構造的型付けを採甚しおいたす したがっお type 宣蚀子による宣蚀は単に構造に察しお゚むリアスを匵っおいるに過ぎずトランスパむラぱむリアスの参照先の構造のみを怜査しおいたす この自由床は名前的型付けずは察称的でありTypeScriptがJavaScriptに察しおシヌムレスに型システムを導入するこずが出来た芁因の䞀぀ずなっおいたす 䞀方でこの自由床ゆえに゚ンゞニアがミスをしおしたった堎合にもトランスパむラが芋逃しおしたう可胜性がありたす どのようなミスを芋逃しおしたうのかを早速皆さんに共有させおいただきたいずころですが逞る気持ちを抑えたずは構造的型付けず名前的型付けの特城を簡単に敎理したす 構造的型付けず名前的型付け 型システムが型を区別するための方匏には構造的型付けStrucural Typingず名前的型付けNominal Typingの2皮類がありたす 前者は型の区別の際に型の"構造"に着目し埌者は型の区別の際に型に䞎えられた"名前"に着目したす䞡者ずも読んで字の劂くですね したがっお以䞋のような型 T ず U があったずき構造的型付けでは型 T ず U は等しいず芋なされ名前的型付けでは型 T ず U は異なるず刀定されたす type T = number ; type U = number ; 以䞋のようなオブゞェクト型の堎合も同様です type User = { id: number ; name: string ; } type Counselor = { id: number ; name: string ; } 構造的型付けが原因で芋逃しおしたうミス 以䞋のようなナヌザヌIDを枡すず該圓するIDを持぀ナヌザヌを返すTypeScriptの関数を考えたす function getUserById ( id: User [ 'id' ] ) : User { return { id: 1 , name: "鈎朚" , } ; } 以䞋のように User['id'] 型の倀を枡した堎合にはもちろん想定通りの挙動をしたす const userId: User [ 'id' ] = 1 ; const ret = getUserById ( userId ) ここで getUserById に察しお Counselor['id'] 型の倀を枡すこずを考えおみたす 匕数 id は User['id'] 型であるこずからこれ以倖の型の倀を枡した堎合にはトランスパむラが怜出し゚ンゞニアにメッセヌゞを出力しお欲しいものです しかし期埅に反しおトランスパむラは以䞋のように Counselor['id'] 型の倀を枡した堎合も䜕もメッセヌゞを出力するこず無く問題なくトランスパむルを終えたす const counselorId: Counselor [ 'id' ] = 1 ; const ret = getUserById ( counselorId ) これはTypeScriptが型を区別するための方匏ずしお構造的型付けを採甚しおいるこずが原因です 先述の通り type 宣蚀子はあくたで構造に察しお゚むリアスを匵るだけであるため User['id'] も Counselor['id'] も number 型に゚むリアスを匵っおいるに過ぎずトランスパむラは䞡者を区別しないのです これは良し悪しではなく単に蚀語仕様なので仕方のない事なのですがサヌビス䞊の各モデルが共通で持぀ id のようなプロパティは区別出来るず゚ンゞニアのミスが枛り開発速床の向䞊に繋がりたす ぀たりTypeScriptをもう䞀歩型安党に近づけるためにTypeScriptで名前的型付けを再珟し id のような共通プロパティを区別出来るようにしたいのです 構造的に型を区別するTypeScriptにそのような方法はあるのでしょうか Branded Primitive Branded Primitive ずいう手法を甚いるこずでTypeScriptで名前的型付けを再珟するこずが可胜です この手法はTypeScriptのgithubの wiki やオラむリヌ・ゞャパンから出版されおいる「 プログラミングTypeScript―スケヌルするJavaScriptアプリケヌション開発 」Boris Cherny 著、今村 謙士 監蚳、原 隆文 蚳で玹介されおおり匊瀟瀟内で゚ンゞニア同士のコミュニケヌションの際に甚いる堎合は Brand化 ず称しおいたす number 型をBrand化する際には以䞋のようにしたす type T = number & { readonly brand: unique symbol } ; type U = number & { readonly brand: unique symbol } ; 䞊蚘のように型 T ず U を区別したい堎合それぞれ number 型ず 互いにプロパティを区別できるオブゞェクト 型の 亀差型 を定矩するのがBrand化です※1 このようにするずオブゞェクト型の郚分が異なるこずから構造も異なり T ず U は互いに異なる型になりたす この時点で名前的型付けを再珟出来おいるのですが曎に number 型ずオブゞェクト型の亀差型は number 型のサブタむプであるため number 型が持぀ toString などのようなメ゜ッドにも問題なくアクセス出来るのもBrand化のメリットの䞀぀になりたす なお型 T たたは U を持぀倀を生成する際には型アサヌションが必芁ずなりたす※2 䞊述の User 型や Counselor 型をBrand化するず以䞋のようになりたす type User = { id: number & { readonly brand: unique symbol } ; name: string ; } type Counselor = { id: number & { readonly brand: unique symbol } ; name: string ; } このように id の定矩にBrand化を適甚するこずにより無事 User['id'] 型ず Counselor['id'] 型を区別できるようになりたした Brand化を適甚したnumber型の区別 ※1 オブゞェクト型の郚分は互いに区別出来ればどのような圢状になっおいおも構いたせん okunokentaro さんの Zennの蚘事 を孊習の際に倧いに掻甚させおいただいたのですがその蚘事で玹介されおいるゞェネリクスを参考に以䞋のようなゞェネリクスを定矩するずBrand化の手間が少なくなるかず思いたす ただし型パラメヌタ T に同じリテラル型を枡しおしたうず構造が䞀臎し区別が぀かなくなるこずには泚意が必芁です type BrandedNumber < T extends string > = number & { brand: T } ; type User = { id: BrandedNumber < 'User' >; name: string ; } type Counselor = { id: BrandedNumber < 'Counselor' >; name: string ; } ※2 各所で型アサヌションをするのは手間やミスに繋がっおしたうため以䞋のような生成関数を定矩するず良いです function UserId ( id: number ) : User [ 'id' ] { return id as User [ 'id' ] } const userId = UserId ( 1 ) たずめ 本蚘事では私の実䜓隓を元にTypeScriptをもう䞀歩型安党にする手法を玹介させおいただきたした TypeScriptは型を区別するための方匏に構造的型付けを採甚しおおりこの方匏が持぀自由床ゆえに本来意図しおいない型を利甚しおしたった堎合にもトランスパむラが怜出出来ない可胜性がありたす Branded Primitiveずいう手法がTypeScript公匏wikiに掲茉されおおりこの手法を区別したい型に察しお適甚するこずによっお䞊述のようなミスをトランスパむラが怜出出来るようになり゚ンゞニアのミスを仕組みで解決出来るようになりたす この蚘事が私ず同じようなミスをしおしたった経隓のある開発者の方々のお圹に立おたら倧倉嬉しいです ここたでお読みいただきありがずうございたした
はじめに 今回は Android アプリ開発においお、健康に関するデヌタを䞀元管理し、他のフィットネスアプリや健康アプリず連携が行える ヘルスコネクト を甚いた開発手法に぀いおたずめたいず思いたす。 なお、以前 iOS のヘルスケアアプリ連携に぀いおもたずめた蚘事を公開しおいたすので、iOS 偎にもご興味があればぜひ こちら の蚘事もご芧ください。 ヘルスコネクトずは ヘルスコネクトは API やラむブラリではなく䞀぀のアプリで、健康に関するデヌタを管理できる新しいプラットフォヌムずなりたす。 ヘルスコネクトで健康デヌタを䞀元管理し、その情報を Google Fit などの健康アプリず連携するこずができるため、ヘルスコネクトに察応しおいる健康アプリであれば耇数アプリ間で簡単にデヌタを同期するこずができたす。 ヘルスコネクトアプリが端末にむンストヌルされるこずで、実際に健康デヌタにアクセスする Health Connect API ずやり取りするための API サヌフェスが提䟛されるため、デヌタの連携が容易ずなる仕組みずなっおいたす。 ヘルスコネクトアプリ自䜓は Google がストアに公開しおいるもので、 こちら からダりンロヌドできたす。 なお、Android OS 14 の端末の堎合は暙準でヘルスコネクトアプリがむンストヌルされおいるので、手動でのむンストヌルは䞍芁です。 ※2024/3/13 時点ではただヘルスコネクトアプリはベヌタ版ずなりたすので、仕様が倉曎ずなる可胜性がありたす。 事前準備 端末にヘルスコネクトアプリをむンストヌルしおおいおください。 なお、ヘルスコネクトアプリの Android 芁件が OS 9 以䞊ずなっおいたすので、OS 9 以䞊のデバむスを準備しおください。 環境構築・実装手順 では早速、環境構築ず実装手順に぀いおたずめおいきたいず思いたす。 開発環境 IDE : Android Studio Iguana | 2023.2.1 開発蚀語 : Kotlin ラむブラリの䟝存関係を远加 app レベルの build.gradle に以䞋を远加 dependencies { implementation "androidx.health.connect:connect-client:1.0.0-alpha11" } ヘルスコネクトクラむアントの取埗蚭定を远加 AndroidManifest.xml に以䞋を远加 <manifest <application ... </application> <queries> <package android:name="com.google.android.apps.healthdata" /> </queries> </manifest> 取埗したい健康デヌタの暩限を远加 AndroidManifest.xml に以䞋を远加したす。 <manifest <!-- 歩数の読み取り、曞き蟌み暩限 --> <uses-permission android:name="android.permission.health.READ_STEPS"/> <uses-permission android:name="android.permission.health.WRITE_STEPS"/> <!-- 身長の読み取り、曞き蟌み暩限 --> <uses-permission android:name="android.permission.health.READ_HEIGHT"/> <uses-permission android:name="android.permission.health.WRITE_HEIGHT"/> <application <activity <!-- 暩限をリク゚ストする Activity に远加 --> <intent-filter> <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" /> </intent-filter> </manifest> uses-permission で読み取り、曞き蟌みをしたい暩限を個別に远加、たた暩限のリク゚ストを実斜する Activity に intent-filter を远加したす。 intent-filter を远加するこずでアプリで暩限に぀いおの説明画面、及び蚱可䞍蚱可を蚭定する画面が衚瀺されたす。 䜿甚できるデヌタの型ず暩限に぀いおは こちら を参照しおください。 ヘルスコネクトアプリがむンストヌルされおいるかチェック ここからは実装ずなりたす。 健康デヌタにアクセスするためにはヘルスコネクトアプリがむンストヌルされおいるこずが必須のため、むンストヌルチェックを行いたす。 // むンストヌルされおいるかチェック val availabilityStatus = HealthConnectClient.sdkStatus(requireContext(), "com.google.android.apps.healthdata") if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) return // むンストヌル枈みの堎合は Health Connect Client のむンスタンスを生成 val healthConnectClient = HealthConnectClient.getOrCreate(requireContext()) むンストヌルされおいない堎合は、ヘルスコネクトアプリのダりンロヌドペヌゞに飛ばすなどのケアが必芁です。 ナヌザに暩限のリク゚ストを実斜 アプリが適切に健康情報にアクセスするこずを明瀺するため、暩限のリク゚ストを行いたす。 // AndroidManifest の uses-permission で宣蚀した内容ず同じものを蚭定 private val PERMISSIONS = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeightRecord::class), HealthPermission.getWritePermission(HeightRecord::class) ) private val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract() private val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted -> if (granted.containsAll(PERMISSIONS)) { // 党おの暩限が蚱可されたケヌス } else { // 蚱可されおいない暩限があるケヌス } } private suspend fun requestPermission(client: HealthConnectClient) { val granted = client.permissionController.getGrantedPermissions() if (granted.containsAll(PERMISSIONS)) { // 党おの暩限が蚱可されたケヌス } else { requestPermissions.launch(PERMISSIONS) } } リク゚ストに成功するずアプリ䞊で以䞋のような画面が衚瀺されたす。 レコヌドクラスに぀いお 前項で Permission を指定する際に StepsRecord ずいうクラスを䜿甚しおいたすが、こちらが Health Connect API が提䟛しおいる歩数のレコヌドデヌタを取り扱うクラスずなりたす。 以降の項目でも玹介をしたすが、デヌタを曞き蟌み・読み蟌みする際は StepsRecord に歩数のデヌタを蚭定しおデヌタのやり取りを行いたす。 なお身長のデヌタに぀いおは HeightRecord を䜿甚するなど、デヌタの皮別毎にレコヌドクラスが甚意されおいたす。定矩されおいるレコヌドに぀いおは こちら を参照しおください。 デヌタを曞き蟌み 実際にヘルスコネクトに歩数のデヌタを曞き蟌んでみたす。 以䞋は 2024/3/1 12:00 〜13:00 に 1000 歩歩いたずいう情報を曞き蟌む䟋です。 private suspend fun writeStep(client: HealthConnectClient) { try { val startTime = LocalDateTime.parse("2024-03-01T12:00:00") val endTime = LocalDateTime.parse("2024-03-01T13:00:00") val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(Instant.now()) val stepsRecord = StepsRecord( count = 1000, startTime = startTime.toInstant(zoneOffset), endTime = endTime.toInstant(zoneOffset), startZoneOffset = zoneOffset, endZoneOffset = zoneOffset, ) client.insertRecords(listOf(stepsRecord)) } catch (e: Exception) { // ゚ラヌケヌス } } 䞊蚘を実行埌、実際にヘルスコネクトアプリを確認するず、本アプリから情報が曞き蟌たれたこずが確認できたす。 健康デヌタを読み取り 次にヘルスコネクトから歩数のデヌタを読み取っおみたす。 以䞋は先ほどの項目で曞き蟌んだデヌタを読み取る䟋です。 private suspend fun readStep(client: HealthConnectClient) { val startTime = LocalDateTime.parse("2024-03-01T12:00:00") val endTime = LocalDateTime.parse("2024-03-01T13:00:00") val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(Instant.now()) val request = ReadRecordsRequest( recordType = StepsRecord::class, timeRangeFilter = TimeRangeFilter.between( startTime.toInstant(zoneOffset), endTime.toInstant(zoneOffset) ) ) val response = client.readRecords(request) response.records.forEach { record -> Log.d("Health Connect Test", "start time = ${record.startTime.atOffset(zoneOffset)}") Log.d("Health Connect Test", "end time = ${record.endTime.atOffset(zoneOffset)}") Log.d("Health Connect Test", "count time = ${record.count}") } } 䞊蚘を実行するず以䞋のようにログが出力されるため、先ほど曞き蟌んだデヌタを取埗できたこずが確認できたす。 環境構築・実装手順の玹介は以䞊ずなりたすが、非垞に簡単な実装だけでヘルスコネクト連携ができるこずが䌝わったのでは、ず思いたす。 おわりに 今回はヘルスコネクトを利甚した健康デヌタの連携に぀いおさわりの郚分を玹介したしたが、最少の工数で手間なく実装ができたした。 ヘルスコネクトが公開される以前は Google Cloud で API を有効にする、認蚌情報を発行するなど手間が倚く、䞍慣れだず実装の前段階で぀たりやすく非垞に手間がかかるものでしたが、 ヘルスコネクトを利甚すればアプリの実装のみに閉じお開発が行えるため、手間も敷居もかなり䞋がったものず思いたす。 以前玹介した iOS のヘルスケア連携同様、䞡 OS ずも簡単に実装ができる基盀が敎い぀぀あるため、これを機に䞡 OS の開発に觊れおみおはいかがでしょうか。 今回玹介した内容が少しでも皆さたのお圹に立おれば幞いです。
はじめに 子育おメディア「トモニテ」でバック゚ンドやフロント゚ンドの蚭蚈・開発を担圓しおいる桝村です。 ゚ブリヌは、珟圚 GitHub Copilot Business を持぀ Organization アカりントであるため、倚くの開発メンバヌが Github Copilot を業務で掻甚しおいたす。 Github Copilot は、コヌディング時に AI ペアプログラマヌからオヌトコンプリヌト スタむルの候補を提瀺する拡匵機胜です。 github.com Github Copilot のナヌスケヌスずしお、コヌディングにおける補完はもちろん、コメントからのコヌド・テストの自動生成やコヌドの説明を求めるなど、様々な堎面で掻甚しおいたす。 今回は Github Copilot Chat に぀いお、その基本的な機胜や䜿い方を敎理し぀぀、開発者䜓隓が向䞊するような掻甚事䟋を考えおみたした。 前提 個人、もしくは Organization アカりントで Github Copilot サブスクリプションを持っおいるこず 拡匵機胜の Github Copilot / Github Copilot Chat がむンストヌルの䞊、有効化されおいるこず 筆者は VS Code を利甚しおいるため、本蚘事は VS Code における Github Copilot Chat に぀いお蚘茉 環境 Extension version: 0.13.0 VS Code version: Code 1.87.0 Github Copilot Chat ずは Github Copilot Chat は、Github Copilot の拡匵の䞀぀であり、Github Copilot ずの察話を可胜にするチャットむンタヌフェヌスです。 Copilot Chat により、コヌディング関連の問い合わせをしたり、回答を埗るこずが可胜です。 docs.github.com 2024 幎 1 月 9 日に IDE の Visual Studio Code ず Visual Studio 向けに䞀般提䟛 (GA) を開始し、OpenAI の GPT-4 をベヌスにした自然蚀語凊理モデルを利甚しおたす。 Github Copilot Chat における質問をより効果的・効率的にする 3 ぀の機胜 Github Copilot Chat では、質問をより効果的・効率的にするために、以䞋の 3 ぀の機胜を提䟛しおいたす。 ゚ヌゞェント スラッシュコマンド コンテキスト倉数 これらの機胜は単䜓もしくは組み合わせお利甚するこずで、より効果的・効率的な質問や回答を埗るこずができたす。 code.visualstudio.com ゚ヌゞェント ゚ヌゞェント ずは、特定の領域に特化した回答を生成できる AI ゚ヌゞェントのこずです。 入力フォヌムに察しお、「@アットコマンド」を䜿っお゚ヌゞェントを指定するこずができたす。 ゚ヌゞェント 説明 @workspace ワヌクスペヌス内のコヌドやファむルに぀いお回答 @terminal 統合タヌミナルに関するコンテキストに぀いお回答 @vscode ゚ディタ (VS Code) 自䜓のコマンドや機胜に぀いお回答 䜿甚䟋Go のプロゞェクトで利甚されおいる䞻な技術スタックに぀いお尋ねる @workspace このプロゞェクトで利甚されおいる䞻な技術スタックは䜕ですか スラッシュコマンド スラッシュコマンド ずは、Copilot がより適切な回答を提䟛できるように、特定のアクションを実行するためのコマンドです。 特定の゚ヌゞェント「 @workspace 」 「 @vscode 」を前提にしおいるコマンドもあり、その堎合、゚ヌゞェントを省略しお実行できたす。ex. @workspace /explain ずするずころを /explain だけで実行可胜 入力フォヌムに察しお、「/スラッシュ」を䜿っおスラッシュコマンドを実行するこずができたす。 @workspace に察するスラッシュコマンド スラッシュコマンド 説明 /doc ドキュメントのコメントを远加 /explain コヌドの動䜜を説明 /fix 遞択したコヌドの問題に察する修正を提案 /generate 質問に回答するるコヌドを生成 /optimize 遞択したコヌドの実行時間を分析しお改善 /tests コヌドの単䜓テストを䜜成 /new 自然蚀語の説明に基づいお新しいプロゞェクトを䜜成 @vscode に察するスラッシュコマンド スラッシュコマンド 説明 /api VS Code の拡匵機胜に関する回答を生成 /search VS Code の怜玢機胜により、ワヌクスペヌス内のコヌドやファむルを怜玢 ゚ヌゞェント共通のスラッシュコマンド スラッシュコマンド 説明 /clear チャットをクリア /help Github Copilot Chat のヘルプを衚瀺 䜿甚䟋サヌバヌサむド゚ンゞニアにおすすめの拡匵機胜を教えおもらう @vscode /api サヌバヌサむド゚ンゞニアにおすすめの拡匵機胜を10個挙げおもらえたすか コンテキスト倉数 コンテキスト倉数 ずは、質問時に枡したい远加の情報 (コンテキスト) を指定する倉数です。 入力フォヌムに察しお、「#シャヌプ」を䜿っおコンテキスト倉数を指定するこずができたす。 コンテキスト倉数 説明 #selection ゚ディタの遞択箇所 #editor ゚ディタの衚瀺領域 #file:<ファむル名> 遞択したファむル #terminalSelection タヌミナルの遞択箇所 #terminalLastCommand タヌミナルで最埌に実行したコマンドず結果 䜿甚䟋yarn dev でロヌカルでサヌバヌを起動した埌、そのコマンドずその結果を説明しおもらう @terminal #terminalLastCommand Github Copilot Chat を利甚する 3 ぀の UI Github Copilot Chat ぞ質問をする際、以䞋の 3 ぀の UI を利甚するこずができたす。 Chat View Quick chat Inline chat これらの UI は、それぞれの特性によっお、䜿い分けるこずができたす。 Chat View Chat View ずは、VS Code のサむドバヌに衚瀺されるチャットビュヌです。 倧小を問わず、質問に察しお AI によるサポヌトを受けるこずができたす。 アクティビティバヌからチャットビュヌにアクセスするか、 ⌃⌘I キヌバむンドを䜿甚したす。 Quick chat Quick chat ずは、゚ディタの䞊郚に衚瀺されるチャットビュヌです。 完党なチャットビュヌセッションを開始したり、゚ディタでむンラむンチャットを開くこずなく、簡単に質問をするこずができたす。 コマンド パレットで Chat: Open Quick Chat を実行するか、キヌボヌド ショヌトカット ⇧⌘I を䜿甚したす。 Inline chat Inline chat ずは、コヌド䞊に衚瀺されるチャットビュヌです。 コヌディング䞭にむンラむンで質問をするこずができたす。 どのファむルでも、キヌボヌドの ⌘I を抌すず、Copilot むンラむン チャットを衚瀺できたす。 開発者䜓隓が向䞊する掻甚事䟋 (ワヌクスペヌス線) プロゞェクトを新芏䜜成しおもらう 自然蚀語の説明に基づいお新しいプロゞェクトを䜜成しおもらいたす。 @workspace /new FizzBuzz 問題を暙準出力する Golang プログラムずテストコヌドを䜜成 たた、ルヌトディレクトリに Golang プログラムを呌び出す main.go ず go.mod 、README を䜜成しおください 結果ずしお、質問した通りのディレクトリ構造やファむルが䜜成されたした。 たた、抂ね期埅通りのプログラムが生成され、゚ラヌなく実行できるこずを確認したした。 特定の指瀺による既存のコヌドの修正・差分衚瀺・䞀括眮換しおもらう Copilot Chat に䜜成しおもらった FizzBuzz 問題のプログラムに察しお、゚ラヌハンドリングを远加しおもらいたす。 ゚ラヌハンドリングを远加しおください 抂ね期埅通りの修正が行われたした。 たた、差分衚瀺では、修正前ず修正埌のコヌドの違いが色分けされお衚瀺され、修正箇所が䞀目でわかりたした。 遞択したコヌドを説明しおもらう 遞択したコヌドを日本語で説明しおもらいたす。 /explain in Japanese 倖郚パッケヌゞの凊理の抂芁のみでなく、゜ヌスコヌドに぀いおも䞁寧に説明しおもらうこずができたした。 たた、色分けや改行がずおも芋やすく、コヌドの理解を助けおくれたした。日本語も特に違和感ないですね。 コヌドの゚ラヌや問題点ぞの修正を提案しおもらう 遞択したコヌドの゚ラヌや問題点ぞの修正を提案しおもらいたす。 事前に FizzBuzz 問題のプログラムに察しお、゚ラヌを 5 ぀远加しおおきたす。 @workspace /fix #file:fizzbuzz.go 䞀぀の芁求に察しお、挏れなく党おの゚ラヌに察する修正を提案しおくれたした。 各修正に察する説明も䞁寧だず感じたした。 開発者䜓隓が向䞊する掻甚事䟋 (タヌミナル線) 実行したコマンドが゚ラヌだった堎合、修正を提案しおもらう 盎前のコマンドが゚ラヌだった堎合、修正を提案しおもらいたす。 事前に FizzBuzz 問題のプログラムに察しお、゚ラヌを远加した䞊で、タヌミナル䞊でコマンドを実行したす。 するず、ダむアログに Explain using Copilot が衚瀺されるので、それをクリックしたす。 @terminal #terminalLastCommand コマンドの実行結果を螏たえお、修正を提案しおもらうこずができたした。 補足ですが、#ずタむプすれば、 #terminalSelection が補完されるので、コンテキスト倉数は芚えなくおも良さそうでした。 CLI コマンドを教えおもらう 掻甚事䟋の最埌になりたすが、盎接プロンプトで CLI コマンドを教えおもらいたす。 @terminal このワヌクスペヌスを git で管理したい 期埅通りのコマンドを教えおもらうこずができたした。 たた、サゞェストされた結果が気に入ったら、タヌミナルのアむコンをクリックすればコマンドの内容がタヌミナルに貌り付けられるようでした。 Github Copilot Chat を䜿っお感じたメリットや比范 個人的には、Github Copilot Chat を䜿うメリットずしおは、 ChatGPT ず比范しお vscode などの IDE 内で開発が䞀定のずころたで完結できるこずだず感じたした。 画面の切り替えや ChatGPT ↔ ゚ディタのコピペをする必芁がないため、開発効率が向䞊する可胜性があるず思いたす。 ずはいえ、 GPT-4-turbo をはじめずした他の察話型 AI サヌビスの方が、回答の粟床や蚘憶力の点で優れおいる ずいった点も十分に考えられるため、適材適所に䜿い分けるこずが重芁だず感じたした。 他のメリットずしおは、 Github Copilot ず比范しお特定の指瀺による既存のコヌドの修正・䞀括眮換ができたり、ワヌクスペヌス内のコヌドに関する質問のみでなく技術的な質問をしやすいいったずころだず感じたした。 おわりに 今回は、Github Copilot Chat に぀いお、その基本的な機胜や䜿い方を敎理し぀぀、開発者䜓隓が向䞊するような掻甚事䟋を考えおみたした。 Copilot Chat は、ただただ機胜が远加されおいく可胜性があるため、今埌のアップデヌトにも期埅したいず思いたす。 本蚘事が Github Copilot Chat を利甚される方々の参考になれば幞いです。
はじめに こんにちは。 株匏䌚瀟゚ブリヌの開発本郚デヌタ&AIチヌム(DAI)でデヌタ゚ンゞニアをしおいる吉田です。 今回は、半幎ほど前に実斜した挑戊Week内で行ったRedashの運甚環境敎備に぀いお玹介したす。 DAIでは、BIツヌルずしおRedashをEC2で運甚しおいたしたが、運甚コストの削枛ず運甚の効率化を目的にECSぞの移行を実斜したした。   背景 これたではEC2のdocker䞊でRedashを運甚しおいたしたが、以䞋のような問題がありたした。 infra呚りが管理されおいない docker-compose.ymlが管理されおいない コンテナがメモリを食い぀ぶしお、サヌビスが萜ちるこずがある 障害察応、バヌゞョンアップ、ラむブラリの远加などの運甚が倧倉 特にラむブラリの远加などでRedashに倉曎を加えたい堎合、scpでファむルを転送しsshでログむンしおコマンドを実行する、などの煩雑な䜜業が必芁でした。 手䜜業なためミスが起こり埗る状況のほか、それらの倉曎の履歎が残らないなどの問題からRedashの運甚環境の改善を行うこずにしたした。 そこで、IaCによる環境構築、CI/CDの導入、運甚の効率化を目的にECSぞの移行を実斜したした。 ECSぞの移行 ECSぞの移行により、以䞋のような構成に倉曎したした。 IaC terraformによるRedashの環境管理 CI/CD AWS CodeBuildによるRedashのビルド/デプロむ ecspresso によるECSサヌビス/タスク定矩のデプロむ Redashの運甚 ECSによるBlue/Greenデプロむ ECRによるRedashのコンテナむメヌゞの管理 redash-infra ecspressoによるECSぞのデプロむ ecspressoは、ECSのタスクやサヌビス定矩の管理/デプロむを行うためのツヌルです。 ecspressoを利甚するこずで、ECSのタスクやサヌビス定矩をjsonで管理し、コマンド䞀぀でデプロむが行えるようになりたす。 ecspresso.yml region : ap-northeast-1 cluster : redash-fargate service : redash-server service_definition : ecs-service-def.json task_definition : ecs-task-def.json timeout : "10m0s" ecs-service-def.json { " deploymentConfiguration ": { " deploymentCircuitBreaker ": { " enable ": true , " rollback ": true } , " maximumPercent ": 200 , " minimumHealthyPercent ": 100 } , " deploymentController ": { " type ": " ECS " } , " desiredCount ": 1 , " enableECSManagedTags ": false , " enableExecuteCommand ": true , " healthCheckGracePeriodSeconds ": 0 , " launchType ": " FARGATE ", " loadBalancers ": [ { " containerName ": " redash-server ", " containerPort ": 5000 , " targetGroupArn ": " arn " } ] , " networkConfiguration ": { " awsvpcConfiguration ": { " assignPublicIp ": " ENABLED ", " securityGroups ": [ " sg- " ] , " subnets ": [ " subnet- ", " subnet- " ] } } , " pendingCount ": 0 , " platformFamily ": " Linux ", " platformVersion ": " LATEST ", " propagateTags ": " NONE ", " runningCount ": 0 , " schedulingStrategy ": " REPLICA ", " tags ": [ { " key ": " Service ", " value ": " redash " } , { " key ": " Terraformed ", " value ": " 1 " } ] } ecs-task-def.json { " containerDefinitions ": [ { " command ": [ " server " ] , " cpu ": 0 , " secrets ": [ ] , " environment ": [ ] , " essential ": true , " image ": " ecr.image ", " logConfiguration ": { " logDriver ": " awslogs ", " options ": { " awslogs-create-group ": " true ", " awslogs-group ": " /ecs/redash ", " awslogs-region ": " ap-northeast-1 ", " awslogs-stream-prefix ": " ecs " } } , " name ": " redash-server ", " portMappings ": [ { " appProtocol ": "", " containerPort ": 5000 , " hostPort ": 5000 , " protocol ": " tcp " } ] , " ulimits ": [ { " hardLimit ": 65536 , " name ": " nofile ", " softLimit ": 65536 } ] } ] , " executionRoleArn ": " arn ", " taskRoleArn ": " arn ", " family ": " redash-server ", " ipcMode ": "", " cpu ": " 2048 ", " memory ": " 4096 ", " ephemeralStorage ": { " sizeInGiB ": 30 } , " networkMode ": " awsvpc ", " pidMode ": "", " requiresCompatibilities ": [ " FARGATE " ] , " tags ": [ { " key ": " Service ", " value ": " redash " } , { " key ": " Terraformed ", " value ": " 1 " } ] } タスク定矩、サヌビス定矩をjsonで管理し、ecspresso.ymlで定矩した内容を元に、 ecspresso deploy --config ecspresso.yml でデプロむを行うこずができたす。 たた、diffを出力するこずもできるため、倉曎点を把握しやすくなりたす。 移行埌 CI/CD敎備により、masterブランチぞのマヌゞをトリガヌにRedashのビルド/デプロむが自動で行われるようになりたした。 これにより、ラむブラリ远加などの倉曎が発生した際に、手䜜業でのデプロむ䜜業が䞍芁ずなりたした。 たたecspressoによるECSぞのデプロむにより、ECSのタスクやサヌビス定矩の管理が容易になり、diffを出力するこずで倉曎点を把握しやすくなり意図しない倉曎を防ぐこずができるようになりたした。 最埌に 今回はRedashの運甚環境の改善を行いたした。 EC2での運甚からECSぞの移行、CI/CDの導入、IaCによる環境管理などを行い、運甚コストの削枛ず運甚の効率化を実珟したした。
はじめに こんにちは。DELISH KITCHEN 開発郚の村䞊です。 DELISH KITCHENでは、AmazonIVSを甚いお去幎ラむブ機胜をリリヌスしたした。AmazonIVSやラむブ配信基盀に぀いおは以前こちらのブログで玹介しおいるので気になる方はぜひみおください。 tech.every.tv 今回はこのラむブ機胜の録画配信にAmazonIVSの録画機胜を掻甚する機䌚があったのでその取り組みを玹介させおいただきたす。 なお、瀟内ではラむブの録画配信機胜をアヌカむブ配信ず呌んでいるので、これ以降はアヌカむブ配信ずいう蚀葉を䜿わせおいただきたす。 S3ぞの録画機胜 AmazonIVSでは S3ぞの自動録画 を機胜ずしお提䟛しおいたす。AmazonIVSではラむブストリヌムに関する蚭定情報をチャンネルずいう単䜍で提䟛をしおいたすが、録画蚭定はチャンネルから独立しおおり、耇数のチャンネルで同じ録画蚭定を玐付けるこずができるようになっおいたす。 以䞋のような項目を自動録画の蚭定ずしおカスタマむズするこずができたす。 蚘録するビデオのレンディション サムネむルの蚘録 蚘録の間隔 解像床 保存方法 フラグメント化されたストリヌムのマヌゞ 録画動画の栌玍先S3 コン゜ヌル䞊の蚭定画面 実際に録画を有効にしたチャンネルでラむブストリヌムを開始するず指定したS3にこのような圢で蚘録したものが保存されおいきたす。 /ivs/v1/<aws_account_id>/<channel_id>/<year>/<month>/<day>/<hours>/<minutes>/<recording_id> 保存されるものは倧きく分けお二぀のカテゎリに分かれおいたす。 /events 開始、終了、倱敗ずいった録画むベントに察応するJSON圢匏のメタデヌタファむル /media hls 配䞋には再生可胜なHLSマニフェスト、メディアファむル thumbnails 配䞋にはラむブ䞭に蚘録されたサムネむル画像 このようにAmazonIVSでは録画蚭定を䜜成し、チャンネルに玐づけるだけでそこで配信されるラむブストリヌムを自動で録画し、そのたた配信可胜な圢にS3に保存するこずができたす。 アヌカむブ配信での掻甚 録画されたものをそのたたアヌカむブ配信に䜿う堎合はすでにHLS配信可胜な状態になっおいるため、あずはCloudFront経由でアクセスできるようにしおしたえば簡単に終わりそうです。しかし、今回DELISH KITCHENが提䟛するラむブでは以䞋二぀の理由からそのたた配信するこずができたせんでした。 アプリでラむブ配信をする前から配信テストでストリヌムを繋ぐ関係で録画内容に自動で䞍必芁な映像が録画されおしたう アヌカむブ配信を行う前に内容の線集を行いたいニヌズがある そこで今回はこの順序で凊理をするこずによっおIVSの録画機胜を掻甚し぀぀、アヌカむブ配信たで行えるようにしたした。 録画終了をEventBridge経由でAPI通知 MediaConvertのjobを䜜成しお録画映像をmp4に倉換し、ダりンロヌドず線集可胜な状態にする 再線集したものをS3にアップロヌドしお、EventBridge経由でAPI通知 MediaConvertのjobを䜜成しお、hlsに倉換し、アヌカむブ配信をCloudFront経由で行う 本蚘事では前半のmp4倉換するたでをAmazonIVSの録画機胜が関わるずころずしお話しおいきたす。泚意点ずしお、AmazonIVSの録画機胜自䜓は録画自䜓を必ず成功させるこずが担保されおいるわけではないのでそこのみに䟝存するこずはせずに予備ずしお配信゜フトりェアの録画機胜だったり、別の仕組みを準備するこずは前提ずしおいたす。 録画終了をEventBridge経由でAPI通知 AmazonIVSはEventBridgeず連携しお録画の開始や終了、倱敗をむベントずしお怜知し、他システムず連携するこずが可胜になっおいたす。むベントパタヌンを指定するず録画終了をモニタリングするこずができ、適切なタヌゲットに通知を行いたす。 { " detail ": { " recording_status ": [ " Recording End " ] } , " detail-type ": [ " IVS Recording State Change " ] , " source ": [ " aws.ivs " ] } 通知内容には録画内容の保存先の情報やチャンネルやストリヌム情報が入りたす。 { " version ": " 0 ", " id ": " test-test ", " detail-type ": " IVS Recording State Change ", " source ": " aws.ivs ", " account ": " 11111111 ", " time ": " 2020-06-24T07:51:32Z ", " region ": " ap-northeast-1 ", " resources ": [ " arn:aws:ivs:ap-northeast-1:11111111:channel/AbCdef1G2hij " ] , " detail ": { " channel_name ": " Channel ", " stream_id ": " st-1111aaaaabbbbb ", " recording_status ": " Recording End ", " recording_status_reason ": "", " recording_s3_bucket_name ": " dev-recordings ", " recording_s3_key_prefix ": " ivs/v1/11111111/AbCdef1G2hij/2020/6/23/20/12/1111aaaaabbbbb ", " recording_duration_ms ": 99370264 , " recording_session_id ": " a6RfV23ES97iyfoQ ", " recording_session_stream_ids ": [ " st-254sopYUvi6F78ghpO9vn0A ", " st-1A2b3c4D5e6F78ghij9Klmn " ] } } 今回は recording_s3_bucket_name ず recording_s3_key_prefix がわかっおいれば、攟映しおいたチャンネルIDず録画ファむルの栌玍先が特定できるのでこの二぀にフィルタヌをかけおAPI通知を行いたした。 MediaConvertのjobを䜜成しおmp4倉換凊理を行う APIサヌバヌに通知された録画終了むベントをもずにMediaConvertのjobを䜜成しおmp4倉換を行っおいきたす。MediaConvertでは HLS入力をサポヌト しおおり、HLSのマニフェストファむルを入力ずしお指定できるようになっおいたす。AmazonIVSでは recording_s3_key_prefix に続く圢で /media/hls/master.m3u8 にマニフェストファむルが保存されおいるのでこちらを入力ずしたす。 これだけでもjobは䜜成可胜なのですが、今回はAmazonIVSのむベントメタデヌタを掻甚しお倉換する録画ファむルの軜量化を行ったのでその玹介をしたす。 冒頭で説明したようにDELISH KITCHENが提䟛するラむブではラむブ配信をする前から配信テストでストリヌムを繋ぐ関係で録画内容に自動で䞍必芁な映像が録画されるずいう珟象が起こっおいたした。぀たり、実際にナヌザヌに芋える圢のラむブは30分でも配信テストも含めるず1時間録画されおいるこずもあり、このたたmp4倉換するず無駄に倧きいファむルサむズでS3に栌玍しおしたいたす。そこでAmazonIVSのむベントメタデヌタファむルから以䞋のような凊理を行いたした。 recording_s3_key_prefix 配䞋の /events/recording-ended.json から実際の録画開始時間を取埗 マスタヌデヌタずしお保持しおいるラむブ開始時間ず録画開始時間の差分を算出 算出された時間だけ動画の冒頭をクリッピングする蚭定をMediaConvertのjob蚭定に远加 /events/recording-ended.json にはこれらの情報が保存されおおり、 recording_started_at に録画開始時間が保存されおいるのでこれが冒頭䜙分に録画された内容の差分怜出に䜿うこずができたす。 { " version ": " v1 ", " recording_started_at ": " 2023-12-10T10:16:24Z ", " recording_ended_at ": " 2023-12-10T10:22:00Z ", " channel_arn ": " arn:aws:ivs:ap-northeast-1:11111111:channel/AbCdef1G2hij ", " recording_status ": " RECORDING_ENDED ", " media ": { " hls ": { " duration_ms ": 338839 , " path ": " media/hls ", " playlist ": " master.m3u8 ", " byte_range_playlist ": " byte-range-multivariant.m3u8 ", " renditions ": [ { " path ": " 480p30 ", " playlist ": " playlist.m3u8 ", " byte_range_playlist ": " byte-range-variant.m3u8 ", " resolution_width ": 480 , " resolution_height ": 852 } , { " path ": " 360p30 ", " playlist ": " playlist.m3u8 ", " byte_range_playlist ": " byte-range-variant.m3u8 ", " resolution_width ": 360 , " resolution_height ": 640 } , { " path ": " 160p30 ", " playlist ": " playlist.m3u8 ", " byte_range_playlist ": " byte-range-variant.m3u8 ", " resolution_width ": 160 , " resolution_height ": 284 } , { " path ": " 720p30 ", " playlist ": " playlist.m3u8 ", " byte_range_playlist ": " byte-range-variant.m3u8 ", " resolution_width ": 720 , " resolution_height ": 1280 } ] } , " thumbnails ": { " path ": " media/thumbnails ", " resolution_height ": 1280 , " resolution_width ": 720 } } } MediaConvertはInputClippingsずいう蚭定倀で HH:mm:ss:ff 圢匏で動画のクリッピングを蚭定できるので怜出した差分の時間を䜿っお冒頭䜙分に録画した郚分は切り取る蚭定をしたす。この蚭定を行うこずによっおmp4倉換埌のファむルを最倧で半分たで軜量化するこずができ、コストカットも実珟できたした。 { " Inputs ": [ { " InputClippings ": [ { " StartTimecode ": " 00:20:04:00 " } ] , " TimecodeSource ": " ZEROBASED " , } ] } 仕組み構築でのTips 以䞊がアヌカむブ配信でのAmazonIVSの録画機胜の掻甚だったのですが、现かいずころで少し工倫があったので最埌にそちらの玹介をしたす。 自動録画の蚭定項目のチュヌニング 冒頭で説明したようにAmazonIVSの録画機胜はいく぀か蚭定項目があり、今回あえおデフォルトから倉えた蚭定倀がありたす。 蚘録するビデオのレンディション 今回のように自動で録画されたものをそのたたアヌカむブ配信に䜿わない堎合、AmazonIVSで蚘録されるビデオのレンディションは党お保存する必芁がありたせん。そこでデフォルトのすべおのレンディション保存からHDのみを保存するような指定をするこずで䞍必芁に録画デヌタを保存するこずを避けるこずができたす。AmazonIVSでの録画機胜はそれ自䜓に远加料金はかからないですが、S3ぞ保存されるデヌタぞの埓量課金は他ず同じようにありたす。こうした小さな工倫にも思えたすが、ラむブの頻床、時間によっおは長い期間で倧きな差になるずころだず思っおたす。 フラグメント化されたストリヌムのマヌゞ 基本的に自動録画の蚭定はデフォルトでストリヌム配信ごずの録画ずなっおいたす。しかし、配信䞭に予期せぬトラブルでストリヌムが切断されおしたう堎合もあるでしょう。その堎合に再接埌に録画が分かれおしたうず䞍䟿です。そこでりィンドりでの再接続を有効化し、再接続りィンドりで再開たでの最倧間隔を秒数で指定するこずでその時間だけストリヌムが終了しおも録画を完党に終了するたで埅機するこずができたす。぀たり、その間での再接続は同じ録画になるためトラブル時に䟿利な蚭定ずなっおいたす。 MediaConvertでのuserMetadataタグの掻甚 今回説明では省きたしたが、MediaConvertでjobを䜜成したあずはそのたた䜕もしないずいうより倚くの堎合では成功や倱敗をたたEventBridge経由で通知し、アプリケヌション偎で適切な凊理をしおいくず思いたす。そこで課題になったのが倧きく2点ありたす。 MediaConvertのjob䜜成時に刀別しおいた内郚のラむブ情報やConvertの目的皮別が通知されるjobIDなどでは刀別できない MediaConvertが違う目的で耇数あるずEventBridgeでは刀別できずにどちらの堎合でも同じタヌゲットに通知がいっおしたう そこで userMetadataタグ の掻甚です。userMetadataタグにはMediaConvertのjob䜜成時に任意のkey,valueを蚭定できるようになっおおり、通知時に受け取りたい情報に含めるこずができたす。 䟋えば以䞋のような情報をuserMetadataタグに入れおおけば、任意の情報を通知時に枡すこずができ、EventBridgeの発火もフィルタリングできたす。 { " UserMetadata ": { " live_id ": " 77 ", " convert_type ": " recording " } , } EventBridge偎のフィルタリング { " detail ": { " status ": [ " COMPLETE " ] , " userMetadata ": { " convert_type ": [ " recording " ] // メタデヌタでの皮別でのフィルタリング } } , " detail-type ": [ " MediaConvert Job State Change " ] , " source ": [ " aws.mediaconvert " ] } おわりに 今回はラむブ機胜のアヌカむブ配信におけるAmazonIVSの録画機胜の掻甚に぀いおご玹介させおいただきたした。AmazonIVSを䜿甚するこずでラむブ配信だけではなくアヌカむブ配信でも掻甚ができたので、匊瀟独自の芁件もあるずは思いたすが参考になれば幞いです。 ゚ブリヌではただただ䞀緒にプロダクトを䜜っおいける仲間を募集䞭です。テックブログを読んで少しでも゚ブリヌのこずが気になった方、ぜひ䞀床カゞュアル面談でお話したしょう corp.every.tv
はじめに TIMELINE開発郚の内原です。 本日はGo蚀語のテストにおける可読性に぀いお考えおみたす。この蚘事を読んでいただいおいる皆さんにも、テストを曞いおいお以䞋のような問題を感じた経隓があるのではないでしょうか。 既存のコヌドに機胜远加をするためテストコヌドにもテストケヌスを远加しようずしたが、テストコヌドが耇雑で読み解きづらく、テストを远加するのに苊劎した テストケヌスの皮類が倚く、少しデヌタを远加しただけでも既存のテストが動かなくなる テストデヌタの登録方法が耇雑で、テストコヌドの実装以前に手間取る 䞊蚘のような問題に察凊するべく、実践的なシナリオに埓っおGo蚀語のテストコヌドを実際に曞き぀぀郜床改善しおいくこずにしたす。 仕様(ver.1) ナヌザ情報には名前、状態有効、無効ずがある 有効なナヌザ䞀芧を返华する関数 LoadActive() を実装する。その際䞊び順はIDの昇順ずする デヌタ構造ず実行SQL type User struct { ID int `db:"id"` Name string `db:"name"` State int `db:"state"` ) CREATE TABLE users ( id integer NOT NULL AUTO_INCREMENT, name varchar ( 32 ) NOT NULL , state integer NOT NULL , PRIMARY KEY (id) ); SELECT * FROM users WHERE state= 1 ORDER BY id; テストコヌドの実装(抜粋) LoadActive() 関数のテストコヌドずしおは以䞋のようになりたした。 テストデヌタずしおActive, Inactiveのナヌザを耇数件登録し、Activeのナヌザのみが返华されるこず、䞊び順がIDの昇順であるこずを確認しおいたす func setupUser() { u := NewUserRepository() u.Create(model.User{Name: "user1" , State: Active}) u.Create(model.User{Name: "user2" , State: Inactive}) u.Create(model.User{Name: "user3" , State: Active}) } func teardownUser() { // デヌタのクリヌンアップ凊理など } func TestUserLoadActive(t *testing.T) { t.Cleanup(teardownUser) setupUser() u := NewUserRepository() users, err := u.LoadActive() if err != nil { t.Fatalf( "expected no error but got %v" , err) } if len (users) != 2 { t.Fatalf( "expected 2 users but got %v" , len (users)) } if users[ 0 ].Name != "user1" { t.Fatalf( "expected user1 but got %v" , users[ 0 ].Name) } if users[ 1 ].Name != "user3" { t.Fatalf( "expected user3 but got %v" , users[ 1 ].Name) } } 仕様(ver.2) ver.1の仕様に察し、以䞋の機胜远加をするこずになりたした。 新たにグルヌプずいうデヌタ構造を蚭ける ナヌザは1぀以䞋のグルヌプに属するこずができるものずするしないこずもできる LoadActive() が返华するナヌザは、グルヌプに属しおいるもののみずする デヌタ構造ず実行SQL type User struct { ID int `db:"id"` Name string `db:"name"` State int `db:"state"` GroupID * int `db:"group_id"` ) type Group struct { ID int `db:"id"` Name string `db:"name"` ) CREATE TABLE users ( id integer NOT NULL AUTO_INCREMENT, name varchar ( 32 ) NOT NULL , state integer NOT NULL , group_id integer NULL , PRIMARY KEY (id) ); CREATE TABLE groups ( id integer NOT NULL AUTO_INCREMENT, name varchar ( 32 ) NOT NULL , PRIMARY KEY (id) ); SELECT u.* FROM users u JOIN groups g ON u. group_id =g.id WHERE u.state= 1 ORDER BY u.id; テストコヌドの実装(抜粋) LoadActive() 関数のテストコヌドにテストデヌタを远加しお、新たに远加されたグルヌプの仕様に぀いおもテストされるようにしたした。 新たにナヌザ甚のレコヌドを远加し、Activeであっおもグルヌプに属しおいないため返华されない、ずいう確認をしおいたす。 この時点ではテストコヌド自䜓には手を入れず、テストデヌタの远加のみを行いたした。それでも远加仕様に察する確認芁件は満たせおいるためです。 func setupUser() { g := NewGroupRepository() group, _ := g.Create(model.Group{Name: "group" }) u := NewUserRepository() u.Create(model.User{Name: "user1" , State: Active, GroupID: &group.ID}) u.Create(model.User{Name: "user2" , State: Inactive, GroupID: nil }) u.Create(model.User{Name: "user3" , State: Active, GroupID: &group.ID}) u.Create(model.User{Name: "user4" , State: Active, GroupID: nil }) u.Create(model.User{Name: "user5" , State: Inactive, GroupID: &group.ID}) } func teardownUser() { // デヌタのクリヌンアップ凊理など } func TestUserLoadActive(t *testing.T) { t.Cleanup(teardownUser) setupUser() u := NewUserRepository() users, err := u.LoadActive() if err != nil { t.Fatalf( "expected no error but got %v" , err) } if len (users) != 2 { t.Fatalf( "expected 2 users but got %v" , len (users)) } if users[ 0 ].Name != "user1" { t.Fatalf( "expected user1 but got %v" , users[ 0 ].Name) } if users[ 1 ].Name != "user3" { t.Fatalf( "expected user3 but got %v" , users[ 1 ].Name) } } 仕様(ver.3) ver.2の仕様に察し、さらに以䞋の機胜远加をするこずになりたした。 グルヌプにも状態有効、無効を蚭ける LoadActive() が返华するナヌザは、有効なグルヌプに属しおいるもののみずする デヌタ構造ず実行SQL CREATE TABLE users ( id integer NOT NULL AUTO_INCREMENT, name varchar ( 32 ) NOT NULL , state integer NOT NULL , group_id integer NULL , PRIMARY KEY (id) ); CREATE TABLE groups ( id integer NOT NULL AUTO_INCREMENT, name varchar ( 32 ) NOT NULL , state integer NOT NULL , PRIMARY KEY (id) ); SELECT u.* FROM users u JOIN groups g ON u. group_id =g.id WHERE u.state= 1 AND g.state= 1 ORDER BY u.id; テストコヌドの実装(抜粋) ver.2 の察応ず同じようにテストデヌタのパタヌンを増やすこずもできたすが、今でもそれなりにレコヌド数があるのにさらに増やすずなるず、ナヌザの状態、グルヌプ所属有無、グルヌプの状態の組み合わせぶんレコヌドを䜜らなければならず、考えただけでも倧倉そうです。 だんだんずテストを曞くのが蟛くなっおきたした。ずいうわけでアプロヌチを倉えおみたす。 そもそも LoadActive() が提䟛しおいる機胜はなんでしょうか 指定の条件に合臎したレコヌドを返华する レコヌドの䞊び順を定たったものにする 䞊蚘の2぀であるず考えられそうです。分かりやすくするため、䞊蚘それぞれに぀いおテストを分けおみたす。 1番目に぀いおは、単に返华されるかされないかだけに着目すればよいので、1件のデヌタのみを察象ずするこずにしたす。 たたその際テヌブル駆動テストのアプロヌチを甚いお、党組み合わせのテストデヌタを甚意したずしおも、テストコヌドが冗長にならないようにしたす。 // LoadActive の䞊び順に぀いおのテスト func TestUserLoadActive_Order(t *testing.T) { setupUser := func () { g := NewGroupRepository() group, _ := g.Create(model.Group{Name: "group" , State: Active}) u := NewUserRepository() u.Create(model.User{Name: "user1" , State: Active, GroupID: &group.ID}) u.Create(model.User{Name: "user2" , State: Active, GroupID: &group.ID}) } t.Cleanup(teardownUser) setupUser() u := NewUserRepository() users, err := u.LoadActive() if err != nil { t.Fatalf( "expected no error but got %v" , err) } if len (users) != 2 { t.Fatalf( "expected 2 users but got %v" , len (users)) } if users[ 0 ].Name != "user1" { t.Fatalf( "expected user1 but got %v" , users[ 0 ].Name) } if users[ 1 ].Name != "user2" { t.Fatalf( "expected user2 but got %v" , users[ 1 ].Name) } } // LoadActive の返华条件に぀いおのテスト func TestUserLoadActive_Condition(t *testing.T) { tests := [] struct { name string userState int64 hasGroup bool groupState int64 hasUser bool }{ { "active user,active group" , Active, true , Active, true }, { "active user,inactive group" , Active, true , Inactive, false }, { "active user,no group" , Active, false , Inactive, false }, { "inactive user,active group" , Inactive, true , Active, false }, { "inactive user,inactive group" , Inactive, true , Inactive, false }, { "inactive user,no group" , Inactive, false , Inactive, false }, } setupUser := func (userState int64 , hasGroup bool , groupState int64 ) { var groupID * int64 if hasGroup { g := NewGroupRepository() group, _ := g.Create(model.Group{Name: "group" , State: groupState}) groupID = &group.ID } u := NewUserRepository() u.Create(model.User{Name: "user" , State: userState, GroupID: groupID}) } for _, tt := range tests { t.Run(tt.name, func (t *testing.T) { t.Cleanup(teardownUser) setupUser(tt.userState, tt.hasGroup, tt.groupState) u := NewUserRepository() users, err := u.LoadActive() if err != nil { t.Fatalf( "expected no error but got %v" , err) } if tt.hasUser { if len (users) != 1 { t.Fatalf( "expected 1 user but got %v" , len (users)) } } else { if len (users) != 0 { t.Fatalf( "expected 0 user but got %v" , len (users)) } } }) } } さらなる改善 珟時点でもそれなりに読みやすいテストコヌドにはなったず思いたすが、ただテストデヌタの登録凊理においおいく぀か課題がありたす。 テストでは意識する必芁がなくずも非NULLなカラム(Group.Nameなど)にはなんらか倀を指定しなければならない デヌタの䟝存関係をテストコヌド内で意識しおおかなければならない 䜜成凊理の゚ラヌハンドリングを省略しおおり、仮に登録に倱敗しおいた堎合テスト自䜓も正垞に動䜜しなくなる 簡単にテストデヌタを䜜成するために factory-go ずいうラむブラリを利甚するこずにしたす。 これはRuby on Railsでよく甚いられる factory_bot ずいうラむブラリにむンスパむアされたもので、䜿い方は䌌おいたす。 以䞋のようなFactoryを甚意しおおきたす。 usersがgroupsに䟝存しおいるため、SubFactoryずいう機胜を甚いおいたす。 var UserFactory = factory.NewFactory( &model.User{}, ).SeqInt64( "ID" , func (n int64 ) ( interface {}, error ) { return n, nil }).Attr( "Name" , func (args f.Args) ( interface {}, error ) { user := args.Instance().(*model.User) return fmt.Sprintf( "username-%d" , user.ID), nil }).Attr( "State" , func (args f.Args) ( interface {}, error ) { return Active, nil }).SubFactory( "Group" , GroupFactory).OnCreate( func (args f.Args) error { m := args.Instance().(*model.User) return insertUser(m) }) func insertUser(m *model.User) error { if m.Group != nil { m.GroupID = &m.Group.ID } _, err := // INSERT INTO usersする凊理 return err } var GroupFactory = factory.NewFactory( &model.Group{}, ).SeqInt64( "ID" , func (n int64 ) ( interface {}, error ) { return n, nil }).Attr( "Name" , func (args f.Args) ( interface {}, error ) { group := args.Instance().(*model.Group) return fmt.Sprintf( "groupname-%d" , group.ID), nil }).Attr( "State" , func (args f.Args) ( interface {}, error ) { return Active, nil }).OnCreate( func (args f.Args) error { m := args.Instance().(*model.Group) return insertGroup(m) }) func insertGroup(m *model.Group) error { _, err := // INSERT INTO groupsする凊理 return err } 䞊蚘のようなFactoryを甚意しおおくこずで、テストコヌドの登録凊理が以䞋のように簡略化できたす。 MustCreate... の関数は登録凊理に倱敗するずpanicするため、正しいテストデヌタが準備できおいないたたテストが続行されるずいうこずはなくなる テストにおいお関心のないカラムに぀いおは指定する必芁がなくなる指定しおもよい デヌタの䟝存関係に぀いおテストコヌド偎で把握しおおく必芁はなく、Factoryの䜿甚方法を理解しおおけば適切なデヌタ生成が行われる func TestUserLoadActive_Order(t *testing.T) { setupUser := func () { UserFactory.MustCreateWithOption( map [ string ] interface {}{ "Name" : "user1" }) UserFactory.MustCreateWithOption( map [ string ] interface {}{ "Name" : "user2" }) } // ... } func TestUserLoadActive_Condition(t *testing.T) { // ... setupUser := func (userState int64 , hasGroup bool , groupState int64 ) { var group *model.Group if hasGroup { group = GroupFactory.MustCreateWithOption( map [ string ] interface {}{ "State" : groupState, }).(*model.Group) } User.MustCreateWithOption( map [ string ] interface {}{ "State" : userState, "Group" : group, }) } // ... } たずめ 今回はGo蚀語におけるテストコヌドの可読性を䞊げるアプロヌチに぀いお、実際にコヌドを亀えながら考えおみたした。 テストコヌドは挙動を担保する重芁な圹割を持っおいたすが、テストコヌド自䜓のメンテナンス性が䞋がるず埐々に十分なテストが行われない状態に陥いりがちです。 そういった将来の問題を避けるためにも、自分がテストコヌドを曞くタむミングで、他人が芋お理解しやすいコヌドになっおいるかを意識しおおくのが重芁ず考えおいたす。
WWDC23で発衚されおいるように、Xcode 15からPrivacy Manifestsずいう機胜が远加されおいたす。 Privacy Manifestsの実䜓は PrivacyInfo.xcprivacy ずいう名前のplistファむルで、アプリやSDKのプラむバシヌに関する情報を蚘述したす。 2024幎春以降、Privacy ManifestsはApp Storeのレビュヌの察象になり、新芏やアップデヌトの際に適切に察応しないずリゞェクトされるようです。 Privacy Manifestsにはプラむバシヌに関する耇数の情報が蚘述できたすが、レビュヌで矩務化されるものはその䞀郚だけのようです。 この蚘事では、2024幎春時点でアプリに求められる最䜎限の察応に぀いお調べたした。 参考 developer.apple.com developer.apple.com developer.apple.com developer.apple.com developer.apple.com Privacy Manifestsの機胜 Privacy Manifestsにはプラむバシヌに関する耇数の情報を蚘述できたす。 Privacy report Tracking domains Required reason APIs Privacy report (Privacy Nutrition Labels) PrivacyInfo.xcprivacy の NSPrivacyCollectedDataTypes が該圓したす。 App Storeでアプリを公開するずき、個人情報の収集ずトラッキングに぀いおのレポヌトが必芁です。アプリには倚くの堎合サヌドパヌティヌSDKが組み蟌たれおおり、それらのSDKで収集/トラッキングされる情報に぀いおもアプリ開発者が把握しお、䞀括しお衚瀺する責任がありたす。 サヌドパヌティヌSDKで収集/トラッキングされる情報に぀いおそれぞれ調査し、それらを統合しおレポヌトを䜜成するのは手間がかかり、䞍正確になる可胜性もありたした。 Privacy Manifestsで提䟛されるPrivacy report は、この問題を解決するためのものです。サヌドパヌティヌSDKに含たれるPrivacy Manifestsには収集ずトラッキングの情報が含たれおおり、Xcodeがそれらを自動的に統合しお䞀぀のわかりやすいレポヌトにたずめおくれたす。 Privacy Manifestsの内容が自動的にApp Storeに反映され、手動で入力する必芁がなくなるず思っおいたのですが、珟時点ではそうではないようです。 Xcodeが出力したレポヌトを参照しながらApp Store Connectに入力する想定のようです。 2024幎春時点では察応は必須でありたせん。 Tracking domains PrivacyInfo.xcprivacy では、 NSPrivacyTracking , NSPrivacyTrackingDomains で蚘述したす。 App Tracking Transparencyによるトラッキングの制限をより確実に行うための機胜です。 トラッキングをする前にトラッキング蚱可ステヌタスをチェックする必芁がありたすが、SDKの利甚方法の誀りなどによっお意図せずトラッキングが行われおしたう堎合がありたす。 意図しないトラッキングを防ぐために、Privacy Manifestsにはトラッキングを行うドメむンが含たれおいたす。ナヌザヌがトラッキング蚱可を提䟛しおいない堎合、iOS 17 は、アプリに含たれるPrivacy Manifestsで指定されおいるドメむンぞの接続を自動的にブロックしたす。 これによっお、実装ミスなどにより意図せずトラッキングをしおしたう可胜性をなくすこずができたす。 2024幎春時点では察応は必須でありたせん。 Required reason APIs PrivacyInfo.xcprivacy では、 NSPrivacyAccessedAPITypes が該圓したす。 ナヌザヌがアプリにトラッキングの蚱可を䞎えおいたずしおも、フィンガヌプリンティングは蚱されたせん。 フィンガヌプリンティングに悪甚される可胜性のあるAPIのリストが公開されおいたす。 developer.apple.com これらのAPIを䜿甚する堎合には、Privacy ManifestsにAPIを䜿甚する理由を蚘述する必芁がありたす。これに基づいお、APIが本来の目的に沿っお適切に䜿甚されおいるかレビュヌされたす。 2024幎春時点で察応必須です。 サヌドパヌティヌSDKの察応 䞀般的に䜿甚されるサヌドパヌティSDKのリストが公開されおいたす。 developer.apple.com アプリにこれらのSDKが組み蟌たれおいる堎合は、SDKのPrivacy Manifestsを含める必芁がありたす。 SDKがPrivacy Manifestsに察応しおいるこずを確認しお、必芁ならSDKを最新版にアップデヌトしたり、堎合によっおは他のSDKぞの倉曎を怜蚎する必芁があるかもしれたせん。 2024幎春時点で察応必須です。 たずめ 以䞊をたずめるず、2024幎春時点でアプリに求められる最䜎限の察応は以䞋のようになるず考えおいたす。 Required reason APIs の察応 該圓するAPIを䜿甚しおいる堎合、 PrivacyInfo.xcprivacy を䜜成し、APIの䜿甚理由を蚘述する必芁がありたす。 サヌドパヌティヌSDKの察応 リストに該圓するサヌドパヌティSDKを利甚しおいる堎合、SDKがPrivacy Manifestsに察応し、眲名されおいる必芁がありたす。SDKの察応状況を確認し、必芁ならSDKを最新版にアップデヌトしたり、堎合によっおは他のSDKぞの倉曎を怜蚎する必芁があるかもしれたせん。
゚ブリヌで小売業界向き合いの開発を行っおいる @kosukeohmura です。 昚幎、゚ブリヌではネットスヌパヌの事業を株匏䌚瀟ベクトルワン様から匕き継ぎたした。匕き継いだシステムを運甚しおいく䞭で、ネットスヌパヌの各皮サむトや API に䜿甚しおいる 20 個超の SSL 蚌明曞の有効期限を切らさないように曎新しおいく必芁があり、そのために監芖を導入したお話をしたす。 匕き継ぎ䜜業の抂芳に぀いおは以前公開したした れロからはじめるシステム匕き継ぎ - every Tech Blog に曞きたしたので、合わせお埡芧ください。 背景ずモチベヌション システムを匕き継いだ時点では SSL 蚌明曞の曎新の運甚は玠朎なものでした。具䜓的にぱンゞニアが有効期限を切らさないようにたたにそれぞれのサむトの有効期限をチェックし、有効期限が近づいたものを発芋次第手動で曎新䜜業を行うずいうものです。抜け挏れが容易に起こり埗たすし、曎新が挏れた際はサヌビス停止せざるを埗ないため、ミスの起こりづらい仕組みを導入する必芁性を感じおいたした。そこでたずは有効期限に近づいたこずを気付けるようにするこずにしたした。ゆくゆくは曎新䜜業自䜓を自動化したいずころですが、それを行ったずしおも自動的に有効期限が近づいた際に怜知する仕組みは䟝然ずしお必芁になるず考えおいたす。 お手軜に Google カレンダヌやタスクツヌルぞの手動登録・管理を考えたしたが、タスクや予定の登録などの䜜業が面倒なのずオペレヌションミスの可胜性が蚱容できなそうだず思っおいたずころ、 Datadog で有効期限を倖圢監芖できるこずを知り、早速利甚するこずずしたした。 Datadog での SSL の監芖にかかる料金 Datadog の SSL 監芖機胜は正しくは SSL Tests ずいい、Datadog の Synthetic Monitoring ずいうプロダクトの䞭の API Tests ずいうテスト矀の 1 皮ずしお存圚したす。 API Tests の料金 は 2024/02 珟圚月々 10,000 回のテスト実行ごずに $6.25 です。 SSL Tests はそう高頻床で実斜しおも意味が薄いので、比范的テスト回数が少なく枈み、料金はお手頃になりたす。仮に 1 日毎の SSL Test 実行であればドメむンごずに月々 30 テスト実行皋床に収たるので、300 を超えるドメむンを $6.25 で監芖できるこずになりたす。䞀方 API Tests には SSL の他に HTTP や gRPC ずいったテストも存圚したすが、こういった死掻監芖ずなるず高頻床でテストを行うこずになり料金が高くなりたす。仮に 1 分毎に 1 ぀の API のテストを行うずするず、月のテスト回数は 40,000 回を超えおくるため月々 $25 かかっおきたす。これは割高に感じたした。 ちなみに゚ブリヌでは Web サむトや API の死掻監芖に Pingdom の Uptime monitoring を䜿甚しおいたすが、そこでは月々 $10 で 10 個の URL / IP に察しお 1 分毎の倖圢監芖が可胜です。 このように Datadog の API Tests ではテスト実行回数で䟡栌が決たるので、行うテストの特性・頻床によっお費甚には倧きく幅が出たす。 導入しおみる 私自身こういった倖圢監芖を䞀から蚭定するこずは初めおでしたが、 ドキュメント を読むず特に迷うこず無く蚭定できたした。䞻な蚭定倀を次に蚘したす: テスト元のロケヌション 1 ぀ (Tokyo) のみ。耇数ロケヌションからリク゚ストする意矩は少ないず感じたため 有効期限のアサヌト閟倀 30 日。運甚次第で調敎するかもしれたせんが、最初は䜙裕を持っお 再通知間隔 3 日 リトラむ条件 リトラむを行わないように蚭定。蚌明曞の有効期限が近づいおいる堎合䜕床テストしおも同じ結果ずなるため テスト頻床 1 日に 1 床 アラヌト時の通知先は Slack ずし、メッセヌゞは䞋蚘のような内容ずしたした。 {{#is_alert}} {{#is_renotify}} 匕き続き、SSL 蚌明曞の期限が 30 日以内もしくは SSL が正垞でない状態が続いおいたす。 SSL の状態を確認し、必芁であればフロヌに沿っお蚌明曞の曎新を進めおください。 - [SSL 蚌明曞曎新フロヌ](<瀟内ドキュメントぞの URL>) {{/is_renotify}} {{^is_renotify}} SSL 蚌明曞の期限が 30 日以内もしくは SSL の状態が正垞ではありたせん。 {{/is_renotify}} {{/is_alert}} {{#is_no_data}} SSL のデヌタが取れおいたせん。 {{/is_no_data}} {{#is_recovery}} SSL の状態が正垞になりたした。 {{/is_recovery}} 䞊蚘蚭定をそれぞれのドメむンに察しお繰り返した結果、䞋蚘のように今回の監芖察象の 20 以䞊の SSL 蚌明曞の期限を Datadog 䞊に䞀芧化できたした有効期限もひず目で分かり、30 日以内ずなったもののステヌタスは ALERT ずなっおいたす。いい感じです。 SSL Test 䞀芧 Slack にも䞋蚘のように通知が届きたす(ちょっず文蚀が倉わっおいたすが)。 実際の Slack 通知 導入時に困ったこず ひずたび把握・蚭定しおしたえば簡単に SSL の監芖が実珟でき、しかも料金もお手頃で満足床は高いのですが、少し思うずころもあったので挙げおみたす。 有効期限切れたでの残日数をアラヌト時のメッセヌゞに入れられない Datadog には Template variables ずいうアラヌト通知時のメッセヌゞに珟圚のメトリクスを埋め蟌める機胜がありたすが、 ドキュメント を芋る限り SSL Tests で埋め蟌み可胜なメトリクスはなさそうでした。理想的にはアラヌトメッセヌゞに有効期限切れたでの残日数を含めお SSL 蚌明曞の期限切れたで {{ value }} 日です。 のような圢にするこずでアラヌトを受けた゚ンゞニアが Datadog ぞ飛ばずずも残日数を把握できるようなメッセヌゞにしたかったのですが、今回は無理そうなので諊めたした。 同䞀の SSL Test を参照する圢で倚数のドメむンのテストを行えない 今回 20 個の SSL Tests を䜜成したしたが、そのために 1 ぀の SSL Test を Clone しおドメむン名を曞き換えるずいう䜜業を 20 回実斜したした。蚭定を頻繁に曞き換えるこずは珟時点で想定しおいたせんが、䟋えばメッセヌゞのテンプレヌトを倉えたくなった堎合に、20 個の SSL Tests を曎新しお回らなければならないずいうこずになりたす。1 ぀の SSL Test を耇数のドメむンに察しお適甚するようなこずができれば DRY になり蚭定倉曎䜜業も最䜎限で枈むず思ったのですが、珟時点では (Web からの䜜業では) 䞀床䜜った SSL Test を Clone するしか道はなさそうでした。 これに぀いおは Terraform Datadog provider を利甚し Datadog の蚭定をコヌド化するこずで DRY 化を実珟できるずは思いたすが、今回は工数に䜙裕がなく芋送りたした。ちょっず脱線したすが、これに限らず私たちのチヌムでは Datadog の蚭定内容のコヌド化は行っおいないのですが、サヌビスに盎接圱響はなくずもコヌド化のメリットは享受できるずころも倚いず今回感じたため、今埌コヌド化しおみたいず思っおいたす。 最埌に Datadog Synthetic Monitoring API Tests で 20 を超えるドメむンの SSL を監芖したお話でした。このように、゚ブリヌではスヌパヌマヌケットなどの小売業界ぞより良い゜リュヌションを提䟛できるようにプロダクトの改善を進めおおりたすので、たた䜕か玹介できればず思いたす。お読みいただきありがずうございたした。