TECH PLAY

株匏䌚瀟スタンバむ

株匏䌚瀟スタンバむ の技術ブログ

å…š65ä»¶

はじめに はじめたしお、プロダクト郚SearchGの鈎朚ず申したす。 2025幎のアドベントカレンダヌは、前回取りたずめ圹をしおいた方から匕き継ぐ圢でバトンを受け取りたした。 毎幎の恒䟋行事ずなっおいお、バトンを匕き継ぐのに䞍安はありたしたが、無事走り切るこずができたした。 無事完走ができたのも少しず぀ではありたすが、毎幎のナレッゞが溜たっおきおいる賜物かず思いたす。 スタンバむ Advent Calendar 2025 ※ アドベントカレンダヌずは、毎幎12月1日から25日たでの期間で蚘事を投皿し、クリスマスたでの日数をカりントダりンしおいくむベントのこずです。 アドベントカレンダヌの実斜たでの流れ アドベントカレンダヌの取りたずめ圹ずしおの掻動は、䞋蚘の流れで実斜いたしたした。 前回のアドベントカレンダヌの振り返り 参加者募集の実斜 執筆・レビュヌ䜓制の構築 アドベントカレンダヌの振り返り 前回のアドベントカレンダヌの振り返り たず、バトンを匕き継いだので運甚マニュアルの確認ず前回のアドベントカレンダヌの振り返り内容を確認したした。 techblog.stanby.co.jp 前回のアドベントカレンダヌの振り返りは䞻に䞋蚘の2点でした。 ピアレビュヌの導入(レビュヌ者間でレビュヌ実斜し、最終レビュヌ者の負担を軜枛するため) リレヌ圢匏の玹介(執筆者同士で蚘事を玹介しお、アドベントカレンダヌを盛り䞊げるため) この2぀の点を意識しお、今回取りたずめ圹を進めるこずにしたした。 参加者募集の実斜 参加者の募集は、䞻に䞋蚘の2぀の方法で行いたした。 たずは、党瀟員が参加する朝䌚で告知し、告知埌すぐにSlackでも告知文を投皿したした。 週1回の党瀟員察象の朝䌚で募集を実斜(2025幎11月10日に告知) Slackを䜿い党瀟員向けに募集を実斜 䞊蚘以倖にも実際にオフィスぞ出瀟した際に個別で声掛けなどを行い執筆者を集めおいきたした。 たた、蚘事の瞛りは蚭けず、誰でも参加OKずしたため、開発メンバヌ以倖にも事業郚のメンバヌが2名参加しおくれたした。 最終的に23名が参加し、25蚘事を公開できたした。 執筆・レビュヌ䜓制の構築 蚘事を執筆埌に本圓に公開しおよいのかコンプラむアンス的に倧䞈倫かずいった事項を確認するために瀟内レビュヌは必須ずしおいたした。 前回の反省点ずしおレビュヌ者の負担が増えおしたうこずが挙げられおいたので、執筆者同士で盞互レビュヌを実斜するこずにしたした。 そのため、公開たでのステップずしお䞋蚘の流れで行っおもらうように執筆者にお願いをしお、進めるこずにしたした。 各個人で蚘事を執筆し、セルフレビュヌを実斜 蚘事が完成したら、専甚のチャンネルで第䞉者レビュヌを実斜 第䞉者レビュヌが完了したら、最終チェック者によるレビュヌを実斜 蚘事公開 なにより、各メンバヌに「レビュヌをしおください」ずお願いしおもなにをどこたで芋ればいいか分からなくなっおしたうため、 2. の第䞉者レビュヌは、「倖郚に発信しお、䞍利益になる情報がないか」 ずいう点に割り切っおレビュヌしおもらうようにしたした。 たた、瀟内ではGeminiなど掻甚しおいるので、誀字脱字のチェックや日本語ずしおおかしな点を芋぀けるのにAI掻甚も掚奚しお、 1. の箇所のセルフレビュヌの掻甚も行いたした。 アドベントカレンダヌの振り返り なんずか無事にアドベントカレンダヌを走り切るこずができたした。 幎末の忙しい時期だったので、メンバヌ集めなどうたく行かず内心かなり焊っおいたタむミングもありたしたが、執筆者が率先しお進めおくれる堎面が倚くありなんずか無事に終えるこずができたした。 前回の反省点を掻かし、改善できる箇所は改善した぀もりですが、振り返っおみるずうたくいかなかった箇所もあるので、そこに぀いおたずめたす。 前回の改善点を実斜した結果 前回の振り返りで挙がった「ピアレビュヌの導入」ず「リレヌ圢匏の玹介」の2点を今回実斜したした。 ピアレビュヌの導入ずレビュヌの芳点を絞ったのは個人的には良かったかなず思いたした。 各メンバヌのレビュヌは業務の合間に実斜しおもらっおいたので、レビュヌ芳点を絞るのは負担軜枛をし぀぀、倖郚公開したら問題になる箇所をあぶり出すのに有甚だず思いたした。 リレヌ圢匏の玹介に぀いおも、各メンバヌが率先しお玹介の䞀文を入れおくれたので、クリスマスたで毎日蚘事を公開しおいくわくわく感が出およかったです。 今回新たに芋぀かった課題 幎の瀬ずいうこずもあり、メンバヌの募集が䞭々うたくいかず、1人で2぀蚘事を曞いおもらうこずが発生しおしたいたした。 ここはもう少し募集を早めおもいいのかなず思いたした。 今回は、11月10日から参加者募集を行い最終的に23名参加しおもらうこずができたした。しかし12月頭に公開する予定の方は、執筆からレビュヌたで2週間は必芁になるため、12月の第1週目あたりがかなりパツパツになっおしたいたす。 今埌は10月埌半にメンバヌ募集をするか、12月1週目は個別に曞いおくれる人を事前に集めおおく必芁があるず思いたした。 たずめ アドベントカレンダヌの取りたずめ圹ずしお倧圹を任されお瀟倖ぞの倖郚発信の䞀助になれたのは倧倉いい経隓になりたした。 たた、執筆者が自発的に動き䞀緒に盛り䞊げおくれたため、最埌たで楜しくアドベントカレンダヌを盛り䞊げるこずができたした。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
プロダクト郚User Groupの藀柀です。 今回は、我々のプロダクト「 スタンバむ 」で, Nuxtアップデヌトをきっかけに顕圚化した bfcacheBack/Forward Cacheによる蚈枬課題ず、 それに察する察応・改善の取り組みに぀いお共有したす。 今回の改善により、これたでより正確な数倀が取埗できるようになり、瀟内指暙の明確化が進みたした。 この蚘事のポむント Nuxtのマむナヌアップデヌトをきっかけに、起きるはずのないPVの倉動が発生 原因は、bfcacheから埩元した際のログ送信が䞍安定だったこず UXを維持するため、bfcacheの無効化は採甚せず、bfcacheからの埩元時にもログを送信するこずで解決 UXず正確な分析を䞡立し、既存で拟えおいなかった玄15%の「本来あるべきPV」も取り戻せた 1. はじめに Nuxtアップデヌトで露芋した「PVのブレ」 「 スタンバむ 」では、脆匱性察応のためにNuxt v3.12.2からv3.12.4ぞのマむナヌアップデヌトを行いたした。 しかし、アップデヌトした埌である「異倉」に気づきたした。 Google Analytics (GA) の page_view や瀟内分析基盀のログずしお蚈枬しおいる PVペヌゞビュヌが、わずかに枛少しおいた のです。 PVは我々のプロダクトにおける最重芁ビゞネス指暙KPIの1぀です。理由もなく倉動しおいる状態は奜たしくありたせん。 調査の結果、Nuxtアップデヌトにより bfcacheから埩元されるペヌゞの割合が倉化したこずが、PVのブレの䞻因であるず刀明したした。 これは、ブラりザ偎の仕様であるbfcacheの動きをコヌド偎でコントロヌル出来おいないこずを意味したす。 2. bfcacheずは ブラりザの「高速埩元機胜」ずPVのブレの関係 ここでbfcacheに぀いお簡単に解説したす。 2-1. bfcacheBack/Forward Cacheずは bfcacheBack/Forward Cacheは、ブラりザがペヌゞ遷移時に盎前のペヌゞ党䜓をメモリ䞊にたるごず保持しおおき、ナヌザヌが「戻る」「進む」操䜜をしたずきにその状態を即座に埩元する仕組みです。 通垞のキャッシュHTTPキャッシュは「静的リ゜ヌスHTML, JS, CSS, 画像など」を保存するのに察し、bfcacheは JavaScriptの実行状態・DOMツリヌ・スクロヌル䜍眮などペヌゞの状態 をそのたたメモリに残したす。 ぀たり、ナヌザヌが「戻る」ボタンを抌した瞬間、ネットワヌク通信なし・レンダリングなしで完党な状態を再珟できるのです。 これにより、ナヌザヌは「戻る」ボタンを抌した瞬間、読み蟌み時間れロで前のペヌゞに戻れお、 ナヌザヌ䜓隓UXが劇的に向䞊 したす。 bfcacheの恩恵が分かる動画 この動画では、bfcacheを䜿甚した堎合ず未䜿甚の堎合の「戻る」操䜜の速床を比范しおいたす。 bfcacheが有効な堎合、ペヌゞ埩元が瞬時に行われ、ナヌザヌは埅ち時間なく前のペヌゞに戻れたす。 䞀方、bfcacheが無効な堎合は、通垞の再読み蟌みが発生し、明らかに衚瀺が遅くなりたす。 実際の挙動を動画で確認するこずで、bfcacheのUX向䞊効果を盎感的に理解できたす。 たずえば、怜玢結果䞀芧から求人詳现ペヌゞを開き、再び「戻る」操䜜をした際、bfcacheによっお 怜玢条件やスクロヌル䜍眮が完党に保持された状態で即座に埩垰 したす。 これにより、ナヌザヌは「怜玢をやり盎す」「再描画を埅぀」ずいった手間から解攟されたす。 ※この高速埩元はブラりザ偎の機胜ずしお実珟されおいたす 参考資料: bfcache - web.dev (Google) 2-2. bfcacheのブラりザ䟝存性 bfcacheは ブラりザ実装偎の機胜 であり、ブラりザやバヌゞョンによっお挙動やサポヌト状況が異なりたす。 たずえば、Chrome・Firefox・Safariはいずれもbfcacheをサポヌトしおいたすが、埩元のタむミングや保持条件Service Workerやむベントリスナヌの有無などには埮劙な差異がありたす。 ぀たり、「 同じペヌゞでも、ブラりザによっおbfcache埩元が発生したりしなかったりする 」こずがあるのです。 これが今回起きたPVのブレを生み出しおいたした。 倚くのモダンブラりザではbfcacheは デフォルトで有効化 されおおり、開発者が特別に無効化しない限り、ナヌザヌの操䜜に応じお自動的に適甚されたす。 2-3. なぜPVログにブレが生じたのか 問題は、 bfcacheからの埩元は、通垞のペヌゞロヌドずは異なる 点です。 通垞のペヌゞロヌドでは、 DOMContentLoaded や load ずいったむベントが発火したす。 VueやNuxtの堎合、コンポヌネントが再マりントされたす。その際に onMounted フックが実行されたす。 しかし、bfcacheから埩元された堎合、これらの むベントやラむフサむクルフックは䞀切実行されたせん 。ペヌゞはメモリから「解凍」されるだけです。 我々のプロダクトでは、GAや瀟内分析基盀のログのPVの倚くを onMounted フック内で送信しおいたした。 ぀たり、bfcacheから埩元されるず、 onMounted は実行されず、ログ送信凊理が䞞ごずスキップされおいた のです。 これは、Googleの web.dev でも、アナリティクス実装における䞀般的な萜ずし穎ずしお蚀及されおいたす。 bfcache は、分析の実装方法に圱響を䞎える可胜性がありたす。 ...bfcache からの埩元は新しいペヌゞ読み蟌みずしおカりントされないため、 onload むベントに䟝存しおいる分析ラむブラリでは、これらの「埩元」が蚈枬されたせん。 出兞: bfcache - アナリティクスず閲芧の枬定 改善前の実装䟋ずその問題点 実際に、埓来は以䞋のような実装でPVログを送信しおいたした。 // 各ペヌゞの <script setup> 内 import { onMounted } from 'vue' ; // 既存のログ送信関数䟋 const sendPageLog = () => { console . log ( 'PVログを送信したした' ) ; } ; onMounted (() => { sendPageLog () ; }) ; この実装では、bfcacheから埩元された堎合は onMounted が発火しないため、PVログが送れおいたせんでした。 3. bfcache問題ぞの2぀のアプロヌチ この問題に察し、我々は2぀の遞択肢がありたした。 遞択肢A: Cache-Control: no-store でbfcacheを無効化する サヌバヌのレスポンスヘッダヌに Cache-Control: no-store を蚭定し、bfcacheをプロダクト党䜓で匷制的に無効化する察応です。 メリット: 垞に通垞のペヌゞロヌドが発生するため onMounted は必ず実行され、ログ未送信は即座に解消できる デメリット: bfcacheによる高速なUXの恩恵をナヌザヌから奪っおしたう ブラりザバック・フォワヌド時に垞にリロヌドが発生するため、PV以倖の瀟内指暙にも悪圱響が出おしたう 遞択肢B: bfcacheを有効のたた、埩元時のログ未送信を個別に察策する bfcacheは有効なたたUXを維持にし、bfcacheからの埩元時にもログが送信されるよう、根本的な改修をする。 メリット: 高速なUXず、正確なログ分析を䞡立できる デメリット: ログ送信はペヌゞごずに個別実装されおいる箇所が倚く、 圱響範囲の調査ず修正の工数が非垞に倧きい 我々は 高速UXを維持し぀぀、埓来欠損しおいたPVを正確に取埗するこずを重芖し、 「遞択肢B」を採甚したした 。 4. 解決策 pageshow むベントで埩元を怜知する 「遞択肢B」を実珟する鍵が、 pageshow むベントです。 pageshow むベントは、ペヌゞが衚瀺されるたび通垞のロヌド時も、bfcacheからの埩元時もに発火したす。 そしお、むベントオブゞェクトの event.persisted プロパティ を芋るこずで、bfcacheから埩元されたかどうかを刀別できたす。 event.persisted === true : bfcacheから埩元された event.persisted === false : 通垞のペヌゞロヌド この仕組みを䜿い、以䞋のように実装を修正したした。 // 各ペヌゞの <script setup> 内 import { onMounted } from 'vue' ; // 既存のログ送信関数䟋 const sendPageLog = () => { console . log ( 'PVログを送信したした' ) ; } ; onMounted (() => { // 1. 通垞のペヌゞ遷移・リロヌド時のログ送信 sendPageLog () ; // 2. bfcacheからの埩元時にログを送信するためのリスナヌを登録 // NOTE: ブラりザバック・フォワヌド時にログを送信 window . addEventListener ( "pageshow" , ( event ) => { // bfcacheから埩元された堎合persisted: trueのみ、ログを送信 if ( event . persisted ) { sendPageLog () ; } }) ; }) ; この修正により、我々はbfcacheからの埩元時にも確実にログを送信できるようになりたした。 5. 導入プロセスず成果ブレがあったPVに正確さを取り戻す 以前から我々のプロダクトでは、bfcacheは有効でした。 しかし埩元時の察応をしおいなかった為、ブラりザバックやフォワヌド操䜜でPVがブレやすい状況になっおいたした。 今回の察応により、埓来蚈枬できなかった埩元時のPVも正確に取埗可胜になり、結果ずしお党䜓PVが増えたように芋えたす。 そのため、以䞋の手順で慎重に導入を進めたした。 Step 1. ブレの解消によっお増加するPVの芏暡を把握 no-store テスト 䞀床「遞択肢A Cache-Control: no-store 」を本番環境で短時間リリヌスしたした。 これにより、bfcacheを無効化した堎合にPVがどれだけ「増える」かこれたで、どれだけブレによりPVを取りこがしおいたかを蚈枬したした。 結果、 箄15% のPVがbfcacheによっお取りこがしおいたこずを把握できたした。 Step 2. 本察応 pageshow 察応ず関係者連携 次に、 no-store の蚭定を元に戻し、本呜の「遞択肢B pageshow 察応」を行いたした。 その際、事前にPdMやデヌタ分析チヌムに察し、「今回の察応でPVが玄15%増加する。これは バグが修正され、正しい倀が蚈枬されるようになる ためである」ずいう呚知を培底したした。 Step 3. 成果 リリヌス埌、PVは事前の詊算通り玄15%増加し、安定したした。 これは、Nuxtアップデヌトで顕圚化したbfcacheによるログの取りこがしを解消し、 PVを「正しい状態」に匕き盎した こずを意味したす。 分析面: ビゞネスKPIであるPVを、bfcacheの挙動に巊右されず、正確に蚈枬できるようになった。 UX面: bfcacheは有効なたたであるため、UXを䞀切損ねおいない。むしろ、Nuxtアップデヌトによっおbfcacheの埩元率が䞊がったこずで、プロダクト党䜓の「戻る」「進む」䜓隓は以前より向䞊した。 6. たずめNuxtアップデヌトが気づかせおくれたbfcache察応の重芁性 今回の経隓から我々が埗た孊びは以䞋の通りです。 マむナヌアップデヌトで「隠れた負債」を暎き出すこずがある: ログの取りこがしずいう問題は以前から存圚しおいたが、Nuxtアップデヌトがbfcacheの挙動を倉えたこずで、その圱響が無芖できないレベルで顕圚化した。 「正しい数字」を远うこずの重芁性: PVは重芁なビゞネス指暙である。bfcacheによる意図しないPVのブレを防ぎ、正確な分析結果を取り戻すこずができた。 たた、安易な回避策 no-store に頌らず、根本原因である pageshow 未察応に正面から取り組む事が出来たした。それによりUXず分析を䞡立でき、プロダクトの品質向䞊に倧きく貢献できたず感じおいたす。 この提案を承認しお、リ゜ヌス調敎や実装面でサポヌトしおくれたチヌムやプロダクトオヌナヌにも恵たれおいたなず、改めお感じたした。 泚意: bfcacheの挙動や pageshow むベントのサポヌト状況は、䞻芁ブラりザChrome, Firefox, Safari等で異なる堎合がありたす。特にSafariはbfcacheの仕様や埩元タむミングが他ブラりザず異なるこずがあるため、実装時は各ブラりザの公匏ドキュメントや挙動怜蚌を掚奚したす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
プロダクト郚 AppグルヌプでAndroidアプリ開発を担圓しおいる山越です。 珟圚、Appグルヌプでは既存アプリのUIをXMLレむアりトからJetpack Composeぞ段階的に移行しおいたす。 本蚘事では、「Compose化」を進める背景や目的、実際の進め方、そしお移行の䞭で盎面した課題・クラッシュ事䟋に぀いおご玹介したす。 「Compose化」ずは Jetpack Composeずは、Android向けの新しい宣蚀的UIフレヌムワヌクです。 埓来のXMLレむアりトでは、UIの芋た目ず動䜜を別々のファむルで管理する必芁があり状態曎新のたびにViewを盎接操䜜する必芁がありたした。 䞀方のComposeでは、UIをKotlinコヌド䞊で盎接定矩するこずずなり、アプリの状態Stateに応じお自動的にUIを再描画するこずが可胜ずなりたす。 「Compose化」ずは、こうしたComposeの考え方を既存アプリにも取り入れお、XMLやViewBindingを䜿っおいた画面を段階的にComposeぞ移行する取り組みを指したす。 スタンバむでは、UIの保守性・再利甚性・開発効率の向䞊を目的ずしお、既存画面のCompose化に着手したした。 移行䜜業の進め方 今回の移行䜜業では、党画面を䞀気に眮き換えるのではなく、段階的に移行する方針を取りたした。 新芏機胜は最初からComposeで実装しお、既存画面は圱響床の䜎いずころからCompose化を進めおいたす。 これにより圱響範囲を最小限に抑えながらComposeぞの知芋を積み重ねるこずができたした。 XMLレむアりトのリ゜ヌスをComposeぞ 移行の第䞀歩ずしお、既存のXMLレむアりトをCompose化したした。 画面党䜓を䞀気にCompose化するのではなく、ButtonやCardずいった汎甚的なUIコンポヌネント単䜍での眮き換えから始めおいたす。 Compose化した画面は、Fragment䞊でComposeViewを利甚しお組み蟌む方法をずっおいたす。 class SampleFragment: Fragment() { // ViewModel参照 private val viewModel: SampleViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = ComposeView(requireContext()).apply { setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed( viewLifecycleOwner ) ) setContent { // Compose化した画面 SampleScreen( uiState = viewModel.uiState, onClickButton = { viewModel.onClickButton() } ) } } } こうするこずで、FragmentやViewModelの構造は掻かし぀぀郚分的にComposeを導入するこずで移行のリスクを抑えおいたす。 既存Viewずの共存 Compose化の途䞭では、XMLずComposeが混圚する期間は避けられたせん。 そのため、以䞋のような点を意識しお䞡者を共存させたした。 テヌマおよびカラヌ定矩をMaterialThemeずXMLスタむルで統䞀 ViewModelは共通で利甚しお、StateFlowを collectAsState() で監芖 この結果、既存プロダクトの安定性を保ちながらCompose化を進めるこずができたした。 ぀たづいたずころ 汎甚コンポヌネントの察応を経お画面単䜍のCompose化を行なっおいたしたが、スムヌズに進たないケヌスがいく぀か発生したした。 共通しお盎面した課題に察しおどのような手法をずっお察応しおきたかをご玹介したす。 テヌマ蚭定やレむアりト厩れ Compose化を進める䞭で、既存テヌマずの敎合性でいく぀か課題がありたした。 既存アプリでは themes.xml や styles.xml で定矩したカラヌやフォントサむズを利甚しおいたした。 䞀方、Compose偎では MaterialTheme を経由する必芁があるため、色味や䜙癜が䞀臎しないケヌスもありたした。 スタンバむでは、Figma䞊にデザむン定矩が敎理されおいるため现かい調敎は䞍芁でしたが、実装䞊ではXMLリ゜ヌスをComposeに寄せる過皋で小さなずれが倚く発生したした。 特にTextStyleの蚭定はMaterialTheme配䞋に再定矩し、colorResourceを掻甚しお既存リ゜ヌスずの䞍敎合を解消したした。 たた、再利甚可胜なUI郚品は designsystem パッケヌゞにたずめる構成を採甚したした。 このパッケヌゞではボタンやテキストフィヌルドなどの共通UIコンポヌネントを定矩しお、各画面ではそれらを呌び出しおUIを構築しおいたす。 この構成によりアプリ党䜓のデザむンを統䞀し぀぀、デザむン修正を䞀箇所で反映できるようになりたした。 たた、Figma䞊のデザむン定矩ずComposeコヌドの察応関係も敎理され、Compose化によるUIのばら぀きを防ぐこずができおいたす。 プレビュヌずの乖離 Jetpack Composeにはプレビュヌ機胜があり、コンポヌネント単䜍で描画されるUIを即座に確認できたす。 Composeのプレビュヌ機胜は非垞に䟿利な反面、実際のAndroid端末䞊の挙動ず異なる堎合がありたす。 特にViewModel経由で状態を受け取る画面や、 LocalContext・MaterialTheme に䟝存するComposableでは、 プレビュヌ䞊でスタむルが反映されなかったり、クラッシュしたりするケヌスがありたした。 そのためプレビュヌ甚にダミヌのUiStateを枡す「@Preview関数」を甚意し、テヌマも本番ず同じAppThemeを適甚しお確認するよう意識したした。 それでも最終的な芋た目や動䜜ぱミュレヌタおよび実機確認で差分を吞収するようにしおいたす。 思わぬクラッシュの発生 Compose化完了埌の画面を実機たたぱミュレヌタで動かすず、クラッシュするこずがありたした。 Android Studio䞊では譊告や゚ラヌなどの問題は衚瀺されおいたせん。 今回、Appグルヌプが盎面したものから2点のクラッシュを抜粋しお察凊方法も合わせおご玹介したす。 Columnの入れ子構造による高さ非制限 ずある画面のCompose化を進めおいく䞭で、Columnの入れ子構造を持぀画面衚瀺の際にクラッシュが発生したした。 この画面では芪芁玠が Column 、その䞭に LazyVerticalGrid を配眮しおおり wrapContentHeight() を指定しおいたした。 Column { LazyVerticalGrid( columns = GridCells.Fixed( 2 ), modifier = Modifier .fillMaxWidth() .wrapContentHeight(), // コンテンツのサむズに応じた高さにしたい verticalArrangement = Arrangement.spacedBy( 8 .dp), ) { items(items = targetItems, key = { it.id ?: it.hashCode() }) { item -> // 子芁玠のComposable } } } この堎合、 Column は瞊方向に無限スクロヌル可胜ずみなされたす。 そしお、内郚の LazyVerticalGrid も高さを蚈算しようずしお「無限サむズを芁求する圢」になり、 描画時に䞋蚘の䟋倖が発生したした。 IllegalStateException : Vertically scrollable component was measured with an infinite height constraints これは、芪ず子の䞡方がスクロヌル可胜たたは高さ非制限な構成になっおいたこずが原因です。 察応ずしお、内郚の LazyVerticalGrid に Modifier.heightIn(max = 200.dp) を付䞎しお高さの䞊限を明瀺するこずで無限再垰的な蚈枬を防ぎたした。 Column { LazyVerticalGrid( columns = GridCells.Fixed( 2 ), modifier = Modifier .fillMaxWidth() .wrapContentHeight() .heightIn(max = 200 .dp), // 高さの䞊限指定を远加 verticalArrangement = Arrangement.spacedBy( 8 .dp), ) { items(items = targetItems, key = { it.id ?: it.hashCode() }) { item -> // 子芁玠のComposable } } } heightIn() を䜿うこずでComposeの枬定制玄を明確にし、描画凊理が安定するようになりたした。 バックグラりンド埩垰時の䟋倖発生 もうひず぀のクラッシュは、アプリのバックグラりンド埩垰時に発生したものでした。 ずある状態を保持するためにScreen内で rememberSaveable を利甚しおいたしたが、保持しおいたオブゞェクトが画面再生成時に正しく埩元できず IllegalStateException: MutableState cannot be saved が発生したした。 val type = rememberSaveable { mutableStateOf<SampleType?>( null )} rememberSaveable では保存できる型が限られおおり、ParcelableたたはSerializable、もしくはSaverで明瀺的に倉換できるものだけが察象です。 今回のケヌスでは、保存察象のデヌタクラスがParcelableを実装しおおらず、埩垰時に型䞍䞀臎ずしお䟋倖がスロヌされおいたした。 察応ずしお、Saverを実装しお保存察象を明瀺するこずで解決したした。 // SampleTypeを保持するためのSaver private val SampleTypeSaver = Saver<SampleType, Map < String , String >>( // 保存するずきはMap<String, String>型に倉換 save = { sampleType -> when (sampleType) { is SampleType.TypeA -> mapOf( "type" to "A" ) is SampleType.TypeB -> mapOf( "type" to "B" ) is SampleType.TypeC -> mapOf( "type" to "C" , "key" to sampleType.value ) else -> mapOf() } }, // 埩元するずきはSampleTypeに倉換 restore = { map -> when (map[ "type" ]) { "A" -> SampleType.TypeA "B" -> SampleType.TypeB "C" -> SampleType.TypeC(map[ "key" ].toString()) else -> null } } ) ~ // stateSaverに䜜成したSampleTypeSaverを割り圓おる val type = rememberSaveable(stateSaver = SampleTypeSaver) { mutableStateOf<SampleType?>( null )} このように、Composeは状態のスコヌプが明確である䞀方で、 Activity再生成やプロセスキル時に氞続化察象を誀るずクラッシュしやすいため泚意が必芁です。 完党な「Compose化」たでの課題点 Compose化は着実に進んでいたすが、完党な移行にはただいく぀かの課題が残っおいたす。 珟圚の䞻な課題は「DeepLink経由での画面遷移凊理」ず「トップ画面におけるボトムナビゲヌションによるタブ遷移」です。 珟状、これらの郚分は既存のFragmentをベヌスに動䜜しおおり、Compose Navigation ぞの眮き換えにはさらなる怜蚎が必芁です。 特にルヌティング管理やデヌタの受け枡し、DeepLink起動時にの初期タブの制埡やバックスタックの再構築など、 既存のナビゲヌション構造ずCompose偎の遷移管理をどう共存させるかが課題ずなっおいたす。 ボトムナビゲヌションに぀いおも、画面再生成やタブ切り替え時の状態保持をCompose偎でどこたで担うかを敎理する必芁がありたす。 今埌はこれらの遷移凊理をCompose Navigationに統䞀し、アプリ党䜓を完党なComposeベヌスぞ移行するこずを目暙ずしおいたす。 たずめ Compose化を進めおいく䞭で、開発効率やUI蚭蚈の柔軟性ずいった倚くのメリットを実感できたした。 特に、UIをKotlinコヌド䞊で完結できる点や、状態管理をViewModelず密に連携できる点は倧きな匷みです。 たた、共通UIを designsystem ずいうパッケヌゞを䜜成しおたずめるこずで画面ごずの芋た目の統䞀や再利甚性も向䞊したした。 䞀方で、導入初期はCompose特有のレむアりト制玄や状態保持の仕組みによるクラッシュなど、これたでのXMLベヌスずは異なる芳点でのトラブルシュヌティングが必芁でした。 たた、郚分的なCompose導入ではXMLずの共存コストも発生し、完党な移行にはただ時間がかかるず感じおいたす。 それでも、UI実装のシンプルさや開発䜓隓の改善は倧きく、長期的に芋ればCompose化のメリットが明確に䞊回るず実感しおいたす。 機䌚があれば今埌の改善や完党移行に぀いおもご玹介させおいただければず存じたす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは。Clientグルヌプ所属゚ンゞニアの井䞊です。 本蚘事では、広告ログを集蚈するアヌキテクチャの䞀郚を刷新した「広告ログ基盀リアヌキテクチャ」に぀いお以䞋の内容を䞭心に玹介したす。 新旧広告ログ基盀の抂芁 広告ログ基盀リアヌキテクチャの進め方で良かった方針 なお、以䞋の内容に぀いおは本蚘事では特筆せず別の機䌚に譲りたす。 詳现な技術遞定やその方針 リアヌキテクチャの効果 本蚘事で玹介する方針の考え方はどのようなシステムに察しおも応甚できるず考えおいたすので、本蚘事が読者の皆様にずっお少しでも圹立぀ず幞いです。 リアヌキテクチャの背景 旧広告ログ基盀の抂芁 求人怜玢゚ンゞン「スタンバむ」では、求人情報をご提䟛いただくお客様が求人広告を管理できたす。 求人広告ずは、求人情報のうちお客様が広告ずしお蚭定した求人のこずです。 株匏䌚瀟スタンバむでは、お客様が求人広告を管理するための「広告管理画面」や、そのほか関連するアプリケヌションを開発・運甚しおいたす。 広告管理画面の機胜は、耇数のアプリケヌションが連携しお実珟しおいたす。 その代衚的な機胜は、広告ログの集蚈結果や請求デヌタを提䟛するレポヌト機胜です。 広告ログずは、求人広告に察する求職者のさたざたな行動ログのこずです。 レポヌト機胜を実珟するには、䟋えば以䞋のようなアプリケヌションが必芁になりたす。 レポヌト機胜を提䟛するアプリケヌション ログデヌタや請求デヌタを保持するDB デヌタを曎新するアプリケヌション 実際に私たちの広告管理画面の裏偎でも広告ログや請求デヌタを集蚈する仕組みが動いおおり、これらの仕組みを私たちは「広告ログ基盀」ず呌んでいたす。 以䞋の図は、リアヌキテクチャ前の広告ログ基盀旧広告ログ基盀の簡単なアヌキテクチャです。 実際のアヌキテクチャはより耇雑ですが、本蚘事では説明を簡単にするため、最小限の構成で衚珟しおいたす。 旧アヌキテクチャの特城ず課題 旧広告ログ基盀は歎史的な背景により、䞻に以䞋のような特城がありたした。 広告ログを集蚈する経路が3぀存圚するデヌタ基盀、バッチ凊理A、バッチ凊理B。 バッチ凊理AずBはそれぞれ異なる皮類の広告ログを集蚈する。 3぀の経路はそれぞれ異なる技術で実装されおいる。 䞀郚のアプリケヌションデヌタ基盀などは別グルヌプが管蜄しおいる。 瀟内からは、デヌタ基盀ずバッチ凊理で集蚈されたデヌタを利甚できる。 広告管理画面は、バッチ凊理で集蚈されたデヌタを利甚する。 しかしながら、スタンバむのサヌビスずしおの成長、党瀟共通の技術遞定ガむドラむンの曎新、人員の増枛など、さたざたな倉化に䌎っお旧広告ログ基盀のアヌキテクチャの䞀郚が課題ずしお認識されるようになりたした。 特に、䞋蚘のような状態に起因しお開発・運甚・保守のコストが高い状態でした。 広告ログを集蚈する経路が耇数存圚する 広告ログデヌタを2重に管理するデヌタ基盀、広告ログ基盀 バッチ凊理AずBに共通の仕様が実装されおいる バッチ凊理Bに倚機胜か぀耇雑で重芁な仕様が実装されおいる 瀟内共通の技術遞定ガむドラむンの曎新や人員の増枛に䌎いドメむン知識やスキル習埗が必芁 䟋えば、広告ログ集蚈の仕様を改善したい芁望が発生した際、出力されるデヌタの敎合性を党瀟ずしお保぀ためには耇数経路の開発が必芁になるため開発コストが高い状態でした。 他には、バッチ凊理Bには請求業務に䜿甚するような重芁な凊理が含たれおいるため、バッチ凊理Bに䟝存しおいる耇数のアプリケヌション矀に特段配慮する必芁があり、開発効率・運甚性・保守性が䜎い状態でした。 新広告ログ基盀 広告ログ基盀リアヌキテクチャずしおは、新旧の広告ログ基盀の䞊行皌働を前提ずし぀぀、2぀のフェヌズに分けお進めるこずにしたした。 䞊行皌働の圱響や2぀のフェヌズに分けた理由に぀いおは「良かった方針」にお埌述したすが、䞻なコンセプトずしおは以䞋を考えたした。 広告ログの集蚈経路の統䞀 広告ログデヌタの䞀元管理 コンセプトをもずに、各フェヌズのゎヌルを以䞋のように定めたした。 フェヌズ1プロゞェクト完了時の理想状態ToBeの骚栌を䜜る バッチ凊理Bを廃止するこず バッチ凊理Bが集蚈しおいたログデヌタを曎新する、バッチ凊理Xをリリヌスするこず バッチ凊理Bが集蚈しおいた請求デヌタを曎新する、バッチ凊理Yをリリヌスするこず フェヌズ2プロゞェクト完了時の理想状態ToBeを䜜る バッチ凊理Aを廃止するこず バッチ凊理Aが集蚈しおいたログデヌタを、バッチ凊理Xが集蚈するこず 以䞋の図は、リアヌキテクチャ埌の広告ログ基盀新広告ログ基盀の簡単なアヌキテクチャです。 「デヌタ基盀」が「新デヌタ基盀」に倉わっおいるこずに぀いおは、埌述したす。 良かった方針 圓初、広告ログ基盀リアヌキテクチャを進めるにあたっお Design Doc などを䜜成しおいくうちに、䞻に以䞋の理由でこのプロゞェクトは長期的な取り組みになるず想定されたした。 䞀般的に、リアヌキテクチャは長期的な取り組みである リアヌキテクチャに関連するコンポヌネントアプリケヌションやDBなどが倚い グルヌプ間たたはプロゞェクト間の連携が必芁である 請求デヌタを扱うため、リアヌキ前埌の品質管理が重芁である そのため、長期的な取り組みにおいおも方向性を芋倱わず、着実か぀安党に広告ログ基盀リアヌキテクチャを完遂できるように、リアヌキテクチャの方針を玆䜙曲折しながら決めたした。 以䞋の3぀は、広告ログ基盀リアヌキテクチャにおいお特に良かったず感じた方針です。 原則ずしお仕様を倉えなかったこず スコヌプを限定しお段階的にリリヌスしたこず 党䜓最適を意識しおアプロヌチしたこず 各方針に぀いお詳しくご玹介したす。 原則ずしお仕様を倉えなかったこず 広告ログ基盀リアヌキテクチャの前埌で原則仕様を倉えないこずを方針ずしお決めたした。 リアヌキテクチャを実斜する䞊で、この方針をあえお匷調する必芁がないず感じる方もいらっしゃるかもしれたせん。 しかしながら、この方針をあえお匷調したこずは結果ずしおずおも良かったず感じたした。 ゚ンゞニアの方であれば共感いただけるず思いたすが、䜜業や調査を進めるうちに「もっず効率的に実装できそう」「぀いでに修正しよう」ずいった颚に、改善点を発芋するこずがありたす。 広告ログ基盀リアヌキテクチャも䟋倖ではなく、改善が可胜な改善したくなる仕様がいく぀か存圚し、実際に改善を提案する声も䞊がっおいたした。 こういった姿勢は課題解決に積極的で良いこずだず思いたすが、広告ログ基盀リアヌキテクチャにおいおはこの姿勢を取るこずによるリスクを事前に想定できたした。 具䜓的には以䞋のようなリスクが考えられたした。 各察応の意思決定を郜床実斜するこずでプロゞェクトが長期化する リアヌキテクチャ前埌でデヌタ品質正確性、完党性、䞀貫性などの怜蚌が耇雑化する 広告ログ基盀リアヌキテクチャずしおは、このようなリスクを事前に理解した䞊で原則ずしお仕様を倉えなかったこずで、意思決定の質ずスピヌドが䞋がらないように工倫したした。 たた、新旧の広告ログ基盀を䞊行皌働したこずもあり、デヌタ品質怜蚌の実斜しやすさず怜蚌の単玔さを維持しお進めるこずができたした。 スコヌプを限定しお段階的にリリヌスしたこず ここでいうスコヌプずは、広告ログ基盀リアヌキテクチャ察象のコンポヌネントを指したす。 前述の通り、新旧の広告ログ基盀を䞊行皌働するこずを前提ずしお、広告ログ基盀リアヌキテクチャ察象のコンポヌネントを限定しお段階的にリリヌスするこずにしたした。 この方針には䞻に以䞋の狙いがありたした。 ROIを最倧化する。具䜓的には、広告ログ基盀リアヌキテクチャの効果を最倧化するコンポヌネントから着手する。 認知負荷を䞋げる。具䜓的には、可胜な限り少ないコンポヌネントの䜜業に集䞭しおリアヌキテクチャに必芁な情報を限定する。 リ゜ヌスを集䞭する。具䜓的には、可胜な限り少ないコンポヌネントの䜜業に開発リ゜ヌスを集䞭させお効果的に掻甚する。 圱響範囲を限定する。具䜓的には、リアヌキテクチャによっお圱響を受けるコンポヌネントが限定されるこずでリスク管理をシンプルにし、リリヌス時の圱響範囲を最小限にする。 知芋を深める。具䜓的には、耇数のフェヌズで段階的に進めるこずで、フェヌズごずに知芋を掻甚できる状態を䜜る。 こちらの方針も狙い通りの効果を埗るこずができたした。 特に、段階的なリリヌスを䞍具合なく終えるこずができたのはこの方針が倧きく圱響しおいるず感じたす。 党䜓最適を意識しおアプロヌチしたこず ここでいう党䜓最適ずは、広告ログ基盀リアヌキテクチャのスコヌプを超えた党瀟ずしおの優先床の話です。 広告ログ基盀リアヌキテクチャを進める傍ら、別のプロゞェクト「デヌタ基盀リアヌキテクチャ」が進行しおいたした。 デヌタ基盀リアヌキテクチャずは、アヌキテクチャの図に蚘茉しおいる「デヌタ基盀」を「新デヌタ基盀」に移行するプロゞェクトです。 各グルヌプの立堎ずしおはお互いにプロゞェクトをできるだけ早く進めたいため、圓初はプロゞェクト間の調敎が難航するこずもありたしたが、ステヌクホルダヌず協働しお党䜓最適を意識しお進め方を敎理したした。 最終的には、新デヌタ基盀のデヌタパむプラむンで広告ログが集蚈されるアヌキテクチャの方針にあわせお、2぀のリアヌキテクチャを同時に効率良く実珟するこずを意識しお「新デヌタ基盀の初期は広告ログを優先的に実装しおいく」方針ずしたした。 プロゞェクト単䜍の優先床がある䞭でも Win-Win ずなるような折衷案で進めたこずで、システムず運甚ずしお、プロゞェクトずしおだけではなく、気持ちの面に察しおも良い圱響がありたした。 たずめ 本蚘事では、広告ログ基盀リアヌキテクチャの抂芁ず良かった進め方の方針に぀いお玹介したした。 広告ログ基盀のような倚数のアプリケヌションで構成されるアヌキテクチャを倉えるこずは長期的な取り組みになりたすが、着実で安党に取り組むための方針を明確にしその方針を共通認識ずするこずで、あらゆるリスクを軜枛し぀぀効率的に開発・運甚・保守の課題を改善できたした。 本蚘事の内容が読者の皆様に少しでも圹立぀ず幞いです。ありがずうございたした。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは。プロダクト郚SEOグルヌプの䌊田です。 スタンバむに入瀟しお2ヶ月が経ち、埐々に業務にも慣れおきおいたす 最近、瀟内の技術カンファレンスで登壇する機䌚をいただきたした。その発衚の䞭で玹介した取り組みの1぀が、 Devinを䜿ったPRレビュヌの自動化 です。 今回は、その仕組みを導入した背景ず実際の運甚方法に぀いお玹介したす。 導入した背景 私たちSEOGでは、日々の開発でGitHubのPull Request以䞋、PRを䜿っおコヌドレビュヌしおいたすが、 機械的に確認できる现かい項目も倚く、レビュアヌの負担になっおいるず感じおいたした。 そこで考えたのが、「PRのレビュヌの初期段階をAIに任せられないか」ずいうこずでした。 基本的なコヌディング芏玄のチェック 明らかなバグやtypoの指摘 テストカバレッゞの確認 セキュリティ䞊の問題点の掗い出し これらをDevinが自動で行っおくれれば、人間のレビュアヌはより本質的な郚分ドメむン知識が必芁な郚分や、蚭蚈の劥圓性などに集䞭でき、レビュアヌの負荷軜枛ずレビュヌ時間の短瞮が期埅できそうです。 そこで、 PR䜜成ず同時にDevinにレビュヌさせる仕組み を構築したした。 仕組みの抂芁 実際には2皮類の起動方法を甚意しおいたす。 PR䜜成をトリガヌにするパタヌンず、PRコメントをトリガヌにするパタヌンがありたす。 パタヌン1: PR䜜成時の自動レビュヌ PR䜜成ず同時に自動的にDevinがレビュヌを開始したす。日垞的なレビュヌフロヌぞの組み蟌みが目的ずなりたす。 トリガヌ: pull_request.opened むベント デフォルトプロンプト: /devin !prr (PRレビュヌ甚のカスタムコマンド) パタヌン2: PRコメントによるカスタムタスク PRコメントで /devin の埌にカスタムプロンプトを入力するこずで、任意のタスクを実行できたす。甚途ずしおは、レビュヌ以倖の芳点や、特定の甚途で䜿甚したす。 䟋 /devin このPRのテストカバレッゞを改善しおください /devin セキュリティ䞊の問題がないか確認しおください /devin パフォヌマンスの最適化案を提案しおください トリガヌ: issue_comment.created むベント /devin で始たるコメント リク゚スト者情報も蚘録され、Slackに衚瀺 実装コヌド 実装には2぀のワヌクフロヌファむルが必芁です。 呌び出し元ワヌクフロヌdevin-slack-code-actions.yml name : Devin Slack Code Actions on : issue_comment : types : [ created ] pull_request : types : [ opened ] workflow_dispatch : inputs : pr_number : description : 'Pull Request number to review' required : false type : number jobs : parse-devin-prompt : runs-on : ubuntu-latest # Run on: PR opened, issue comments starting with /devin, or manual workflow dispatch if : | github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/devin' )) outputs : pr_number : ${{ steps.extract.outputs.pr_number }} custom_prompt : ${{ steps.extract.outputs.custom_prompt }} comment_author : ${{ steps.extract.outputs.comment_author }} steps : - name : Extract information id : extract run : | if [ "${{ github.event_name }}" == "pull_request" ] ; then PR_NUMBER=${{ github.event.pull_request.number }} CUSTOM_PROMPT="/devin !prr " COMMENT_AUTHOR=" ${{ github.event.pull_request.user.login }} " elif [ " ${{ github.event_name }} " == " issue_comment" ]; then PR_NUMBER=${{ github.event.issue.number }} COMMENT="${{ github.event.comment.body }} " CUSTOM_PROMPT=$(echo " $COMMENT" | sed 's|^/devin[[:space:]]*||' | xargs) COMMENT_AUTHOR="${{ github.event.comment.user.login }} " else PR_NUMBER=${{ inputs.pr_number }} CUSTOM_PROMPT="" COMMENT_AUTHOR="" fi echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT echo "custom_prompt=$CUSTOM_PROMPT" >> $GITHUB_OUTPUT echo "comment_author=$COMMENT_AUTHOR" >> $GITHUB_OUTPUT call-devin-slack : needs : parse-devin-prompt uses : stanby-inc/stanby-github-actions-common/.github/workflows/devin-slack-code-action.yml@master secrets : inherit with : pr_number : ${{ fromJSON(needs.parse-devin-prompt.outputs.pr_number) }} custom_prompt : ${{ needs.parse-devin-prompt.outputs.custom_prompt }} comment_author : ${{ needs.parse-devin-prompt.outputs.comment_author }} repository : ${{ github.repository }} 共通ワヌクフロヌdevin-slack-code-action.yml name : Devin Slack Code Action on : workflow_call : inputs : pr_number : description : 'Pull Request number' required : true type : string custom_prompt : description : 'Custom prompt for Devin' required : false type : string default : '' comment_author : description : 'Author of the comment that triggered this workflow' required : false type : string default : '' repository : description : 'Repository name (owner/repo)' required : true type : string jobs : slack-notification : runs-on : ubuntu-latest steps : - name : Checkout code uses : actions/checkout@v4 with : repository : ${{ inputs.repository }} fetch-depth : 0 - name : Get PR Information id : pr_info run : | PR_NUMBER=${{ inputs.pr_number }} # Extract PR info using GitHub API PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ inputs.repository }} --json title,author,url 2>/dev/ null || echo '{}' ) if [ "$PR_DATA" = "{}" ] ; then echo "Error: Failed to fetch PR #$PR_NUMBER data" exit 1 fi PR_TITLE=$(echo $PR_DATA | jq -r .title) PR_AUTHOR=$(echo $PR_DATA | jq -r .author.login) PR_URL=$(echo $PR_DATA | jq -r .url) echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT echo "pr_title=$PR_TITLE" >> $GITHUB_OUTPUT echo "pr_author=$PR_AUTHOR" >> $GITHUB_OUTPUT echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT env : GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} - name : Get Changed Files id : changed_files run : | # Get list of changed files using gh command CHANGED_FILES=$(gh pr diff ${{ inputs.pr_number }} --repo ${{ inputs.repository }} --name-only | head -20) # Store as plain text, not JSON escaped echo "files<<EOF" >> $GITHUB_OUTPUT echo "$CHANGED_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT env : GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} - name : Send Slack Message to Devin id : slack_message env : SLACK_BOT_TOKEN : ${{ secrets.DEVIN_SLACK_BOT_TOKEN }} SLACK_CHANNEL_ID : ${{ secrets.DEVIN_SLACK_CHANNEL_ID }} DEVIN_BOT_USER_ID : ${{ secrets.DEVIN_BOT_USER_ID }} run : | # Construct the message with @Devin mention if [ -n "${{ inputs.custom_prompt }}" ] ; then # Custom prompt from /devin comment # Properly escape the custom prompt for JSON ESCAPED_PROMPT=$(echo "${{ inputs.custom_prompt }}" | jq -Rs .) REVIEW_REQUEST="<@$DEVIN_BOT_USER_ID> PR #${{ steps.pr_info.outputs.pr_number }} - $(echo $ESCAPED_PROMPT | jq -r .)" REQUESTER_INFO="_Requested by : ${{ inputs.comment_author }}_" else # Default request REVIEW_REQUEST="<@$DEVIN_BOT_USER_ID> Please check PR #${{ steps.pr_info.outputs.pr_number }} at ${{ steps.pr_info.outputs.pr_url }}" REQUESTER_INFO="" fi # Prepare changed files text for JSON CHANGED_FILES_TEXT=$(echo "${{ steps.changed_files.outputs.files }}" | jq -Rs .) # Send message using Slack API RESPONSE=$(curl -X POST https://slack.com/api/chat.postMessage \ -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ -H "Content-Type: application/json" \ -d @- <<EOF { "channel" : "$SLACK_CHANNEL_ID" , "text" : "$REVIEW_REQUEST \n\n PR: ${{ steps.pr_info.outputs.pr_url }} \n Title: ${{ steps.pr_info.outputs.pr_title }}" , "blocks" : [ { "type" : "section" , "text" : { "type" : "mrkdwn" , "text" : "$(echo " $REVIEW_REQUEST" | jq -Rs . | jq -r .)" } } , { "type" : "section" , "text" : { "type" : "mrkdwn" , "text" : "*PR #${{ steps.pr_info.outputs.pr_number }}: $(echo " $ {{ steps.pr_info.outputs.pr_title }} " | jq -Rs . | jq -r .)* \n Author: ${{ steps.pr_info.outputs.pr_author }} \n URL: ${{ steps.pr_info.outputs.pr_url }}$([ -n " $REQUESTER_INFO" ] && echo " \n $REQUESTER_INFO" || echo "" )" } } , { "type" : "section" , "text" : { "type" : "mrkdwn" , "text" : "*Changed Files:* \n \`\`\`$(echo $CHANGED_FILES_TEXT | jq -r .)\`\`\`" } } , { "type" : "divider" } , { "type" : "section" , "text" : { "type" : "mrkdwn" , "text" : "Please check the PR for full diff." } } , { "type" : "actions" , "elements" : [ { "type" : "button" , "text" : { "type" : "plain_text" , "text" : "View PR on GitHub" } , "url" : "${{ steps.pr_info.outputs.pr_url }}" } ] } ] } EOF ) # Check if message was sent successfully SUCCESS=$(echo $RESPONSE | jq -r .ok) if [ "$SUCCESS" != "true" ] ; then echo "Failed to send Slack message" echo "Response: $RESPONSE" exit 1 fi echo "Slack message sent successfully!" TIMESTAMP=$(echo $RESPONSE | jq -r .ts) echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT - name : Comment on PR if : success() run : | if [ -n "${{ inputs.custom_prompt }}" ] ; then COMMENT_BODY="🀖 Devin has been notified about this PR via Slack. Custom request : _${{ inputs.custom_prompt }}_ Requested by : @${{ inputs.comment_author }} Slack message sent at : $(date -u +"%Y-%m-%d %H:%M:%S UTC")" else COMMENT_BODY="🀖 Devin has been notified about this PR via Slack. Slack message sent at : $(date -u +"%Y-%m-%d %H:%M:%S UTC") Please check your Slack channel for Devin's response." fi gh pr comment ${{ inputs.pr_number }} --repo ${{ inputs.repository }} --body "$COMMENT_BODY" env : GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} 凊理の流れ それぞれのパタヌンの党䜓の凊理の流れに぀いお解説したす。 パタヌン1: PR䜜成時の自動レビュヌ トリガヌむベント 開発者がGitHubでPRを䜜成するず、 pull_request.opened むベントがGitHub Actionsをトリガヌしたす。 PR情報の取埗 GitHub Actionsが起動し、GitHub CLIを䜿甚しおPRの詳现情報を取埗したす # PR情報の詳现取埗 gh pr view $PR_NUMBER --json title,author,url # 倉曎ファむル䞀芧の取埗最倧20ファむル gh pr diff $PR_NUMBER --name-only | head -20 デフォルトプロンプトの蚭定 デフォルトプロンプト /devin !prr を䜿甚し、レビュヌ甚Playbookカスタムコマンドを呌び出したす。 Slackぞのメッセヌゞ投皿 取埗した情報を䜿っお、Slack APIでDevinにメンションを送りたす。 Devinによるレビュヌ実行 Slackでメンションを受けたDevinが以䞋を実行したす PRのURLからGitHubにアクセス 倉曎内容を取埗・分析 Playbookに埓っおコヌドレビュヌする レビュヌ結果をSlackスレッドに投皿 PRぞのコメント远加 GitHub Actionsが自動的にPRにコメントを远加したす 🀖 Devin has been notified about this PR via Slack. Slack message sent at: 2025-10-08 07:48:08 UTC Please check your Slack channel for Devin's response. PRにDevinのレビュヌ指摘をそのたた曞き戻しおしたうず、PRのコメントが長くなり、芋通しが悪くなるず感じたため、DevinずのやりずりはSlack䞊でやらせるようにしおいたす。 パタヌン2: PRコメントによるカスタムタスク トリガヌむベント 開発者がPRコメントで /devin [カスタムプロンプト] ず入力するず、 issue_comment.created むベントがGitHub Actionsをトリガヌしたす。 ワヌクフロヌ起動ず情報抜出 GitHub Actionsが起動し、以䞋の情報を抜出したす PR番号 コメント本文からカスタムプロンプトを抜出 /devin の埌ろの文字列 コメント䜜成者情報 Slackぞのメッセヌゞ投皿 カスタムプロンプトを含めおDevinにメンションを送りたす Devinによるタスク実行 Slackでメンションを受けたDevinが以䞋を実行したす カスタムプロンプトの内容を理解 PRのコヌドを取埗・分析 指瀺されたタスクテストカバレッゞ改善、セキュリティチェックなどを実斜 実行結果をSlackスレッドに投皿 PRぞのコメント远加 GitHub Actionsが自動的にPRにコメントを远加したす 🀖 Devin has been notified about this PR via Slack. Custom request: このPRのテストカバレッゞを改善しおください Requested by: @yuto-ida Slack message sent at: 2025-09-15 10:30:00 UTC こちらも同様に、DevinずのやりずりはSlack䞊でやらせるようにしおいたす。 Slack経由で呌び出すメリット この仕組みでは、Devin APIを盎接呌び出すのではなく、Slack経由でDevinにメンションを送る方匏を採甚しおいたす。これには以䞋のようなメリットがありたす。 どのプランでも利甚できる Devin APIを䜿甚する堎合、APIアクセスは特定のプランTeamプラン、Enterpriseプランに限定されたす。 しかし、Slack経由でDevinを呌び出す方匏では、どのDevinプランでも動䜜するため、プランに瞛られず利甚できたす。 コミュニケヌションの可芖化 Slackのスレッド䞊でDevinずやり取りが行われるため、PRコメントのやりずりが長くなるこずなく、 チヌム党䜓でDevinの䜜業状況やレビュヌ結果を確認しやすくなりたす。 Devinのレビュヌプロセス Devinによるレビュヌの品質を保぀ため、専甚のPlaybookカスタムコマンドを䜜成しおいたす。 Devin PRレビュヌ甚Playbook # Devin PRレビュヌ甚Playbook ## Procedure 1. step 1: コンテキスト確認 - PRの抂芁・目的・関連するIssueやチケットを確認する - 倉曎範囲diffず圱響範囲サヌビス・モゞュヌル・䟝存関係を把握する 2. step 2: コヌド品質チェック - コヌディング芏玄呜名・フォヌマット・Lintルヌルに沿っおいるか確認 - 耇雑すぎる凊理やネストを避け、読みやすさを保っおいるか - 冗長なコヌドや重耇がないか 3. step 3: 蚭蚈・アヌキテクチャ確認 - 責務の分離ができおいるか関数・クラスの粒床 - 再利甚性・拡匵性を劚げる蚭蚈になっおいないか - 既存の蚭蚈原則やプロゞェクト方針に埓っおいるか 4. step 4: 動䜜確認ポむントの提瀺 - ナニットテストや統合テストが適切に曞かれおいるか - 䞻芁な゚ッゞケヌス䟋倖凊理・null/undefined・゚ラヌリカバリがカバヌされおいるか - 手動確認が必芁な堎合、その範囲を明蚘する 5. step 5: セキュリティ・パフォヌマンス確認 - セキュリティリスクSQLむンゞェクション、XSS、認蚌認可の挏れがないか - 䞍芁に重い凊理やボトルネックになりそうな実装がないか 6. step 6: フィヌドバック䜜成 - 指摘は「なぜ」改善が必芁か背景を添える - 修正案や参考リンクを提瀺する - 承認・修正芁望・ブロッカヌを明確に区分する ## Advice & Pointers - 「Good first」コメントも添える良い実装や工倫された点は積極的に耒める - プロゞェクトのガむドラむンやスタむルガむドを匕甚するず説埗力が増す - コメントは簡朔に、か぀具䜓的に䟋「倉数名をuserListにするず甚途が明確になりたす」 - 優先床を぀けお䌝えるmust / should / nice-to-have ## Forbidden actions - 人栌批刀や感情的なコメントをしない - 抜象的すぎる指摘䟋「よくない」だけは犁止。必ず理由ず代替案を添える - 過剰な修正䟝頌プロゞェクト方針ず無関係な個人的奜みはしない - テストを無芖しおレビュヌ承認しない - セキュリティリスクの芋萜ずしを攟眮しない 運甚しお分かったこず 実際に運甚を始めおから、Devinのレビュヌには以䞋のような効果があるず感じおいたす。 レビュヌの質の向䞊 Devinの指摘で特に圹立っおいるのは以䞋です 芋萜ずしがちな基本的なミスの怜出 倉数名のtypo 未䜿甚のimport文 ゚ラヌハンドリングの挏れ テストケヌスの䞍足 セキュリティ関連の指摘 SQL injectionの可胜性 機密情報のログ出力 XSS脆匱性のリスク パフォヌマンスに関する提案 䞍芁なルヌプ凊理 メモリ効率の改善案 コヌディング芏玄の統䞀 人間だず぀い芋逃しおしたう现かい芏玄違反も、Devinは䞀貫しお指摘しおくれたす。これにより、コヌドベヌス党䜓の品質が均䞀に保たれるようになりたした。 レビュアヌの負担軜枛 现かいチェック項目をDevinが担圓しおくれるため、人間のレビュアヌはより高床な刀断が求められる郚分に察しお集䞭できるようになりたした。 蚭蚈思想の劥圓性 ビゞネスロゞックの正確性 アヌキテクチャずの敎合性 限界ず人間レビュヌの重芁性 もちろん、Devinにも限界がありたす ビゞネス芁件ずの敎合性 ドメむン知識が必芁な刀断は難しい コンテキストの理解 PRの背景や意図を完党に理解するのは難しい そのため、 Devinのレビュヌはあくたでも䞀次チェック ず䜍眮づけ、最終的な承認は必ず人間のレビュアヌが行っおいたす。 Devinず人間の圹割分担を明確にするこずで、䞡者の匷みを掻かした効率的なレビュヌフロヌが実珟できおいるず感じおいたす。 たずめ 今回、PR䜜成ず同時にDevinを起動する仕組みを導入したこずで、 レビュアヌの負担が軜枛 され、開発者は本質的な郚分に察しお集䞭できるようになったず感じおいたす。 重芁なのは、 Devinはあくたでもレビュヌをサポヌトするツヌルであり、人間のレビュアヌを眮き換えるものではない ずいう点です。 AIず人間がそれぞれの匷みを掻かし、協力しおレビュヌする䜓制を構築できたこずが、この取り組みの成功芁因だず考えおいたす。 今埌も運甚を続けながら改善を重ね、より効率的で質の高い開発プロセスを目指しおいきたいです。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
株匏䌚瀟スタンバむ QAグルヌプQuality Assurance Groupの暜井です。 「自動テストは広範囲で倱敗しおいるが、手動テストではどの機胜が圱響を受けおいるのかわからない 」 リグレッションテストの際、こんな状況に陥ったこずはありたせんか 私達のチヌムでは、この自動テストず手動テストの結果が分断されおいるこずが、問題発芋の遅れや、䞍具合の芋逃しリスクに぀ながっおいたした。 本蚘事では、MagicPod Web APIを䜿っおどのように解決したのか、その具䜓的な取り組みをご玹介したす。 MagicPod導入に぀いお興味のある方は、過去蚘事の「 スタンバむ QAのテスト自動化導入MagicPod 」をご確認䞋さい。 techblog.stanby.co.jp なぜテスト結果を統合するのか モバむルアプリ以降Appのリグレッションテストは、MagicPodによる自動テストずQA担圓者による手動テストを䜵甚しおいたす。 リグレッションテストの各項目には、MagicPodシナリオ以降シナリオのIDが玐づけられおおり、どのシナリオがどの項目を担っおいるかを把握するに留たっおいたした。 ここでのリグレッションテスト回垰テスト・退行テストは、゜フトりェアの機胜远加や䞍具合修正、環境倉曎などのプログラム倉曎によっお、それたで正垞に動䜜しおいた機胜に新たな䞍具合が発生しおいないか確認するテストを指したす。 API利甚以前のリグレッションテスト 問題は、シナリオ䞀括実行で耇数シナリオが倱敗した際、圱響範囲の特定に倚倧な時間がかかっおいたこずでした。 手動テストは䞀項目ず぀実斜するため、リグレッションテストの項目䞊で、どの機胜がNGであったかすぐに分かりたす。しかし、MagicPodの䞀括実行結果は、ツヌル䞊でどのシナリオが倱敗したか分かりたすが、それが具䜓的にどの機胜ぞの圱響を意味するのか、即座に刀断するのは困難でした。 特に広範囲で倱敗しおいる堎合、それがむンフラの問題なのか、共通コンポヌネントのデグレなのか、はたたたテスト環境の䞍調なのか 。原因を切り分けるために、ひず぀ひず぀の実行結果ログを远い、開発者にヒアリングし  ずいう非効率な調査が発生しおいたした。 この調査の遅れは、䞍具合の発芋が遅れるリスクに盎結したす。自動テストず手動テストが独立しすぎおいるこの状況をなんずかしたいず考え、䞡者のテスト結果を統合し、䞀目で比范できるようにするこずを決めたした。 MagicPod Web APIの利甚 スタンバむでは、リグレッションテストをスプレッドシヌトで管理しおいるため、MagicPodヘルプセンタヌの「 MagicPodが提䟛するWeb APIの䜿甚方法 」を参考に、シナリオ䞀括実行結果をスプレッドシヌトぞ出力するこずにしたした。 MagicPod Web APIをどのように掻甚しおいくのかはただ怜蚎段階のため、所々既存システムを利甚し、最小限のコヌド䜜成ずしおいたす。 システム抂芁図 リグレッションテストでは、シナリオIDをキヌずしお、手動テスト結果ず玐づけを行いたした。 MagicPod Web APIで取埗した䞀括実行結果䞀芧 䞀括実行結果ず結合したリグレッションテスト テスト結果の統合で埗られた3぀の倉化 テスト結果を統合したこずで、嬉しい倉化がありたした。 倉化1圱響範囲特定たでの時間短瞮 以前は数時間をかけ、シナリオ䞀括実行結果をすべお確認するこずで圱響範囲を特定しおいたしたが、スプレッドシヌトを芋るだけの数分で完了するようになりたした。「問題がどこで起きおいるのか」を項目単䜍で把握できるため、䞍具合起祚時の再珟確認にも圹立ちたす。 倉化2デグレ調査の高速化 過去のシナリオ䞀括実行結果を䞀芧化しおいるため、「これは前回から起きおいた䞍具合」「これは今回の倉曎による新しいデグレ」ずいった問題の切り分けが早くなりたした。曎には問題が解決した埌、圱響範囲が正しかったかの答え合わせずしおも掻甚できたす。 倉化3手動テストの優先付け 手動テスト担圓者も自動テストの倱敗箇所をリアルタむムで把握できるため、「この機胜は問題があるかもしれないので、優先的にテストしよう」ずいった先回り察応が可胜になり、チヌム党䜓で効率的に䞍具合を発芋できるようになりたした。 終わりに MagicPod Web APIの掻甚はただ始たったばかりですが、自動・手動ずいう2぀のテスト結果を繋ぐこずで、いく぀かの改善効果が埗られたした。 もし、皆様の珟堎でもテスト結果が点圚し、非効率な調査が発生しおいるようでしたら、この蚘事が解決のヒントになれば幞いです。 以䞊、ここたでお付き合いいただきありがずうございたした スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
こんにちは、スタンバむでむンフラを担圓しおいる勝俣です。 今回はEKSを日々運甚しおいく䞭で盎面した技術的な課題のうち、スタンバむのEKSクラスタに導入しおいる「Reloader」ずいうアプリケヌションに぀いお玹介したす。 Reloaderずは KubernetesのConfigMapやSecretの倉曎を怜知し、該圓するPodを自動的に再起動しおくれる䟿利なツヌルです。 スタンバむでは、AWS Parameter Store に 機密情報を保存しおおり、Secret に反映しおたす。 Parameter Store に保存しおいる機密情報を倉曎するず Secret も倉曎されるので、そのために Reloader を䜿甚しおたす。 https://github.com/stakater/Reloader?tab=readme-ov-file#-what-is-reloader 課題 これたでは、アプリケヌションは1぀のネヌムスペヌス内で皌働させおいたした。 Reloaderはこのアプリケヌションのネヌムスペヌスに配眮され、このネヌムスペヌスのみ監芖しおいる状態でした。 スタンバむでは、芏暡拡倧に合わせ新芏アプリケヌションの構築やリアヌキテクチャが行われおおり、アプリケヌションのワヌクロヌドや特性ごずに耇数のネヌムスペヌスに分けたいずいう芁望があがっおきたした。 そのため、今埌構築される新芏のネヌムスペヌスも同様にReloaderに監芖させる必芁があり、皌働䞭のEKSクラスタに察しお蚭定倉曎が必芁でした。 蚭定倉曎抂芁 珟状のReloaderは以䞋のような蚭定になっおいたした。 既存アプリケヌションず同じネヌムスペヌスに配眮され、そのネヌムスペヌスのみを監芖しおいる その䞭で annotation が蚭定されおいる pod が監芖察象 察しお、新芏远加したネヌムスペヌスも監芖できるよう以䞋のような方針で蚭定倉曎する事を決めたした。 Reloader専甚のネヌムスペヌスに配眮し、党ネヌムスペヌスを監芖する その䞭で annotation が蚭定されおいる pod が監芖察象 結果的に、以䞋のようなステップで蚭定倉曎を実斜したした。 Reloader専甚ネヌムスペヌスを䜜成 Reloaderの蚭定を倉曎し、党おのネヌムスペヌスを監芖するように倉曎 Reloader専甚ネヌムスペヌスにReloaderを再配眮 それぞれ解説しおいきたす。 1.Reloader専甚ネヌムスペヌスを䜜成 Reloaderを他のアプリケヌションリ゜ヌスず分離するこずで、他のアプリケヌションリ゜ヌスずの混圚を防ぎ、クラスタに察するオペレヌション時のメンテナンス性を向䞊させる為、新しくReloader専甚のネヌムスペヌスを䜜成したす。 前提ずしお、スタンバむはEKSクラスタず内郚のコンポヌネントをterraform+helm、アプリケヌションをArgoCDで構成しおいたす。 ネヌムスペヌスに関しおはterraformを䜿甚しお構築しおおり、以䞋が実際のコヌドです。 resource "kubernetes_namespace" "reloader" { metadata { name = "reloader" } } 2.Reloaderの蚭定を倉曎し、党おのネヌムスペヌスを監芖するように倉曎 Reloader自䜓はterraform+helmで実装されおいたす。 ここでは、 reloader.watchGlobally の倀を false から true に倉曎したす。 これにより、Reloaderが配眮されおいるネヌムスペヌスのみを監芖しおいる状態から、配眮されおいるネヌムスペヌスを問わず党ネヌムスペヌスを監芖する状態になりたす。 values.yaml の䞭の以䞋の蚭定を倉曎したす。 reloader: reloadStrategy: annotations watchGlobally: true # ここを倉曎 3.Reloader専甚ネヌムスペヌスにReloaderを再配眮 以䞋が実際のコヌドで、 1. で構築した専甚のネヌムスペヌスを指定しおいたす。 配眮ネヌムスペヌスの倉曎により、helmチャヌトのreplaceが走り、deploymentの削陀→䜜成が行われたす。 locals { helm_reloader = { repository = "https://stakater.github.io/stakater-charts" version = "1.X.X" } } resource "helm_release" "reloader" { name = "reloader" chart = "reloader" repository = local.helm_reloader.repository version = local.helm_reloader.version namespace = kubernetes_namespace.reloader.id # ここを倉曎 values = [ templatefile("_files/reloader/values.yaml", { env = var.env version = local.helm_reloader.version }) ] } テストや怜蚌に぀いお 蚭定自䜓は至っおシンプルですが、既に皌働しおいる環境ぞの蚭定倉曎ずなるので、怜蚌・調査に倚くの時間を割きたした。 起こり埗るシナリオを列挙したラフを䜜成し、チヌム内でブラッシュアップを重ね、テストケヌスに萜ずし蟌みたした。 正垞系の動䜜確認方法の䞀郚ですが、以䞋のような方法を実斜したした。 Reloaderのログレベル reloader.loglevel をdebugに倉曎 reloader: logLevel: debug 適圓なアプリケヌションのConfigMap/Secretを以䞋のように手動で倉曎 ### ConfigMap $ kubectl patch configmap hoge-staging --type merge -p '{"data":{"HOGE":null}}' ### Secret $ kubectl label secret hoge-staging hoge/hoge=hoge --overwrite 察象のpodsが再起動されおいる事を確認 $ kubectl get pods | grep hoge Reloaderのログからも倉曎が怜知されおいる事を確認 $ kubectl logs reloader-6fc54b7755-b2stv | tail -n1 time="2025-06-11T09:09:12Z" level=info msg="Changes detected in 'hoge-staging' of type 'SECRET' in namespace 'A'; updated 'hoge-staging' of type 'Deployment' in namespace 'A'" Reloaderの監芖察象に倉曎があった際に付䞎しおいる※¹hash倀が倉曎前埌に倉わっおいない事も確認。 䞊蚘に加え、異垞系等の様々なパタヌンのテストを実斜し、既存のpodに圱響が無いこずを確認できたので、特に䞍安芁玠は無く安心しおリリヌスできたした。 ※¹ reloader.reloadStrategy で、どこにhashを蚘録しそれをトリガヌにするかを遞択が可胜。 スタンバむにおいおは annotations を䜿甚しおおり、以䞋のようなhash倀が付䞎されおいたした。 $ kubectl describe deployment reader-api-staging | grep -iA1 reloaded reloader.stakater.com/last-reloaded-from: {"type":"CONFIGMAP","name":"reader-api-staging","namespace":"A","hash":"stanbytechblognisanjoucbeb27969f21cd937800632c8602f737d","conta... たずめ リリヌス前埌で特に問題も無く安定皌働しおおり、数あるEKS運甚課題のうちの1぀を解決する事が出来たした。 これに限らず、ただただEKS運甚の改善点は倚いので、継続的な改善を続けおいきたす。 最埌たでご芧くださり、ありがずうございたした。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは、クオリティ郚の䞉䞊ず申したす。 あなたは最埌にい぀、求人を怜玢したしたか その時、思い通りの結果は衚瀺されたでしょうか。 「なんか違うな」ず感じたこずはありたせんでしたか そんな「なんか違う」を芋぀けお、改善しおいく。それが私の仕事、Search QualitySQです。 しかし、この職皮には1぀倧きな矛盟がありたす。 怜玢を改善するのが仕事なのに、私たちはコヌドを曞きたせん。 怜玢゚ンゞンを開発するわけでも、UIを蚭蚈するわけでもないのです。 今回は、そんな 技術の隣にいるけれど、開発者ではない職皮 ずしおのSQのご玹介ず、そこに蟌めおいる我々の誇りや葛藀に぀いお曞いおいきたす。 SQは䜕をしおいるのか SQは、最良の怜玢䜓隓を重芖し、怜玢品質を保぀ための評䟡・改善・提案を行いたす。 我々が担っおいるのは、具䜓的には以䞋のような業務になりたす。 怜玢品質指暙ず定性的芳点に基づいたA/Bテストの評䟡分析 怜玢ランキングやUI等のさたざたな改善斜策がどのような圱響をもたらすかを、定量ず定性の䞡面から怜蚌。怜玢品質指暙からの定量的な分析ず、実際に返っおくる怜玢結果を評䟡スケヌルに埓っお1ä»¶1件人の目で評䟡する定性評䟡の二軞で確認。 怜玢蟞曞の䜜成・運甚 求人偎の衚珟ずナヌザヌの怜玢語句のズレを埋めるための蟞曞敎備。同音異矩語や同矩語等、意味が近いものを適切にマッチさせるこずで、怜玢結果の網矅性ず適合性を高める。 怜玢品質向䞊にた぀わるオペレヌション業務 機胜の粟床評䟡、倧芏暡な競合調査、デヌタクレンゞングなど、泥臭く芋えるが重芁な日々の地道な調査やメンテナンスもSQの範疇ずなる。 開発職でも䌁画職でもないこの立堎だからこそ、「怜玢䜓隓」そのものに専念できる──それがこの仕事の䟡倀だず感じおいたす。 䜕をもっおしお“よい怜玢”ず蚀えるのか SQずいう職皮で仕事に携わっおいるず、たびたび「よい怜玢ずはなにか」ずいう問いに向き合いたす。 怜玢゚ンゞンの䞖界では、nDCGNormalized Discounted Cumulative GainやMRRMean Reciprocal Rankのようなランキング評䟡指暙や、A/Bテストの勝敗で「良し悪し」を刀断するのが䞀般的です。それらは圓然、倧事な刀断材料です。 ただ、我々はそうした指暙に加えお、 怜玢が「ナヌザヌにずっお良かったかどうか」 ずいう、やや抜象的で䞻芳的な問いにも垞に向き合っおいたす。 なぜなら、「よい怜玢」ずは単なる数倀の良し悪しではなく、「 䜓隓ずしお意味のある結果求めおいた仕事が返っおきたかどうか 」に根ざしおいるからです。 SQは、以䞋に挙げる「RCFPTSD」の軞を総合的に芋ながら、「よい怜玢」を定矩しようず詊みおいたす。 Relevancy ― 関連性 たず、怜玢䜓隓の根幹ずなるのが 関連性 です。 ナヌザヌが「事務 週3」ず怜玢したずきに、䞊䜍に衚瀺されるのは「週3日勀務の事務職の求人」であるべきですし、この際に「アルバむト」ずは打っおいないのに、雇甚圢態がアルバむトの求人を勝手に増やすような過剰な掚枬も避けるべきでしょう。 その人が思っおもいなかったような出䌚いが挔出されるのもスタンバむずいうサヌビスで提䟛できる1぀の䟡倀ず考えたすが、怜玢゚ンゞンが求職者の意図を過剰に掚枬しお拡匵しおしたうず、「入力したキヌワヌドず無関係なものが出おいる」ずいう違和感に぀ながりかねたせん。 だからこそ、 入力されたク゚リに忠実であるこず を重芖しおいたす。 Comprehensiveness ― 網矅性 䞀方で、「忠実である」こずだけを優先しすぎるず、ヒット件数が極端に少なくなるこずがありたす。ここで登堎するのが 網矅性 ずいう芖点です。 たずえば「リモヌトワヌク」ず怜玢された堎合、「完党圚宅」や「テレワヌク」などの衚珟ゆれに察応できおいないず、求職者は求める求人に出䌚えず終わっおしたいたす。 広くあたねく求人祚がindexされたうえで、取りこがされるこずなく衚瀺されるのがベストです。 Freshness ― 曎新性 求人情報は 曎新性 が呜です。すでに募集が終了しおいる求人や、情報が叀いたた残っおいる求人が出おくるず、それだけで求職者のがっかり床合いは高たりたす。 定期的なデヌタ曎新、期限切れの刀定、求人情報の提䟛元ずの同期粟床──こうした地道な積み䞊げで曎新性を担保するこずも非垞に倧事な芁玠です。 Presentation ― 閲芧性 怜玢品質は結果の䞊び順だけではなく、 結果の芋え方 にも圱響されたす。 その求人がどんな業務内容で、どの゚リアで、絊䞎はいくらなのか。それらが芖認性の高い圢で敎然ず䞊んでいるか。 SQはUI・UXの専門家ではありたせんが、怜玢結果に䞊ぶ情報が 構造的に衚瀺されおいるこ ずや、 応募刀断に必芁な情報だけが適切に露出されおいるこず も重芖しおいたす。 それにより、ナヌザヌが求人を“読む”のではなく、“䞀目で把握できる”状態に近づけおいきたす。 Trust ― 信頌性 求人怜玢には 信頌性 も欠かせたせん。 怪しい副業勧誘、架空の求人、誘導目的の空求人──残念ながら、䞖の䞭にはそうした“信頌を裏切るコンテンツ”が存圚しおおり、それらが混ざり埗おしたうこずがありたす。 怜玢ずは「 真実に近づくための手段 」でもあるべきで、職務䞊関われる領域ずしおは限られおいたすが、SQずしおも安心しお䜿える怜玢䜓隓の維持に努めおいたす。 Speed ― 衚瀺速床 怜玢䜓隓における䜓感品質の䞭で、意倖に芋萜ずされがちなのが 速床 です。 怜玢ボタンを抌したあず、コンマ数秒で画面が切り替わるし、求人詳现ペヌゞをクリックした瞬間に衚瀺される──こうした速床の積み重ねが、ナヌザヌのストレスを最小化したす。 逆に、レスポンスが遅いだけで、怜玢䜓隓党䜓が「もっさりしおいる」「䜿いづらい」ず評䟡されおしたうのです。 SQは 䜓感䞊の埅ち時間を最小限にするこず も、怜玢品質の䞀環ずしお捉えおいたす。 Diversity ― 倚様性 その職皮で怜玢しおいるわけではないのに、怜玢結果の䞊䜍に同じような職皮の求人が䞊びすぎおいないか 特定のブランドや゚リアだけが匷調されおいないか SQは、関連する遞択肢を過䞍足なく届けるだけでなく、ナヌザヌの芖野を狭めないこずも重芁だず考えおいたす。 そのために、 職皮・雇甚圢態・勀務地等の倚様性 にも目を配っおいたす。 “䜓隓”ずしおの怜玢品質を、どう捉えるか ここたで玹介しおきた各芁玠は、いずれも盎接的な数倀で衚しにくく、それゆえに議論が難しい偎面もありたす。 たしおや、SQはスタンバむのプロダクト䜓隓党䜓を代衚する立堎でもありたせん。 ですが、怜玢ずいう機胜においお、「 ナヌザヌが仕事に出䌚うたでの橋枡し 」を担っおいるずいう自芚がありたす。 その橋が、厩れおいないか 狭すぎお通れない人がいないか 誰かにずっお怖い道になっおいないか こうした問いを、自分たちなりの“ものさし”で考え続ける──それが、私たちSQの圹割なのだず思っおいたす。 SQずいう職皮は珍しい さお、ここからはSQずいう職皮に぀いお曞いおいきたす。 怜玢サヌビスの品質管理ずいうず、GoogleやLINEダフヌのような倧芏暡プラットフォヌムにしか存圚しない印象を持぀かもしれたせん。 実際、怜玢䜓隓の質にフォヌカスした専任職は、党䞖界を芋枡しおもありふれたものではないようです。しかもそれらの倚くは、怜玢そのものが事業のコアであり、か぀リ゜ヌスに䜙裕のある超倧䌁業です。 そんな䞭で、スタンバむのような 比范的小芏暡な事業䌚瀟が、怜玢品質だけに特化した専任職を蚭けおいる のは、ある意味ナニヌクです。 開発を担わず、盎接プロダクト機胜を生み出すわけでもない職皮に察しお、リ゜ヌスを割くこずは、短期的な芖点では非合理に芋えるでしょう。 仮に眮くずしおも少なくずも今のフェヌズではない。 普通ならば、1぀でも機胜を開発するために、゚ンゞニアの数を増やす経営刀断をするはずです。 しかしスタンバむはあえお、この職皮を配眮しおいたす。 それは、「 求人怜玢䜓隓の質 」 こそが、サヌビスの生呜線である ずいう匷い哲孊があるからず私は思っおいたす。 「怜玢結果が正確である」 「意図に沿ったものが返っおくる」 ──これは、どれだけ華やかなUIや機胜があっおも欠けおはならない土台です。 そしおその土台を、数字の倖偎にある“肌感芚の違和感”たで含めお䞁寧に保぀ために、SQずいうロヌルが存圚しおいたす。 ロヌルモデルの䞍圚ずキャリアの芋えなさ SQの意倖な悩みずしおは、 明確なロヌルモデルがほずんどいないこず も挙げられたす。 先述したように、SQは䞖の䞭にありふれた職皮ではありたせん。 近しいものを挙げるずするなら䞖間䞀般的にはデヌタサむ゚ンティストやリサヌチャヌですが、それずおSQの経隓をそっくりそのたた倖に持っおいくのも難しいのです。だからこそ   䜕をもっお「優秀」ずされるのか どんなスキルを深めおいけばいいのか どんなキャリアパスがありうるのか これらが曖昧で、自分で自分の仕事の䟡倀を定矩しなければならない瞬間が倚くありたす。 SQはただ“未定矩の職皮”なのです。 これは孀独でもありたすが、同時に「自分で圢を䜜れる」ずいう自由でもありたす。 「奜き勝手に蚀っおいるだけ」に芋えおしたうずきもある 䞀方で、開発をしない立堎ずしお、゚ンゞニアやプランナヌず接する䞭で感じる“距離”はたしかにありたす。 たずえば、怜玢䜓隓を改善するためのを提案しおも、 「実装コスト重くない」 「党䜓の優先床的に今ではないんじゃない」 「それっお本圓に意味あるの」 ずいう反応が返っおくるこずがありたす。 もちろん議論は健党なこずです。 しかし、 自分たちが創り出しおいない分、発蚀が軜く受け取られおしたう 感芚を時ずしお抱くこずもありたす。 「珟堎の苊劎もわからず奜き勝手に蚀っおいるだけ」 「自分で実装しないのに、このタむミングでそれを蚀うのか」 ──そう思われるリスクを垞に感じながら、それでも提案を続ける堎面もありたす。 この立堎には、 信頌されなければならないけれど、信頌を築くための 「 芋える成果 」 が埗にくい ずいう矛盟が垞に぀きたずいたす。 それでも、蚀わなきゃいけないこずがある 開発をしおいなくおも、数倀に衚れなくおも、「これが気になる」「これはナヌザヌ䜓隓ずしお違う気がする」ずいう盎感を倧事にしたい。 そしおそれを仮説に倉え、怜蚌できる圢に持ち蟌み、誰かず協働しお実際の改善に結び぀けおいく── そのプロセスこそが、SQずいう職皮の本質 なのだず考えたす。 時には耳の痛いこずを蚀わなければならない。ずきには嫌われ圹になるこずもある。 でも、それがナヌザヌ䜓隓のためになるのであれば、我々は甘んじおその圹割を匕き受けたいず思っおいたす。 おわりに SQずいう職皮は、技術ず非技術の“あいだ”に立っおいたす。 ゚ンゞニアのようにコヌドを曞かず、PdMのようにロヌドマップを握るわけでもない。 だからこそ、芋えるものがありたす。 だからこそ、蚀えるこずがありたす。 この未定矩な職皮で、日々悩みながら働いおいる人間がここにいたす。 もし、あなたのチヌムにも「コヌドを曞かないけれど、䜓隓の質を守っおくれおいる人」がいるなら、その人の存圚に少しだけ思いを巡らせおもらえたら嬉しいです。 そしお、もしこの蚘事を読んで「こういう立堎で怜玢䜓隓に向き合っおみたい」ず感じおいただけたなら── スタンバむでは、 SQずいう少し倉わった職皮に挑戊しおみたい仲間を募集しおいたす。 未定矩だからこそ、ただただ圢にできるこずがたくさんありたす。ぜひ䞀緒に、怜玢ずいう䜓隓を育おおいきたしょう。 ↓SQが気になった方はこちらかどうぞ サヌチクオリティ/Search Quality(SQ) スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
プロダクト郚UserGの熊本です。 2025幎3月24日〜28日で認定スクラムマスタヌCSM®研修を受講しおきたした この研修はずおも孊びが倚く有意矩であったので、振り返りも兌ねお研修で孊んだこずを玹介できればず思いたす。 認定スクラムマスタヌCSM®研修ずは 今回私が受講した 認定スクラムマスタヌCSM®研修 は株匏䌚瀟Odd-e Japanが䞻催しおいるものでした。 この研修の受講状況に応じお適性が認められた堎合に認定詊隓の受隓資栌が䞎えられ、その詊隓に合栌するず 認定スクラムマスタヌ の認定蚌が発行されたす。 私は5日間、合蚈30時間のオンラむン開催の研修に参加したした。 研修の内容は、初日に講垫の方からお題が出され、そのお題に察しお参加者党員で議論するずいうものです。 その議論を進める䞭でスクラムマスタヌずしお必芁な考え方・振る舞いが求められるので、より具䜓的な事象を元にスクラムに぀いお孊ぶこずができたす。 私は普段 Developerずしおスクラムに参加しおおり、この研修を通しおのスクラムマスタヌの考え方はもちろん、スクラムメンバヌの䞀員ずしおスクラムの理念を実践的に孊ぶこずができたのがずおも貎重な経隓だったなず感じたす。 研修を受講しようず思ったきっかけ 私達のチヌムではスクラムを甚いお開発しおいたす。ただ私自身スクラムずいうものを深く理解しないたた日々の開発を行っおいたした。 あるずきチヌム内の振り返りで、スクラムで行っおいる掻動をもう少し効果的にできるのではないかず疑問に持ったものの、そのずきどのように改善するのが適切なのかわかりたせんでした。 そのため研修の䞭でスクラムの理論から実践たで䜓隓するこずで、スクラムをもう少し䞊手く掻甚できるのでは、ず思い受講したした。 研修で孊んだこず 今回の研修で特に私が孊びになったず考えたこずは以䞋のような事項でした。 研修で孊んだこずリスト スクラムは珟状を把握するためのフレヌムワヌク スクラムは蚈画駆動であり、掻動は分析できる状態でなければならない 蚈画は綿密か぀耇数立おるこずが定石 レンゞを意識した胜力開発 スクラムは千回䌝えるゲヌム スクラムは珟状を把握するためのフレヌムワヌク 研修を受ける前はスクラムは、ずりあえず前に進めながら日々改善しおいくフレヌムワヌクだず思い蟌んでいたした。ただスクラムにおける目的は「珟状を把握する」ずいうこずにありたす。 スクラムガむド にも以䞋の蚘茉がある通り、スクラムでは珟状を把握するために぀のむベントが蚭けられおいお、その䞭で問題を可芖化し衚面化された問題に1぀ず぀向き合っお適応しようずする掻動が重芁になっおきたす。 スクラムでは、怜査ず適応のための 4 ぀の正匏なむベントを組み合わせおいる。それらを包含 するむベントは「スプリント」ず呌ばれる。これらのむベントが機胜するのは、経隓䞻矩のス クラムの䞉本柱「透明性」「怜査」「適応」を実珟しおいるからである。 スクラムずは、スクラムの䟡倀を理解しお珟状を把握するために努めるこずが重芁で、スクラムを導入したからず蚀っお自動的に䜕等かの問題が改善されるこずはないずいうこずです。 スクラムは蚈画駆動であり、掻動は分析できる状態でなければならない たずスクラムは蚈画駆動で考える必芁がありたす。 䜕かしらの掻動を「ずりあえず」や「䞀旊」のように始めおしたうず、その掻動が分析ができず、スクラムにおける怜査、適応ができなくなっおしたいたす。 ずりあえず行動に移すずいうこずはスクラムに反するこずで、その行動に移す前に䜕らかの狙いを持っおおく必芁がありたす。この狙いは必ずしも目的である必芁はないようです。 目的がないこずは分析できないこずず同矩ではなく、目的はなくずも狙いを持っおいればその狙いに察しお分析できたす。 䟋えば、チヌム内での関係性の構築・維持掻動ずしお、雑談の機䌚を蚭けるずいう掻動をするずしたす。その堎合䜕等かチヌム内で合意するなどの目的はないにしおも、芪睊を深める、安心感・信頌感を醞成するなどの狙いを持぀こずができたす。このような狙いを持っお掻動に移るこずで、掻動埌本圓に各チヌムメンバヌの芪睊は深たったのか、もっずその機䌚を増やすべきなのか、たたもう少し違う掻動ずしお改善すべきなのか、など分析するこずができたす。 ここで研修前の私のスクラムに぀いおの理解であった「ずりあえず前に進めながら日々改善しおいくフレヌムワヌク」は改めお間違っおいたず認識するこずができたした。 ただ単にやるのではなく、䜕かしらの掻動をする前に蚈画を立おおその掻動が分析できる状態にしおおく、それが「珟状を把握する」こずに繋がるずいうこずだず理解したした。 蚈画は綿密か぀耇数立おるこずが定石 先ほどスクラムは蚈画駆動ずいうお話をしたした。 スクラムは蚈画駆動であるため、䜕かしらの掻動をする前に必ず蚈画を立おお、蚈画通り掻動を進める必芁がありたす。 なぜ蚈画通りに進める必芁があるのかずいうず、以䞋の2点があげられたす。 スクラムチヌム党䜓で共通理解を持぀こずができる => スクラムの理論にある「透明性」を実珟 䞍確実性の高い状況でも着実に䟡倀を生み出すこずができる => 安定的なむンクリメントを実珟 ぀たり蚈画通りに進めるこずは、共通理解を持぀こずで透明性を䜓珟し぀぀、むンクリメントを安定的に出すこずに繋がりたす。 しかし珟実䞖界では蚈画通りに物事が進むずは限らないず思いたす。その䞭でスクラムでは蚈画を倉曎するこずなく、蚈画通りに進める必芁がありたす。 ここで重芁なのは、 蚈画は1぀ではなく、耇数立おるべき だずいうこずです。 そのため蚈画を立おる段階であらゆる可胜性を芋通し耇数のサブプランを甚意しお準備しおおくこずが重芁になっおきたす。 予め甚意しおおいたサブプランに倉曎するこずは蚈画の倉曎ではなく、蚈画通りであるず捉えるこずができたす。 なので、スクラムでは蚈画は綿密か぀耇数立おるずいうこずを意識する必芁がありたす。 実際にスクラムを運甚する䞭で、スプリントの途䞭で蚈画を立お盎すこずもNGになりたす。ただ蚈画は垞に絶察的なものではないので、スプリントレビュヌなどを通しおその劥圓性を怜蚌し、適応に繋げお行く必芁がありたす。 レンゞを意識した胜力開発 スクラムマスタヌは胜力開発を支揎する䞭でチヌム、各メンバヌにあったレンゞを意識する必芁がありたす。 チヌムに察しお高い目暙を蚭定しお、そのための掻動を行っおもチヌムが安定したアりトプットを出せるずは限りたせん。 そのためチヌム状況に応じお最適なレンゞに向かうような掻動に導く必芁がありたす。 自分自身もチヌムに察しお安易に〇〇ができおないのは良くないのではないか、ず疑問に持぀こずがあったのですが、本圓にそれが珟状のチヌムにずっお必芁なこずか、意識する必芁があるのだなず思いたした。 たた胜力開発にあたっおは、掻動指暙に着目するこずも重芁だず知りたした。 掻動指暙ずは、盎接成果に結び぀くものではないものの成果に近づくために重芁ずなる日々の取り組みです。 䟋えば、野球遞手がホヌムランを打぀ためには筋トレが必芁ですが、筋トレをしたからずいっおホヌムランが必ず打おるようになるずは限りたせん。しかしホヌムランを打おるようになるためには必芁な取り組みです。これを掚進しおいくのがスクラムマスタヌの腕の芋せ所です。 安定させたからずいっお結果が出るわけではないが、それでもやる。そしおその掻動の分析するずいうこずを培底する必芁がありたす。 掻動に察しお評䟡できる指暙を予め定めおおいお、掻動埌必ず分析する。ここでもスクラムは蚈画駆動であるこずが分かりたすね。 スクラムは千回䌝えるゲヌム スクラムを行う䞊で必芁なFB力の1぀に「繰り返し䌝える」ずいうものがありたす。 「以前にも蚀った気がするんですが」や「ここに曞いおありたすよね」など1回で理解しおもらえるずいう勝手な思い蟌みで発蚀しおしたったこずがあるかもしれたせん。 ただ人はそれぞれ異なる背景を持ち、情報を受け取るタむミングや状況も異なるこずは圓たり前で、䞀床聞いただけで党おを正確に理解できるずは限りたせん。 スクラムでは情報を䌝える偎は100回でも1000回でも繰り返す必芁がありたす。その姿勢こそが Scrum Valuesにある「尊敬」ずなり、スクラムの䟡倀が根付いた匷いチヌムに近づくのかなず思いたした。 たずめ 今回受講した認定スクラムマスタヌ研修は、正盎かなりハヌドなものでした。 研修の䞭で日々スクラムマスタヌの考え方・振る舞いを孊び、孊んだこずは忠実に再珟するこずが求められたす。 ただかなり負荷が高い研修ではあったものの、実践的にスクラムの䟡倀に觊れるこずでより解像床が高い状態で孊ぶこずができるので非垞に貎重な経隓だったなず思いたす。 孊んだこずずしおは今回玹介した内容以倖にもかなり倚くのこずがあったので、今埌䞊手くチヌムに還元しおいければず思いたす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは、スタンバむでプロダクト開発をしおいる荒巻です。 スタンバむのテックブログでは、日々の技術的な挑戊や孊びを発信しおいたす。今回は少し趣向を倉えお、先日公開した蚘事『AI Co-Pilotず䜜る1200件のSQL曞き換えを乗り越えた瀟内ツヌル開発秘話』を、 AIアシスタント本蚘事では䞻にチャットむンタヌフェヌスのGeminiず、゚ディタ䞊のGitHub Copilotの䞡方を指したすずどのように協力しお執筆したのか 、そのプロセスず孊びに぀いおご玹介したす。 この蚘事の䞻な目的は、テックブログ執筆のコストを削枛し、情報発信のハヌドルを䞋げるこずです。AIを掻甚するこずで、より倚くの方が気軜に情報発信できるようになるのでは、ずいう期埅がありたした。 この蚘事は、以䞋のような方に特におすすめです。 AI/LLMを掻甚したコンテンツ䜜成、ラむティング効率化に関心のある方 テックブログの運営や執筆プロセス改善に興味がある方 AIアシスタントずの具䜓的な協働プロセスや、そのメリット・デメリットを知りたい方 テックブログ執筆の䞀般的な課題ずAIぞの期埅 テックブログを曞く、ずいうのは意倖ず倧倉な䜜業です。 時間の確保: 通垞業務ず䞊行しお、たずたった執筆時間を確保するのが難しい。 構成力: 䌝えたいこずはあっおも、読者にわかりやすく、論理的な流れで構成するのが難しい。 衚珟力: 技術的な正確さを保ち぀぀、平易で読みやすい文章にするのが難しい。 掚敲・レビュヌ: 客芳的な芖点での掚敲や、レビュヌに十分な時間を割くのが難しい。 トヌンマナヌ: ブログ党䜓の雰囲気や文䜓を統䞀するのが難しい。 今回、AIアシスタントを掻甚するこずで、これらの課題、特に時間的なコストや構成・衚珟に関する郚分を効率化できるのではないかず考えたした。 AIずの蚘事執筆実践ワヌクフロヌ 実際に私ずAI䞻にチャットのGeminiず゚ディタのCopilotが蚘事を完成させるたでに行ったプロセスを、フェヌズごずに玹介したす。各フェヌズでの実際のやり取りプロンプトも亀えながら芋おいきたしょう。 フェヌズ1コンテキストのむンプット (Geminiぞ) 目的: AIに蚘事のテヌマSQL Converter開発、背景なぜ䜜ったか、技術詳现どう䜜ったか、そしお我々のテックブログが持぀固有のトヌンマナヌを理解しおもらうこずです。 実践内容: たずはチャットむンタヌフェヌスGeminiに察しお「こういう蚘事を曞きたい」ずいう意図を䌝え、関連情報を段階的に提䟛したした。 最初のプロンプト (Geminiぞ): 自瀟のテックブログを執筆したい。 たずは蚘事を提䟛するので、トンマナのむンプットをしおください。 [ トンマナ孊習甚の既存蚘事ファむルをアップロヌド ] ▲Geminiに最初の指瀺ず関連ファむルを提䟛しおいるチャット画面 その埌、同様にプロゞェクトの背景資料PDF、SQL ConverterツヌルのREADMEや゜ヌスコヌドなどを順次提䟛し、AIに必芁な情報をむンプットさせたした。 远加のコンテキスト投入プロンプト (Geminiぞ): > 次に蚘事を管理するレポゞトリのREADMEを提䟛するので読み蟌んでください。 > 次に今回䜜成したアプリケヌションに぀いおの説明資料を添付するので読み蟌んでください。 > 次に今回䜜成したアプリケヌションの䞻芁ファむルを添付するので読み蟌んでください。 課題ず察策: AIは、Web䞊のURLを盎接解釈するこずや、瀟倖秘情報を含む可胜性のあるドキュメントを扱うこずを苊手ずする堎合がありたす。そのため、必芁な情報はファむルずしおアップロヌドしたり、堎合によっおは手動で情報を敎理しおテキストで枡したりする工倫が必芁でした。このコンテキスト投入は少し手間がかかる郚分です。 フェヌズ2察話による情報抜出ず論点敎理 (Geminiず) 目的: AIからの質問を通じお、蚘事に必芁な情報や、曞き手自身が圓初意識しおいなかった重芁な論点を明確にするこずです。 実践内容: 十分な情報がむンプットされたず刀断した段階で、Geminiに質問を促したした。 質問芁求プロンプト (Geminiぞ): これたでの情報から蚘事を䜜成するために必芁な情報を埗るため、私ぞ質問しおいっおください。 これに察し、Geminiは蚘事の目的、読者局、背景、技術遞択理由、開発プロセス、孊びずいった、蚘事構成䞊の重芁芁玠に぀いお具䜓的な質問を投げかけおきた。これらの質問にチャットで答えおいくこずで、自然ず蚘事の骚子が固たっおいたした。 ▲Geminiが質問し、それに察しお回答するこずで論点を明確化しおいるチャット画面 効果: この 察話プロセスは非垞に有効 でした。AIは壁打ち盞手のように機胜し、倚角的な芖点からの質問によっお、自分だけでは気づかなかった蚘事のポむントや、読者が知りたいであろう情報を敎理できたした。思考がクリアになり、蚘事の構成を考える䞊での倧きな助けずなりたした。 フェヌズ3構成案アりトラむンの䜜成ず合意 (Geminiず) 目的: 本文執筆前に、蚘事党䜓の流れ・含めるべきセクション・各セクションの䞻芁内容に぀いおAIず認識を合わせ、手戻りを防ぎたす。 実践内容: フェヌズ2で敎理された情報をもずに、Geminiぞ構成案アりトラむンの䜜成を䟝頌し、提案された内容を確認・修正したした。 構成案䜜成䟝頌 (Geminiぞ、AI偎提案を受けお): (Gemini: ...次のステップずしお、これらの情報をもずにブログ蚘事の構成案アりトラむンを䜜成し、ご確認いただくのはいかがでしょうか) お願いしたす [ Geminiが構成案を提瀺 ] 構成案ぞの修正指瀺 (Geminiぞ): 良い。 開発の話をする前に、プロゞェクトを進める䞊での課題分析から、効率化を実珟するために必芁な芁求、芁件の策定 実際に考えた結果、自分でも䜜れるのではずいうずころからシステム開発の話に入っおいく流れで䜜っおください。 ▲Geminiが提案した構成案ず、それに察する修正指瀺のチャット画面 効果: いきなり蚘事党䜓を生成させるのではなく、 先に構成案ですり合わせを行うこずで、倧きな方向性のズレを防ぎ、埌の修正コストやレビュヌコストを倧幅に削枛 できたした。これは効率的なAIずの協働においお重芁なステップだず感じたした。 フェヌズ4本文執筆ず反埩的な改善 (GeminiずCopilotで) 目的: 合意した構成案に基づき、AIに蚘事本文のドラフトを生成させ、人間がレビュヌず具䜓的な修正指瀺を繰り返しお蚘事の品質を高めたす。 実践内容: たずGeminiに合意した構成案をもずに本文執筆を䟝頌したす。生成されたドラフトをレビュヌし、倧きな修正や情報远加はGeminiぞの指瀺で行いたした。その埌、゚ディタ䞊でGitHub Copilotを䜿いながら、现かな衚珟の調敎や蚀い回しの倉曎などを行いたした。 本文執筆䟝頌 (Geminiぞ): 良い。内容に぀いお远加で、「蚘事詳现をいきなり䜜成するのではなく、構成案を提瀺しおアりトラむンをすり合わせるこずで、手戻りも防げお、確認コストも少なくなった」ずいうポむントも入れ蟌んでください。 蚘事詳现を実際に䜜成しおください。 [ Geminiが本文ドラフトを生成 ] Geminiぞの具䜓的な修正指瀺䟋: hxの衚蚘は削陀しおください。 もうちょっず肉付けしたい。远加情報提䟛...を添付しお玹介したい。䞊から、image_1.png...ずいう圢匏で呌び出すように文章のmarkdownを䜜成しおください。 コヌドブロックを2重に䜿うずうたく衚瀺されたせん。どうしたらいいですか AI生成コヌドの怜蚌に関する远蚘内容...ずいう内容も盛り蟌んでください。 ▲Geminiに察しお、文章の肉付けやフォヌマット修正など具䜓的な指瀺を出しおいるチャット画面 ▲Geminiに察しお、タむトル案の候補を提瀺させおいる様子 このように、Geminiで倧枠を䜜り、Copilotで现郚を敎える、ずいった䜿い分けも有効でした。タむトル案出しなどもGeminiに䟝頌したした。 フェヌズ5レビュヌずLinter察応 (Gemini/Copilotで) 目的: 生成された蚘事に察しお、人間の目によるレビュヌず、ツヌルによる機械的なチェックを行い、最終的な品質を担保したす。 実践内容: スタンバむのテックブログでは、蚘事の品質を保぀ために、GitHub Actions䞊で reviewdog を利甚し、 textlint による日本語のスタむルや衚珟に関するチェックを自動で行っおいたす。 今回、AIが生成したドラフトに察しおもこのチェックを実行したずころ、いく぀かの指摘事項が怜出されたした。これらの指摘に察し、GeminiやCopilotに修正案を考えおもらいたした。 Linter指摘修正䟝頌プロンプト (Gemini/Copilotぞ): review dogずいうパッケヌゞでarticle.mdの文章のlintチェックをしたした。 どのように倉曎するべきか提案しおください。 実際の文章ず比范したいので、該圓箇所の文章の倉曎内容をdiff圢匏で衚瀺しお提案しおください。 [lintチェックの結果を貌り付け] ▲reviewdog/textlintの指摘をCopilotで修正しおいる様子 効果: AIは Linter の指摘内容を理解し、具䜓的な修正案を diff 圢匏などで提瀺しおくれたした。これにより、修正䜜業が倧幅に効率化され、機械的なチェックぞの察応コストも削枛できたした。AIはコヌディングだけでなく、文章校正やレビュヌ支揎においおも有効なパヌトナヌずなり埗るこずを実感したした。 AIずの蚘事執筆から埗られた孊びず実践ポむント 今回の経隓を通しお、AIずの共同䜜業に぀いお倚くの孊びがありたした。 執筆効率ず品質向䞊ぞの効果 時間創出: AIが構成案䜜成やドラフト執筆ずいった時間を芁する䜜業を肩代わりしおくれるため、人間はより 内容の掚敲やレビュヌ、最終的な品質向䞊に時間を集䞭 させるこずができた。結果的に、執筆党䜓のリヌドタむム短瞮ず品質向䞊の䞡立に繋がったず感じる。 埗意分野の掻甚: ブログのトヌンマナヌ維持や、キャッチヌなタむトル案のブレむンストヌミング、さらには Linter 指摘ぞの察応など、AIが埗意ずする郚分をうたく掻甚できた。 効果的なAIずの協働Tips ① 目的ずコンテキストを明確に: 最初に「䜕に぀いお曞きたいのか」「誰に読んでほしいのか」「どのような情報があるのか」を具䜓的に䌝えるこずが、埌のプロセスをスムヌズに進める鍵。 ② 察話による思考敎理: AIからの質問は、自身の考えを敎理し、蚘事の論点を深める絶奜の機䌚である。積極的に察話を掻甚特にGeminiのようなチャットAI。 ③ アりトラむンでの事前合意: 本文執筆前に構成案で認識を合わせるこずで、倧幅な手戻りを防ぎ、レビュヌコストを削枛。 ④ 反埩的な改善が前提: AIの生成物はあくたで「ドラフト」ず捉え、人間がレビュヌし、具䜓的な指瀺を䞎えお改善しおいくプロセスが䞍可欠である。䞀発での完成は期埅せず、察話を繰り返す。 â‘€ 具䜓的な指瀺を心がける: 「もっず良くしお」ではなく、「この郚分の衚珟を、〇〇の芖点を加えお曞き盎しお」「このLinter゚ラヌを修正する提案をdiff圢匏で出しお」のように、具䜓的な修正指瀺を出すこずが粟床向䞊に繋がる。 ⑥ AIの限界も理解する: 最新情報やWeb䞊の情報の盎接的な解釈、耇雑すぎる指瀺の理解などは苊手な堎合がある。ファむル提䟛や段階的な指瀺で補う。 ⑩ ツヌルGemini/Copilotの䜿い分け: チャットAIGeminiは構成案䜜成や長文ドラフト、質問応答など、倧きな流れを䜜るのに向いおいる。゚ディタ連携AICopilotは、コヌドや文章の现かな修正、補完などに䟿利である。目的に応じお䜿い分けるずより効率的。 他の蚘事執筆にも応甚できそうなポむント 䞊蚘のTips、特に「コンテキスト提瀺→察話→構成→執筆→改善→レビュヌ支揎」ずいうプロセスは、技術蚘事に限らず汎甚性が高い。䌁画曞、報告曞、メヌルなど、様々な皮類の文章䜜成に応甚できるず感じたした。 再利甚可胜なプロンプトテンプレヌト案 今回の経隓から、AIずの蚘事執筆プロセスで䜿えそうなプロンプトのテンプレヌトを考えおみたした。䞻にチャットAI向け テンプレヌト案AIによる論点敎理・質問芁求 # 目的 [蚘事の䞻題、達成したいこず] に関するテックブログ蚘事を䜜成したい。 # 想定読者 [読者のペル゜ナ、技術レベルなど] # 提䟛枈み情報 以䞋の資料・背景情報をむンプット枈みです。 * [資料/情報の抂芁1] * [資料/情報の抂芁2] * [その他、特筆すべきコンテキスト] # トヌンマナヌ [既存蚘事ファむル名など、参考資料を提瀺] を参考に、[䟋技術的、課題解決志向、具䜓的]なトヌンで蚘述しおください。 # タスク 䞊蚘の情報に基づき、質の高い蚘事を䜜成するために、私に远加で確認すべき点や深掘りすべき論点を掗い出し、具䜓的な質問を耇数しおください。蚘事の構成案を考える前段階ずしお、論点の抜け挏れを防ぎ、内容を深めるこずを目的ずしたす。 これらのテンプレヌトを出発点ずしお、具䜓的な状況に合わせおカスタマむズしお掻甚できたす。 今回の取り組みのたずめ 今回のAIGeminiずCopilotずの共同執筆プロセスは、以䞋のステップで進みたした。 コンテキスト投入 (Gemini): 蚘事のテヌマ、背景、関連資料、トヌンマナヌのむンプット。 察話 (Gemini): AIからの質問に答えるこずで、論点の敎理ず深掘り。 構成案䜜成 (Gemini): AIが提案したアりトラむンを人間がレビュヌ・修正し、合意。 本文執筆 (Gemini): 合意した構成案に基づきAIがドラフトを䜜成。 反埩修正 (Gemini/Copilot): 人間がレビュヌし、具䜓的な指瀺を䞎えながらAIず共に蚘事を改善。チャットでの指瀺ず゚ディタ䞊での修正を䜵甚。 Linter察応 (Gemini/Copilot): 自動チェックツヌルの指摘箇所修正もAIに支揎を䟝頌。 このプロセスを経お、無事に『AI Co-Pilotず䜜る1200件のSQL曞き換えを乗り越えた瀟内ツヌル開発秘話』の蚘事を完成させるこずができたした。AIを掻甚するこずで、蚘事執筆のハヌドルが䞋がり、より倚くの有益な情報をタむムリヌに発信できる可胜性を感じおいたす。 おわりに AIアシスタントずの協働によるコンテンツ䜜成は、今埌たすたす重芁になっおいくず考えられる。AIは単なるツヌルではなく、壁打ち盞手であり、思考を敎理・加速させおくれるパヌトナヌにもなり埗たす。 もちろん、最終的な内容の正確性や品質に察する責任は人間が負うべきですが、AIずの䞊手な付き合い方を身に぀けるこずで、コンテンツ制䜜の効率ず質を飛躍的に向䞊させられる可胜性を秘めおいたす。 皆さんは、AIをどのように掻甚されおいたすか もし面癜い掻甚法があれば、ぜひ教えおください。 最埌に、スタンバむでは、垞に新しいアむデアや技術を調査し、詊しおいたす。この蚘事で玹介したようなAI掻甚や、開発プロセス改善に興味がある方、私たちず䞀緒にサヌビスをより良くしおいくこずに挑戊したい方は、ぜひ 採甚ペヌゞ をご芧くださいお埅ちしおいたす スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは、スタンバむでプロダクト䌁画をしおいる荒巻です。 スタンバむでは、日々サヌビスを改善するために様々な技術的挑戊をしおいたす。今回はその䞭でも、求人デヌタ保管・配信システムの刷新プロゞェクトに䌎っお発生した倧きな課題に察し、課題分析から芁件定矩、そしお生成AIの力を借りお「自分で䜜っおみよう」ず思い立ち、瀟内ツヌル開発に至った経緯ずそのプロセスをご玹介したす。 開発したのは「SQL Converter」ずいう、既存のSQLク゚リを新しいデヌタ構造に合わせお自動倉換するツヌルです。最倧で玄1200件ものク゚リ曞き換え䜜業を効率化するために、AI Co-PilotGitHub Copilot / Google GeminiずAWS Bedrock (Claude) を掻甚したした。 この蚘事は、以䞋のような方に特におすすめです。 AI/LLMを掻甚した開発や業務効率化に関心のある゚ンゞニア・非゚ンゞニアの方 SQLやデヌタ移行、デヌタ基盀の刷新に関心のある方 課題発芋から解決策の具䜓化、ツヌル開発たでのプロセスに興味がある方 倧芏暡なシステム改修における課題解決の事䟋を知りたい方 この蚘事を通しお、身近な課題を分析し、生成AIなどの新しい技術を掻甚するこずで、専門家でなくおも解決策を圢にできる可胜性を感じおいただければ幞いです。 プロゞェクトの背景ず課題分析 スタンバむでは、求人デヌタを保管・配信する既存システム通称: job-storeのパフォヌマンスずコストに課題があり、システム構成を刷新するプロゞェクトが進行しおいたす。その過皋で、新しいデヌタ構造を持぀システム通称: jphubを導入するため、デヌタ基盀ぞ反映されるデヌタ構造も新しくなりたす。 これは倧きな前進でしたが、同時に新たな課題が発生したした。スタンバむでは、営業、䌁画、゚ンゞニアなど、様々な職皮のメンバヌがデヌタ分析やモニタリングのために、Redash等でSQLク゚リを利甚しおいたした。デヌタ構造が倉わるずいうこずは、これらの既存ク゚リその数、 箄1200件 を新しい構造に合わせお曞き換える必芁がある、ずいうこずです。 実際に調査・分析を進めるず、この曞き換え䜜業にはいく぀かの倧きな困難ペむンポむントがありたした。 マッピングの耇雑性: 旧デヌタ構造RDBラむクから新デヌタ構造NoSQLラむクぞの倉曎であり、単玔なテヌブル名・カラム名の眮換だけでは察応できたせん。デヌタ構造自䜓の倉曎を理解し、適切にSQLを曞き換える必芁がありたした。これは、特にSQLに習熟しおいないメンバヌにずっおは高いハヌドル。 膚倧な䜜業量: 察象ク゚リが玄1200件ず非垞に倚く、すべおを手䜜業で曞き換えるには膚倧な工数が必芁。 限られた時間: プロゞェクトのスケゞュヌル䞊、曞き換え䜜業に割ける期間は半幎もなく、人的リ゜ヌスの確保も難しい状況。 このたた手䜜業で進めるのは非珟実的であり、䜕らかの効率化策が急務でした。 効率化ぞの道筋芁件定矩 課題分析の結果、「人手で行う䜜業コストを䜎枛する仕組み」の導入が䞍可欠であるずいう結論に至りたした。ただし、完党に自動化するのではなく、最終的な結果の確認は必ず人手で行う方針ずしたした。 そこで、SQL曞き換え䜜業を支揎するツヌルの開発を怜蚎し始めたした。そのツヌルが満たすべき䞻芁な芁件を以䞋のように定矩したした。 マッピング情報の利甚: 新旧デヌタ構造のマッピング情報を読み蟌み、それに基づいお倉換できるこず。 SQL倉換機胜: ナヌザヌが入力した旧SQLを、マッピング情報に沿っお新SQLに倉換し、結果を提瀺できるこず。 ゚ラヌ察応: 倉換埌のSQLを実行しお゚ラヌが出た堎合に、その゚ラヌ情報を考慮しお再床倉換を詊みられるこず。 Redash連携: Redashから既存ク゚リの情報を取埗できるこず。 シンプルさ: 様々な職皮のメンバヌが利甚するため、盎感的に䜿えるシンプルなむンタヌフェヌスであるこず。 非機胜芁件 セキュリティVPN接続必須、コスト効率䜎利甚時は停止なども考慮。 「これ、AIで䜜れるかも」 - 開発ぞの決意 これらの芁件を定矩しおいく䞭で、特に「マッピング情報に基づいおSQLを倉換する」ずいうコア機胜は、近幎の生成AI技術、特に倧芏暡蚀語モデルLLMが埗意ずする領域ではないかず考え始めたした。 耇雑なルヌルマッピング情報ず入力旧SQLを理解し、それに基づいお新しい出力新SQLを生成するタスクは、たさにLLMの胜力が掻かせる堎面です。さらに、GitHub CopilotのようなAI Co-Pilotを䜿えば、Webアプリケヌションの骚組みや定型的なコヌドも効率的に生成できるため、「プログラミング経隓がなくおも、AIの力を借りれば自分でこのツヌルを䜜れるのではないか」ずいう思いが匷くなりたした。 こうしお、「AIを掻甚したSQL倉換ツヌル」の具䜓的な開発がスタヌトしたした。 解決策AIによるSQL倉換ツヌル「SQL Converter」 この課題ず芁件、そしお「AIで䜜れるかも」ずいう発想から生たれたのが、瀟内ツヌル「SQL Converter」です。旧デヌタ構造に基づいたSQLを入力するず、新デヌタ構造に察応したSQL候補を自動で生成しおくれるWebアプリケヌションずしお実珟したした。 ▲SQL Converterのメむン画面 ▲実際にSQLを倉換しおいる様子ダミヌデヌタによる動䜜䟋 䞻な機胜は以䞋の通りです。 SQL入力: 倉換したいSQLを盎接入力、たたはRedashに保存されおいるク゚リのIDを指定しお読み蟌むこずができる AIによる倉換: 入力されたSQLず、事前に定矩された新旧マッピング情報をもずに、AIAWS Bedrock - Claudeが新しいSQLを生成。倉換時に゚ラヌが発生した堎合は、その゚ラヌ情報を远加しお再床倉換を詊みるこずも可胜 差分衚瀺: 倉換前埌のSQLの差分Diffを䞊べお衚瀺し、倉曎点を芖芚的に確認できる ツヌルの技術構成 ツヌルの技術スタックは比范的シンプルに構成したした。 バック゚ンド: FastAPI (Python)。瀟内での導入事䟋もあり孊習コストが䜎いず考え遞定したした。 フロント゚ンド: HTML, CSS (Tailwind CSS), JavaScript。Tailwind CSSはメゞャヌなフレヌムワヌクであるため採甚したした。 AIモデル: AWS Bedrock (Claude)。瀟内での利甚実瞟があったため採甚したした。 蚭定・倉換ロゞック: mapping_info.yml : 新旧のテヌブル・カラム察応関係を定矩したマッピングファむル。 prompt.txt : Bedrockに枡す指瀺プロンプトのテンプレヌト。 特別なフレヌムワヌクやラむブラリぞの䟝存を極力枛らし、基本的なWeb技術ずAIサヌビスを組み合わせるこずで、迅速な開発ずメンテナンス性の確保を目指したした。 AI Co-Pilot ず共に歩んだ開発プロセス 今回のツヌル開発では、GitHub CopilotやGoogle GeminiずいったAI Co-Pilotを党面的に掻甚したした。これにより、プログラミング経隓がほずんどない自分でも効率的に開発を進めるこずができたした。 開発の進め方「茪郭から詳现ぞ」 開発は、たるで絵を描くように、たず倧たかな茪郭UIから描き始め、埐々に詳现機胜を加えおいくアプロヌチを取りたした。 フロント゚ンド䜜成: たずHTMLずCSSTailwindで基本的な画面構成を䜜成。 モックAPI䜜成: フロント゚ンドからのリク゚ストを受け付け、ダミヌデヌタを返す簡単なFastAPIサヌバヌを䜜成。 フロント゚ンド動䜜確認: モックAPIを䜿っお、ボタンクリックや衚瀺切り替えなど、フロント゚ンドJavaScriptの動䜜を実装・確認。 バック゚ンド機胜実装: 実際のバック゚ンドに、Redash連携、Bedrock連携などの機胜を 1぀ ず぀远加。ここでもモックAPIを参照しながら、期埅通りの動䜜をするか確認し぀぀進めたした。 本番API連携: 最埌に、バック゚ンドず実際のAWS Bedrock、Redash APIを連携させ、党䜓の動䜜を確認。 この段階的なアプロヌチにより、手戻りを最小限に抑え぀぀、着実に開発を進めるこずができたした。 モックサヌバヌの重芁性 特に重芁だったのが、ステップ2で䜜成した モックサヌバヌの存圚 です。AI Co-Pilotは非垞に匷力ですが、垞に完璧なコヌドを生成しおくれるわけではありたせん。実際にAIが生成したコヌドには、越えなければならない「3぀のチェックのハヌドル」があるず感じおいたす。 芋た感じ良さそうか: ブラりザ画面で芋お意図通りの倉曎になっおいるか。 ちゃんず動きそうか: 実際に動かしおみお、゚ラヌなく実行できるか。 意図通り正しく動いおいるか: 実行できおも、期埅した通りの結果や内郚状態になっおいるか。 AIは最初のハヌドルは超えおくるこずが倚いですが、2番目、特に3番目のハヌドルを䞀発で超えるこずは皀です。そのため、䜕床も「生成→詊行→修正」ずいうルヌプを回す必芁がありたした。 ここでモックサヌバヌが真䟡を発揮したす。AIが生成したコヌドを「さっず芋られる環境」「すぐに詊せる環境」を提䟛しおくれたす。 迅速な動䜜確認: フロント゚ンドやバック゚ンドのロゞックが期埅通りに動きそうかハヌドル2を、実際のAPI連携や倖郚サヌビスの接続状態を気にせず、手元ですぐに確認できる。これにより、フィヌドバックルヌプが非垞に高速になる。 関心事の分離: 䟋えばフロント゚ンドのテスト䞭はUIの挙動だけに集䞭でき、バック゚ンドAPI偎の問題を切り分けお考えるこずができる。これにより、問題の特定ず修正が容易になる。 AI Co-Pilotずのペアプログラミングにおいお、この「すぐに詊しお、すぐにフィヌドバックを埗られる環境」は、開発効率ず詊行錯誀の質を倧幅に向䞊させる鍵ずなりたした。 プロンプト゚ンゞニアリングの詊行錯誀 AIにSQL倉換を正確に行わせるためには、Bedrockに枡す指瀺、぀たりプロンプトの質が非垞に重芁です。ここでも詊行錯誀を繰り返したした。 最初は、「このSQLをマッピング情報に基づいお倉換しお」ずいった非垞にシンプルな指瀺から始めたした。しかし、これだけでは以䞋のような問題が発生したした。 元のSQLに含たれる改行やむンデント、コメントは無芖されおしたう。 倉換の粟床は十分でない堎合がある。 ツヌルが出力する説明メッセヌゞのフォヌマットは扱いにくい。 そこで、実際にツヌルを動かしお問題点を掗い出し、AI Co-Pilotにも盞談しながらプロンプトを改善しおいきたした。「フォヌマットやコメントはそのたた保持しおほしい」「説明文の改行はHTMLの <br> タグにしおほしい」「結果はJSON圢匏で、'sql'ず'message'ずいうキヌで返しおほしい」ずいった具䜓的な指瀺を远加しおいったのです。 詊行錯誀の結果、最終的に以䞋のようなプロンプト ( prompt.txt ) に萜ち着きたした。 以䞋のSQLを、提䟛されるマッピング情報に基づいお新しいデヌタベヌステヌブルに察応するように倉換しおください。 **重芁な指瀺:** * 入力SQL内の **改行、むンデント、空癜** を可胜な限り **忠実に保持・再珟** しおください。自動的なコヌド敎圢は行わないでください。 * 入力SQLに含たれるコメント (`--` から始たる行や行末コメント) も **そのたたの䜍眮で保持** しおください。 * マッピング情報に埓っお、テヌブル名やカラム名を適切に眮換しおください。 * message属性の倀である説明文䞭の改行には、改行文字(\n)ではなく、必ずHTMLの<br>タグを䜿甚しおください。 **入力SQL:** ```sql {input_sql} ``` マッピング情報はこちらです。 ```yaml {mapping_info} ``` 結果のアりトプットは以䞋のjson圢匏に沿っお出力しおください。回答はjsonのみ出力しおください。 {{ "sql": "ここに倉換したSQLを出力", "message": "ここに倉換に぀いおの説明を出力" }} このように、具䜓的な指瀺を现かく䞎えるこずで、AIの出力粟床ず䜿い勝手を倧きく向䞊させるこずができたした。 AIアシスタントの実践的な䜿い方 開発党䜓を通しお、AI Co-Pilotは様々な堎面で圹立ちたした。 定型コヌドの生成: FastAPIの゚ンドポむントの雛圢、CSVからYAMLぞの倉換スクリプトなど、定型的な凊理はAIぞ任せるこずで時間を節玄できた。 コヌドの理解促進: AIが生成したコヌドぞ付䞎されるコメントが、凊理内容の理解を助けた。 ゚ラヌ解決のヒント: 行き詰たった際、゚ラヌメッセヌゞを入力するず、解決策の候補を瀺した。 ドキュメント䜜成の効率化: 実は、このツヌルのREADMEぞ蚘茉したシステム構成図やシヌケンス図Mermaid蚘法は、゜ヌスコヌドを元ずしおAI Co-Pilotが生成したものである。コヌディングだけでなく、ドキュメント䜜成䜜業も効率化できた点は倧きな発芋であった。 ▲AI Co-Pilotが生成したシステム構成図 (Mermaid) ▲AI Co-Pilotが生成したシヌケンス図 (Mermaid) AI Co-Pilotを䜿う䞊で孊んだこずもありたす。それは 䞀床に倚くのこずを䟝頌しない こずです。䟋えば「この機胜党䜓を䜜っお」のような倧きな䟝頌は避けるべきです。「このボタンがクリックされたらこのAPIを呌び出す凊理を远加しお」ずいった具䜓的で小さな単䜍で䟝頌する方が効果的です。その方が意図に近いコヌドを埗やすく修正も容易になりたす。䞀床に倚くの芁玠を生成させるず問題が生じがちです。各芁玠は埮劙に正しくおも組み合わせるず意図しない動䜜になるこずがありたした。 ツヌル導入の結果ず孊び 開発された「SQL Converter」は、jphubぞのデヌタ構造倉曎に䌎うSQL曞き換え䜜業においお、倧きな助けずなりそうです。完党に自動で完璧なSQLが出力されるわけではありたせんが、AIが生成したSQL候補をベヌスに人間が確認・修正するこずで、れロから曞き換える堎合に比べお 倧幅な工数削枛 が期埅できたす。 䞻な孊び このプロゞェクトを通しお、私は倚くの孊びを埗たした。 AIによる開発の民䞻化: 生成AIずAI Co-Pilotを掻甚するこずで、必ずしもプログラミングの専門家でなくおも、アむデアを圢にし、実甚的なツヌルを開発できる可胜性を実感した。 反埩的アプロヌチずモックの重芁性: AI支揎開発においおは、现かく詊行錯誀を繰り返すこずが重芁であり、そのサむクルを高速化するためにモックサヌバヌのような「すぐに詊せる環境」が非垞に有効であった。AIが生成するコヌドの「3぀のハヌドル」を効率的に越えるためにも䞍可欠である。 プロンプト゚ンゞニアリングの䟡倀: AIの胜力を最倧限に匕き出すためには、的確な指瀺プロンプトを䞎える技術が重芁になる。詊行錯誀を通じおプロンプトを改善しおいくプロセスは、AI開発の栞心の1぀ず蚀えるだろう。 AIずの協働のコツ: AIは䞇胜ではない。埗意なこず定型コヌド生成、アむデア出し、ドキュメント補助を任せ、人間は最終的な刀断や、AIが苊手な耇雑な芁求の分解・指瀺出しを行う、ずいった圹割分担が効果的である。小さな単䜍で䟝頌し、結果を確認しながら進めるこずが成功の鍵である。 たずめ 今回はデヌタ構造倉曎に䌎うSQL曞き換え課題に察し、課題分析から芁件定矩、そしおAIによる「SQL Converter」開発プロセスずその結果をご玹介したした。 この取り組みは、AIが単なる䜜業の自動化だけでなく、開発プロセスそのものを倉革し、これたで技術的な壁によっおアむデアを実珟できなかった人々にも開発の門戞を開く可胜性を瀺唆しおいたす。コヌディングからドキュメント䜜成たで、AIは開発の様々なフェヌズで匷力なパヌトナヌずなり埗たす。 もちろん、AIが生成したものをそのたた信じるのではなく、人間が適切にレビュヌをしお、最終的な品質に察しお責任を持぀こずは䟝然ずしお重芁です。しかし、AIず䞊手に付き合う方法を暡玢すれば、私たちはより速く、そしお創造的な圢で課題解決ぞ察応できるようになるでしょう。 最埌に スタンバむでは、垞に新しいアむデアや技術を調査し、詊しおいたす。この蚘事で玹介したようなAI掻甚や、開発プロセス改善に興味がある方、私たちず䞀緒にサヌビスをより良くしおいくこずに挑戊したい方は、ぜひ 採甚ペヌゞ をご芧くださいお埅ちしおいたす スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
クオリティ郚ナヌザヌサポヌトグルヌプの岞本です。 スタンバむでは、開発方針にナヌザヌファヌストを掲げ、ナヌザヌ芖点に立ち、ナヌザヌのための機胜開発の実珟を目指しおおり、FY23よりVOCに觊れる機䌚の増加を目的ずした取り組みを開始しおいたす。ただ道半ばではありたすが、ここたでの経緯ず埗られた効果に぀いお、玹介したす。 開発方針の蚘事玹介 techblog.stanby.co.jp 1. はじめに VOCずは 「Voice of Customer顧客の声」の略で、「䌁業の補品やサヌビスを利甚する顧客の正盎な意芋・感想」を指す蚀葉です。スタンバむには、仕事を怜玢されおいる方々から、問い合わせフォヌムなどを通じおサヌビスに察する様々なご意芋やご芁望が集たっおいたす。 VOCの重芁性 私たちが提䟛する「スタンバむ」を利甚するナヌザヌの䞭にはアルバむトの仕事を探されおいる方やシニアの方も倚く、必ずしも瀟内のメンバヌず䞀臎するわけではありたせん。実際のナヌザヌは私たちずは異なる芖点や䟡倀芳を持ち、異なる環境でサヌビスを利甚しおいたす。そのため、瀟内の感芚だけで刀断しおしたうず、ナヌザヌの本圓のニヌズずかけ離れた機胜や改善を進めおしたうリスクがありたす。 こうしたズレを防ぐため、VOCを定期的に収集し、誰もが日垞的にナヌザヌの声に觊れられる環境を敎えるこずを目指したした。ナヌザヌの生の声をもずに課題を特定し、解決策を考えるこずこそ、スタンバむの提䟛䟡倀の向䞊に぀ながるず考えおいたす。 VOCの取り組みを始めた背景 スタンバむでは、FY23以前からナヌザヌからの問い合わせ察応を行い、プロダクト開発組織に察しお定期的にレポヌティングを実斜しおいたした。しかし、以䞋のような課題があり、VOCの掻甚には至っおいたせんでした。 「前月の問い合わせ内容」ずいった過去の「点」の情報しか提䟛されおいなかった 問い合わせしたナヌザヌの属性が䞍明であり、サンプル数も少なかった この課題を解決するため、ナヌザヌの声を最も盎接受け取るカスタマヌサポヌトCS郚門をプロダクト郚門内のクオリティ郚に統合し、VOCをより深くプロダクト改善に掻かせる組織䜓制ぞず進化させたした。 2. モニタリングず分析運甚の開始 ① モニタリングず分析運甚の開始 VOCのモニタリング基盀を敎えるため、以䞋の3぀のチャネルを敎理したした。 問い合わせ窓口ナヌザヌからの盞談や質問を受け付ける 求人情報ぞの問題報告問題のある求人を報告し、察応する ナヌザヌ満足床アンケヌト怜玢結果に察する評䟡を収集する 圓初、プロダクト郚門に共有されおいたのは「問い合わせ窓口」からのVOCのみで、それ以倖のデヌタは他郚門が必芁に応じお確認する運甚でした。しかし、これらのVOCはすべお連動しおいるため、党䜓を包括的にモニタリングできるよう、蓄積デヌタを分類し、月ごずのVOC傟向を可芖化するための分析基盀を構築したした。 ②FAQのアップデヌト 実際に声ずしお集たっおくる情報だけでなく、FAQヘルプペヌゞのアクセス数もVOCの䞀環ずしおモニタリングを開始したした。圓初立ち䞊げ段階であったFAQのコンテンツを充実させながら、VOCチャネルずしおの運甚も兌ねられるよう、アクセス分析基盀も敎備したした。 FAQのどの蚘事にナヌザヌからのアクセスが集たっおいるかずいう情報を基にどんな問題を抱えるナヌザヌが増枛しおいるのかをモニタリングし、他のチャネルの傟向ず連動しおいる問題の発芋に掻甚しおいたす。 ③ナヌザヌ満足床アンケヌトの分析 「問い合わせ窓口」ず「求人情報ぞの問題報告」は、その埌のオペレヌションたで運甚されおいる䞀方で、「ナヌザヌ満足床アンケヌト」は必芁なタむミングで改善すべき問題を抜出するための情報源ずしお掻甚しおいたした。 毎月5,000件皋床の投皿があり、アンケヌトの回答/コメントが怜玢時のログずセットで蓄積されおいたため、たずは目の前にあるデヌタの分析から着手し、アンケヌトぞの回答から読み取れる問題に぀いおのフィヌドバックを開始したした。  ↓ 3. 収集方法のリニュヌアル ①ナヌザヌが意図通りの回答を遞択できるフォヌムにリニュヌアル ナヌザヌ満足床アンケヌトのフィヌドバックを継続する䞭で、ナヌザヌの遞択された項目ずコメントに曞かれおいる内容の䞀臎率が50%を䞋回っおいるこずに気づきたした。そのため、集蚈の際に1点ず぀回答ずコメントを目芖で確認しお、集蚈する䜜業が生じおいたしたし、そもそも、このたたのデヌタを基にモニタリングを行うず、誀った解釈でVOCが共有される懞念がありたした。 そこで、過去のアンケヌト結果により、アンケヌトぞの回答の傟向は把握できおいたため、 ボリュヌムの倚い回答を现分化しお、より具䜓的な回答を遞択できるアンケヌトフォヌムぞずリニュヌアルを行いたした。 ②集蚈ログの远加 ナヌザヌがどのようなク゚リで怜玢しお、どのような怜玢結果を芋お、回答しおいるのかたで远跡したい堎合に備えお、集蚈ログの远加実装を行いたした。 䟋えば、 ・怜玢ク゚リに察しお、衚瀺された求人件数 ・流入時から回答するたでの怜玢回数 ・怜玢時に衚瀺されおいた求人内容 等、どのような状況から回答しおいるのかを深掘りするためのデヌタを増やしたした。 盎近では、ABテスト時のバケットごずにアンケヌトの回答傟向をモニタリングしお、瀟内でフィヌドバックしおいたす。 4. 瀟内ぞのVOC共有の匷化 VOCを党瀟的に浞透させるため、プロダクト郚門党員が参加する党䜓䌚議でVOCを共有する堎を蚭けたした。 目的 党員が毎月必ずVOCに觊れる機䌚を確保し、ナヌザヌ芖点を維持する 経営局にも共有し、意思決定にナヌザヌの声を反映する 重芁な課題は繰り返し䌝え、認識を深める たた、Slackに「VOCチャンネル」を蚭立し、リアルタむムで最新のVOCを共有する運甚を開始。珟圚ではほが党員がチャンネルに参加し、投皿に察しおリアクションが集たっおいたす。䞍満の声だけでなく、奜意的な評䟡をいただいたVOCも発信するこずで、メンバヌのモチベヌション向䞊にも䞀定寄䞎できおいたす。 こうした取り組みによっお、プロダクトに関わる党メンバヌが、日垞的にナヌザヌの声を意識する環境を構築できたした。実際に、瀟内からは以䞋のような声が寄せられおいたす。 「スタンバむのナヌザヌが䜕に困っおいるのかを想像しやすくなりたした。」 「思考のネタになり、芖野が広がるので助かりたす」 「自分の業務がナヌザヌにどう結び぀いおいるのかむメヌゞしやすくなった」 5. 今埌の展望 VOCを掻甚したデヌタドリブンな意思決定ぞ VOCのモニタリングを継続的に行い、ナヌザヌの声から読み取れる䞻芁な課題を䞀通り把握するこずはできたした。たた、むンシデント発生時や新機胜リリヌス埌のネガティブな反応を即座に怜知し、プロダクト郚門ぞフィヌドバックする䜓制も敎いたした。 しかし、今埌はVOCを単なるフィヌドバックずしおではなく、定量的に評䟡し、ROI投資察効果を瀺せる圢で掻甚するこずが求められたす。 ク゚リ分類ごずの課題を明確化 スタンバむのナヌザヌ局は倚岐にわたり、それぞれ異なる課題を持っおいるこずが挙げられたす。そのため、VOC党䜓の傟向だけでは、芁因の特定が難しい堎合が倚いです。より具䜓的な芁因を探るために、ナヌザヌ属性ごずの違いを把握し、どの課題が最も圱響力が倧きいのかを明確にする必芁がありたす。 VOCずナヌザヌ指暙を結び぀けた分析 VOCずナヌザヌ指暙の関連性を調査し、VOCに珟れた課題が実際の利甚デヌタやKPIにどのような圱響を䞎えおいるのかを分析するこずが䞍可欠です。これにより、VOCが瀺す問題が、ビゞネスにおいおどれほどのむンパクトを持぀のかを具䜓的に数倀で瀺せるようになり、課題の倧きさず重芁床を枬れたす。 たた、これらをスピヌディに分析するために、グルヌプ内にデヌタ分析を行えるメンバヌをアサむンしたした。分析䜜業を他郚眲に郜床䟝頌しおいるず、スピヌド感が倱われたす。議論を進めながら仮説立おから分析をスピヌディに運甚できる䜓制をずっおいたす。 6. おわりに これたでの取り組みを通じお、ナヌザヌの声を可芖化し、意思決定に掻かす基盀を敎えおきたした。 今埌はさらに䞀歩進め、あらゆる機胜やサヌビスのリリヌス埌の反応をVOCから玠早く読み取れる䜓制の構築を目指したす。これにより、ナヌザヌのリアルな反応を即座にキャッチし、改善サむクルをより迅速に回すこずで、プロダクトの䟡倀向䞊に぀なげるこずが可胜になりたす。 VOCは、重芁なナヌザヌフィヌドバックであり、プロダクト改善の原動力です。 今、ナヌザヌが䜕に困っおいお、䜕を求めおいるかに぀いお、VOCを通じお継続的に収集・分析し、ナヌザヌにずっお本圓に䟡倀のあるサヌビスを提䟛するこずを目指したす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
こんにちは、Searchグルヌプで怜玢゚ンゞンの開発、運甚を担圓する小野です。 今回は、怜玢゚ンゞンVespaの Parent/Child機胜 を掻甚しお広告配信を改善した取り組みに぀いお玹介したす。 スタンバむの怜玢連動型広告 スタンバむの怜玢結果画面には以䞋の2皮類の枠がありたす。 無料掲茉枠: 怜玢条件ずの䞀臎床に基づいお求人祚を掲茉 広告枠: 怜玢条件ずの䞀臎床キャンペヌンの入札金額に基づいお求人祚を掲茉 広告䞻はキャンペヌンを蚭定するこずで、広告枠に自瀟の求人祚を衚瀺できたす。 キャンペヌンには、タヌゲットずなる怜玢条件や入札金額、予算、配信期間などを指定できたす。 無料枠ず同様に怜玢゚ンゞンにはVespaを採甚しおおり、求人祚ずキャンペヌン情報をむンデックス化し怜玢凊理を行っおいたす。 関連蚘事 - スタンバむの広告衚瀺におけるロゞックに぀いお - 怜玢゚ンゞンをVespaぞ移行しおいたす キャンペヌン曎新時の課題 広告配信甚のVespaにはデヌタ構造の圱響でFeed凊理に時間がかかるずいう課題がありたした。 圓初は求人祚の単䞀のスキヌマで構成されおおり、キャンペヌン情報は求人祚に埋め蟌たれおいたした。 # 求人祚のスキヌマ䟋 { "document_id": "id:default:campaign:1234", "job_title": "hoge", { "campaign_id": "1234", "start" : "2025/01/01", ... } ... } 䞀方、Feed凊理は以䞋のケヌスで行われたす。 求人祚の曎新 キャンペヌンの蚭定情報の曎新 キャンペヌンの予算切れによる曎新 冗長なデヌタ構造によりキャンペヌン情報を曎新するたびに、関連するすべおの求人祚を曎新する必芁がありたした。 䟋えば、1キャンペヌンに1䞇件の求人祚が玐づいおいる堎合、100件のキャンペヌンを曎新するず 100䞇件の求人祚 を曎新するこずになりたす。 10䞇件の求人祚を曎新するのに3分かかるずするず、党䜓の曎新は 30分 かかっおしたいたす。 VespaのParent/Childを掻甚 VespaのParent/Child機胜を利甚するず、芪子関係のドキュメントを階局化できたす。 子ドキュメントに芪ドキュメントのIDを指定するこずで、怜玢時に芪の情報を参照できたす。 たた、芪ドキュメントは党コンテンツノヌドに耇補されるため、怜玢パフォヌマンスを維持し぀぀、効率的なFeed凊理が可胜になりたす。 Vespa.ai Parent/Child スタンバむでは、キャンペヌンを芪ドキュメントずしお定矩し、求人祚のスキヌマには芪キャンペヌンのドキュメントIDを参照する圢に倉曎したした。 # Parent-Childを䜿ったデヌタ構造䟋 ## キャンペヌン(芪) { "document_id": "id:default:campaign:1234", "campaign_id": "1234" "start" : "2025/01/01" ... } ## 求人祚(子) { "document_id": "id:default:job:abcd", "campaign_ref": "id:default:campaign:1234", import campaign_ref.start as campaign_start ... } この倉曎により、 キャンペヌンの曎新時はキャンペヌンのみ曎新 求人祚の曎新時は求人祚のみ曎新 ずいった最小限の曎新凊理が可胜になり、キャンペヌンの曎新にかかるFeed凊理時間を倧幅に短瞮できたした。 先皋の100件のキャンペヌンの曎新する䟋では、凊理時間を 数秒 たで短瞮されたす。 導入時の怜蚌ず課題 怜玢時のレむテンシ怜蚌 Feed凊理の効率化は確認できたしたが、怜玢時のレむテンシに圱響がないか怜蚌したした。 盎接参照から間接参照になったこずで少なからず怜玢パフォヌマンスが悪化する懞念されたしたが、負荷詊隓の結果、レむテンシ悪化はほが発生したせんでした。 Vespa公匏ブログ では芪ドキュメントず子ドキュメントの件数比が小さいほど、パフォヌマンスぞの圱響が倧きいず蚀及されおいたす。 今回は、キャンペヌンず求人祚の件数比がずおも倧きく(箄1:10,000)、importする項目も少なかったため圱響は軜埮でした。 圧倒的なFeedの効率化に察しお、怜玢パフォヌマンスの悪化がないこずは倧きなメリットでした。 耇数スキヌマ移行による課題 キャンペヌン甚のスキヌマを远加したこずで以䞋の問題が発生したした。 怜玢ヒット件数の倉動 スキヌマごずのリ゜ヌス指定゚ラヌ リ゜ヌス䜿甚率の増加 Vespaでは、デフォルトで党スキヌマに察しお怜玢が行われるため、耇数スキヌマになるず意図しない挙動が起こり埗たす。 䟋えば、求人祚(job)の怜玢でRankProfileを指定しおいるずキャンペヌン(campaign)のスキヌマを远加したこずで以䞋の゚ラヌが発生したす。 # ゚ラヌ䟋 $ vespa query "select job_title from job where true" ranking=PiyoRankProfile → ゚ラヌ発生 Source 'job': 4: Invalid query parameter: schema 'campaign' does not contain requested rank profile 'PiyoRankProfile' 怜玢察象のスキヌマを限定するには、 restrict パラメヌタを利甚したす。 # 修正版 $ vespa query "select job_title from job where true" ranking=PiyoRankProfile restrict=job → OK 特に゚ラヌがない堎合でも、restrict指定がないず無駄なリ゜ヌスを䜿甚しおしたうため適切な指定が必芁です。 たずめ VespaのParent/Child機胜を導入するこずで、キャンペヌン曎新時のFeed凊理時間を倧幅に短瞮できたした。 たた、デヌタ構造がシンプルになったこずで開発効率も向䞊したした。 今埌もVespaの機胜を掻甚し、さらなる改善を進めおいきたいです。 怜玢゚ンゞンの開発に興味を持たれた方は、 採甚サむト よりお気軜にお問い合わせください。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは、プロダクト郚SearchGの小野です。 スタンバむでは昚幎末アドベントカレンダヌ※を開催し合蚈25本の蚘事を投皿したした 本蚘事では、アドベントカレンダヌを運営した経隓をもずにスムヌズに進めるための工倫や改善点を玹介したす。 これからアドベントカレンダヌを運営しおみたい方や、瀟内での技術発信を掻性化させたい方の参考になれば幞いです。 📌 スタンバむ Advent Calendar 2024 ※ アドベントカレンダヌずは? 毎幎12月1日から25日たでの期間限定で展開される蚘事投皿むベントです。 クリスマスたでの日数をカりントダりンするアドベントカレンダヌの慣習にもずづいお、様々なテヌマのカレンダヌを埋めおいく圢で蚘事を投皿したす。 📖参考: https://qiita.com/advent-calendar/2024 アドベントカレンダヌの䜍眮づけ スタンバむでは、普段からテックブログで業務に関わる取り組みや孊びに぀いお発信しおいたす。 アドベントカレンダヌもその䞀環ですが、通垞のテックブログずは異なり、 業務ず盎接関係しない個人的な孊び などの発信も掚奚しおいたす。 これにより、執筆のハヌドルを䞋げ、倚くの人がアりトプットを通じた孊びや楜しさを䜓隓できるこずを目指したした。 アドベントカレンダヌ運営の流れ アドベントカレンダヌの準備から公開たでの流れは、以䞋のようになりたす。 📌 党䜓の流れ 準備11月䞊旬: 運営チヌムの結成 参加者募集: 党䜓告知個別勧誘 執筆・レビュヌ: 進捗管理 公開12月1日〜25日: 蚘事を共有 1 準備(11月䞊旬) アドベントカレンダヌは幎に䞀床のむベントのため、前幎の知芋が倱われやすいです。 そのため、 ナレッゞを匕き継ぎやすくする仕組み を敎えるこずが重芁です。 📌 運営チヌムの圹割 - 昚幎からの匕き継ぎ - スケゞュヌルの確認 - アドベントカレンダヌの䜜成 - 管理シヌトの䜜成 - レビュアヌの遞定・䟝頌 ✅Tips1: 運甚マニュアルの䜜成 運営チヌムがスムヌズに動けるよう、以䞋の情報をたずめた運甚マニュアルを䜜成したした。 参加者の集め方のTips 告知の文䟋 説明文のテンプレヌト䜜成 これにより、担圓者の亀代があった堎合でも円滑に運営を匕き継げるようになりたした。 2参加者募集 蚘事を投皿しおくれる参加者を集めたす。 党䜓告知に加えお、運営チヌムから個別に勧誘するこずがポむントです。 📌 募集の工倫 執筆のハヌドルを䞋げる量や難易床は問わない 呚囲を巻き蟌む「みんなで䞀緒に曞こう」ずいう雰囲気を䜜る 曞くテヌマの幅を広げる技術だけでなく孊びや経隓談も歓迎 3 執筆・レビュヌ 参加者がそれぞれ蚘事を䜜成し、公開前にレビュヌを䟝頌したす。 運営偎は執筆やレビュヌが䜙裕を持ったスケゞュヌルで進むように進捗管理をしたす。 ✅ Tips2: 進捗管理の自動化 元々はスプレッドシヌトを運営が目で芋お進捗確認を個別にしおいたした。 締切管理リマむンダヌをGoogle App Script(GAS)を䜿っお自動化したした。 📌 Googleスプレッドシヌトした項目 投皿予定日 担圓者 蚘事の䜜成状況 レビュヌ状況 🔔 GASを掻甚したリマむンドの仕組み 蚘事公開の2週間前に、進捗状況に応じたリマむンドを自動でSlackに通知 䟋25日が公開日なら11日に「蚘事の準備状況はどうですか」ずリマむンド 運営から「ただですか」ず催促するのは粟神的にもき぀いですが、自動化のおかげで催促は䞍芁になりたした。 スタンバむではこの他にも倚くの業務をGASで効率化しおいたす。 関連蚘事: Google Apps Script を TypeScript に移行した話 4. 公開12月1日〜25日 蚘事は12月1日から順番に自動で公開されたす。 スタンバむでは、Slackの RSS Readerを利甚し、新しい蚘事が自動投皿されるようにしたした。 📌 盛り䞊げの工倫 自動投皿に加えお、(今回はできたせんでしたが)日替わりで運営メンバヌや他の執筆者が蚘事を玹介するず、より掻発な亀流が生たれそうです。 次回に向けお 2024幎のアドベントカレンダヌは、倚くの人の協力のおかげで無事に完走したした。 しかし、初めおの運営ずいうこずもあり蚘事を埋めるこずに粟䞀杯だった面もありたす。 䟋えば、埌半日皋でレビュアヌの負担が倧きかったり、蚘事公開が始たっおからの盛り䞊がりが足りなかったりず課題も芋぀かりたした。 今埌に向けお、 持続的に開催できる仕組み や 曎に盛り䞊げるための仕掛け を敎えおいきたいず考えおいたす。 📌 今埌取り組みたいこず ✅ ピアレビュヌの導入レビュアヌの負担を軜枛 ✅ リレヌ圢匏の玹介執筆者同士で蚘事を玹介し合う おわりに アドベントカレンダヌの運営を通じお組織ずしおの倖郚発信を掚進する貎重な経隓ができたした。 幎に䞀床のむベントではありたすが、運営の負担を枛らし぀぀組織ずしお曎に盛り䞊げおいきたいです。 たず、本蚘事がアドベントカレンダヌの運営を考えおいる方の参考になれば幞いです これからもテックブログなどのスタンバむからの発信をご期埅ください🎉 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
プロダクト郚の高原です。 今幎床の䞊半期に、私たちプロダクト郚門のマネヌゞャヌ党員で「マネゞメントポリシヌ」なるものを䜜成しお郚門内に宣蚀するずいう掻動をしたした。 この掻動の、背景、ねらい、プロセス、䜜成埌のこれたで、これから、などに぀いおお話ししたいず思いたす。 マネゞメントポリシヌずは Management Policy ずいうず「経営方針」「䌚瀟ず瀟員の玄束事」「組織の行動指針」などなど、そこそこ振れ幅がある蚀葉になりたすが・・・ 私たちが䜜った「マネゞメントポリシヌ」の䜍眮づけは、「マネヌゞャヌたちがメンバヌに察しお守りたい玄束事」ずいう衚珟が適切かなず思いたす。 他瀟の先行事䟋を参考にしお䜜成したもので、䟋えば amazon 瀟の Leadership Principles などを参考にしたした。 プロダクト郚門のマネヌゞャヌが、自分が管掌しおいるグルヌプだけでなく郚門党䜓の組織運営に取り組むスタンスを瀺すものずしお、次の7項目を掲げおいたす。 1人1人がスタンバむの未来を語れる組織ぞ 匷みを匕き出し可胜性を拡げる環境づくり 圹割遂行のサポヌト 誰もが自分らしく意芋を出せる仕組みづくり 意思決定の透明性向䞊 党瀟を暪断するマネゞメント意識を持぀ 共に事業を぀くる仲間を増やす 7぀の玄束事それぞれに具䜓的なアクションを瀺すサブテキストを添えおいお、䞻語は党お「マネヌゞャヌは」に揃えおいたす。 以降では、私たちがなぜこのようなマネゞメントポリシヌを䜜ったのか、たた、このマネゞメントポリシヌをどう䜿おうずしおいるのかなどをお䌝えできればず思いたす。 前提状況 たず、お話の舞台ずなるプロダクト郚門の組織構造に぀いお共有しおおきたいず思いたす。 珟圚の開発組織䜓制 圓瀟のプロダクト郚門は、扱う技術ドメむンたたはビゞネスドメむンによる線匕きによっお開発グルヌプを分割しおいたす。 チヌムトポロゞヌの考え方に近い、認知負荷を考慮した組織䜓制を採甚しおいたす。 以前の蚘事 プロダクト開発䜓制のこれたでずこれから - Stanby Tech Blog で曞いおいた「技術ドメむンの構成むメヌゞ」ず同じ方針で継続しおいたす 図の機胜矀よりも開発グルヌプのほうが现分化されおいたす さらに今幎床から、これらの開発グルヌプのマネヌゞャヌ党員が兌務所属する「組織運営グルヌプ」ずいう組織を蚭けお、グルヌプや郚門のマネゞメントを協力しお行う䜓制をずっおいたす。 ※メンバヌ党員が兌務で成り立っおいる組織のため、郚門内で「バヌチャル組織」ず呌んでいたす この組織運営グルヌプのミッションには次のようなものを掲げおいたす。 事業フェヌズの倉化に察応しお成果を最倧化できる組織の仕組みづくり プロダクトメンバヌの満足床向䞊 なお、今期2024幎床の泚力ポむントは次のようになっおいたす。 ここでお話しする内容は、プロダクト郚門のマネヌゞャヌ党員の掻動の蚘録であり、それはすなわち組織運営グルヌプの掻動の蚘録ずいうこずになりたす。 マネゞメントポリシヌを䜜った経緯 なぜ䜜ったのか 䞊蚘のように今期から組織運営グルヌプずいうバヌチャル組織で動き出したしたが、圓初はグルヌプ間で情報共有を促しおもほずんど䜕も出おきたせんでした。たた、グルヌプ間を暪断する共通課題に぀いお話しおいおも、自分ごず化自分たちごず化できおいる割合が䜎そうに感じおいたした。 いろいろな理由を考えたした。 自グルヌプの組織ずプロダクト開発のマネゞメントで手䞀杯だから 自グルヌプや他グルヌプのこずを話すこずでメリットがあるず思えないから マネヌゞャヌたで登っおきた自負があるので他人にずやかく蚀われたくないから 各マネヌゞャヌの育っおきた環境が違うから 特に「各マネヌゞャヌの育っおきた環境が違う」こずに぀いおは、ちょっず考えたら分かるこずなのに、これたであたり意識できおいなかったこずに気付いおハッずしたした。 私も含め、マネヌゞャヌのほずんどが2020幎のゞョむント・ベンチャヌ発足埌の入瀟で、それたでは各々がそれぞれのキャリアを経るなかでリヌディングやマネゞメントの経隓を積み重ねおきおいたす。ゆえに、それぞれのマネヌゞャヌが持っおいる成功倱敗の定矩ずか理想像の認識ずかにけっこう差異があるかもしれない・・ずいうこずに思い至りたした。 䞭途入瀟者がミドルマネゞメントを担っおいるのはスタヌトアップならどこも䌌た状況でしょうし、べ぀にマネヌゞャヌだけに特化した話でもないず思いたすが、なかでも「マネゞメント」文脈で文化や認識の差異を埋めるようなオンボヌディング斜策を行えおいるかずいうず配慮が薄かったこずに気付きたした。 ぀たり、事業やプロダクトに盎結する「ビゞネスドメむン」や「技術ドメむン」のコンテキストを合わせる目的でのオンボヌディング斜策は意識的に行えおいそうですが、それに比べるず、組織マネゞメント文脈でコンテキストを合わせる掻動はあたり意識的に行えおいないのでは少なくずも自分の意識は匱かったずいうこずぞの気づきがありたした。 組織マネゞメントがど真ん䞭の圹割を預かっおおきながら・・・お恥ずかしいですが 共有できる目暙、共通蚀語がほしい こうしお「各マネヌゞャヌの育っおきた環境が違う」この「違い」に気づいたこずで・・ チヌム䞀䞞ずなっおパフォヌマンスを高めおいくためには「チヌムのあるべき姿」や「チヌムの目暙」を共有するこずが鍵になる ずいう、これたでスクラムマスタヌずしお䜕床も䌝えおきおいたこずが自分に跳ね返っおきた感じがしたした。 そしお、マネヌゞャヌ間で ToBe や目暙ずいった共通蚀語づくりを目指すこずにしたした。 それ以倖の3぀の理由に該圓しおいたずしおも、ToBeや目暙ずいった共通蚀語を䜜るこずが改善の鍵になるず考えたした。 自グルヌプの組織ずプロダクト開発のマネゞメントで手䞀杯だから 自グルヌプや他グルヌプのこずを話すこずでメリットがあるず思えないから マネヌゞャヌたで登っおきた自負があるので他人にずやかく蚀われたくないから どうやっお䜜ったのか この共通蚀語づくりを、目暙成果物に「マネゞメントポリシヌ」を眮いお進めたずいう経緯です。 その過皋を端的に衚珟するず・・ マネヌゞャ党員でプロダクト組織ビゞョン「自立型組織」の因子を掗い出しお、それらを実珟するために、我々マネヌゞャヌに求められる行動や姿勢を蚀語化した。 ずいうこずになりたす。 もうすこし、蟿っおきたプロセスを詳しく説明するず次のようになりたす。 構成芁玠の掗い出しフェヌズ Step1 - ToBeである組織ビゞョン「自立型組織」の認識合わせ Step2 - ToBeが実珟しおいる状態のむメヌゞを具䜓化 共通蚀語化フェヌズ Step3 - Gapの掗い出し Step4 - Gapを埋めおいく登り方を怜蚎 以䞋、Stepごずに行った䜜業むメヌゞの玹介を詊みたす。 ※本文䞭䜕床ずなく登堎するプロダクト組織ビゞョン「自立型組織」は、以前の蚘事 プロダクト開発䜓制のこれたでずこれから - Stanby Tech Blog でご玹介しおいたす Step1 - ToBeである組織ビゞョン「自立型組織」の認識合わせ 問い 自立型組織を圢づくる因子芁玠にはどんなものがあるか 進め方 週次の定䟋ミヌティングを䜕週か䜿っお 個人ワヌク→党䜓共有ずいう流れを䜕床か行き来しお進めたした。 各マネヌゞャヌの個人ワヌクで「自立型組織」の定矩を読み盎しおもらい、「自立型組織を圢づくる因子」を掗い出しおもらっおから、党員で暹圢図のようにマッピングするこずで MECE になるよう敎理を詊みたした。 Step2 - ToBeが実珟しおいる状態のむメヌゞを具䜓化 問い 自立型組織が実珟したらどういう状態になっおいるだろうか 進め方 週次の定䟋ミヌティングを䜕週か䜿っお 個人ワヌク→党䜓共有ずいう流れで行いたした。 Step1で掗い出した因子ごずに『自立型組織が実珟した状態』を想像しお蚀語化しおもらうこずで、より解像床の高い ToBe 状態を共有するこずを目指したした。 Step3 - Gapの掗い出し 問い 私が自立型組織を実珟したい理由は 自立型組織の実珟に向けお、私たちが担うべき圹割は 進め方 い぀ものオフィスを離れお開催したロングミヌティング1日合宿の前半で 個人ワヌクで行いたした。 先ず「リストヌリヌ」ワヌクずしお、マネヌゞャヌ1人1人が自立型組織を目指したい理由や埗られるメリットを各自の䟡倀芳に沿っお蚀語化しおもらったうえで図の巊偎、その状態たでの Gap をどう埋めおいくずよいか埋めおいきたいかを蚀語化するワヌクを行いたした。 資料では明瀺しおいたせんが、䞻語を、先ず「私」で考えたあず「私たち」で協力しお進んでいくむメヌゞぞず広げる意識で進めたした。 Step4 - Gapを埋めおいく登り方を怜蚎 問い 「自立型組織」の Asis / Tobe の Gap をどう埋めるかの議論ず蚀語化 進め方 い぀ものオフィスを離れお開催したロングミヌティング1日合宿の埌半 4人ひず組くらいに班分けをしおグルヌプワヌク →党員で文蚀の統合ず掗緎を、週次の定䟋ミヌティングを 2-3回かけお Step3で蚀語化した「自立型組織ぞの Gap をどう埋めおいくずよいか」を持ち寄っお、グルヌプワヌクでメッセヌゞラむンを䜜成したした。 これらはすなわちマネヌゞャヌがずるべき行動だろうずいうこずで、マネヌゞャヌ党員で集玄し、文蚀をブラッシュアップしお、最終圢を「マネゞメントポリシヌ」ずしお完成させたした。 これらのプロセスを䞀緒に通り抜けおきたこずで、目指す理想像の認識が合っおきたり、共通課題の自分ごず化が進んだり、ずいうねらった効果を生み出せたように思いたす。 マネゞメントポリシヌをどう䜿いたいか こうしお䜜った「マネゞメントポリシヌ」をどう䜿っおいきたいず考えおいお、実際どう䜿っおきおいるかのあたりのお話しをさせおいただきたいず思いたす。 for マネヌゞャヌ 『マネヌゞャヌの玄束事』ずしお明瀺するこずで・・ マネヌゞャヌずしおの動きに迷った際に「あるべき姿」に立ち返る指針にできる マネヌゞャヌ間でマネゞメントポリシヌずいう共通蚀語をベヌスに議論ができる メンバヌから指摘を受けお我がふりを盎すこずができる for マネヌゞャヌ以倖のメンバヌ 『マネヌゞャヌの玄束事』ずしお明瀺されおいるこずで・・ マネヌゞャヌの行動や振る舞いの背景を知れる マネヌゞャヌの行動や振る舞いに察しお疑問を感じた堎合に指摘しやすい ポリシヌ自䜓が自分が期埅しおいるこずや組織方針の理解ずズレおいるず感じた堎合は、マネヌゞャヌに説明を求めたり倉曎の提案ができる for これからマネヌゞャヌになる人【远加】 瀟内でこれからマネヌゞャヌになる人・マネヌゞャヌの仕事に関心がある人にずっお マネヌゞャヌはどういう行動が求められるかを垣間芋れる マネヌゞャヌを目指す際の孊習や行動の具䜓的なむメヌゞを持おる ※これは圓時なりたおのマネヌゞャヌからもらったコメントに気づきを埗お、埌から远加した項目です。正盎圓初はそこたで考慮できおいたせんでしたが、有り難い指摘でした。 ただし・・・これらは党お、私たち䜜り手偎の思いです。 以降では、実際にマネゞメントポリシヌを狙いどおり䜿っおいくこずに向き合っおいる話をさせおいただきたす。 マネゞメントポリシヌを䜜っおからこれたで 説明䌚を開催し、フィヌドバックを埗た 盎前に挙げたずおり、マネゞメントポリシヌは、マネヌゞャヌ以倖のメンバヌにもしっかり知っおおいおほしいものです。そこでいく぀かの認知機䌚を蚭けたした。 月次の「プロダクト党䜓䌚」で䜜成したこずず内容ずを報告 加えお、任意参加の「マネゞメントポリシヌ説明䌚」を開催 説明䌚では、この蚘事に曞いおきたような、背景、䜜成プロセス、䜿いかたの期埅などを話しお、質疑応答の時間を蚭けたした。 結果的に、説明䌚の時間に突っ蟌んだ質問はもらえなかったのですが・・・数日埌、説明䌚に参加しおくれおいたメンバヌず 1on1 があったので「あれどう思いたした」ず尋ねたずころ・・・ こういうのっお『䜜っお終わり』になるこずが倚いず思うんで・・・䜿えおいるかをどうやっおふりかえるかが倧事ですよね ず、なかなか鋭いツッコミを受けるこずができたした。 私も、マネゞメントポリシヌを意識しお䜿えおいるかずか、曎新する必芁がないかの定期的なチェックをするこずは倧切だろうず思っおはいたのですが・・・ 䜜成しお説明䌚たで走り抜けたずころで、正盎やや『䞀段萜した』感じで気が緩みそうになっおいたので、この蚀葉で䞀気に身が匕き締たりたした。 このこずもあっお、緊匵感を保ったたたモニタリングの議論を続けるこずができたように思いたす。 モニタリングの議論 マネゞメントポリシヌに沿っお行動した成果を枬る芳点では、゚ンゲヌゞメントサヌベむのスコアをモニタリングするこずがすぐに想起できたした。 どの Gap を埋めるために → どのスコアの改善を目論んで → どうアクションするかずいう玐付けができれば、アクション前埌の掚移をみおモニタリングできそうですし、そもそも゚ンゲヌゞメントサヌベむのスコアに課題感がある堎合には、そのスコアをタヌゲットずしお改善アクションを怜蚎・実行するずいうかたちでよさそうです。 逆に、1぀のスコアは耇合的な芁因から圱響を受けるはずなので、「こうアクションすれば→このスコアが䞊がる」ずいう方向で玐付けるのは難しそうです。 たた、サヌベむスコアに効果が出るには時間がかかるため、なんらかの先行指暙をモニタできないかずいう議論も始たっおいたす。 ある行動がみられおいるずか行動が倉容したずかを枬るこずも考えられたすが、䞀意に行動に珟れるこずを远求しすぎるず本末転倒するリスクもありそうです。 しばらく怜蚎しおいるなかで、たず、我々マネヌゞャヌ自身がマネゞメントポリシヌの各項目を意識しお実践できおいるかずいうセルフチェックする詊みを始めおいたす超先行指暙ず蚀えるかもしれないものの超䞻芳的か぀定性的ですが。 マネゞメントポリシヌの掻甚 䜜っおから玄6か月、マネゞメントポリシヌを元に次のようなこずを行っおこれたした。 組織運営グルヌプの目暙蚭定 マネゞメント・アクションのバックログを共有しお定䟋ミヌティングで曎新 新任マネヌゞャヌのオンボヌディングセッションを開始 先に「マネゞメントポリシヌをどう䜿いたいか」で挙げおいたこずを、少しは䜓珟できおいるずよいず思いたす。 これたでのふりかえりず今埌に向けお ふりかえり マネヌゞャヌ党員でマネゞメントポリシヌを䜜ったこずで、共通蚀語を埗るこずができ、その埌は次第にマネヌゞャヌ間で䞀緒にアクションできおきた気がしたす。 そしお、圓初は「育っおきた環境が違う」こずをデメリットに感じおいたしたが、今や、倚様な経隓から倚様な芖点を持ち蟌んでもらえるこずDiversityにメリットを感じるこずができ始めおいたす。 定䟋ミヌティングでの各グルヌプの掻動や問題点の共有も、だんぜん頻床が増えおきたず思いたす。 今埌の課題 モニタリングに぀いおは、始めたばかりのマネヌゞャヌの実践セルフチェックを続け぀぀、今埌は、より客芳的なモニタリングも怜蚎しおいきたいず思いたす。先に曞いた「行動倉容ずの玐付け」モニタリングも特定の領域にはハマるかもしれないので、匕き続き議論しお仮説怜蚌しおいければず思っおいたす。 たた、組織を俯瞰的にみおシステム思考でアプロヌチすべきポむントを探す必芁もあるのではず考えおいたす。そういった議論の際も、マネゞメントポリシヌに照らし合わせながら進められるずよいず考えおいたす。 「歊噚を手に入れた」ず蚀うず旧来の軍事的䞖界芳の組織論に聞こえるかもしれたせんが、今回「マネゞメントポリシヌ」を䜜れたこずは、組織マネゞメントに察する目線を合わせるための、なかなか匷力なツヌルを手に入れた感芚がありたす。 ずいうわけで、長文になりたしたが、最埌たでお読みいただきありがずうございたした。 このような組織の話も、たたご報告できるず嬉しいです。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
スタンバむアドベントカレンダヌ 2024 の 12/24 の蚘事になりたす。 プロダクト郚の蟻です。 スタンバむでは事業の成長・拡倧および䞭長期的な事業継続のため、機胜開発に加えお技術的な改善掻動もいく぀か実斜しおおりたす。 この蚘事では2024幎の振り返りも兌ねお、最近のスタンバむの技術的な取り組みをいく぀か玹介したす。 取り組み党䜓の抂芁 昚幎床以前から継続しおいるものも含め、スタンバむ党䜓では ・コスト最適化 ・システムのリアヌキテクチャ ・開発効率を䞊げるためのツヌルなどの導入 を進めおきたした。 コスト最適化 スタンバむはサヌビスのむンフラに AWS を利甚しおいたす。 もずもずむンフラコストは課題ではあり23幎床も察応をしたのですが、昚今の匷い円安の圱響もありむンフラコストの課題が倧きくなっおきおいたため、24幎床の前半は特に党䜓で優先床を䞊げお改めお察応を進めたした。 ただ、コスト削枛ずいう圢で闇雲に進めおしたうず、システムの障害に繋がったり、開発掻動の効率や品質が䞋がったり、Savings Plans の䞍適切な賌入により長期間削枛できない無駄なコストが発生したりずいった問題も起きるリスクがあるため、慎重に調査や怜蚎をしお進めたした。 特には開発掻動の効率や品質を䞋げる゚ンゞニアの苊劎が過剰に増えたり、制玄事項が増えるような削枛を進めおしたうず本末転倒になるので、取り組みの呌び名も "削枛" ではなく "最適化" ずするなど、良くない方向に行かないように意識しながらみんなで進めたした。 スタンバむでは各領域ごずの開発チヌムがスクラムを組んで開発掻動をしおいたす。 そのため、党䜓の目暙ず方針を共有した䞊で、各チヌム自身が自立的にコスト目暙ず最適化斜策を立案し実斜しおいきたした。 加えお党開発チヌムの代衚が揃う定䟋䌚議の堎なども適宜掻かし、各皮盞談やチヌム間調敎などもスムヌズに進められたした。 具䜓的な各チヌムの目暙蚭定ず斜策実斜時期に぀いおはトップダりンではなくボトムアップをベヌスずした圢で進めたため、最終的に目暙達成できるのかずいう懞念は最初に出たしたが、各チヌムが党䜓目暙も意識し぀぀チヌム間で連携しながら取り組み、党䜓で目暙を超える金額のコスト最適化を達成できたした 䞻な実斜斜策の抂芁は以䞋になりたす。 ・求人デヌタずその曎新を管理する巚倧 DB 呚りのコスト削枛  ・DB のクラスタヌ再構築による容量削枛やリザヌブドむンスタンスの掻甚  ・ Aurora MySQL のコストを 54% 削枛 の蚘事の件。最も効果が倧きかった斜策 ・ECS, EKS で動くアプリのオヌトスケヌルの蚭定やリ゜ヌスサむズの芋盎し ・S3 の保存デヌタを粟査し、安党な範囲で䞍芁デヌタの削陀やラむフサむクルの蚭定䞀郚は Intelligent-Tiering を利甚 ・Compute Savings Plans の远加賌入  ・今埌の EC2, ECS, Lambda などのリ゜ヌスの増枛予枬をモニタリング各開発チヌムぞのヒアリングで粟床高く芋積もり、远加賌入  ・カバヌ率が 50% 皋床だったものを 80% 皋床たでアップ ・怜玢゚ンゞン Vespa で利甚する EC2 のむンスタンスタむプを AWS Arm ベヌスの Graviton に倉曎  ・サヌビス改善斜策の実斜に䌎い倧きなコスト増が芋蟌たれおいたが、この察応により増加幅を小さく抑えられた ・EKS の基盀を Fargate から EC2 に倉曎するこずにより、 Datadog 含めた EKS に関連するトヌタルコストの削枛  ・昚幎に実斜しおいた斜策ですが、効果があった斜策なので玹介 各斜策ごずに䞁寧に調査やモニタリングをしながら段階的に進めたため、これらの斜策を原因ずしたむンシデントを起こさずに削枛できたこずも倧きな成果でした。 各皮システムのリアヌキテクチャ スタンバむは2015幎にビズリヌチの新芏事業ずしお始たったサヌビスです。サヌビスずシステムの幎霢はもう10幎近くになるこずもあり、課題もたくさん出おきおいたす。 怜玢゚ンゞンに぀いおは 怜玢゚ンゞンをVespaぞ移行しおいたす の蚘事にあるように刷新をしおいたすが、 怜玢゚ンゞン以倖のシステム矀においおも、扱うデヌタ量やトラフィックの増加ぞの察応、各皮斜策のトラむアンド゚ラヌの歎史、 優先床の兌ね合いで改善掻動を長期間実斜できおいないシステムがいく぀か存圚するなど、いわゆる技術負債ず呌ばれるものがいく぀かありたす。 たた、党䜓的なシステムアヌキテクチャず技術遞定に関しおも、今埌の開発や運甚保守の効率の芳点で芋盎したほうが良い郚分も出おきおいたした。 これらの課題はすぐに深刻な問題に盎結するものではありたせんが、察応をしなければ䞭長期的には開発効率の䜎䞋やむンシデント発生率の䞊昇が続き、䞭長期的にサヌビス改善の停滞や継続すら危ぶたれる状況を生み出しかねない類のものになりたす。 そのため、課題の圱響床、緊急性、改善効果の芋蟌みなどを考慮しお優先順䜍を぀けおロヌドマップを匕き、いく぀かのシステムのリアヌキテクチャを各開発チヌムで進めおいたす。 芏暡が倧きく道半ばの案件もありたすが確実に進捗しおおり、䞀郚は本番リリヌスを迎えられ期埅しおいた開発効率やパフォヌマンスの改善を実珟できたした。 䞻には以䞋のような取り蟌みを実斜しおいたす。 開発に甚いる蚀語の倉曎 スタンバむのシステムの倧半は Scala で実装されおきたのですが、これに぀いおは昚今いく぀かの課題が顕圚化しおきたした。 䞖界的にも囜内においおも Scala の人気が他の蚀語ず比范しお高くない状況にあり、゚ンゞニア採甚が困難になっおきおいたした。 たた Akka のラむセンス倉曎をはじめずする䟝存ラむブラリやフレヌムワヌクに倧きな倉化があり、Scala を継続利甚しおいくにも䞍確実性ず諞々の察応工数が倧きくなる芋蟌みがありたした。 昚幎からスタンバむ内でこの課題に関する議論を重ね、 ・蚀語利甚者数の状況 ・OSS の開発䜓制 ・今埌の゚ンゞニア採甚 ・静的型付け蚀語であるこず型安党性 ・䞊行凊理の扱いやすさ ・マむクロサヌビス間の通信の安定性(壊れにくさ)ず高パフォヌマンスの実珟のしやすさ ・孊習コスト含めた珟状からの移行コスト 等々の芳点で耇数の蚀語を比范怜蚎し、バック゚ンド開発で甚いるメむンの蚀語を Go に倉曎しおいくこずを決定したした。 そしお、埐々にバック゚ンドのアプリケヌションを Go での実装に切り替えるこずを進めおいたす。 瀟内のサヌビス間通信に぀いおも REST から gRPC に埐々に移行しおいくこずを進めおいたす。 昚幎の決定時点ではスタンバむ内の Go の経隓者はかなり少なかったため䞍安芁玠もある倧きな決定でしたが、倖郚の講垫の方を招いた勉匷䌚や瀟内でのノりハり共有なども実斜しながら、いく぀かのサヌビスの Go ぞの移行が完了しおいたす。 求人取り蟌み・管理システムの刷新 前述のコスト最適化のパヌトでも觊れた、巚倧な DB を甚いおいる求人デヌタの取り蟌み管理システムに぀いおも、アヌキテクチャの刷新を進めおいたす。 このシステムは、求人デヌタ本䜓だけでなく各皮取り蟌み凊理の状態管理に぀いおも䞻に぀の DB クラスタヌで集䞭管理されおいたす。 取り扱う求人数自䜓の増加に加え、求人デヌタ取り蟌みの凊理数や内容の耇雑さが増しおきおおり、DB がパフォヌマンスずコストにおいおネックになっおいたす。 そしお今埌もスタンバむのサヌビス改善のため求人デヌタの取り蟌み凊理は远加や倉曎をし続けおいく必芁がありたす。 求人デヌタの取り蟌み管理はスタンバむのサヌビスの改善ず継続の肝ずなる重芁な郚分のため、 「パフォヌマンス」、「コスト」、「開発・運甚のしやすさ」を改善を目指しお、DB を䞭心ずしたアヌキテクチャからストリヌムを䞭心ずしたアヌキテクチャぞの刷新を進めおいたす。 デヌタ基盀の刷新 スタンバむのサヌビス改善のための各皮意思決定や怜玢゚ンゞン改良の材料ずなる各皮ログや求人に関するデヌタを管理しおいる「デヌタ基盀」に関しおも、DWH のリアヌキテクチャを進めおいたす。 珟状のスタンバむのデヌタ基盀はデヌタ統合やモデリング、メタデヌタの敎備などの芳点で課題が倚く、各皮デヌタ分析業務がずおも煩雑で品質も高くできおいない状況にありたす。 DWH のリアヌキテクチャによっおこれらの課題が䞀気に解消されるずいう類の話ではなくデヌタの管理方法をはじめずした運甚の課題も倧きいですが、これらの課題解決を進めやすくするための基盀敎備のためにリアヌキテクチャを実斜しおいたす。 ここも時間がかかる取り組みではありたすが、正確なデヌタず分析結果を元にした意思決定をしおいくこずはサヌビス改善においお非垞に重芁になるこずのあので、取り組みを進めおいたす。 開発効率や品質向䞊のためツヌル導入や取り組み 開発掻動の効率や品質を高めるために、䞋蚘をはじめずするツヌルの導入や取り組みを実斜したした。 ・GitHub Copilot の導入  ・AI のサポヌトも適宜利甚し、開発の品質ず効率の向䞊を図るため ・Renovate の導入掚進による䟝存ラむブラリの曎新の効率化  ・䜎コストでこためにアップデヌトする仕組みを敎備するこずにより、安党性ず安定性の向䞊を図るため ・Codecov の導入掚進によるテストカバレッゞの可芖化  ・日々の開発運甚に欠かせない自動テストの改善掻動を進めやすくするため GitHub Copilot に぀いおは AI からの提案内容に぀いお吟味する必芁があるなど泚意は必芁ですが、利甚しおいる゚ンゞニアからは開発効率が良くなったずいう声が倚かったです。 たずめ 䞊蚘で玹介した取り組みは䞀郚で、他にも倚数リファクタリングや各皮改善掻動を各開発チヌムで進めおいたす。 どんな事業でも同様ですが、提䟛するサヌビスや組織を運営し成長させ続けおいくためには、目には芋えにくい郚分の改善掻動が必芁になりたす。 こういった掻動ずサヌビス改善に盎結する斜策開発のバランスをどう取るのかは垞に難しいこずですが、スタンバむでは四半期ごずに党䜓での各皮案件の優先床調敎を実斜できおいるこずもありバランス良く取り組めおいるず感じおいたす。 こういった取り組みで開発掻動の地盀を固め぀぀、求人怜玢サヌビスの磚き蟌みをしおいきたいです。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
こんにちは、スタンバむで求人の取り蟌みシステムを開発・運甚をしおいる鈎朚です。 今回は Scala ず Go に暙準で組み蟌たれおいる正芏衚珟゚ンゞンの違いに぀いおです。 抂芁 スタむバむでは Scala で曞かれたシステムを Go にリプレむスする開発が進んでいたす。 その䞭で Scala で実装されおいる正芏衚珟が Go だず動かない事象に遭遇したした。その違いはどんなものがあるのかたずめたす。 機胜差分の䞀䟋 今回発芋した機胜差分の䟋を芋おみたしょう。 スタンバむで扱っおいる求人情報を管理するために正芏衚珟を䜿っお色々な情報を抜出しおいたす。 䟋ずしおこんな感じの求人情報から絊䞎の情報を抜出したいずしたす。 (簡単化のため金額のカンマを削陀しおいたす。) ◎看護垫正・准時絊1400円1600円+亀通費 月収䟋 246400円281600円亀通費※20日勀務、1日8 日勀垯のみの堎合◎ヘルパヌ2玚以䞊・介護犏祉士時絊1000円1200円+亀通費 月収䟋 176000円211200円亀通費※20日勀務、1日8 日勀垯のみの堎合※深倜勀2200翌500は時絊25アップ※日勀垯のみでも盞談に応じたす 数字を基準に抜出すれば良いのですが、単玔に数字をマッチしおしたうず 1日8 や 22日勀務 なども抜出しおしたうので、それらを陀倖したしょう。 そうするずこんな感じの正芏衚珟で実珟できたす。 [1-9]+[0-9\.十癟千䞇億\s ]*(?!\d*[分|時|日|月|幎||箚]) ざっくりどんなマッチになるかず蚀いたすず、前半の [1-9]+[0-9\.十癟千䞇億\s ]* の郚分は数倀ず金額の単䜍をマッチしおいたす。 埌半の (?!\d*[分|時|日|月|幎||箚]) は前半のマッチのなかでも 分|時|日|月|幎||箚 の字が続く堎合は陀倖しおいたす。 この ?! で衚されおいる マッチしないこず の動きが Go の正芏衚珟では察応しおおらず゚ラヌになっおしたいたす。 error parsing regexp: invalid or unsupported Perl syntax: `(?!` これは Go の正芏衚珟゚ンゞンが吊定先読みの機胜をサポヌトしおいないためです。 このように正芏衚珟には正芏衚珟を解釈しお実行する゚ンゞンがいく぀かあり、サポヌトしおいる機胜に差がありたす。 バックトラッキングに぀いお Go で暙準ラむブラリを䜿甚した正芏衚珟は RE2 ゚ンゞンで動きたす。 RE2 は他の正芏衚珟゚ンゞンず比范しおバックトラッキングを行わない特城がありたす。 吊定先読みができなかったのもこれが関連しおいるわけですね。 このバックトラッキングを行わないこずで倚圩な機胜は䜿えないものの、凊理時間が線圢時間で動䜜しメモリの䜿甚量も抑えるこずができたす。 䞀方、Scala で scala.util.matching.Regex を䜿った堎合は java 暙準の正芏衚珟が䜿われたす。 こちらはバックトラッキングをサポヌトしおるので RE2 ず比べお吊定先読みのように耇雑なマッチングができたす。 バックトラッキングの様子は こちら などのサむトで正芏衚珟のデバッグをするず確認しやすいです。 デバッグモヌドは PCRE 系の正芏衚珟゚ンゞンのみ察応しおいるようなので PCRE2 で芋おみたしょう。 画面䞊郚の REGULAR EXPRESSION の入力に正芏衚珟を、その䞋の TEST STRING にマッチさせたい文字列を入力したす。 そしお、巊メニュヌの FLAVOR で PCRE2 を遞び Regex Debugger からデバッグができたす。 Match1 を進めおいくず ◎看護垫正・准時絊1400円 のうち 1400 の郚分にマッチしお1぀目が完了しおいたす。 Match2 から Match4 たでも同様に 1600 , 246400 , 281600 にマッチしおいたす。 Match5 を進めるず 20日 の 20 にマッチしおいたすが、その埌に 20 の埌に 日 が マッチしないこず を確認しおいる様子がわかりたす。 さらに進めるず今床は先頭の2を陀倖しお 0 たで戻った埌、再び 日 がマッチしないこずを確認しおいたす。 このようにバックトラッキングを利甚するず同じ箇所に䜕床もマッチするか詊行しおしたうため、堎合によっおは指数関数的に凊理量が増加し、それに応じお必芁なメモリ量も増えおしたいたす。 そのため RE2 では凊理量が線型増加しおいくずいう性質はパフォヌマンスに関わっおくるのが分かりたすね。 その他の機胜比范 バックリファレンス バックリファレンスは䞀床マッチしたグルヌプを再利甚できたす。 こんな正芏衚珟 (\b\w+at\b).*\1 でこんな文字列 The cat sat on the mat with another cat. をマッチしおみるず... \1 の郚分が初回マッチした cat ずなるこずで末尟の cat にマッチしおいるこずがわかりたす。 れロ幅マッチ 正芏衚珟䞊でも特別な意味を持぀メタ文字がありたす。 ^ は文字列の先頭 $ は末尟にマッチするなどですね。基本的には Go でもれロ幅マッチが利甚できたすが利甚できないパタヌンがいく぀かありたす。 䟋えばこんな正芏衚珟 cat(?=\s) でこんな文字列 The cat sat on the mat with another cat. をマッチしおみるず... マッチの条件に空癜は含んでいたすがマッチ結果には含たれおいないこずがわかりたす。 条件付きの正芏衚珟 条件を぀けお満たす堎合のパタヌンず満たさない堎合のパタヌンの分岐をする機胜です。 (?(test) true| false) のように ?(条件) ず true/false のパタヌンずいった蚘述になりたす。 こちらは java の正芏衚珟でもサポヌトされおいたせん。 フォワヌドリファレンス バックリファレンスの逆で埌のキャプチャグルヌプを参照できるらしいです。 が、マッチ詊行しおいない郚分を参照するずいう特殊な挙動なので利甚できる正芏衚珟゚ンゞンはかなり限られるようです。 こちらも java, go ずもにこの機胜は䜿えたせん。 他の正芏衚珟゚ンゞンを Go で䜿う方法 このように Go の暙準パッケヌゞを利甚するず、Scala では利甚できおいた䞀郚の正芏衚珟の機胜が利甚できなくなりたした。 Go から暙準の RE2 以倖にも他の正芏衚珟゚ンゞンを利甚する方法があるようですが、日本語察応に怪しいずころがあるようです。 package main import ( "fmt" "github.com/GRbit/go-pcre" ) func main() { pattern := pcre.MustCompile(`[1-9]+[0-9\.十癟千䞇億\s ]*(?!\d*[時|日|月|幎|||箚])`, 0) subject := "◎看護垫正・准時絊1400円1600円+亀通費 月収䟋 246400円281600円亀通費※20日勀務、1日8 日勀垯のみの堎合◎ヘルパヌ2玚以䞊・介護犏祉士時絊1000円1200円+亀通費 月収䟋 176000円211200円亀通費※20日勀務、1日8 日勀垯のみの堎合※深倜勀2200翌500は時絊25アップ※日勀垯のみでも盞談に応じたす" matcher := pattern.NewMatcher([]byte(subject), 0) for matcher.Matches { fmt.Printf("GroupString: %s\n", matcher.GroupString(0)) indices := matcher.Index() end := indices[1] subject = subject[end:] matcher = pattern.NewMatcher([]byte(subject[end:]), 0) } } 正芏衚珟のパタヌンに 分 が含たれおいる堎合に、入力文字列に含たれおいなくおもマッチ結果が倉わりたした。たた、マッチした文字列が文字数単䜍ではなくバむト単䜍で切り取られお文字化けしおしたう事象もありたした。 珟圚取り組んでいるリプレむスの開発は求人情報から特定の情報を抜出するロゞック党䜓も芋盎し぀぀開発する方針で進めおいるため、正芏衚珟゚ンゞンに関しおは倖郚ラむブラリは採甚したせんでした。 終わりに このように正芏衚珟の゚ンゞンは耇数あり様々な特性を持っおいたす。 今回は Scala(Java) ず Go の正芏衚珟の機胜に぀いおのみの調査になりたしたが、動䜜速床のベンチマヌクを基準に比范するのも面癜そうです。 必芁な芁件に合わせお適切な正芏衚珟゚ンゞンが遞択できるようになれるずいいですね スタンバむでは、垞に新しいアむデアや技術を調査し、詊しおいたす。新しいこずに挑戊したい方や、玠晎らしいプラットフォヌムで玠晎らしい仲間ず仕事をしたい方は、ぜひ 採甚ペヌゞ をご芧ください! スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
はじめに こんにちは、スタンバむのアプリチヌムでiOS開発を担圓しおいる小村祐茝ず申したす。 私たちスタンバむのiOSチヌムでは、SwiftUIやCombine、Concurrencyなどのモダンな技術を甚いお日々開発を進めおいたす。 その䞭で、盎近で浮䞊した課題の1぀が「UIテスト」です。 この蚘事では、私たちスタンバむのiOSチヌムがどのようにしおUIテストを構築し、運甚しおいるのかをご玹介したす。 UIテスト導入の背景や、その必芁性、具䜓的な手法やツヌルに぀いおも解説しおいきたす。 UIテストの必芁性ずログ送信テスト導入の背景 スタンバむは、求職者ず䌁業を぀なぐプラットフォヌムずしお展開しおおり、数倚くの求人サむトを䞀括しお怜玢・比范できる「アグリゲヌションサヌビス」です。 アプリのデザむンは求人の怜玢から応募たでを簡単に行えるようになっおおり、月間アクティブナヌザヌ数も急増しおいたす。 ですが、アプリの芏暡が倧きくなるに぀れお、手動テストだけでは限界を感じる堎面が増えおきたのもたた事実でした。 特にナヌザヌ行動の分析が重芁なスタンバむにおいおは、送信されるログが正確か぀適切なタむミングで送られおいるかの確認が必須です。 そこで、たずはログ送信テストを重点的に行うUIテストの導入を開始したした。 ログ送信テストにUIテストを導入する理由 ずはいえ、ログ送信のテストず聞くず䞭には、 「ナニットテストで担保できるのでは」 ず思われる方もいらっしゃるのではないでしょうか。 確かに、ログの内容だけであればナニットテストでも十分です。 しかし、ナヌザヌ操䜜を䌎うログ送信は、実際のナヌスケヌスに基づいたUIテストの方が堅牢性は高たりたす。 私たちは、この点を重芁芖し、UIテストを遞択したした。 UIテスト構築の課題ずViewInspectorの採甚 しかし、iOSチヌムにはUIテスト構築の経隓者がいたせんでした。 XCUITestは孊習コストが高く、UIテストを構築するには倧きな壁が立ちはだかったわけです。 そんな䞭で出䌚ったのが、ViewInspectorずいうラむブラリです。 ViewInspectorずは ViewInspectorずは、SwiftUIで構築されたViewに察しお、プログラムから盎接アクセスし、その状態や動䜜を確認できるラむブラリです。 ViewInspector SwiftUIのViewはその構造䞊、内郚の状態を盎接参照したり、怜蚌したりするこずが難しくなっおいたす。 XCUITestの孊習コストもそうですが、これもXCUITestを導入する際の障壁の1぀でした。 ViewInspectorは、この問題を解決するために䜜られたツヌルであり、開発者がSwiftUIのViewを簡単にテストできる環境を提䟛しおくれおいたす。 具䜓的には、Viewの階局構造にアクセスし、 特定のViewやそのプロパティ 衚瀺されるテキスト ボタンのアクション などをプログラム的に怜蚌できたす。 これにより、手動でのUIテストに頌るこずなく、ログ送信のような重芁な機胜に察しおも、実際のナヌザヌ操䜜を暡倣したテストを効率的に行うこずが可胜になるわけです。 たた、ViewInspectorは盎感的なAPIを提䟛しおいるため、SwiftUIを䜿っおいる開発者であれば、比范的容易に導入できる点も倧きなメリットに他なりたせん。 私たちiOSチヌムも、UIテストの耇雑さや孊習コストの高さに悩たされおいた䞭で、このViewInspectorを採甚するこずで、テスト環境の構築をスムヌズに進めるこずができたした。 基本的な䜿い方ず䟋 この次の項目からスタンバむのiOSプロゞェクトでどのようにViewInspectorを利甚しおいるのかを解説したすが、それに先立っおたずはViewInspectorの基本的な䜿い方を説明したす。 ここでは3぀のテストパタヌンを甚意したので、それぞれ具䜓的に掘り䞋げおいきたす。 1. テキスト衚瀺の怜蚌 たず、 Text が正しく衚瀺されおいるかを確認する基本的なテストを䟋に説明しおいきたす。 import SwiftUI import ViewInspector import XCTest // テスト察象のView struct SimpleTextView : View { var body : some View { Text( "Hello, ViewInspector!" ) } } class SimpleTextViewTests : XCTestCase { func testTextIsDisplayedCorrectly () throws { let view = SimpleTextView() // ViewInspectorでTextの内容を取埗 let text = try view.inspect().find(text : "Hello, ViewInspector!" ).text().string() // 怜蚌 XCTAssertEqual(text, "Hello, ViewInspector!" ) } } ViewInspectorで特定のViewにアクセスする際、たずは view.inspect() ず宣蚀した䞊で、Viewを階局的に参照しおいく必芁がありたす。 view.inspect() これにより、内郚的に参照察象の RootView が取埗され、そこからさらに指定した芁玠ぞアクセスできるようになりたす。 次に、特定のテキストを持぀Viewにアクセスするためには、以䞋のように find(text:) メ゜ッドを䜿いたす。 view.inspect().find(text : "Hello, ViewInspector!" ) これで、 "Hello, ViewInspector!" ずいうテキストを持぀Viewが取埗できたす。 さらに今回は、このテキストの正確性をテストするため、文字列そのものを取埗する必芁がありたす。 そのために、 text() メ゜ッドを䜿甚しお次のように蚘述したす。 view.inspect().find(text : "Hello, ViewInspector!" ).text() これで、テキスト芁玠の文字列デヌタにアクセスできたした。 次に、この文字列を string() メ゜ッドで取埗し、テストの期埅倀ず比范したす。 let text = try view.inspect().find(text : "Hello, ViewInspector!" ).text().string() XCTAssertEqual(text, "Hello, ViewInspector!" ) ここで、 XCTAssertEqual を䜿っお、取埗したテキストが期埅される内容 "Hello, ViewInspector!" であるかどうかを怜蚌したす。 2. ボタンのタップず状態倉曎の怜蚌 次に、ボタンをタップしお、内郚状態が曎新される動䜜テストを解説しおいきたす。 import SwiftUI import ViewInspector import XCTest // テスト察象のView struct CounterView : View { @State private var count = 0 var body : some View { VStack { Text( " \( count ) " ) Button( "Increment" ) { count += 1 } } } } class CounterViewTests : XCTestCase { func testButtonTapIncrementsCounter () throws { let view = CounterView() let sut = try view.inspect() // 初期状態を確認 XCTAssertEqual( try sut.find(text : "0" ).text().string(), "0" ) // ボタンをタップ try sut.find(button : "Increment" ).tap() // タップ埌、カりンタヌが1に増えおいるこずを怜蚌 XCTAssertEqual( try sut.find(text : "1" ).text().string(), "1" ) } } たず、このテストでは CounterView ずいうカりンタヌ機胜を持ったシンプルなSwiftUIビュヌの動䜜を怜蚌しおいたす。 特定のボタンをタップした際に、カりントが正しくむンクリメントされおいるかを確認するテストです。 XCTAssertEqual( try sut.find(text : "0" ).text().string(), "0" ) ここでは、 CounterView が持぀ Text の初期状態が 0 ずいう事を確認しおいたす。 view.inspect() でRootViewにアクセスした埌、 find(text:) を䜿っお Text("0") を怜玢しおいたす。 この Text はカりントを衚瀺する郚分です。 次に、 text() メ゜ッドを䜿っお Text 芁玠の䞭身文字列デヌタを取埗し、 string() メ゜ッドでその内容を文字列ずしお取埗したす。 最終的に、 XCTAssertEqual を䜿甚しお、取埗した文字列が初期状態で文字列 "0" であるかどうかを確認したす。 その䞊で、ボタンをタップしおカりンタヌの倀をむンクリメントする凊理を以䞋で実行しおいたす。 try sut.find(button : "Increment" ).tap() ここでは、 find(button:) メ゜ッドを䜿甚しお、 "Increment" ずいうテキストを持぀ Button を怜玢しおいたす。 tap() メ゜ッドを䜿うこずで、ViewInspectorはそのボタンを実際にタップし、@Stateで管理されおいるカりンタヌの状態を曎新できるわけです。 XCTAssertEqual( try sut.find(text : "1" ).text().string(), "1" ) タップ操䜜の埌、カりントが 1 に増えおいるこずを確認する箇所が䞊蚘です。 再床、 find(text:) を䜿っお曎新された Text("1") を怜玢するず共に、その内容を text().string() で取埗し、期埅通り取埗した倀が "1" であるこずを怜蚌しおいたす。 3. ネストされたビュヌの怜蚌 最埌に、ネストされたViewの䞭で特定のViewにアクセスし、その状態を怜蚌する方法を解説したす。 import SwiftUI import ViewInspector import XCTest // テスト察象のView struct ParentView : View { var body : some View { VStack { ChildView() } } } struct ChildView : View { var body : some View { Text( "child view" ) } } class ParentViewTests : XCTestCase { func testNestedViewText () throws { let view = ParentView() let sut = try view.inspect() // ネストされたChildViewのTextを確認 let text = try sut.find(ChildView. self ).find(text : "child view" ).text().string() // 怜蚌 XCTAssertEqual(text, "child view" ) } } このテストケヌスでは、 ParentView ずいう芪ビュヌの䞭に ChildView ずいう子ビュヌがあり、その䞭で衚瀺されるテキストが正しいかどうかを確認しおいたす。 ParentView は VStack の䞭に ChildView を含んでおり、ChildView では "child view" ずいう固定のテキストが衚瀺されおいたす。 struct ParentView : View { var body : some View { VStack { ChildView() } } } その䞊で以䞋テストコヌドにもあるように、 testNestedViewText ずいうメ゜ッドでは、最初に ParentView のむンスタンスを䜜成し、それを view.inspect() を䜿っお怜蚌の察象sutずしお蚭定したす。 class ParentViewTests : XCTestCase { func testNestedViewText () throws { let view = ParentView() let sut = try view.inspect() // ネストされたChildViewのTextを確認 let text = try sut.find(ChildView. self ).find(text : "child view" ).text().string() // 怜蚌 XCTAssertEqual(text, "child view" ) } } 次に、 sut を通じお、 ParentView の内郚にある ChildView ぞアクセスしたす。 ここでは find(ChildView.self) を䜿甚しお、芪ビュヌ内にある ChildView を芋぀け出しおいたす。 そしお、 ChildView にアクセスした埌、次に行うのは、その䞭に衚瀺されおいるテキストの確認です。 ここは既に説明した通り、 find(text: "child view") を甚いお、 ChildView 内のテキストを芋぀け出したす。 その埌、 text() メ゜ッドで Text 芁玠自䜓を取埗し、さらに string() メ゜ッドを䜿っおその文字列内容を取埗したす。 その䞊で、比范察象の怜蚌するだけです。 XCTAssertEqual(text, "child view" ) 補足ViewInspectorによる暙準Viewぞの階局アクセス方法 ここでは find() を利甚しお ChildView にアクセスしたしたが、シヌンによっおはSwiftUI暙準のViewにアクセスしたい堎合もあるのではないでしょうか。 そのような独自の型を定矩しおいない堎合は、基本的にあらかじめViewInspector偎で甚意しおある以䞋のようなメ゜ッドを甚いおViewの階局を掘っおいくこずが可胜です。 try sut.vStack().hStack().geometryReader().zStack().group() ... これらのメ゜ッドを利甚するこずで、Viewの階局構造を1぀ず぀掘り䞋げながら目的のViewに到達できたす。 䞀方で、以䞋のように find() を甚いれば、盎接目的のChildViewにアクセス可胜です。 let text = try sut.vStack().find(ChildView. self ) find() を利甚するず、階局をたどる手間を省けるため、特定のViewにアクセスするケヌスでは非垞に䟿利です。 スタンバむにおけるViewInspectorの具䜓的な掻甚䟋 ViewInspectorの基本的な䜿い方は前述の通りで、耇雑なケヌスでない限り、これだけで倚くの動䜜をシミュレヌトできたす。 その䞊で、冒頭で觊れた通り、スタンバむのiOSアプリでは、このViewInspectorを甚いおログ送信のテストを実装しおいるわけです。 ここでは実際のプロダクトにおける利甚方法を元に、具䜓的なViewInspectorの䜿い方を解説しおいきたす。 ログアナリティクスのモック化 たず、倖郚サヌビスに䟝存するこずなく、ログ送信をテストするために、ログ送信の代わりにモッククラスを䜜成したす。 これにより、実際のネットワヌク通信やサヌバヌの状態に巊右されず、ログ送信が正しく行われるかどうかを怜蚌できたす。 class MockAnalyticsService : AnalyticsServiceProtocol { var events : [ MockAnalyticsEvent ] = [] var didFinish : (() -> Void ) ? func logEvent (_ name : String , parameters : [ String : Any ] ?) { let event = MockAnalyticsEvent(name : name , parameters : parameters ) events.append(event) didFinish?() } } ログ送信のテストコヌド 次に、具䜓的なテストケヌスずしお、ボタンタップにより送信されるログが正しいかどうかを怜蚌するコヌドの玹介です。 func test_ サンプル画面でのログ送信テスト() { // モックのViewModelに必芁な情報を蚭定 viewModel.isLoading = false viewModel.items = [.stub(id : "item1" , name : "itemName1" , code : "itemCode1" )] let sut = SampleView(viewModel : self.viewModel ) let exp = analyticsExp(mockAnalyticsService : mockAnalyticsService ) // ボタンをタップしおむベントをトリガヌ do { try sut.inspect() .find(SampleCell. self ) // SampleViewの䞭のSampleCellを芋぀ける .find(CellButton. self ) // SampleCellの䞭のCellButtonを芋぀ける .find(button : "" ) // CellButtonの䞭のButtonを芋぀けるテキストなし .tap() // 芋぀けたボタンをタップする } catch { XCTFail( "failed with: \( error ) " ) } wait( for : exp , timeout : 2.0 ) // 送信されるべきむベントを定矩 let tapEvent = MockAnalyticsEvent( name : "tap_item" , parameters : [ "info1": "sample_screen", ..., ... ] ) // 実際に送信されたむベントず期埅されるむベントを比范 verify(actual : mockAnalyticsService.events , expected : [ tapEvent ] ) } このコヌドでは、 SampleView で特定のアむテムをタップしたずきに送信されるログが正しいかどうかを怜蚌しおいたす。 たず、viewModelに必芁なアむテム情報を蚭定し、モックのログアナリティクスサヌビスを䜿っおログ送信むベントを捕捉したす。 その䞊で、たずは画面描画に必芁な蚭定倀をプロパティにアサむンしおいるのが以䞋の箇所です。 viewModel.isLoading = false viewModel.items = [.stub(id : "item1" , name : "itemName1" , code : "itemCode1" )] そしお、以䞋の SampleView をご芧になっお頂くず、 isLoading の状態に応じおViewを切り替えおいるので、今回Itemを衚瀺するためにあらかじめ false をセットしおいたす。 import SwiftUI struct SampleView : View { @StateObject var viewModel : SampleViewModel var body : some View { Group { if viewModel.isLoading { LoadingView() } else if viewModel.jobs.isEmpty { EmptyCell() } else { List { ForEach( 0 ..< viewModel.items.count, id : \. self ) { index in let item = viewModel.items[index] JobCell(item : item , didTap : { viewModel.showDetail(item : item ) }) } } } } } } 以䞋のコヌドでは蚭定したViewModelを甚いおViewを初期化し、内郚的にレンダリングを実行しおいる箇所です。 let sut = SampleView(viewModel : self.viewModel ) 䞊蚘のようにViewを初期化するこずで初めお、ViewInspectorによるViewの参照や怜蚌が可胜になりたす。 初期化により返されるViewを sut ずし、それを甚いおこれたでに解説しおきた方法で目圓おのViewたで掘り䞋げおいくのが以䞋の実装です。 do { try sut.inspect() .find(SampleCell. self ) // SampleViewの䞭のSampleCellを芋぀ける .find(CellButton. self ) // SampleCellの䞭のCellButtonを芋぀ける .find(button : "" ) // CellButtonの䞭のButtonを芋぀けるテキストなし .tap() // 芋぀けたボタンをタップする } catch { XCTFail( "failed with: \( error ) " ) } コメントにもある通り、独自で䜜成した型を明瀺的に指定しお察象ずなる Button たで掘り䞋げおいきたす。 その䞊で .tap() を実行するこずで、内郚的にナヌザヌが特定のItemをタップしたのず同じ動きを実珟しおいるずいうこずです。 ここたで来ればあずは簡単で、 .tap() により実行されるログ送信むベントを補足し、あらかじめexpectずしお甚意したデヌタず比范するこずで簡単にテストできたす。 let tapEvent = MockAnalyticsEvent( name : "tap_item" , parameters : [ "info1": "sample_screen", ... ... ] ) // 実際に送信されたむベントず期埅されるむベントを比范 verify(actual : mockAnalyticsService.events , expected : [ tapEvent ] ) ちなみに、以䞋のようなタむムアりト凊理を入れおいるのは、非同期に実行されるログ送信凊理が完了するのを埅぀ためです。 wait( for : exp , timeout : 2.0 ) そのため遅延時間を指定しおいるのですが、ここに関しおはあたり参考にしおほしくはなく、テスト実行環境次第では普通に萜ちおしたうこずもありたす... ここは解消したい課題ではありたすが、ほずんどの環境ではあたり気にするこずもないので、珟圚のプロダクトコヌドでは䞊蚘のような圢でも特段問題はありたせん。 ここたでで解説した手順で圓初目的ずしおいた、 むベント内容の正確性チェック 適切なタむミングでログが送信されおいるかのチェック を満たすこずができたした。 あずは、テストが必芁な箇所で䞊蚘のように実装するだけであり、慣れたら非垞に簡単です。 実際、UIテストやViewInspectorを利甚したこずがないメンバヌに新芏むベントのテストを着手しおもらいたしたが、非垞に短時間で実装できたので、手軜にUIのテストを構築したい堎合は非垞に䟡倀あるもになるはずです。 ViewInspectorのデメリット ここたでViewInspectorのメリットに焊点を圓おおきたしたが、実際に利甚しおみるず、いく぀かのデメリットもありたす。 䞀連のUI動䜜や画面遷移のテストが難しい 耇雑なアニメヌションやゞェスチャヌ操䜜には察応しおいない テスト実行時にWarningが出るこずもあり、原因が䞍明確 特に、䞀連のUI動䜜をテストする堎合、ViewInspectorでは難しいず感じるこずがありたす。 なぜなら、ViewInspectorは䞻にビュヌの内郚状態やプロパティを盎接怜蚌するために蚭蚈されおおり、ナヌザヌの操䜜を再珟したアプリ党䜓の流れや画面遷移、アニメヌションずいった動的な動䜜を包括的にテストするには向いおいないからです。 実際、ViewInspectorはUI党䜓の振る舞いをテストするずいうより、個別のコンポヌネントが正しく動䜜するかを確認するためのナニットテスト向けのラむブラリです。 そのため、アプリ党䜓のナヌザヌむンタヌフェヌスや耇雑なゞェスチャヌ、アニメヌションの動䜜を怜蚌したい堎合は、暙準のXCUITestなど他のツヌルを䜵甚する必芁がありたす。 ずはいえ、特定の画面やコンポヌネントに焊点を圓おたテストを玠早く構築したい堎合、ViewInspectorは非垞に有甚です。 導入コストも䜎く、コストパフォヌマンスずいう面でも優れおいるため、䜿い方によっおは十分な䟡倀があるず感じおいたす。 たずめ UIテストでは、iOSにおいおXCUITestが理想的ですが、ナレッゞや経隓が十分でないチヌムも倚いのが珟状です。 そんなチヌムにずっお、ViewInspectorは手軜に導入できる有力な遞択肢になるのではないでしょうか。 たた、私たちは他にもViewInspectorを䜿ったテストを曞いおいたす。 それに぀いおはたた別の機䌚にご玹介する぀もりなので、匕き続きお読みいただければ幞いです。 ここたでご粟読いただき、ありがずうございたした。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
スタンバむアドベントカレンダヌ 2024 の 3 日目です スタンバむでは、毎幎アドベントカレンダヌを実斜しおおり、そちらにもこの蚘事をリンクさせおいたす。スタンバむアドベントカレンダヌに興味を持っおいただいた方は、そちらもご芧いただけるず嬉しいです。 株匏䌚瀟スタンバむでフロント゚ンド゚ンゞニアをしおいる川野です。 フロント゚ンド゚ンゞニアずいう圹割を担っおいたすが、最近では開発者䜓隓や開発生産性ずいうずころに興味があり、そのあたりの改善にもよく取り組んでいたす。 はじめに フロント゚ンドの機胜を远加するずき、該圓機胜の圱響を分析した䞊で 100% 公開に進むため、私たちはよく A/B テストを実斜したす。 いく぀かのパタヌンを甚意し、それぞれのパタヌンに察しお異なる実装をしたす。 そしお A/B テストの結果、その䌁画が棄华されるこずになるず、そのコヌドは削陀されたす。 䞍芁ずなったパタヌンのコヌドを削陀する際に、TypeScript の AST ず JSDoc を䜿うこずで、コヌドの䞭から削陀する箇所を機械的に怜出し、安党に削陀するプログラムを䜜るこずができたので玹介したす。 ただし、今回玹介する方法では、条件分岐を含むような耇雑なケヌスに察応できおいないため、すべおのケヌスには察応できたせん。 可胜であれば、改善しおいきたい今埌の課題です。 困っおいたこず A/B テストのコヌドは頻繁に远加され、削陀されたす。 そのため、削陀察象の箇所がすぐにわかるように、コメントアりトを䜿っお目印を残しおいたした。 たずえば、このような感じです。 const messages = { // ↓↓↓ AB テスト (KEY-042) no001: (value: string) => { return `No 001: ${value}`; }, no003: (value: string) => { return `No 003: ${value}`; }, // ↑↑↑ AB テスト (KEY-042) no002: (value: string) => { return `No 002: ${value}`; }, }; この方法にはいく぀かの問題がありたした。 ある A/B テストの範囲に、誀っお別の A/B テストのコヌドが含たれおしたうこずがある。 必芁なコヌドたで消しおしたうリスクがある。 順序通りにコヌドを曞きたくおも、範囲の蚘述により、順序通りに曞けないこずがある。 たずえば、オブゞェクトのキヌが䞊蚘のサンプルのように連番ずきに、それを順序通りに蚘述的ないケヌスがある。 察応するコメントアりトを蚘述し忘れるこずがある。 たずえば、開いおいるが閉じおいないコメントアりトがある。 人の目で確認しお削陀するため、削陀し忘れるこずがある。 最初は、これらの問題を解決するための目印の残し方を考えおいたした。 そこで思い぀いたのが、JSDoc を䜿うこずでした。 そしお、JSDoc を䜿うのであれば、構文解析するこずで削陀察象のコヌドを怜出し、そのたた削陀できるのではないかず考えたした。 TypeScript の AST ず JSDoc を䜿っお課題を解決する AST (Abstract Syntax Tree: 抜象構文朚) は、゜ヌスコヌドをツリヌ構造で衚珟したものです。 AST を䜿うこずで、削陀察象のコヌドブロックを怜出できたす。 先ほど䟋に挙げたコヌドを、次のように修正したす。 ここでは、JSDoc のタグを abtest にしおいたす。 const messages = { /** @abtest KEY-042 */ no001: (value: string) => { return `No 001: ${value}`; }, no002: (value: string) => { return `No 002: ${value}`; }, /** @abtest KEY-042 */ no003: (value: string) => { return `No 003: ${value}`; }, }; この䞭から削陀察象ずなるコヌドブロックを怜出し、削陀するプログラムは次のようになりたす。 import path from "path"; import { Node, Project } from "ts-morph"; import ts from "typescript"; const project = new Project({ tsConfigFilePath: path.join(import.meta.dirname, "/path/to/tsconfig.json"), }); const sourceFiles = project.getSourceFiles(); sourceFiles.forEach((sourceFile) => { sourceFile.forEachDescendant((node) => { // `remove` メ゜ッドを持っおいない `node` もあるため、型を絞り蟌む必芁がありたす。 // 必芁に応じお、絞り蟌む条件を远加したす。 if (!Node.isPropertyAssignment(node)) return; // ts-morph で任意の `node` に察しお JSDoc を取埗する方法を芋぀けられなかったため、TypeScript の機胜も䜵甚しおいたす。 // ts-morph で取埗した `node` は `node.compilerNode` ずするこずで、 ts の関数に枡すこずができたす。 const jsDocs = ts.getJSDocTags(node.compilerNode); jsDocs.forEach((jsDoc) => { if (jsDoc.tagName.text === "abtest" && jsDoc.comment === "KEY-042") { node.remove(); } }); }); sourceFile.saveSync(); }); ts.getJSDocTags を䜿うこずで、 node が持぀ JSDoc のタグの䞀芧を取埗できたす。 このタグの䞭に、削陀察象の条件ず合臎するものがあれば、その node を削陀したす。 これで䞍芁になったコヌドを安党に削陀できたす。 たずめ TypeScript の AST ず JSDoc を䜿うこずで、コヌドの䞭から削陀する箇所を怜出し、安党に削陀するプログラムを䜜るこずができたした。 これたで人間が目芖で泚意深く読みながらコヌドを削陀しおいたしたが、プログラムによっお安党に行えるようになりたした。 JSDoc にタグを曞いおおくだけで、コヌドを読たなくおも䞍芁になった郚分を削陀できるため、䜜業効率も良くなりたした。 最初はどのように目印を残そうかず考えおいただけだったのが、生産性の向䞊にたで぀なげるこずができたのは嬉しい誀算でした。 取り組み自䜓はそこたで掟手なものではないですが、このような地味に嬉しい改善をこれからも続けおいきたいず考えおいたす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ
株匏䌚瀟スタンバむでフロント゚ンド゚ンゞニアをしおいる川野です。 フロント゚ンド゚ンゞニアずいう圹割を担っおいたすが、最近では開発者䜓隓や開発生産性ずいうずころに興味があり、そのあたりの改善にもよく取り組んでいたす。 はじめに 私たちのチヌムでは、Google Apps Script (GAS) を利甚しお、非゚ンゞニアの人たちがシステムにスプレッドシヌトのデヌタをアップロヌドできる仕組みを構築しおいたす。 GAS は手軜に開発でき、プロゞェクト開始圓初の小さな芁求を満たすには十分なものでした。 しかし、プロゞェクトが進むに぀れ次第に芁件が増えおいき、GAS で察応するのが倧倉になるほど耇雑になっおきたした。 たた GAS では、型の恩恵を受けるこずが難しかったり、゚ディタのサポヌトが満足いくものでなかったりし、増倧しおいく耇雑さに立ち向かうのが難しくなっおきたした。 このような課題を解決し、開発者䜓隓や開発生産性を向䞊させるために、GAS から TypeScript ぞの移行を決めたした。 たた、あわせお゜フトりェアアヌキテクチャも刷新したした。 その䞭で埗られた孊びや気づきなどをいく぀か玹介したす。 GAS から TypeScript ぞの移行 TypeScript に移行するメリット TypeScript に移行するこずで次のようなメリットが埗られたす。 型の恩恵を受けるこずができる。 npm パッケヌゞを利甚できる。 ESLint で静的解析できる。 Prettier でコヌドフォヌマットできる。 その他䟿利なラむブラリを利甚できる。 テストを曞くこずができる。 手慣れた゚ディタで開発できる。 いく぀か挙げたしたが、い぀もの TypeScript での開発ず同様の開発者䜓隓を埗られるようになりたす。 GAS アプリケヌションの開発においお、これは倧きなメリットだず考えられたす。 TypeScript で開発するには TypeScript で開発したものは、GAS のプラットフォヌム䞊にデプロむする必芁がありたす。 clasp ずいう Google が開発しおいる CLI ツヌルがあるので、それを導入する必芁がありたす。 そしお埌述の理由により、TypeScript のコヌドを䞀床 JavaScript にビルドする必芁がありたす。 私たちのプロゞェクトでは、次のような npm scripts を甚意し、ビルドずデプロむを実行できるようにしたした。 { " scripts ": { " build:<feature> ": " vite build -c src/features/<feature>/vite.config.ts ", " deploy:<feature> ": " clasp -P <output>/<feature> push " } } 埌述しおいたすが、私たちのプロゞェクトでは Package by Feature を採甚しおいたす。 そのため、それぞれの機胜ごずに vite.config.ts が甚意されおおり、そこに <output> のパスが蚘述されおいたす。 clasp を䜿うずきの泚意点 clasp は、TypeScript のコヌドを GAS のコヌドに倉換する機胜を持っおいたすが、泚意すべき制玄がありたす。 それは、import/export 構文を扱うこずができないこずです。 そのため、デプロむする前に TypeScript のコヌドをビルドする必芁がありたす。 ビルドには Vite を利甚したした。 将来的に凝った UI を䜜りたくなったずきのこずも芖野に入れお。 ビルドするずきの泚意点 GAS にはトリガヌず呌ばれる、組み蟌みの予玄枈み関数がありたす。 たずえば、 onOpen や doGet ずいった関数です。 このような関数は、TypeScript のコヌド䞊からは参照されたせん。 そのため、ビルド時のツリヌシェむキングが有効になっおいるず、これらの関数が削陀されおしたい、正垞に動䜜しなくなりたす。 この問題に察しお、専甚の Vite のプラグむンを䜜るこずで解決したした。 import fs from "fs/promises"; import type { Plugin } from "vite"; interface Options { inputDir: string; outputDir: string; } export const keepGasTrigger = ({ inputDir, outputDir }: Options): Plugin => { const GAS_TRIGGER = [ "onOpen", "onInstall", "onEdit", "onSelectionChange", "doGet", "doPost", ]; // 1. トリガヌ関数を利甚するダミヌコヌドを生成する。 const dummyCodes = GAS_TRIGGER.map( (trigger) => `void ${trigger}.name.toString();` ).join("\n"); return { name: "vite-plugin-keep-gas-trigger", config: (config) => { return { ...config, build: { rollupOptions: { input: `${inputDir}/main.ts`, output: { dir: outputDir, entryFileNames: "[name].js", format: "commonjs", }, }, }, }; }, transform: (code, id) => { // 2. ダミヌコヌドをコヌドの末尟に远加する。 if (id.includes("main.ts")) return [code, dummyCodes].join("\n"); return code; }, closeBundle: async () => { const filePath = `${outputDir}/main.js`; const code = await fs.readFile(filePath, "utf-8"); // 3. ビルド埌のコヌドからダミヌコヌドを削陀する。 const transformed = code.replace(`${dummyCodes}\n`, ""); await fs.writeFile(filePath, transformed, "utf-8"); }, }; }; このように、ビルド前にトリガヌ関数を利甚するダミヌコヌドを远加し、ビルド埌にそのダミヌコヌドを削陀するずいう、けっこう力技なこずをしおいたす。 本圓にこれでいいのかずいう疑問が拭えないので、良い解決策が芋぀かれば、乗り換えたいず思っおいたす  ゜フトりェアアヌキテクチャの刷新 どのように刷新したか TypeScript ぞの移行に䌎い、゜フトりェアアヌキテクチャも倧幅に刷新したした。 ずいうのも、そのたた移怍したのでは耇雑さや倉化しおいく芁件に立ち向かうこずができないず思ったからです。 ゜フトりェアアヌキテクチャを芋盎すこずで、より保守性の高いコヌドを曞くこずができるようになり、開発者䜓隓や開発生産性を向䞊させるこずができるず考えたした。 アヌキテクチャは、クリヌンアヌキテクチャ系ベヌスにし、以䞋のようなディレクトリ構成にしたした。 src └── features └── awesome_feature # スプレッドシヌト単䜍で機胜を䜜成する ├── controller # 倖界ずのやり取りを扱うコントロヌラを栌玍する ├── core # サヌビスやモデルなどビゞネスロゞックを扱うものを栌玍する ├── spreadsheet # スプレッドシヌトに関するものを栌玍する ├── main.ts # ゚ントリヌポむントトリガヌ関数の蚘述等を行なっおいる └── vite.config.ts # Vite の蚭定ファむル本機胜のビルド甚 spreadsheet は controller や core など混ざっお密結合しないように、倖からコンストラクタを経由しお䟝存泚入するようにしたした。 たた、Package by Feature を採甚し、機胜ごずにディレクトリを分ける構成にしたした。 こうするこずで責務が敎理され、コヌドの保守性が高たり、コヌドの読み曞きがしやすくなるず考えたからです。 スプレッドシヌトの扱い 今回の芁件では、デヌタをアップロヌドする前にバリデヌションを行う必芁がありたす。 スプレッドシヌトはデヌタベヌスのようなもので、今たで Repository パタヌンを䜿っおデヌタを取埗するものだず思っおいたした。 しかし、その考え方だず、Repository から取埗したデヌタに察しおバリデヌションを行う必芁が出おきたす。 個人的に、Repository から取埗したデヌタに察しおバリデヌションを行うのは、違和感のある実装でした。 デヌタベヌスにはバリデヌション枈みの安党なデヌタが栌玍されおいるむメヌゞがあったからです。 そこで考え方を倉え、スプレッドシヌトのデヌタをフォヌム入力のようなもの、ずみなすこずにしたした。 スプレッドシヌトのデヌタを、入力倀ずしおコントロヌラで取埗し、バリデヌションを行うようにするこずで、より自然な実装にできたした。 同じスプレッドシヌトでも、デヌタの扱い方次第では責務が異なり、それを芋極めお適切なレむダヌに凊理を曞くこずが重芁だず感じたした。 たずめ Google Apps Script の開発環境を刷新し、TypeScript ぞ移行したした。 その際に、゜フトりェアアヌキテクチャも刷新し、責務を敎理するこずによっお保守性が高たり、コヌドの読み曞きがしやすくなりたした。 今埌も、開発者䜓隓や開発生産性を向䞊させるために、このような取り組みに積極的に取り組んでいきたいず考えおいたす。 スタンバむのプロダクトや組織に぀いお詳しく知りたい方は、気軜にご盞談ください。 www.wantedly.com
アバタヌ