TECH PLAY

株式会社スタメン

株式会社スタメン の技術ブログ

247

こんにちは!スタメンでプロダクトデザイナーをしている森田かすみ( @KasumiMorita )です👋 今回のテックブログでは、先日プロダクト開発部門にて、上半期の締めくくりとして開催された「イネーブルメント共有会」の様子をお届けします! 現在、スタメンのプロダクト開発組織がどのような変化の中にあり、組織としてどんな取り組みに注力しているのか、実際の社内イベントの空気感とともに知っていただければ嬉しいです。 なぜ今、イネーブルメントに注力するのか? 今、スタメンのプロダクト開発では、AI駆動開発への移行が急速に進んでいます。この変化の激しい新体制において、開発のスピードと品質を両立させながら組織全体の成果を最大化するために、私たちはデザイン・フロントエンド・バックエンド・モバイルの各領域における「イネーブルメント」の推進に最注力しています。 ※「イネーブルメント」はチームトポロジーの考え方のうちの1つの役割です。詳しくは、YOSHIBAさん( @ryuzee )の資料をご覧ください。 slide.meguro.ryuzee.com しかし、体制やプロセスが新しくなるからこそ、それぞれのチームが何を目指し、どう動いているのかを横断的に理解することが不可欠です。 そこで、開発チーム全体の理解促進を目的に、今回の共有会は、メンバーからのボトムアップで企画されました。 イベントの目的は、 「AI駆動開発への移行を支えるイネーブルメントの役割や開発プロセスについて、全員で理解を深めること」 です。 当日は職種の垣根を越え、多くのメンバーが集まりました! 各チームのロードマップと現在地の共有 前半のセッションでは、フロントエンド、バックエンド、デザイン、モバイルの4つの領域における、イネーブルメントのロードマップと現在の進捗状況が共有されました。 それぞれのチームが、AI駆動開発という新しい技術や体制をどのように自分たちの領域に組み込み、生産性や顧客インパクトを最大化していくのか。具体的なステップが示されたことで、 イネーブルメントチームの活動に対する、部署全体の解像度を引き上げる ことができました。 また、各チームの進捗報告に加えて、私が所属するデザインイネーブルメントからは、複数プロジェクトを並行して進める中で課題になりがちな「構造設計とコンセプト定義の大切さ」について、時間をとってナレッジを共有しました。 デザインの領域にとどまらず、プロダクトの本質的な価値をいかに早く正しく定義し、開発へ繋げるかというテーマだったため、エンジニアなど他職種のメンバーからも高い関心を持っていただくことができました! 本音で語り合う!リーダー陣によるパネルディスカッション 後半は、各チームのロードマップやナレッジ共有を踏まえ、プロダクト開発の責任者やリーダー陣によるパネルディスカッションを実施しました! 今回のパネルディスカッションの一番のポイントは、事前にメンバーから集めた「リアルな質問」や「普段感じているモヤモヤ」をそのままトークテーマに変換したこと。綺麗事だけではない、リーダー陣の本音が飛び交う熱い「戦略理解の場」となりました。 当日は、以下のような鋭くもリアルなトークテーマでディスカッションが繰り広げられました。 「AI駆動開発の体制における今後の方針を聞きたいです(各チームの役割、会議体を含めた意思決定プロセス等)」 「実際にAI駆動開発をやってみてどうでしたか。もしあれば理想や現実との乖離に感じたことや、メリット・デメリットなどの率直な所感を聞きたいです。」 「新体制により生産性や顧客インパクトの変化はありましたか。将来的に目指すプロダクト価値の方向性について考えを聞きたいです。」 変化の渦中にいるメンバーの率直な問いに対して、リーダー陣が今後のビジョンや現在の課題感をオープンに語り、全員の共通認識を揃えることができました。 私個人としては、普段からイネーブルメントチームやリーダー陣と話す機会が比較的多いからこそ、今回の開催には特別な思いがありました。 最近は新しいメンバーが責任あるポジションに就くことも増え、お互いの理解を深める必要性を感じていたほか、メンバーとの会話の中で情報の乖離や熱量のギャップ、目線合わせができていない部分が少しずつ垣間見えていたからです。 そのため、今回のイベントを通じて全員の共通認識を揃えることができ、本当に良かったと思っています。 参加メンバーからの反響 イベント終了後、参加したメンバーからはたくさんの嬉しい感想が寄せられました! 「各チームの今後のロードマップが直接聞けて、自分たちの向かう方向への解像度が上がった」 「デザインイネーブルメントの『構造設計とコンセプト定義』の話が、開発を進める上でもためになった」 「普段は少し聞きづらいような突っ込んだ質問にもリーダー陣がしっかり答えてくれて、組織としての心理的安全性を強く感じた」 お互いの取り組みへの理解やリーダー陣からの熱量が伝わったことを実感できるコメントをメンバーからもらうことができ、開催して良かったと感じています。 今後の展望とスタメンのこれから この上半期、スタメンのプロダクト開発部には新しく執行役員やシニアメンバーも加わり、組織としてさらにパワーアップしています! 新しい体制のもとで、AI駆動開発のポテンシャルを最大限に引き出しつつ、顧客への提供価値を最速で最大化していくために、今後もこうした相互理解の場とイネーブルメントの取り組みをより一層強化していきます。 変化を楽しみながら、一丸となって強いプロダクト・強い組織をつくっていきます! 最後に(採用のお知らせ) スタメンでは、今回ご紹介したようなAI駆動開発やイネーブルメントを共に推進し、プロダクトを次のステージへと成長させていく仲間を大募集しています! フロントエンド、バックエンド、モバイルなど、各領域で採用を絶賛強化中です。 「AI駆動開発の環境で挑戦してみたい」「組織のイネーブルメントに興味がある」「まずはスタメンのカルチャーについて聞いてみたい」という方は、ぜひ以下のリンクから詳細をチェックしてみてください。 まずはカジュアルにお話ししましょう!👋 ▼ スタメンの採用・募集職種一覧はこちら herp.careers
はじめに みなさん、こんにちは! 初めましての方は、初めまして! プロダクト開発部で TUNAG の iOS を開発しています、 とんとんぼ です。 この記事は、WWDC26 のセッション動画である「Device Hub を最大限に活用する方法」をまとめた記事になります。 WWDC(Worldwide Developers Conference)は、Apple が毎年開催する開発者向けのカンファレンスです。 新しい OS や開発ツールがここで発表され、2026 年の開催が WWDC26 にあたります。 Device Hub は WWDC26 で初登場し、iOS エンジニアだけでなく、iOS アプリ開発に関わるデザイナー、QAエンジニアなどにもぜひ使って欲しいと思い、まとめることにしました。 WWDC とは? WWDC(Worldwide Developers Conference)とは、Appleが毎年6月頃に世界中のソフトウェア開発者や技術者に向けて開催する世界開発者会議のことです。同社の今後のソフトウェア戦略を占う最重要イベントであり、一般のApple製品ユーザーにとっても秋以降に登場する新機能を知るための注目のイベントとなっています。 developer.apple.com Introduction Device Hub は、Xcode 27 に同梱されるデバイス管理アプリです。 アプリをあらゆるデバイス、外観、テキストサイズで確認する作業を一つのアプリにまとめ、画面の確認から操作、設定の変更、診断の取得までをまかないます。 Xcode を起動していなくても単体で使えるため、対象はアプリ開発者にとどまりません。 さまざまな設定でテストする人や、デバイスのインベントリを管理する人も含まれ、実機とシミュレータのどちらでも同じように機能します。 Device Hub の概要 Xcode でシミュレータに対してビルドして実行すると、Device Hub は自動的に立ち上がります。 このとき開くのが コンパクトモード で、ライブ画面と最小限のコントロールだけを備えた軽量なウィンドウです。 下部のコントロールは表示中のデバイスに応じて変わり、Apple TV では再生と一時停止やナビゲーション、Apple Vision Pro では環境とカメラの操作、Apple Watch ではサイドボタンと Digital Crown が並びます。 上部の展開ボタンを押すと フルウィンドウ に切り替わり、デバイスを制御、整理、設定する3つの領域にアクセスできます。 コンパクトモードでもフルウィンドウでも、実機とシミュレータで一貫した操作感が保たれます。 コンパクトモード フルスクリーンモード Control キャンバス キャンバス はフルウィンドウの中央にあり、デバイスのライブ画面を映します。 クリック、ドラッグ、スクロール、トラックパッドのジェスチャーで直接操作でき、実機でもシミュレータでも同じように動きます。 キャンバス上部には、コンパクトモードにないコントロールが並びます。 ズームのほか、1対1の実物大表示にスナップして、現実の寸法でアプリを確認できます。 リサイズモードではアプリの寸法を自由に変えられます 1 。 キーボードキャプチャを使うと、Mac のキーストロークをデバイスへ直接送れるため、キーコマンドやハードウェアサポートのテストが楽になります。 サイドバー サイドバー には、実機とシミュレータのインベントリが一か所にまとまります。 上部のフィルタで表示対象を絞り、複数の条件で並べ替えやグループ化ができます。 デバイスをコンテキストクリックすると、再起動やペアリングといったクイックアクションを呼び出せます。 複数のデバイスは、タブやスタンドアロンのコンパクトウィンドウで同時に開けます。 たとえばサイドバーで複数のシミュレータを選んでダブルクリックすると、それぞれのウィンドウが開き、画面サイズごとのアプリ表示を並べて比較できます。 インスペクタ 右側の インスペクタ は、3つのタブに計5つのパネルを収めます。 デバイスの見た目や動作の変更から診断の確認まで、設定アプリを開かずにここで完結します。 デバイス設定 :外観(ダークモードやテキストサイズ)、位置情報などの条件、オーディオを切り替えます。外観の変更は即座に反映されます 診断レポート :アプリがハングまたはクラッシュしたときの調査の起点になります。クラッシュやスピンなど、デバイスが記録した診断情報が集まります Info、Apps、Profiles :3つ目のタブにまとまります。Info はストレージや機種、シリアル番号を一覧し、Apps はアプリの管理とデータコンテナのダウンロードや置き換えを担い、Profiles は構成プロファイルとプロビジョニングプロファイルの両方を管理します Reproducing a bug セッション後半では、ワークアウトアプリのバグを題材に、これまでの機能を一つの調査の流れの中で使うデモが示されます。 実機側ではデバイスのペアリング、ログプロファイルのインストール、診断やアプリデータの取得を行い、その結果をシミュレータ側に渡します。 シミュレータ側では、同じアプリデータとデバイス設定をミラーリングしてバグを再現します。 横向き、特定の場所、最大のテキストサイズという複数の条件がそろって初めて再現するバグを、Device Hub だけで揃えて再現できる点が見どころです。 各操作の具体的な手順は文章で追うより画面で見たほうが分かりやすいため、詳細は セッション動画 を参照してください。 devicectl スクリプトや自動化には、コマンドラインツールの devicectl を使います。 Device Hub と同じ基盤の上に作られており、テスト環境でのデバイス管理、アプリ管理、診断の取得などに向きます。 デバイスの一覧表示、アプリのインストール、ダークモードとライトモードの切り替えのような設定変更、デバイス情報の取得ができます。 --json-output オプションで構造化された出力を得られるため、CI ワークフローへの組み込みが容易になります。 まとめ Device Hub は、デバイスの画面確認から操作、外観やテキストサイズの変更、診断の取得までを一つのアプリにまとめ、実機とシミュレータを同じ操作感で扱えるようにします。 コンパクトモードは Xcode からの実行時にすぐ使え、フルウィンドウではサイドバーでインベントリを整理し、インスペクタで設定アプリを開かずに条件を切り替えられます。 横向き、特定の場所、最大のテキストサイズといった複数の条件がそろって再現するバグも、Device Hub だけで条件を揃えて追えます。 スクリプトや CI に組み込みたい場合は、同じ基盤の上に作られた devicectl を使えます。 Xcode を起動していなくても単体で動くため、iOS エンジニアに限らず、デザイナーや QA エンジニア、デバイスを管理する人にも役立つはずです。 参考文献 出典: - Get the most out of Device Hub(WWDC26, Session 260) - Device Hub を最大限に活用する方法 - YouTube リサイズ対応の詳細は「Modernize your UIKit app」セッションで扱われます。 ↩
みなさん、こんにちは☀️ スタメンプロダクト開発部の ねぎちゃん です🌷 本日は、5月に開催されたフロントエンドカンファレンス名古屋2026とTSKaigi2026のカンファレンス運営を振り返るレポートをお届けいたします! 【カンファレンス概要】 フロントエンドカンファレンス名古屋2026 開催日:2026年5月9日 場所:ウィンク愛知 フロントエンドカンファレンス名古屋2026 TSKaigi2026 開催日:2026年5月22日〜5月23日 場所:ベルサール羽田空港 TSKaigi2026 エンジニアがカンファレンス運営をするとなると、どうしてもメイン業務は日々の開発になるため、ブースコンテンツやノベルティについてじっくり検討することが難しいケースもあると思います。 そうなると、「なんとなくノベルティを配って、なんとなくアンケートをやってもらって終わり」になりがちです。 そのような中で、今回は「そもそもなぜカンファレンスに出展するのか?」「なぜノベルティを配るのか?」という、なんとなくやっているあれこれを徹底的に因数分解し、 カンファレンス出展の費用対効果を最大限に引き上げる ことと、 エンジニアに刺さるコンテンツやノベルティ に徹底的にこだわりました🔥 正直、採用促進や認知拡大をKPIにおくカンファレンスの成果は、短期的に数字で語ることは難しいです。 私のやったことが正解だったかどうかは、もっとずっとあとになってわかるものです。 そのため、この記事で私が言っていることがカンファレンス運営の正解かどうかはまだわかりません。 それでも、これからカンファレンス運営に挑戦しようとする誰かにとって、考えを膨らませる手がかりにはなれたらと思います。 私たちが「採用促進・認知度向上」という目的に対してどう因数分解し、現場でどんな体験をデザインしたのか、カンファレンス当日までの約3ヶ月の裏側をすべて公開したいと思いますので、ぜひご覧いただけますと幸いです✨ 1. 認知度向上の方程式:『接触回数 × インパクト』 カンファレンスに出展する目的は様々あると思いますが、大きくは 「認知度向上」 と 「採用促進」 が挙げられると思います。 まずは認知度向上に焦点を当てて、どんなテーマでブースコンテンツやノベルティを作るのかの方針を定めたいと考えました。 さて、どうすればスタメン、およびTUNAGは認知されるのか? というか、そもそも人間はどうやって何かを認知するのだろう?そこから理解する必要がありました。 私は、ありがたいことにカンファレンスへの参加経験が豊富だったため、「なぜ私はこの会社を覚えているのだろう?」と自分に問いかけてみると、自ずとある条件が見えてきました。 私の導き出した条件は、 「何度も視界に入る(接触回数)」 か 「一発で記憶に焼き付く(インパクト)」 、あるいはその両方を満たすことでした。 そこで今回は、接触回数とインパクトを最大化することを意識して、ブースコンテンツとノベルティを作ることにしました。 2. 【本番前】前夜祭で本番前から接点を持つ 実は、本番のタイムラインはカンファレンス前日から始まっておりました🤫 私たちスタメンは、名古屋本社にて、フロントエンドカンファレンスの前夜祭を開催しました🎉 【イベント概要】 【公式】Frontend Conference Nagoya 2026 前夜祭! 開催日:2026年5月8日 場所:株式会社スタメン名古屋オフィス 前夜祭の内容としては「リジェクトコン」として、本編のプロポーザルに惜しくも落ちてしまった方々にご登壇いただきました! また、初参加のエンジニアにとっては「一人でポツンとしたらどうしよう」「輪に入れるかな」という不安もあると思います。前日から会場を盛り上げていくことで、本番を120%楽しむための心理的安全性を提供することも大切な目的でした。 実際に、参加者の方から「はじめてのカンファレンス参加で不安だったけど、前の日に知り合いができたおかげで、当日を安心して迎えられた」という嬉しいお声をいただきました🤝 来場してくださる方に「プロポーザル供養の場」「知り合いづくりの場」という価値を届けながら、スタメンとしても前日から早速接点を持つことができ、非常に良い取り組みとなりました。 3. 【本番当日】ブースで「接触回数 × インパクト」を最大化する3つの仕掛け🤫 当日ブースでは、先述の「接触回数 × インパクト」を最大化する3つのコンテンツを用意しました。 ① タイムラインをジャックする「サンクスカードアプリ」💌 先述の通り、カンファレンス参加の経験が豊富だった私は、会期中にハッシュタグとともにセッションの感想や会場の様子がたくさん呟かれる様子を何度も見ていました。 実際、私自身も会期中は少なくとも1回はXのタイムラインをスクロールして、みなさんのつぶやきを追っています。つまり、 Xのタイムラインに載れば確実に来場者の目に留まるはず なので、なんとかしてそこに載りたいと考えました。 しかし、単に宣伝ツイートをお願いしても誰も面白がりません🤦🏻‍♀️ そこで、TUNAGのコア機能である「サンクスカード」を題材に、カンファレンスの運営や登壇者に向けて感謝のメッセージを送れる特設アプリを開発しました💌 皆さんに送っていただいたサンクスカード💌 このアプリからサンクスカードを送信すると、自動的に「#fec_nagoya」のハッシュタグと共にXへポストされる仕組みです。 Cloudflareで実装したサンクスカードアプリの入力フォーム✍️ X投稿までの動線もバッチリ✌️ セッションの感想や会場の様子がつぶやかれる公式タイムラインに、自然と「スタメンブースでサンクスカードを贈りました!」というハートフルな投稿が流れつつ、TUNAGらしさの塊であるサンクスカードを何度も目にして欲しいという想いで作った施策です。 他社と被らないインパクトと接触回数の両方を兼ね備えたこの施策は、実際にXのニュースに取り上げていただけたり、「Xで見ました!」と声をかけていただいたりし、確かな効果があったと感じています😌 一時Xのニュースに取り上げていただきました🙏 ② 2日目の再訪を生む「ポイントシールアート」🖼️ TSKaigiは2日間に渡って開催されました! スタメンは、4択のシール貼りアンケートを企画していましたが、「初日に一度貼ったら、問題を変えなければ2日目はスルーされてしまうのでは?」という懸念がありました。 また、問題を変えたとしても、昨日とやっていること自体は同じなので変わり映えがしません。 加えて、かなり定番のコンテンツなので、他社と被るであろうことも懸念でした。 コンテンツ自体はシール貼りのアンケートでいきたいが、「みんながやっていないこと」をやりたい……。 そこで、回答シールの色を使って2日間かけて全員で1つのドット絵(お題の絵)を完成させる「アート企画」にしました🖼️ 皆さんのおかげで無事アヒルのポイントアートが完成🎨 ただ貼るのではなく、「前の人はここを表現しようとしたのかな……?」と文脈を想像しながら貼る楽しさや、「明日にはどうなっているだろう?」と続きが気になって2日目もブースに足を運びたくなるリピートインサイトを仕込みました💪 こちらも実際に、「だいぶ進みましたね〜!」と再訪してくださる方や、「明日どうなっているか楽しみです✨」と写真を撮ってくださる方もいらっしゃり、来場者の方に楽しんでいただきつつ、スタメンとしても明日も遊びに来てもらえる確率を高められる設計になっていました✍️ そして何よりも、 スポンサーとして 会場にいる全員でひとつのものを作り上げるという、一体感のある体験を作れたことが本当に良かったと思っています🔥 完成を喜ぶスタメンエンジニア🤭 ③ 闘争心に火をつける「セマンティックパズルゲーム」🎮 カンファレンスのコンテンツとして、クイズもまた王道コンテンツです✨ ここでもやはり「みんながやっていないこと」にこだわり、選択式クイズではなく「お題に対して正しくマークアップせよ」というパズルゲームアプリを開発しました🚀 さらなるこだわりは、正答数ではなく「スピード(秒数)」で競う ランキングをブースに常時表示 したことです🤫 ランキングを常時モニター表示していました🔥 「自分のランキング、抜かれてないかな……?」とエンジニアの負けず嫌いな魂を刺激し、こちらも何度もブースの様子を見に来てしまうゲーミフィケーションとして機能していました! 先のサンクスカードアプリとこのセマンティックパズルゲームアプリは、先輩フロントエンドエンジニアがカンファレンスのためだけに実装してくださいました🤝 4. 【本番後】「点」で終わらせない、「線」にするための取り組み ① 一度きりではなく、日常に溶け込むノベルティ 食べたら終わるお菓子や、捨てられがちなチラシではなく、キーホルダーやタンブラーといった「日常に溶け込むノベルティ」を制作しました🌿 カンファレンスが終わった後も、オフィスや自宅のデスクで、ふとした瞬間に毎日視界に入るもの👀これによって、 イベント終了後も長期的に接し続けてもらえる 構造を作りました! キーキャップキーホルダーは「S」「T」「M」「N」の4種展開にして、皆さんのコレクション意欲を掻き立てます😚今後も様々なカンファレンスで配布の可能性がありますので、ぜひ全種類コンプリートしてください💪 STMNキーキャップキーホルダー ② 出会ったご縁を繋ぎ続ける努力 スタメンでは名古屋本社にて毎月勉強会を主催していますが、カンファレンスの出展にタイミングを合わせて、東京本社では初となる自社勉強会 「tech.stmn(テックドットスタメン)」 の記念すべき第1回を開催することにしました🎉 【イベント概要】 tech.stmn 開催日:2026年6月17日 場所:株式会社スタメン東京オフィス このイベントのコンセプトは、「ソフトウェア開発にまつわることなら何でもOK」🙆‍♀️ ガチガチの技術話しか受け付けないような、敷居の高い場所にはしたくありませんでした。 プロダクトマネジメントの話でも、プロダクトデザインの話でもいい。職種の垣根を取り払った、誰もがフラットに集まれる場所にこだわりました。 そこへカンファレンスのブースで出会った方がスタメンのオフィスへ遊びに来てくださいました😭カンファレンスで生まれたご縁が、しっかり繋がっていったことを実感した瞬間でした🤝 どんなにカンファレンス当日にたくさんの接点を持ったとしても、それを繋ぎ続けようとする努力がなければ、点は点のままで終わってしまいます。 引き続き定期的にイベントを開催し、スタメンに出会ってくださった方々とのご縁を大切にしていきたいです🕊️ あっという間に2時間語り込むほどアットホームな空間です🌿 最後に 今回、とにかく『接触回数 × インパクト』にこだわり、様々な仕掛けを考えてカタチにしてきました。 しかし、最後に一番大切にしたのは、マーケティングのための無機質な数字ではありません。 最後はどこまで行っても「来てくれた人が面白かったなと思ってくれるか」「持ち帰るものがひとつでもあるか」という想いが一番大切な指標でした。 スタッフに感謝を届ける「サンクスカードアプリ」を作ったのも、 シールアートでみんなでひとつのものを完成させるという体験を作ったのも、 セマンティックパズルゲームで「自分の腕を試す」というワクワクを作ったのも、 すべては「ブースに立ち寄ってくれた一人のエンジニアを楽しませたい」という想いから逆算した結果 でした。 結局やっていることは、仕様書通りにシステムを組むだけでなく、ユーザーの感情を動かすUI/UXを設計するエンジニアの仕事と何ら変わらず、カンファレンス運営もまた「最高の体験デザイン」でした✨ 約3ヶ月間、メインの開発の傍らで大変なことも多かったですが、振り返ってみると「楽しかった」と嘘偽りなく言うことができています☺️ 改めて、前夜祭、スタメンブース、そして勉強会に足を運んでくださった皆さん、本当にありがとうございました🤝 そして、これからカンファレンス運営に取り組まれる方が来場者に楽しんでもらうことを考えるのはもちろんですが、運営する自分自身が一番カンファレンスを楽しめますように……⭐️ herp.careers
こんにちは、 スタメンでEMをしている あさしん です 最近、Claudeのプランを上げて色々作ったり、自作キーボードの沼に徐々に沈み始めています。 はじめに スタメンが提供する「TUNAG 受付アプリ」は、2022年7月にリリースした来客受付アプリです。来訪者がiPad(当時はAndroidタブレット)を操作し、担当グループ・担当者を選んで呼び出すと、担当者へ通知が届く仕組みになっています。TUNAGをご利用いただいているお客様にご提供しているサービスです。 当初の通知先はTUNAGアプリ内のチャット機能でしたが、今回の改修によって「TUNAG チャット」にも通知できるようになりました。TUNAG チャットは、TUNAGアプリの中の1機能として提供されていたチャットを独立させたアプリで、2025年3月にリリースされました。音声通話やスレッド機能など、コミュニケーション機能をより強化した形で提供しています。サービスの成長に合わせてアプリが分化していく中で、受付アプリ側も新しい通知先に対応した形になります。 リリース当時の技術的な経緯は 2022年のテックブログ記事 にまとめています。かんたんに振り返ると、 Android開発を主担当するエンジニアが、将来のiOS拡張を見越してKotlin Multiplatform Mobile(KMM、現KMP)を採用した のが出発点でした。 当時の判断を引用すると: 「今後iOSアプリへの拡張性を考慮」してデータ・ドメイン層にKMMを導入しました。 この「将来の伏線」をついに回収しました。KMPで積み上げたロジック資産をそのまま活用し、 SwiftUIのUI層を追加するだけでiPad対応を実現した 話をします。 なぜ最小コストで済んだのか 最初にポイントを整理します。 Android版リリース時点で、ビジネスロジックはすでにKMPの shared モジュールにまとまっていました。具体的には: 認証フロー(パスワード認証・SSO判定) グループ・ユーザー一覧のフェッチ ViewModel の UI State 管理 APIクライアント(Ktor) クッキー永続化 これらはすべて commonMain に書かれており、Kotlinのまま動きます。iOS対応でやるべきことは、 SwiftUIの画面を足すだけ でした。ビジネスロジックを書き直す必要はありません。 ここで重要なのは、現在のチームにはiOSメンバーが増えており、ネイティブで0から作り直す選択肢も技術的には十分あった点です。それでもKMPの上に載せる判断をしたのは、 動いている資産を捨てるコストに見合うリターンがないから です。既存のロジックを再実装しても機能は増えません。エンジニアのリソースを「新しいビジネス価値を生む開発」に使える方が、今回の私たちの状況においては、ビジネス判断として合理的でした。 プロジェクト構成 tunag-reception/ ├── androidApp/ # Jetpack Compose による Android UI層 ├── iosApp/ # SwiftUI による iOS(iPad)UI層 └── shared/ # KMP 共有モジュール └── src/ ├── commonMain/ # 共通ロジック(Android/iOS 両方で動く) ├── androidMain/ # Android 固有実装 └── iosMain/ # iOS 固有実装 shared モジュールのレイヤリングはこうなっています。 shared/commonMain/ ├── domain/ │ ├── entity/ # データクラスのみ。ビジネスロジックなし │ └── repository/ # インターフェース定義のみ ├── application/ │ ├── service/ # ユースケース │ ├── dto/ # データ転送オブジェクト │ └── AppContainer.kt # DIコンテナ(唯一の組み立て口) ├── infrastructure/ │ ├── api/ # Ktor クライアント、Cookie管理 │ └── repository_impl/ ├── presentation/ # ViewModel └── stub/ # テスト・Preview 用スタブ 最重要ルールは「ViewModel・Repository・Entity は必ず shared に収める」 こと。iOS/Android のモジュールには View レイヤのみを配置します。この制約を守ることで、ロジックのダブルメンテが構造的に発生しません。 expect/actual パターン:プラットフォーム差を局所化する KMPでプラットフォーム固有の実装が必要な箇所は expect/actual で分岐します。代表的な例として ApiClient を紹介します。 ApiClient HTTPクライアントはプラットフォームごとにエンジンが異なります。 commonMain では expect だけを宣言します。 // commonMain — 共通のシグネチャ定義(各プラットフォームで実装を切り替える枠組み) expect class ApiClient(apiHost: ApiHost) { val apiHost: ApiHost val client: HttpClient suspend fun clearCookies() suspend fun addCookieToStorage(name: String , value: String ) fun close() } iOS側の actual はDarwinエンジン(NSURLSessionベース)を使います。 // iosMain — Darwin エンジンで実装 actual class ApiClient actual constructor ( actual val apiHost: ApiHost) { actual val client: HttpClient = HttpClient(Darwin) { engine { // Ktor の HttpCookies プラグインでクッキーを管理するため // NSURLSession のクッキーストレージを無効化して二重付与を防ぐ configureSession { setHTTPCookieStorage( null ) } } install(HttpCookies) { storage = PersistentCookieStorage(apiHost) } install(UserAgent) { agent = "Tunag-iOS/5.8.0.0 Reception" } } } Android側はOkHttpエンジンが入ります。呼び出し側は ApiClient を使うだけで、プラットフォームを意識しません。 なお、現在のKMPではクラス全体を expect class にするより interface + DI で注入するアプローチが推奨されることが多いです。今回は2022年当時の設計資産をそのまま活かす方針のため、当時の expect class をそのまま使い続けています。 クッキー永続化:iOS/Android の保存先の違いを吸収する アプリ再起動後もログイン状態を維持するため、セッションクッキーを永続化しています。保存先がプラットフォームで異なるため、 Multiplatform Settings ライブラリの Settings クラスで抽象化し、 commonMain に PersistentCookieStorage を実装しました。プラットフォーム固有の保存先は Settings の裏側に隠れるため、共通コードはストレージを意識しません。 // commonMain — プラットフォームを意識しない CookiesStorage 実装 internal class PersistentCookieStorage(apiHost: ApiHost) : CookiesStorage { private val settings = createCookieSettings() // expect/actual でプラットフォームごとの保存先に切り替わる private val storageKey = "ktor_cookies_ ${ apiHost.raw } " override suspend fun get (requestUrl: Url): List <Cookie> = mutex.withLock { ensureLoaded() val host = requestUrl.host val now = currentEpochMillis() cookieMap.entries .filter { (domain, _) -> host == domain || host.endsWith( ". $domain " ) } .flatMap { it.value } .filter { it.expiresAt == null || it.expiresAt > now } .map { it.toCookie() } } } 実装中にひとつ罠がありました。Ktor 3へのアップグレード後、ログアウト直後に再ログインするとクッキーが消えるバグが発生しました。原因はKtor 3での挙動変更で、 max-age=0 が「未設定」扱いから「即時削除」に変わっていました。RFC 6265に立ち返り maxAge <= 0 を正しく削除処理することで解決しましたが、地味な落とし穴でした。 StateFlow を SwiftUI から消費する:Wrapper パターンと SKIE KMPのViewModelはKotlinの StateFlow でUI状態を公開します。 // shared/commonMain — Kotlin ViewModel class SystemViewModel(...) : ViewModel() { val uiState: StateFlow<SystemUiState> = viewModelState .map { it.toUiState() } .stateIn(viewModelScope, SharingStarted.Eagerly, ...) } Swiftから StateFlow を直接 @Published として扱うことはできません。当初はKotlin側にコールバックを受け取るラッパーを用意していましたが( *ViewModelIos.kt )、 SKIE の導入でそれが不要になりました。 SKIEは、 Kotlin Multiplatformで作られたコードをSwiftから利用しやすくするためのコンパイラプラグインです。 Kotlinの Flow をSwiftの AsyncSequence に変換し、 sealed interface をSwiftのパターンマッチで扱えるようにしてくれます。 現在はSwift側に薄いWrapperクラスを置くだけです。 // iosApp — Swift Wrapper @MainActor final class SystemViewModelWrapper : ObservableObject { @Published private ( set ) var uiState : SystemUiState private var observationTask : Task < Void , Never > ? init (container : AppContainer ) { let vm = SystemViewModel( ... ) self .uiState = vm.uiState.value // StateFlow を for-await-in で購読し、@Published を更新する observationTask = Task { [ weak self ] in for await state in vm.uiState { guard let self else { return } self .uiState = state } } } deinit { observationTask?.cancel() } } sealed interface のパターンマッチも自然に書けます。 // SKIE の onEnum(of:) で Kotlin sealed interface を Swift で分岐する var currentBaseUrl : String { switch onEnum(of : uiState ) { case .signIn( let s ) : return "https:// \( s.currentHost ) " case .enterPassword( let e ) : return "https:// \( e.currentHost ) " case .admin( let a ) : return "https:// \( a.currentHost ) " } } SKIEなしではKotlin側にiOS専用のラッパー関数を用意する必要がありました。その手間がなくなり、Android向けに書いたKotlinコードをSwift側から直接使える体験が格段に改善されています。 ※コードは弊社環境のSKIEバージョン(0.10.11)に基づいています。SKIEの比較的新しいバージョンでは onEnum(of:) を使わずネイティブの switch で直接分岐できる場合もあります。 CI/CDを2本立てで構築する AndroidとiOSでCI/CDのパイプラインを分けています。 Android:CircleCI → Google Play 元のリリース時から変わらず、CircleCIを使っています。 # .circleci/config.yml(抜粋) deploy_production_to_playstore : docker : - image : cimg/android:2024.09 steps : - checkout - run : name : Place keystore file command : echo $STORE_FILE_BASE64 | base64 --decode > ./release.keystore - run : name : Build bundle & upload to Play Store command : ./gradlew publishProductionReleaseBundle リリースタグを打つと自動でPlay Storeの内部テスト版にデプロイされます。 iOS:Xcode Cloud → App Store iOS対応を機に Xcode Cloud を採用しました。GitHub連携でpushを検知し、アーカイブ・App Storeへの提出を自動化しています。 CircleCIに統一する案も検討しましたが、採用しませんでした。CircleCIでiOS/iPadOS向けのビルド環境を構築するには、App Store Connectとの連携に必要な認証トークンの発行・管理が必要です。さらにAndroidと同様に「ストアへのアップロード」と「社内テスト版の自動配信」を実現しようとすると、Xcode Cloudで構築するより圧倒的にコストがかかります。Xcode CloudはApple公式サービスだけあって、App Storeとの連携がそのまま動きます。 また、このアプリのテストはUnitTestレベルまでで、ほとんどのテストコードは shared モジュールに閉じています。CircleCIの既存のテストジョブで共通ロジックのテストはカバーできているため、iOS側のCIをCircleCIに統合する必然性がありませんでした。 Xcode CloudはApple公式のCI/CDサービスで、macOS環境が整っている点がメリットです。ただし、KMPの shared フレームワークをビルドするには JDK 17が必要 で、Xcode Cloudの環境にはデフォルトでJDKが含まれていません。 この問題を iosApp/ci_scripts/ci_post_clone.sh で解決しました。Xcode Cloudでは .xcodeproj と同階層の ci_scripts/ にスクリプトを置くことでビルドフックとして自動実行されます。クローン後に実行されるこのスクリプトで、AdoptiumのAPIからJDK 17を取得してセットアップします。 #!/bin/sh # Xcode Cloud: KMP shared framework のビルドに必要な JDK 17 をセットアップする JDK_INSTALL_DIR = " /Volumes/workspace/DerivedData/JDK " JDK_HOME = " $JDK_INSTALL_DIR /Home " # Apple Silicon / Intel を自動判定して対応アーキテクチャのJDKを取得する RAW_ARCH = $( uname -m ) ARCH = $( [ " $RAW_ARCH " = "arm64" ] && echo " aarch64 " || echo " x64 " ) mkdir -p " $JDK_INSTALL_DIR " curl -fsSL --retry 3 \ " https://api.adoptium.net/v3/binary/latest/17/ga/mac/ ${ARCH} /jdk/hotspot/normal/eclipse " \ -o /tmp/jdk.tar.gz # 展開前にトップディレクトリ名を取得してパスを決定的にする TOP_DIR = $( tar -tzf /tmp/jdk.tar.gz | head -1 | cut -d ' / ' -f1 ) tar -xzf /tmp/jdk.tar.gz -C " $JDK_INSTALL_DIR " rm /tmp/jdk.tar.gz # シンボリックリンクでパスを固定する ln -sfn " $JDK_INSTALL_DIR / $TOP_DIR /Contents/Home " " $JDK_HOME " Xcode CloudのワークフローにはJAVA_HOME環境変数として /Volumes/workspace/DerivedData/JDK/Home を設定し、Gradleが参照できるようにしています。Apple Silicon / Intelを uname -m で自動判定することで、Xcodeのインフラが変わっても動き続けます。 まとめ 3年前の設計判断が、今回の意思決定を楽にしました。 KMPで共通化したロジックはそのまま動いた 。認証・グループフェッチ・ViewModel・クッキー管理、すべて再実装ゼロ。 SwiftUIのUI層を追加するだけ でiPad対応が完了した。 SKIEでSwift連携が劇的に改善 した。Kotlin側にiOS専用のコードを書かずに済む。 Xcode CloudでiOSのCI/CDも整備 した。JDKをスクリプトで用意するひと工夫が必要だったが、以降は自動化できている。 「将来iOS対応できるようにKMMで作ります」というAndroidエンジニアの判断が、数年後に「動いている資産を捨てずに最大のリターンを得る」という意思決定の根拠になりました。技術選定は当時の最適解を選ぶだけでなく、将来の選択肢を広げるという意味でも重要です。 「体制が整ったから0から作り直す」という選択肢も魅力的に映ることがありますが、動いている資産を捨てるコストと得られる価値を冷静に天秤にかけた結果、今回はKMPの継続利用がベストだと判断しました。同じような場面で迷っている方の参考になれば嬉しいです。 TUNAG 受付アプリは Google Play 、 App Store で公開中です。 関連記事: 2022年リリース時のテックブログ
みなさん、こんにちは! 初めましての方は、初めまして! プロダクト開発部で TUNAG の iOS を開発しています、 とんとんぼ です。 iOS 26+ で利用可能になった、Apple が提唱する新しいデザイン言語の "Liquid Glass" に、弊社の TUNAG が対応しました! 本記事では、Liquid Glass をどのように TUNAG に導入したか、判断軸や取り組みについて紹介します。 Liquid Glass とは Liquid Glass は、Apple が開発したデザイン言語で、2025年6月に開催された WWDC25 にて発表されました。 背景を反射および屈折させる、流動的でダイナミックなガラスのようなインターフェイスを特徴としています。 このデザイン言語は、iOS 26、iPadOS 26、macOS Tahoe、tvOS 26、visionOS 26、watchOS 26 以上で導入され、それより前の OS バージョンでは、今まで通りのフラットデザインになります。 Liquid Glass - Apple Developer Document 対応スケジュール Liquid Glass の対応は、以下の流れで進めました。 2025年7月 :Xcode 26 と iOS 26 のベータ版がリリースされたので、早速インストールし、iOS チームでクラッシュや大きな UI の崩れ、ビルドエラーがないかを確認しました。 〜11月 :Apple が推奨していることと、TUNAG がビジネスとして求めていることを、最低限チーム内で擦り合わせして修正しました。 12月 :プロダクト開発部全員(PdM、デザイナー、エンジニア)で実際に手元で触り、感想や Liquid Glass の思想の共有、事前に見つけられなかった UI の崩れの確認などを行ないました。 この時期にした理由は、東京本社と名古屋本社のメンバーがオフラインで会えるタイミングだったためです。 オンラインでも問題はなかったと思いますが、意思疎通や画面共有がリアルタイムでやりやすいと考え、オフラインで開催しました。 2026年2月以降 :本腰を入れて、リリースに向けた実装対応を行ないました。 先日、iOS26の Liquid Glass 体験会を実施しました📱✨ iOSエンジニアが自発的に企画して、自社プロダクトにLiquid Glassを適用したベータ版を作ってきてくれました。 pic.twitter.com/ejAbGkdjDI — スタメン デザイナーたち✉️ (@stmn_designer) 2026年1月14日 Liquid Glass の適用範囲 TUNAG では以下の方針で対応をしました。 Navigation 領域を中心に対応:NavigationBar、TabView、Toolbar など、どうしても Liquid Glass 化してしまう箇所は対応することにしました コンテンツ領域は極力対応を見送り:WebView で実装されている機能も多いため、画面内で WebView とネイティブとの差分が大きくならないようにするため ネイティブ開発の強みを活かしつつ、最低限 Liquid Glass 化することで、ユーザーに Liquid Glass 化した TUNAG に慣れてもらう目的と、今後の Liquid Glass のアップデートに備えるという目的があります。 対応に心がけたこと とにかく、情報収集には力を入れました。 Apple の発表 Apple の Liquid Glass に関する発表には常に注目するようにしました。 Apple は、定期的に Liquid Glass 化したアプリを紹介する記事を投稿しています。これを参考に、他のアプリがどのように Liquid Glass に対応しているかを調査しました。 Discover how apps are using the new design and Liquid Glass Discover how apps are using the new design and Liquid Glass - 2026 また、Apple Developer では、Liquid Glass の活用事例をイベント・動画で公開しています。こちらも参考にしました。 Meet with Apple:Liquid Glass 他のアプリケーション調査 主に Apple の標準アプリケーションを参考に、どのようなデザインを採用しているのかを調査しました。 その他、Liquid Glass に対応したと聞いたアプリはすべてインストールし、テックブログで共有されていれば、すべてチェックしていました。 苦労したこと AI が思うように動いてくれないことが特に困りました。 新しい技術かつ、Web と比べて情報量の少ないモバイル開発ということもあって、上手く動いてくれないことが多々ありました。 そこで、AI にはプロジェクトコードの調査を主に担当してもらい、実装計画やドキュメントの参照、一部のコード修正は人間が行なうようにして、臨機応変に対応することにしました。 各UIパーツの対応 カスタムテーマ TUNAG のモバイルアプリには、導入企業様が自由に設定できるカスタムテーマ機能があります。これは TUNAG アプリ自体のテーマではなく、企業様ごとに個別の設定が可能な機能となっています。 一方で、Liquid Glass のガイドラインでは、NavigationBar の背景色などのカスタマイズは非推奨とされています。 そこで、スクロールに連動してカスタムテーマを非表示にすることで、機能を維持しつつ Liquid Glass の特性も両立させる設計としました。 画面トップ スクロール中 通知バッジ UIBarButtonItem の通知バッジは、これまでカスタムビューで表示していました。 しかし、Liquid Glass を適用すると、以下のようにガラスエフェクトの中に通知バッジが表示されてしまいます。 iOS 18以前 iOS26以降 そこで、カスタムビューから Apple 標準の通知バッジを採用し、iOS26 から使える UIBarButtonItem.Badge を使うことにしました。 developer.apple.com これにより、以下の画像のようにバッジをガラスエフェクトの外に出すことができ、見た目もよくなりました。 修正した NavigationItemButton 最後に Liquid Glass はこれで概ね対応ができたかなと思います。 しかし、これが終わりではなく、まだまだ Liquid Glass の始まりだと思っています。 今年も WWDC26 で変更があると思うので、それに備えて、引き続きキャッチアップと対応をしていこうと思います。
みんなで記念撮影。夕日がちょっとまぶしい はじめに こんにちは!スタメンの名古屋オフィスでプロダクトエンジニアをやっているすずき( @u16suzu )です。 2026年4月22~24日に函館で開催されたRubyKaigi 2026にスタメンはPlatinum Sponsorsとして協賛いたしました。 スタメン社内からは、筆者、あさしん( @asashin227 )、ちぇる( @ryuseikarito )、マッキー( @ MFJWR )、 もりしー、さわてつ( @t2ya305 )の6名が参加しました。 この参加は弊社の まるっとカンファレンス補助 を利用して行われました。 また、スタメンのグループ企業の 株式会社スタジアム の手島( @yuji_teshima )が mruby-gpu The Joy of Taking to Hardware in Ruby というタイトルでLTを行いました。 スポンサーブースの運営 今回、筆者ははじめてブース運営をする側になりました。 ブース運営はブース企画を考えるところからスタートしました。 今回は、社内で実際に使われているソースコードを少し修正して、それを元にしたコードクイズを企画しました。 エンジニアなのでコードを読みながら会話すればきっと盛り上がるだろうという意図です。 結果は、なかなかの盛況だったと思います。 ブース運営の様子 エンジニアが主体となってブースの企画を考え、デザイナーと連携しながら進めていくというスタイルはスタメンらしいチーム感があってよかったです。 これまでの一般参加の観点ではあまり認識できていませんでしたが、各社の担当者が工夫を凝らしてRubyKaigiを盛り上げようとしており、その結果として今の隆盛があると感じました。AI時代とはいえ物事を動かしていくのはまだまだ人間の情熱や信念であると感じました。 次に、筆者が興味をひかれた発表についていくつかご紹介します。 オープニングキーノート The Journey of Box Building speakerdeck.com 初日のキーノートでは田籠さん( @tagomoris )がRubyBoxの発表をしてくださいました。RubyBoxはいわゆるNamespaceです。 RubyにおけるNamespace機能はずっと欲しいと思っていたので、これは待望の機能追加ですね。 公式ドキュメントが公開されていますので、これを参考に少し動かしてみます。 docs.ruby-lang.org # ruby_box.rb box = Ruby :: Box .new box.require( ' ./other ' ) X = 2 p X # => 2 p :: X # => 2 p box:: Other :: X # => 1 p box:: Other .new.hello # => "Hello, world!" # other.rb class Other X = 1 def hello " Hello, world! " end end Rubyのバージョンは4.0.5で動作確認しました。 $ ruby -v ruby 4 . 0 . 5 ( 2026-05-20 revision 64336ffd0e ) +PRISM [ arm64-darwin25 ] 実行してみます。 $ RUBY_BOX = 1 ruby ruby_box.rb ruby: warning: Ruby::Box is experimental, and the behavior may change in the future! See https://docs.ruby-lang.org/en/ 4 . 0 /Ruby/Box.html for known issues, etc. 2 2 1 " Hello, world! " 無事、RubyBoxにより処理が切り替わっていますね! mruby-gpu The Joy of Taking to Hardware in Ruby speakerdeck.com 手島さんの発表はRaspberry Piに搭載されているGPUをmrubyを使って動かすというLTでした。 mrubyからC言語経由でVulkan APIを叩いてGPUを操ります。 データを逐次GPUに送っていたところ、パフォーマンスが良くありませんでした。 そこで一定の単位にまとめてBulk的にGPUにデータ送信をするようにしたところパフォーマンス課題が解決しました。 また、いわゆる機械学習界のHello Worldである MNIST というデータを使った手書き文字を認識させるテストが無事動いたことを発表されていました。 記念すべきトークを行う手島さん 弊社スタメンは2023年からRubyKaigiをスポンサーしてきましたが、ついに社内からRubyKaigiに登壇する方が出てきてくれました。筆者にとっては本当に感慨深くて、ありがとうの気持ちでいっぱいでした。 最後に 株式会社スタメンではエンジニアを引き続き採用しております。 もし弊社での開発や改善に興味がありましたら、以下のリンクからカジュアル面談へお申し込みください!! herp.careers
こんにちは、スタメンのWatchy開発チームのディアゴです。TypeScriptによるバックエンド開発を担当しています。最近は『ナニワ金融道』『ミナミの帝王』などの金融漫画にハマっています。 2026年05月22日〜23日の2日間、ベルサール羽田空港で開催された TSKaigi 2026 に、参加者として初参加しました。TSKaigiは国内最大級のTypeScriptのカンファレンスで、今年で3回目の開催となります。大規模な会場がかなり埋まっていて、大盛り上がりでした。今回スタメンは TSKaigi 2026 のブーススポンサーとして出展しており、ブースも大盛況でした! #TSKaigi2026 Day1が始まりました:sparkles: 本日お足元が悪い中ですが、皆さま気をつけてお越しくださいませ:umbrella_with_rain_drops: スタメンブースでは、雨模様も吹き飛ばすくらい元気なメンバーがお待ちしております:laughing: #TSKaigi pic.twitter.com/wLqAanVgUx — stmn, inc. Developers (@stmn_eng) May 22, 2026 現地で見た発表を、「明日からの仕事に活かせる」観点でピックアップして紹介したいと思います。 ① 権限制御を型チェックで守る セッションのテーマは、複雑なカスタマイズが可能な権限制御の開発をどのように安全に行うか、ということです。 型を駆使しつつ、型がカバーできない部分についてはバリデーション・リンター・テストでそれぞれ異なるレイヤーに対策していて参考になりました。 PostgreSQL の plv8(V8ベースのJS実行環境)は知りませんでした。複数のアプリケーションや直にデータベースを編集することはありうるので、DBレベルで複雑なロジックのバリデーションをしているのは安心できそうです。 権限チェック関数の呼び出し忘れはどうしようもないと思っていましたが、それさえLinterで守れるのに驚きました。 型がどこまでカバーしているかの境界の認識を持ち、それをほかのツールも組み合わせられる引き出しは重要そうだと考えました。 とりうる値が列挙できる(閉じた型) → 型だけで一貫性を担保できる とりうる値が列挙できない(開いた型) → 型以外の手段で補う ② React の props は値の集合ではない — UI の状態を宣言するコンポーネント設計 props は値の集合ではない — UI の状態を宣言する React コンポーネント設計 - Slidev テーマは、ReactでUIの状態を宣言する書き方でわかりやすくする、ということです。複雑な状態の表現に、Reactに限らず使える考え方だと思いました。また、例示されていたよくない例のPropsは自分が書いたことのある記憶があったので、こうすればよかったのか、と納得しました。 第1段階として、Discriminated Unionを使う方法を紹介しています。 // status によってとりうる値が異なる type Props = | { status: "loading" } | { status: "error"; error: Error } | { status: "success"; data: User[] } ありえない状態がなくなり、意図が明確で読みやすくなるメリットがあります。 第2段階として、さらにコンポーネントの責務を整理するのを提案しています。 Before: 渡されたPropsでUI表示 After: データフェッチ + 表示 これによって、JSXの構造でUIの状態を表現できる、と説明されていました。たしかにわかりやすくなったように見えます。 <ErrorBoundary fallback= { < ErrorMessage /> } > < Suspense fallback = { < Spinner /> } > < UserList /> </ Suspense > </ErrorBoundary> ③ TypeScriptのclassはなぜこうなったのか TypeScriptのclassはなぜこうなったのか - kosui テーマは、直感的でないclassについて歴史を紐解き、さらに落とし穴の多いclassを使わずに同等のことを表現する方法を解説しています。 classの3つの特性と罠から、対策をまとめているのが理解しやすく感じました。 構造的部分型。 問題点: classのプロパティが同じだと、class名が違ってもコンパイルが通る 対策: Branded Type を使って、値で判定させるようにする 型消去。 問題点: 型検査時と、実行時のinstanceofチェックが一致しない。classのコンストラクタで初期化した場合とオブジェクトリテラルで初期化した場合でinstanceofの結果が一致しなくなる 対策: Discriminated Unionで、実行時に残る値で検証する プロトタイプベース。 問題点: classはprototypeに基づいて構築されていてthisが呼び出し方で動的に決まる。呼び出し方で結果が変わるケースがある 対策: thisをもたない関数で表現する。classフィールドにアロー関数を入れるか、class外の関数を使う classはなるべく避けたほうがいいことや代替の方法、また使わないといけない場面もまだ残っている、ということがよく理解できました。 まとめ 明日の仕事にすぐ役立つような、ビジネスロジックの表現や安全性についての発表が多く、たくさんの知見を得られました。いっぽうで、言語の深い部分(トランスパイラ、型システム...)についての発表はなかなか理解が追いつかなかったところもあります。まだまだ知らないことがあると刺激を受け、もっと勉強していきたいと思いました。 運営の皆さま、登壇者の皆さま、素晴らしい場をありがとうございました。 来年もぜひ、参加したいと思います! #TSkaigi2026 ありがとうございました:sparkles: ブースに来てくださった皆さまのおかげで大きなアヒルの絵が完成しました:hatched_chick: 多くの方にご協力いただき嬉しかったです:relaxed:ありがとうございました! また、様々な楽しいコンテンツをご用意くださった運営の皆さま!心より感謝申し上げます:bouquet: #TSKaigi pic.twitter.com/NrowOttnMe — stmn, inc. Developers (@stmn_eng) May 23, 2026
みなさん、こんにちは☀️ プロダクト開発部の ねぎちゃん です🌱 スタメンは、先日開催された フロントエンドカンファレンス名古屋 2026 にプラチナスポンサーとして、出展・協賛させていただきました! フロントエンドカンファレンス名古屋2026 日時:2026年5月9日(土) 場所:ウインクあいち(〒450-0002 愛知県名古屋市中村区名駅4丁目4-38 ) fec-nagoya-org.github.io プラチナスポンサーとしてブース出展いたしました💪 今回のカンファレンスには、専門領域がそれぞれ異なるスタメンのエンジニアたちが参加しました。 バックエンドメインのメンバーから、AI活用に注目するメンバーまで、多角的な視点でセッションを聴講した結果、「一見地味な罠」への対策から「5年におよぶ刷新劇」の舞台裏、さらには「AIインストールのリアルな地獄」まで、濃厚で痺れる知見がたくさん集まりました✨ このブログでは、参加メンバーが「いちばん印象に残った!」と語る、厳選セッションの感想レポートをそれぞれの視点でお届けします🔥 🔥 スタメンエンジニアが痺れたセッション5選 おしん 💭印象に残ったセッション: 「見た目は同じなのに検索でヒットしない」 ( @wabi_1318 ) wabiさんの「見た目は同じなのに検索でヒットしない」という発表が私にとって特に印象に残りました。 MacOS(NFD)からのアップロードと手入力(NFC)の差分でファイル名検索がヒットしなくなるというバグは、原因究明が難しそうで沼にハマりそうだと感じました。ブラウザやライブラリ側で自動解決されない問題を、Issueの議論などを交えてリアルに語ってくださり、面白かったです。 こうした「一見地味だけど踏むと痛い落とし穴」は中々表に出てこないと思うので、貴重な知見でした。 システムの入力境界でしっかり検証・正規化して、型を活用して素の文字列と区別するという設計思想の重要さを学べたので、今後のプロダクト開発に活かしていきたいと思います。 鈴木 雄一郎 💭印象に残ったセッション:「デザインとコードの境界を溶かす」( @kskwtnk ) 綿貫さんの基調講演「デザインとコードの境界を溶かす」という発表がとても印象に残りました。 私は、普段バックエンドメインで書いており、キャッチアップとしてTypeScript, Reactの学習はしているのですが、デザイン周りの語句もキャッチアップしておくべきという気づきを得ました。 また、デザインシステムを現場に導入したがチームが慣れておらず、うまくワークできなかったという話も、実体験をもとにした話で現場感がありました。こういう失敗談みたいなものはなかなか話しづらいと思うので、共有してくれてとてもありがたかったです。 「プロジェクトの初期はデザイントークンのグローバルトークンを導入するだけでもデザインの統一感が出て、コスパ良く導入できて良い」という話も明日から仕事で即使えそうなナレッジとして参考になりました。 ちぇる 💭印象に残ったセッション: 「いつか誰かが、と思っていた フロントエンド刷新5年間の実践知」 ( @kiichi_sugihara ) 長期的なリアーキテクチャに取り組みたいけど、目の前の改善業務に追われて時間が取れないという、現場でよくある課題への向き合い方が非常に面白かったです。 kiiさんの現場では、当初「業務時間の10%を自由に使えるシステム健全化枠」を設けていたそうですが、機能的な改善の方がチームや社内からも喜ばれやすく、「改善 → 喜ばれる → やめられない」というループに入り、枠を使い切ってしまっていたという失敗談には非常に共感しました。そこから学び、枠ではなく「丸1日システム改善しかやらない『システム健全化デー』」を設けることで集中の分断を防いだ点や、あえて残業時間を減らしてその余白を勉強に充てることが、長期的に組織やシステムへの良い投資になるというお話が印象的でした。 また、技術的負債という目に見えづらい課題を解決するためには、機能開発以上に周囲への説明と合意形成が必要となります。リアーキテクチャに伴うバックエンド側のAPI分離や、デザイントークン作成の必要性など、各ステークホルダーを巻き込む壁があった中で、「理想を描いて、事業プロジェクトのついでに段階的に仕込んでいく」という立ち回りは、どんな仕事においても通じる突破口だと感じました。 必要性の吟味に始まり、周囲の課題と自分の理想をマッチさせながら、最終的に覚悟を持ってプロジェクトを完遂させる姿勢は、まさに「Get things done」の体現だなと思いました。 伊賀本 衛 💭印象に残ったセッション: 「AIと乗り切った1500ページ超のヘルプサイト基盤刷新」 ( @mugi_uno ) 「AIと乗り切った1500ページ超のヘルプサイト基盤刷新」のセッションを聞いて、AIへの過信の危うさを改めて実感しました。 AIがあれば何でもできると思いがちですが、実際に掘り進めると「地獄への始まり」とも言える複雑さが待っています。コードを変えるだけでなく、プロダクトのフェーズに応じた判断が求められ、刷新前後の差分を完全に排除することの難しさも痛感しました。TDDの徹底やドキュメントの積み重ねによってナレッジを蓄積し、誰でも編集できる状態を作るというアプローチはとても参考になりました。 また、AIを使うことで自分の観測範囲に閉じた局所最適に陥りやすいという指摘も刺さりました。自社でも個人スキルの向上は進む一方、プロジェクト横断的なナレッジ共有の仕組みはまだ整備途上です。この課題に向き合い、チーム全体のスキルを底上げする仕組みを作ることが急務だと感じています。自社プロダクトをより多くの人に使いやすく届けるためにも、AIを活用しながらスピード感を持って動いていきたいと思います。 taro 💭印象に残ったセッション: 「いつか誰かが、と思っていた フロントエンド刷新5年間の実践知 」 ( @kiichi_sugihara ) 最も印象に残ったのはこちらのセッションです。 「いつか経験豊富なエンジニアが来て、一緒に刷新を進めてくれるだろう」と思いながら目の前の開発で精一杯だった、というところから始まる 5 年間の物語でした。 何より心を動かされたのは、登壇者の 仕事に対する熱量 です。5年かけて少しずつ信頼と専門性を積み上げ、最終的にフロントエンド刷新をやり切るまでの軌跡は、聞いている自分まで熱くなるものでした。 セッションを聞いて自分も取り入れたいと思ったのは、次の2つです。 1日の時間の使い方 責任者・関係者の巻き込み方 1日の時間の使い方についてです。毎日の業務を同じようにこなすのではなく、「もっと良いやり方があるのではないか」と問い続け、 時間の使い方そのものを設計する 。その積み重ねが複利のように効いてきて、未来を決めるのだという考え方は強く印象に残りました。 責任者・関係者の巻き込み方については、検証が進まない時期に、技術顧問との定例ミーティングを先に設定し、「 検証の進捗を共有する場を作る → 否応なく前進せざるを得ない構造」を作ってしまうという話が象徴的でした。また、1人だと曖昧になりやすい部分が、説明する過程で言語化されてクリアになる、という副次効果も語られていました。 さらに、刷新を「自分の余白」だけでやろうとせず、 事業ロードマップを見据えて事業 PJ に段階的に仕込む という発想にも痺れました。「場を作ってから埋める」アプローチを社内のあらゆる関係者に対して実践している姿勢は、今の自分にとても必要だと感じました。 このような規模のカンファレンスに参加するのは初めての経験でしたが、ブース運営もセッション聴講も、どちらもモチベーションを大きく引き上げてくれる時間でした。次回も機会があればぜひ参加したいですし、いつかは聴く側ではなく発表する側として登壇できるよう、日々の業務と学びを積み重ねていきたいと思います。 おわりに 以上、フロントエンドカンファレンス名古屋 2026の参加レポートでした! 専門領域が異なるメンバーがそれぞれの視点でセッションを聴講したことで、技術的なナレッジはもちろん、チームビルディングやプロジェクト推進のヒントまで、本当に多くの刺激と学びを得ることができました🔥 スタメンでは、こうして得た最先端の知見や現場の実践知を日々のプロダクト開発に還元し、ユーザーの皆さまにより良い価値を届けていけるよう、これからもチーム一丸となって突き進んでいきます💨 改めまして、素晴らしいカンファレンスを企画・運営してくださったスタッフの皆様、そして当日ブースにお立ち寄りいただいた皆さま、本当にありがとうございました💐 それでは、次回のテックブログもお楽しみに🌷 herp.careers
はじめに 初めまして。株式会社スタメンでエンジニアをしている taro です。 2025年12月にスタメンに入社し、ちょうど半年が経ちました。 このタイミングで、これまでの半年で印象に残っている出来事と、そこから感じたスタメンの魅力について書こうと思います。 転職の動機 私はスタメンにフロントエンドエンジニアとして入社しました。 前職は受託開発のエンジニアで、さまざまな案件に携わってきました。 働き方はフルリモートが中心で、黙々と一人で開発する時間が多くを占めていました。 そうやって働いていくうちに、チームで活発にコミュニケーションを取りながら働きたい、そして自分の頑張りが目に見える形で評価される環境に身を置きたい、という思いが少しずつ強くなり、転職を決意しました。 そうして出会ったのがスタメンです。 プロダクト組織の体制変更 入社してから3ヶ月ほど経った頃、プロダクト組織で大規模な体制変更が行われました。 これまでは「バックエンド」「フロントエンド」「モバイル」の専門領域を持ったエンジニアがチームを組んで1機能を担当していましたが、 変更後は一人で1機能を担当する体制に変わりました。 サポートチャット機能を担当することに 私が任されたのは、TUNAG を導入してくださっている企業の担当者様と、スタメンの CS がやり取りするためのサポートチャット機能です。 担当が決まった当初、正直なところ「何から手をつければいいのか分からない」というのが本音でした。 これまでは「何を作るか」はディレクターや PdM から指示を受けていて、自分はその実装に集中していたからです。 「作るもの自体を自分で考える」ということに、最初はとても戸惑いました。 そこでまずは、ビジネスサイドからの機能要望のヒアリングから始めることにしました。 CS の方が積極的に協力してくれた ありがたかったのは、CS の方々が積極的に協力してくれたことです。 機能要望を聞かせてほしいとお願いすると、すぐに時間を取ってくれて、 普段の業務でどんな場面に困っているのか、どんな運用をしているのか、現場の生の声をたくさん共有してくれました。 要望の中には、 対応が技術的に難しいもの まだ自分の機能理解が追いついておらず、内容を把握しきれないもの 小さな工数で実現できそうなもの など、さまざまなものがありました。 そのなかから、比較的簡単に対応できて、かつインパクトが大きそうなものから手をつけていきました。 自分の改善をすごく喜んでくれた ある改善を全社向けに報告したとき、スタメンの CS のメンバーがとても喜んでくれました。 「これ、ずっと困ってたんです」「めちゃくちゃ助かります」と、率直な言葉で感謝を伝えてくれて、 自分が想像していたよりもずっと大きなリアクションが返ってきたことに驚きました。 「作って納品」を繰り返してきた自分にとって、初めての体験 エンジニアとしてのキャリアは、大学時代のアルバイトを含めると6年ほどになります。 それでも、自分のエンジニアリングの成果がここまで喜ばれた経験はこれが初めてでした。 受託開発をしていた頃は、要件通りに実装してお客様に納品するところまでが主な役割で、 自分の作ったサービスが実際に使われているところを見たり、利用者から直接フィードバックをもらえる機会はほとんどありませんでした。 だからこそ、この経験はこれからも忘れられない、自分にとって大きな成功体験となりました。 スタメンには、自分次第でインパクトのある仕事ができる環境がある この半年を振り返ってみて、改めて感じることがあります。 スタメンには、成果を率直に喜んでくれる 賞賛文化 と、プロダクトサイドとビジネスサイドが 越境してコミュニケーションをとれる文化 が根付いています。 だからこそ、自分の姿勢次第でビジネスサイドと直接コミュニケーションを取りながら、ユーザーに近いところでインパクトのある仕事をすることができます。 これからやっていきたいこと この半年で得た経験を活かして、これからはもっと自分から動いていきたいと思っています。 ビジネスサイドの声を拾うだけでなく、自分でも課題を見つけて改善提案ができるエンジニアを目指したいです。 担当している機能にはまだまだ改善の余地がたくさんあるので、CS の方々や企業の担当者様にとって、もっと使いやすく業務がスムーズに進むものに育てていきたいと考えています。 そのために、技術面でも引き続き成長していけるよう挑戦していきたいです。 おわりに もしこの記事を読んで、少しでもスタメンに興味を持ってくださった方がいれば、ぜひ一度お話ししてみませんか。 半年前の私と同じように、新しい一歩を踏み出すきっかけになれば嬉しいです。 herp.careers
はじめに こんにちは、株式会社スタメン、プラットフォーム部の 勝間田 です! 5月14日・15日に名古屋の中日ホールで開催された「 クラウドネイティブ会議 」に参加してきました! 私自身、今年からプラットフォーム部に配属となり、日々の業務でSREやプラットフォームエンジニアリングに携わることが増えました。今回は、各領域の知見を吸収し、現地での参加者との交流を通して、これからの業務に活かせるヒントを得られればと思い参加してきました。 この記事では、当日の会場の様子や、弊社のブース企画で行ったアンケートの結果、現地で聞いたセッションの学びについてまとめたいと思います。 クラウドネイティブ会議とは クラウドネイティブ会議は、「CloudNative Days」「Platform Engineering Kaigi」「SRE Kaigi」の3つのコミュニティが合同で開催したカンファレンスです。 kaigi.cloudnativedays.jp 会場の様子 今回のカンファレンスは、現地参加者 684名、オンライン視聴者 998名と、平日にも関わらずたくさんの方が参加されていたようです! 会場には、いくつかのアンケートボードがありました!(撮影したのはカンファレンス終了間際です) どこから来ましたか? 名古屋での開催ということもあり、中部・関東圏からの参加者が目立ちましたが、関西やそれ以外の遠方から足を運んでいる方も多く、注目度の高さが伺えました。 使っているオブザーバビリティツールは?/ 使っているCI/CDツールは? オブザーバビリティツールについては、Datadog が最も多かったものの、GrafanaやNew Relicなど他のツールも広く使われており、大きく一強というよりは各社のニーズに合わせて選定されている印象でした。一方で、CI/CDツールについては GitHub Actions の使用率が圧倒的で、標準的な選択肢になっていることを改めて確認しました。 使っているコーディングエージェントは? また、個人的に注目していたコーディングエージェントの利用状況では、Claude Code が一歩抜け出している様子でした。ブースで他社のエンジニアとお話ししていても Claude Code を利用しているとの声が多かったです! スタメンでは、現在プロダクトメンバーには Claude Code と GitHub Copilot を配布 しており、各々状況に合わせて活用しております。 懇親会では弊社CTOの野口がスポンサーLTで登壇しました。 ブースアンケートの結果 スタメンは今回ブースを出展させていただき、お越しいただいた皆さんに「お仕事のタイプ」と「AIの活用方法」についてのアンケートをお願いしました。 ご参加いただいた皆様ありがとうございました! 結果は以下の通りでした。 (目で数えたので、数に間違いがある可能性があります...) 「あなたのお仕事はどのタイプ?」の結果 技術探検家を数えるのが辛かった... 技術の探検家(新しいツールや技術を試すのが好き):39 理論の伝道師(アーキテクチャやベストプラクティスを追求する):36 安定の守護神(システムの安定性と信頼性を第一に考える):32 現場の改革者(レガシーな環境をモダンに変えようと奮闘中):30 クラウドネイティブ会議ということもあって、安定性やアーキテクチャに強みを持っていたり、関心が高かったりする方が多いのが印象的でした。 また、現場でレガシーな環境と戦っている方も少なくなく、共感する部分も多かったです。 「あなたのAI活用はどのタイプ?」の結果 こっちは数えやすかった 効率の魔術師(定型作業を撲滅してプロセスを徹底自動化):53 爆速の開拓者(圧倒的なスピードと生産性で開発する):45 価値の演出家(今までにないプロダクト価値や事業成長を生み出す):22 信頼の守護神(システムの品質向上と安全性を強固にする):13 こちらは「効率」や「爆速」といったキーワードに多くの票が集まりました。AIエージェントによる自律的な開発や、日々のトイル削減にAIを活用している方が多そうです。 ブースで直接お話しさせていただく中でも、「一年前と今では仕事の仕方が全く変わった」という声をたくさん聞き、私自身も強く感じています。 2つのアンケートを別のカンファレンスでやってみたらまた違った結果になりそうで、比較してみるのも面白そうだなと思いました。 印象に残ったセッション 現地で実際に聞くことができたセッションの中で特に印象に残ったセッションを2つ紹介します。 エンタープライズの厳格な制約を開発者に意識させない:クラウドネイティブ開発基盤設計 kaigi.cloudnativedays.jp エンタープライズ特有の厳しいセキュリティ要件がある中で、いかにアプリ開発のスピードを落とさないように「開発導線」の整備を進めるかについてのセッションでした。 エンタープライズの制約が複雑でも、ゴールデンパスで吸収することで、開発者は安全かつ高速に前に進めるとのことでした。 今回の事例のような細かい制約は弊社にはないですが、「ゴールデンパス」の必要性を感じています。 スタメンでも、最近は AI-DLC(AI駆動開発ライフサイクル) による体制へとシフトしており、各メンバーが自律的に機能を開発していきます。 「ゴールデンパス」が整備されていれば、開発者の生産性も上がり、余計な不安を感じずに開発できそうです。 そのために、スタメンにおけるプロダクトリリースの「最低限必要なもの」を改めて棚卸しし、ゴールデンパスの整備を進めていきたいと思いました。 また、「良いものを作っても、使われるとは限らない」という話も共感しました。ツールの存在を知らせるだけで終わらず、横で一緒に作ったりする 「イネーブリング」 を通して、その価値を直接伝えていくことの大切さを認識しました。 継続的な負荷検証を目指して kaigi.cloudnativedays.jp サービスが成長し新しいエンドポイントが日々増え続ける中で、いかに負荷検証の「網羅性」を担保し、継続的に試験を行っていくかについてのセッションでした。 ピーク時に特定の条件下でのみ発生する高負荷なエンドポイントが試験から漏れていたという障害の反省から、AIを活用して負荷試験のシナリオを自動生成し、成長するサービスに対して継続的な負荷検証する仕組みを構築したとのことでした。 日々増加・変化するサービスに対して、手動でシナリオを網羅し続けるのには限界があるので、負荷試験のシナリオ作成をAIにやらせることで効率が良くなるのはもちろんですが、「人間では気づけないようなアクセスパターン」を発見できる可能性があるというというお話しはAIならではの強みだと思いました。 作成されたシナリオの妥当性(ビジネス的に意味があるエンドポイントか等)の判断や、実行・評価については人間が行っているとのことで、AIに任せられる部分は任せ、ビジネス面などの重要な判断はやはりまだ人が行う必要があることも再認識しました。 スタメンでも、本番相当の検証環境の用意とAIを活用した検証手法について考えていきたいと思いました。 最後に クラウドネイティブ会議に参加して、新しい学びを得ることができ、また自身の理解が足りていない分野についても浮き彫りになるなど、有意義な2日間となりました。 ここで得た知見を活かし、日々の業務でアウトプットできるよう努めていきたいです。 スタメンではSRE、プラットフォームエンジニアリング領域の採用を積極的に行っています。 ご興味のある方はぜひご応募ください! herp.careers
はじめに こんにちは、スタメンでプロダクトエンジニアをしている おしん ( @38Punkd ) です。 5月9日、ウインクあいちで開催された フロントエンドカンファレンス名古屋 2026 にLT枠で登壇させていただきました。 その発表内容をブログ形式でご紹介できればと思います。 WebViewの文字サイズ、固定されていませんか? この問題は、ネイティブとWebの境界に起因する「実装責務の曖昧さ」から生じがちです。 モバイルアプリ開発において、ユーザーのアクセシビリティへの配慮は不可欠です。特に、スマートフォンの設定で文字サイズを大きくしているユーザーにとって、アプリ内のテキストがその設定に追従するかどうかは、使いやすさに直結します。 私たちのアプリ TUNAG は、モバイルアプリではアプリネイティブとWebView(モバイルアプリでHTMLを表示できる機能)を併用しています。ネイティブUIはOSの文字サイズ設定に追従する一方で、アプリ内のWebViewだけが標準サイズのままでした。文字のリサイズが画面ごとに適用されたりされなかったりすると、ユーザーにとって読みにくいコンテンツになってしまうため、WebViewへの適用を本格的に開始しました。 本記事では、iOSとAndroidそれぞれのWebViewにおいて、OSの文字サイズ設定を適切に反映させるための具体的な実装方法と、その際の注意点について解説します。 文字サイズ設定は「特別対応」ではない 文字サイズ調整は、多くのユーザーが日常的に利用する標準機能です。ある調査によると、 モバイルユーザーの約33%が文字サイズ調整を有効化している というデータもあります *1 。iOSのDynamic Type *2 やAndroid 14以降の200%フォントスケーリング *3 など、OSもこの機能を重視しています。 結論としては、 ネイティブアプリ側のごくわずかな修正と、Web開発時のただ1点のポイントをおさえるだけ で、WebViewでもiOS, Android共に文字サイズ設定が反映されるようになります。 対応工数に対して UX改善のインパクトが大きい領域 であると言えそうです。 iOS WebView:JavaScript注入による動的なフォントサイズ反映 iOSのWebViewでDynamic Typeを反映させる最も柔軟で推奨される方法は、ネイティブ側でOSのフォントサイズを取得し、JavaScriptを介してWebViewに注入するアプローチです。 ① Swift:Dynamic Type 変更を検知してフォントサイズを JS で注入 NotificationCenter. default .addObserver( self , selector : #selector(dynamicTypeDidChange), name : UIContentSizeCategory.didChangeNotification , object : nil ) @objc private func dynamicTypeDidChange () { applyFontSize() } func webView (_ webView : WKWebView , didFinish navigation : WKNavigation! ) { applyFontSize() // ページロード完了時にも適用 } private func applyFontSize () { let size = UIFont.preferredFont(forTextStyle : .body).pointSize webView?.evaluateJavaScript( "document.documentElement.style.fontSize = ' \( size ) px'" , completionHandler : nil ) } UIContentSizeCategory.didChangeNotification を受信したら、Swift側でフォントサイズを取得し、 evaluateJavaScript でWebViewの :root 要素の font-size プロパティに直接書き込みます。この方式は リロードが不要で即時反映され、独自のスケールにも柔軟に対応できる 点が利点です。 ② CSS: font-size は rem 単位で指定 Swift側で :root の font-size を動的に書き換えるため、Webコンテンツ側では rem 単位でフォントサイズを指定することで、すべてのテキスト要素が連動してスケールするようになります。 : root { font-size : 17px ; /* フォールバック。Swift が evaluateJavaScript で上書きする */ } .title { font-size : 1.143rem ; } /* ✅ :root 基準でスケールする */ .description { font-size : 0.857rem ; } /* ✅ :root 基準でスケールする */ 参考:よりシンプルな代替案( -apple-system-body ) CSSに :root, body { font: -apple-system-body; } を指定し、Dynamic Typeの変更検知時に webView.reload() を呼ぶ方法もあります。この方法は手軽ですが、 WebViewのリロードが発生する 点と、 Apple提供のスケールに固定される 点がデメリットです。 Android WebView:「対応できている風」に注意 AndroidのWebViewでは、iOSとは異なる挙動と注意点があります。特に「文字サイズ設定に対応できているように見えるが、実は問題がある」ケースに注意が必要です。 見た目 実際に起きていること 文字が大きく見える Activity再生成によりWebViewが作り直される 全体が拡大される 文字だけでなく画像・余白もズームされる場合がある 設定変更後に戻る スクロール位置・入力中状態が失われる可能性がある android:configChanges に fontScale が含まれていない場合、OSのフォントスケール変更時にActivityが再生成され、WebView全体がズームされます。これは文字以外の要素も拡大し、レイアウト崩れや状態喪失の原因となります。 文字だけを適切に反映できているか を見極める必要があります。 AndroidはOS値をJSで橋渡しする Androidで文字サイズ設定をWebViewに適切に反映させるには、ネイティブ側でOSのフォントスケール値を取得し、JavaScriptを介してWebViewに伝えるアプローチが有効です。 ① AndroidManifest: fontScale を configChanges に追加 <activity android : configChanges = "fontScale|uiMode|density" ... /> AndroidManifest.xml の <activity> タグに android:configChanges="fontScale" を追加し、フォントスケール変更時のActivity再生成を防ぎます。 ② Kotlin:フォントスケール変化を検知して WebSettings.setTextZoom(int) で反映 // 起動時の初期化 applyFontScale(resources.configuration.fontScale) // フォントスケール変化を検知(iOS の didChangeNotification に相当) override fun onConfigurationChanged(newConfig: Configuration) { super .onConfigurationChanged(newConfig) applyFontScale(newConfig.fontScale) } private fun applyFontScale(fontScale: Float ) { // fontScale 1.0 → 100(標準), 2.0 → 200(200%) // px・em 問わず WebView 内のテキスト全体をスケールする webView.settings.textZoom = (fontScale * 100 ).roundToInt() } onConfigurationChanged で newConfig.fontScale を取得し、 WebSettings.setTextZoom(int) でWebViewのテキストズームレベルを設定します。 ③ CSS:テキストは単位を問わずスケールされる WebSettings.setTextZoom(int) はレンダリングエンジンレベルで適用されるため、HTML/JS の変更は不要です。 px 、 em 、 rem のいずれの単位で指定されたテキストもスケールされます。ただし、非テキスト要素はスケールされないため、Web側で柔軟な設計が必要です。 共通の考慮事項:拡大しても壊れないレイアウトにする OSの文字サイズ設定をWebViewに反映させるだけでなく、Webコンテンツ側で、文字が拡大されてもレイアウトが崩れないような柔軟な設計が求められます。 避けたい実装 推奨する実装 固定高さ 内容量に応じて伸びる高さ 1行前提 折り返し・複数行を許容 アイコンと文字の密結合 gap・flex-wrap・min-widthで逃がす 文字サイズ対応の本質は、値の反映だけでなく「拡大を許容するUI」を構築すること にあります。 まとめ 本記事では、iOSおよびAndroidのWebViewにおいて、OSの文字サイズ設定を適切に反映させるための具体的な実装方法と、その際の注意点について解説しました。 iOSでは、Swift側でDynamic Typeのフォントサイズを取得し、JavaScriptでWebViewの :root 要素の font-size を動的に書き換えるアプローチが最も柔軟です。これにより、リロードなしで即時反映が可能となります。Androidでは、 AndroidManifest で fontScale の変更を検知し、 WebSettings.setTextZoom(int) でWebView全体のテキストズームレベルを設定します。 そして、iOSとAndroidの両方でWebView内のテキストをOSの文字サイズ設定に追従させるための共通の鍵となるのが、 CSSでの rem 単位の活用 です。 デフォルトの文字サイズ 拡大した文字サイズ OSではJavaScriptによる :root 操作と連動させるために rem 指定が 必須 となります。一方で、Androidの setTextZoom は単位を問わず追従してくれます。つまり、Web側の実装をiOSに合わせて rem に統一しておけば、Android側でも一切の不都合なく自然に拡大縮小が行われ、両OSに矛盾なく対応できるのです。 スマートフォンのOS設定を尊重し、より多くのユーザーに読みやすいWebコンテンツを届けることは、ユーザーの満足度向上に繋がりやすく重要です。本記事が参考になりましたら幸いです。 サンプルリポジトリ 本記事で紹介した実装の詳細は、以下のサンプルリポジトリでご確認いただけます。 iOSアプリ : GitHub - iOS WebView Dynamic Type Sample Androidアプリ : GitHub - Android WebView Font Scale Sample herp.careers *1 : Ian Savchenko, “Designing for Accessibility: How Text Resizing Works in Different Web Browsers,” PayPal Technology Blog. https://medium.com/paypal-tech/designing-for-accessibility-how-text-resizing-works-in-different-web-browsers-bed9e424e071 *2 : Apple Developer Documentation, “Scaling fonts automatically.” https://developer.apple.com/documentation/uikit/scaling-fonts-automatically *3 : Android Developers, “Features and APIs Overview — Non-linear font scaling to 200%.” https://developer.android.com/about/versions/14/features
はじめに こんにちは、プラットフォーム部の 勝間田 です! 今回は書籍紹介記事の第2弾です! 昨年投稿した第一弾の記事は👇にあります。 tech.stmn.co.jp 今回はそれぞれ職種の異なる4人が各々GWで読んでよかった書籍について、紹介させていただきます! この記事で何か学びになったり、書籍を読むきっかけになったら嬉しいです! SREの知識地図—⁠—基礎知識から現場での実践まで 勝間田が紹介する本は、「SREの知識地図」という書籍です! gihyo.jp 私自身、今年からSRE業務に携わることになったため、SRE関連の書籍を探していたところこの本に出会いました! 読んでみて、SLIやSLO、The Four Golden SignalsといったSRE業務でよく使われる用語の意味をしっかりと学習することができました。用語の意味を理解したことで、普段何気なく活用していたDatadogなどの監視ツールも、より使いやすくなった気がします。 また、この書籍を読んでからシステムの「攻めと守り」や、エラーバジェットについて意識するようになりました。弊社ではまだ明確にSLO等を定義できているわけではないので今後の課題ですが、意識づけができたのはよかったかなと思っています! さらに、普段チームで行っているSRE業務が正しく行えているかの「答え合わせ」ができたのもよかったです。例えば、障害発生時に作成しているポストモーテムについては、本に記載されている通り「再発防止」や「被害の最小化」に向けた具体的なネクストアクションまで落とし込めていることが確認できたので、これは今後も自信を持って継続していきたいです。 弊社ではAIを活用すべく、開発チームの形も大きく変わりました。 本書ではチームトポロジーについても触れられており、4つのチームタイプと3つの主要なインタラクションモードが図解でわかりやすく解説されていました。色々なパターンのSREについて知ることができたので、自組織にあった動きができるよう精進していきたいです! SRE業務を始めることになり、SLIやSLOなどの基礎用語からしっかり理解したい方におすすめです! エンジニアリング組織論への招待 こんにちは、名古屋でEMをしているあさしん( @asashin227 )です。 私がお勧めするのは、「エンジニアリング組織論への招待」です。 gihyo.jp 日々エンジニアリングの現場で直面する様々な不合理に対して『エンジニアリング組織論への招待』は、ビジネスや組織、コミュニケーションの構造的な課題として捉え直し、どのように向き合うべきかを説明しています。 この本を読んだことで「不確実性をいかに最小化するか」、「不確実性を受け入れたまま、いかに前に進めるか」という視点を知ることができました。 プロジェクトを進める中で、「ここが分からないので進めません」「経験がないから難しいです」といったメンバーからの相談を受けることがあります。 本書を読んだことで、未知の領域に対しても「次に進むための構造的な視点」を持って向き合えるようになりました。 現代はAIの進化により、実装方法(How)に頭を悩ませるシーンが劇的に減りました。今私たちが集中すべきなのは、「どのような課題を解決し、どのような価値を創るのか」という本質的な問いです。 このような時代だからこそ、「未知を既知に変えていくプロセス」そのものの重要性が増しています。自分の知らない領域に飛び込むことを恐れず、仮説を持って挑戦し続ける。このマインドセットこそが、技術力以上に求められる現代の重要なソフトスキルではないでしょうか。 本書は、組織論の解説書ではなく、不確実なこのAI時代でエンジニアとして、もしくはリーダーとして「いかに思考し、行動するか」のマインドセットの下地を与えてくれる一冊です。 現状に閉塞感を感じている方や、新しい挑戦に踏み出す勇気が欲しい方に、ぜひ手に取っていただきたいです。 デザインの伝え方 はじめまして、プロダクトデザイナーの hikky です。 最近はコードを書く機会も増えてきたので、エンジニアによる書籍紹介に混ぜてもらいました🐢 私が紹介するのは、オライリー「 デザインの伝え方 」です。 www.oreilly.co.jp AIエージェントの発展でデザイン領域に踏み込むエンジニアの方も増えてきましたが、デザインに直接関わらない場合でも、自分が書いたコードに承認をもらう場面は誰にでもあると思います。そこで切っても切り離せないのが「コミュニケーション」です。本書はその根っこにある考え方を学べる一冊となっています。 本書が教えてくれることはシンプルで、コミュニケーションにおいて「 聞く・伝える・信頼を築く 」がいかに大事か、ということです。文字にすれば当たり前なのですが、その当たり前が一番難しい。 たとえば「聞く」一つとっても、相手に「ちゃんと自分の話を聞いてくれている」と感じてもらえているか、スムーズに本音を引き出せているか、といった観点があります。「伝える」についても、専門用語は同じ知識を持つ人同士では効率の良い言葉ですが、そうでない相手にはノイズになり得ます。立場の違う相手にどう届けるかという視点が必要なのです。 本書を通じて、エンジニアやビジネスサイドのメンバーは自分と違う立場で物事を見ており、それを前提に意図を汲み取ろうというマインドが強くなりました。違うからこそ、お互いを尊重して歩み寄ることがコミュニケーションには欠かせません。AIがどれだけ進化しても、人と人との対話は変わらず残り続けます。 他職種のメンバーともっとうまく連携して、良いものを届けていきたいと感じている方は、ぜひ手に取ってみてください! 書くスキルも設計スキルも飛躍的に上がる! プログラムを読む技術 GW中は首を痛めて、左をほとんど向けなかった、とんとんぼです。 私は最近、「 書くスキルも設計スキルも飛躍的に上がる! プログラムを読む技術 」という書籍を読んでいました。 bookplus.nikkei.com この本は2024年に発売され、当時も一度手に取ったのですが、最近のAIの普及などを受け、改めて読み返してみることにしました。 多くのプログラミング書は「いかにコードを書くか(例:〇〇の実装方法、XX実践入門など)」に焦点を当てたものがほとんどで、「コードの読み方」に特化した本は稀に思えます。しかし、実際の業務ではコードを書くよりも読む時間のほうが圧倒的に長く、比重も大きいのが現実です。さらに、近年では AI がコードを生成してくれるようになったため、提示されたコードの正誤や意図を正しく理解する力はこれまで以上に重要なスキルになっていると感じています。 この本の優れた点は、「理論」と「実践」が明確に分けられていることです。 前半では、コードを読む際の視点や意識すべきポイントについて理論と少しのサンプルコードから学び、後半では、そこで得た知識を活かして実際にさまざまなコードを読み解いていく構成になっています。 サンプルコードには、Python が採用されているため、読みやすく、実際の仕事でのコードでも実践しやすいのも魅力です。 最後に 最後までお付き合いいただきありがとうございました! それぞれ異なる職種のメンバーによる選書はいかがでしたでしょうか? 記事をまとめていて、自分自身も手を伸ばしてみたくなる書籍がありました。 AIで簡単に情報が手に入る時代ですが、本ならではの説得力や納得感を今回改めて感じました。 もし気になる書籍がありましたらぜひ読んでみてください! herp.careers
はじめに こんにちは、スタメンでEMをしている あさしん( @asashin227 )です。 4月12日〜14日の3日間、立川ステージガーデンで開催された try! Swift Tokyo 2026 に参加してきました。 try! Swift Tokyoは、世界中からAppleプラットフォームの開発者が集まる国内最大級のSwiftカンファレンスです。今年は21セッション・5ワークショップの充実したプログラムが用意されました。 tryswift.jp 会場:立川ステージガーデン 昨年から会場が変更され、立川ステージガーデンでの開催となりました。渋谷や新宿といった都心と比べて街が落ち着いており、自然も多く過ごしやすい環境でした。 今年は ステージ背面がオープン になり、外と繋がった構造になっていたことが印象的でした。開放感があり、外から発表をのぞき見ることもできる点が新鮮でした。また、昨年から引き続き スライド下にリアルタイム翻訳が表示 されるようになっており、英語セッションでもストレスなく内容を追えるようになった点は、カンファレンス体験として大変満足度の高いものでした。 私はワークショップ(4/12)+カンファレンス2日間(4/13〜14)のフルで参加しました。 集合写真 iOS Private Playgrounds ワークショップ体験記 5つのワークショップの中から、 iOS Private Playgrounds を選択しました。テーマはその名の通り、iOSのプライベートAPIへアクセスすることです。 プライベートAPIとは Appleはフレームワークの内部実装を非公開にしており、公式ドキュメントに載っていないクラスやメソッドが多数存在します。これらにアクセスすることはApp Storeへの提出では許可されませんが、デバッグや内部挙動の理解、テスト環境での活用といった用途には有効です。 探索ツール:headers.82flex.com プライベートAPIを探す際には headers.82flex.com が有用です。iOSの内部ヘッダーが公開されており、クラス名やメソッド名を検索できます。ワークショップでは実際にこのサイトでメソッドを調べながら実装を進めました。 アクセス方法: value(forKey:) と perform(_:) プライベートAPIへのアクセスには主に2つのアプローチを使います。 // プロパティアクセス(KVC: Key-Value Coding) let value = object.value(forKey : "privatePropertyName" ) // メソッド呼び出し(引数なしのシンプルなケース) object.perform(NSSelectorFromString( "privateMethodName" )) NSObject のKVC(Key-Value Coding)を利用した value(forKey:) でプロパティ値を取得し、 perform(_:) で引数なしのメソッドを呼び出します。ただし、Int型などのprimitive引数を持つメソッドには perform が使えないため、 objc_msgSend を直接呼ぶC言語ヘルパーが必要になります。Obj-Cの型をSwiftに名前で公開したい場合は、Bridging Headerにヘッダーファイルをインポートすることで対応できます。 Objective-Cを書いていた頃にKVCの文脈でよく使っていたメソッドが登場し、懐かしさを感じながら作業しました。当時との違いは、SwiftUIからObj-Cの型を直接参照するために Bridging Header へのヘッダーインポートが必要な点です。 挑戦:Face IDインターフェースの呼び出し ワークショップ内では Face IDのインターフェースをプライベートAPIで呼び出せないか を試みました。 内部ヘッダーを調べながら実装を進めましたが、最終的にFace ID認証の画面そのものを呼び出すことはできませんでした。一方で、 Touch IDの登録画面を表示させること には成功しました。Face IDとTouch IDで内部的な仕組みが異なることを体感できた経験でした。 Touch ID設定画面をiPhone 17 Pro Maxで表示できた 実際のプロダクト開発には使えないものの、iOSのシステム内部を覗く体験としては非常に刺激的なワークショップでした。 印象に残ったセッション 21セッションの中から、特に印象深かった3つを紹介します。 SwiftUIってなんでこうなるの? — Paul Hudson 発表者 : Paul Hudson( @twostraws ) youtu.be Paul Hudsonが「SwiftUIの内部構造を理解することでより良いコードが書ける」というテーマで発表しました。 最も印象的だったのは、 @ViewBuilder 内の if-else が _ConditionalContent に変換される という仕組みの解説です。 // ❌ @ViewBuilder内ではtrueとfalseが「別のView」として扱われる var body : some View { if scaleUp { TestView().scaleEffect( 2 ) } else { TestView().scaleEffect( 1 ) } } // → スケールアニメーションにならず、フェードイン/アウトになる @ViewBuilder が if-else を _ConditionalContent<TrueView, FalseView> に変換するため、SwiftUIは「2つの異なるView」として認識します。アニメーションではなくトランジションになるのはこのためです。 解決策は三項演算子を使うか、 @ViewBuilder のない別プロパティに切り出すことです: // ✅ 三項演算子で同一Viewの状態変化として扱わせる var body : some View { TestView().scaleEffect(scaleUp ? 2 : 1 ) } if-else や switch による分岐で表されるViewが内部的にそれぞれ 異なるViewとして保持されている という点は、SwiftUIを深く理解する上での重要な知識です。アニメーションが意図通りに動かない原因がこの仕組みにあると知り、腑に落ちました。 Swiftの型システムはAIエージェントをどう導くのか — Yuta Koshizawa 発表者 : Yuta Koshizawa( @koher ) スライド : SpeakerDeck youtu.be Swiftの型システムがAIエージェントのコーディング精度にどう影響するかを、実験データで示した発表です。 throwsが型の一部であることの意味 Swiftでは throws が関数の型の一部です: func findUser (name : String ) throws -> User TypeScriptなどの多くの言語では、関数がエラーを投げるかどうかは型の一部ではありません。宣言だけでは判断できず、コードを読む必要があります。 実験結果 Claude Codeを使って、SwiftとTypeScriptで同等のダミー関数群に対して同じタスク(エラーハンドリング付きの関数実装)を合計20回実施しました: 言語 正確なエラー処理(5回中) Swift(throwing) 5/5(100%) Swift(non-throwing) 5/5(100%) TypeScript(throwing相当) 0/5(0%) TypeScript(non-throwing相当) 2/5(40%) AIは関数宣言のみを確認してエラー処理の要否を判断しており、 throws の明示がない場合はコードを末端まで追跡しませんでした。 「より厳格なプロンプト」を与えたTypeScriptでも精度は80%に改善しましたが、入力トークン数が約2倍(187,900 → 400,000)に増加しました。Swiftではほぼ増加なし。 Swiftの型システム——特に throws が関数の型の一部として明示されること——が、AIエージェントのコンテキストそのものになり意図通りのアウトプットを可能にするという言説が非常に興味深かったです。 Swift 6でのActor isolationなど直近の破壊的変更に対して「なぜここまで型で縛るのか」と感じることもありましたが、それらの妥当性を補完する視点としても腑に落ちる内容でした。Swift言語への愛着がさらに高まるセッションでした。 XCUITestコード生成をAIに任せる — Yusuke Kita 発表者 : Yusuke Kita( @kitasuke ) スライド : SpeakerDeck youtu.be E2Eテストパイプラインの構築を通じて発見した知見—— 「より良いプロンプト」ではなく「より良い構造(Structure)」が鍵 ——を語ったセッションです。 LLMがテストコードを書く際の3つの失敗パターン LLMに「ログインテストを書いて」と指示すると、一見正しそうで動かないコードが生成されます: パターン 説明 架空のセレクタ 存在しないアクセシビリティIDを自信満々に生成 不正確なテキスト 「Login」と「Sign in」の1単語の違いでテストが失敗 待機戦略の欠如 CI環境での処理時間を考慮しない 解決策:4レイヤーの構造化コンテキスト 詳細なプロンプトを書こうとするのが直感ですが、それではスケールしません。答えは LLMが読み取れるファイルを与えること です: 信頼できるAI生成テストコード = IR(何をテストするか) + Scenarios(どの手順か) + Definitions(何が存在するか) + Templates(どう書くか) Definitions ファイルにアクセシビリティIDや関数シグネチャを定義しておくことで「架空のセレクタ問題」を直接解決します。IDが変わっても1ファイルの更新で全テストに反映されます。 「AIのために構築するのではなく、チームのために構築してください」 この仕組みはAIのためだけに作られたわけではなく、チームのコンテキスト共有のために作られたもの。AIはたまたまこの構造の上でうまく機能しているだけ、という締めが印象的でした。 koherさんの「型がAIのコンテキストになる」という話との思想的な親和性が高く、 「AIへのコンテキストをいかに構造化するか」という問いが、AI活用の本質的な課題 だと改めて感じました。 まとめ try!Swift Tokyoは今年で10周年を迎え、カンファレンスとしての体験やセッション、ワークショップのレベルも年々上がっておりApple プラットフォームに関わるエンジニアにとって最高の3日間になりました。 カンファレンス最終日、ふらっと立ち寄った店にtry! Swiftの参加者が自然と集まり、そこでの交流も印象的でした。 数年ぶりに再会できた方もおり、このような場の価値は、セッションの内容と同等かそれ以上にあると感じています。 try! Swift Tokyoは、世界中からAppleプラットフォームの開発者が集まる国内最大級のカンファレンスです。スキル向上はもちろん、同じ技術を扱う仲間との交流やキャリアを広げる場として最適です。 運営やスピーカーの皆様、そして参加者の皆様のおかげで、とても充実したカンファレンスでした。本当にありがとうございました。 また来年、参加できることを楽しみにしています。 herp.careers
AIツールの進化が加速するなか、エンジニアの仕事はどう変わっているのか。日々の開発でAIを使い続けるエンジニア3名に、活用の実態から失敗談、半年後の開発スタイルの展望まで、本音で語ってもらいました。 登場人物 名前 役割 あさしん( @asashin227 ) (写真右下) 名古屋プロダクト部のエンジニアリングマネージャー。仕事でもプライベートでもAIをうまく使う方法を常に模索中。エンジニア以外でもAIを使えるようにスタメン内でのハンズオンやAIもくもく会を運営しています おしん( @38Punkd ) (写真左下) iOS開発を得意とするエンジニア。AIを使って積極的にAndroidやWeb技術にチャレンジ中。プライベートではAIでインフラ中心のエンジニアをしている いが( @cochumo ) (写真真ん中) フロントエンドを専門領域としているエンジニア。AIを使ってバックエンド開発にも軸足を伸ばしています。今回のインタビュアーも兼任。 1日の業務の50〜80%がAIと対話。コードの外にも使い道は広がる ── 1日の業務のうち、何%くらいAIと対話したり、作業を任せたりしていますか? あさしん: ミーティングが結構多いので、思ったよりは使えていないんですよね。それでも50〜60%くらいにはなっていると思います。ミーティングの前に依頼しておいて、ミーティング後に確認みたいな使い方をしています。 おしん: 自分はあまりミーティングが多くないので、70〜80%は使っていますね。 いが: 60%ぐらいでしょうか。作るものの方向性についてメンバーとディスカッションする部分は人間がやらないといけないので、100%にはならないですね。 ── どんな場面でAIを活用していますか? おしん: 仕様の方向性をまずAIと話して、提案の形に整えてから人間とのディスカッションに持ち込む流れが増えてきました。ステークホルダーへの合意形成の前段階だったり、CS(Customer Success)へのお知らせ文や顧客との調整の頭出しにも使っています。まるっと投げるというよりは、自分なりの仮説がある状態でブラッシュアップしていく、という使い方が多いですね。 あさしん: 最近はClaude Cowork(以下Coworkと表記)をコーディング以外の場面でも使えるようにしていきたいなと思っていて、少しずつ試しています。割合はこれからも増えていきそうだという感覚はありますね。 いが: Coworkいいですよね。社内のチャットのステータス変更の処理を自動化してスケジューリングさせるような使い方は、本当に助かっています。 スピードは上がった。でも、楽しさの「質」が変わった ── AI導入から、開発のスピード感や楽しさはどう変わりましたか? あさしん: スピード感は確実に上がりましたね。やりたいことを自然言語で書けばとりあえず動く状態になるので、試行錯誤の回数が格段に増えています。ただ、仕事においては「プログラミングは自分がやらなくていい」という目標をもともと持っていたので、AIがコードを書くことへの心理的な変化はそれほどないというか。メンバーが書いてくれるのとAIが書いてくれるのとで、感覚的にはさほど変わらないんです。変わったと思うのは、人との解釈合わせにかかるコミュニケーションコストが減ったことです。AIへの指示は自分の責任で完結するから、より言語化の精度を上げないといけないという意識が強まりましたね。 おしん: 楽しさという意味では、むしろ大きくなりました。これまでネット上の記事を探し回ることに費やしていた時間をAIが肩代わりしてくれるので、「プロダクトの仕様をどう改善すれば売上に貢献できるか」という、本来考えるべきことに頭を使える時間が増えています。 いが: AIの進化にはワクワクするんですけど、AIに実装をやらせているとき自体はそんなにワクワクしなくなってきました。自分が書いていないからのめり込めなくて、複数のことを並行して浅く広く動かす形になってしまっている。コードを書いているときの楽しさは、正直なくなってきましたね。 おしん: ただ、その代わりに。職人的な充実感よりも、事業を前に進めている手応えに重きが移ってきた感覚がありますね。 ── 具体的に「これはAIにやらせて正解だった」という事例はありますか? あさしん: テストケースを大量に作らせるのはAIが得意な領域で、活用しています。あとは先ほど触れたCoworkですね。カンファレンスのグッズを企画するときに、会話の中で出てきたアイデアをそのままデータ化したり、作ったデータをNano Bnanaで画像に合成して、それっぽいイメージを可視化できるのが便利でした。コーディング以外のプロトタイプも、以前より格段に作りやすくなっています。 いが: Coworkは自然言語で指示してワークフローを組むと、ブラウザ操作まで実行してくれます。そこが本当に大きい。こういった活用はこれからさらに広がっていくんだろうなと感じています。 ガードレールを引かないと、リポジトリもドキュメントも静かに汚れていく ── 逆に、失敗したことや、気をつけていることはありますか? あさしん: 個々のミスというより、チーム全体として気になっているのはリポジトリに入っているドキュメントが少しずつ汚れていくことです。うちもそこまでドキュメントの文化が強いわけじゃないので、誰も深く見ていない箇所でAIが誤った内容を書き込んでいても気づけない。ガードレールをきちんと設計しておかないと、気づかないうちに的外れな方向へ進んでしまう。意識して向き合わなければならない課題だと思っています。 おしん: 嘘とまでは言えないけれど、根拠があいまいなままでも断言してしまうのがAIの特性だと思っていて。わずかでも事実と違う内容が混ざると、ドキュメント全体の信頼性が揺らいでしまいますよね。 いが: 仕様書をAIに書かせた場合でもユーザーインタビューに基づいた内容なのか、推測で書いたものなのか、根拠がまったくない記述なのか、読んだだけでは区別がつかない。その3パターンをちゃんと分類する仕組みを作って曖昧なところを明示的に固めていく、そういう工夫をこれからも続けていきたいですね。 ── プロンプトや指示の出し方で、自分なりにこだわっていることはありますか? あさしん: まず一度考えさせる、というのは意識しています。「プランニングしてください」と明示的に書いてから進めるようにしていて。あとは、プロンプトで都度指示することより、ドキュメントを整えて自動的によい動きをしてくれる環境をつくることを優先していますね。スキルの整備やエージェントの動きを定期的に見直すのも続けています。週に一度くらいは、同じ作業を繰り返していたらスキルとして切り出す習慣もつけています。 セッションの履歴を見て、繰り返しやっていることをスキル化するのは効果的です。全セッションを遡る必要はなく、そのセッション内のやり取りから切り出すだけで十分なことが多い。CLI(Command Line Interface)やLSP(Language Server Protocol)をちゃんと使い込むと、その辺りがうまく機能すると思いますね。 これからのエンジニアに求められるのは、ドメイン分解力・抽象力・言語化力だ ── 半年後、自分たちの開発スタイルはどうなっていると思いますか? あさしん: コーディング作業そのものは今より少なくなると思っています。その代わり、課題を持っているステークホルダーとのコミュニケーションがより重要になってくる。FDE(Forward Deployed Engineer)と呼ばれる役割、つまりお客さんの現場に立ってエンジニアとして提案していくような動き方も、これから注目されていくはずです。 いが: すでに別の会社では、CxO(Chief x Officer)にAI活用が得意な人を一人つけて、その人がやりたいことをPoC(Proof of Concept: 概念実証)化していくという動き方をしているところも出てきていますよね。 おしん: Figmaじゃなくてプロダクトレベルでのモックを素早く作る、という段階は確実に進んでいくと思います。エンジニアの強みは、やりたいことに対してどのアプローチが現実的かを具体的に示せる点にあります。自分のタスクや技術領域だけを見ていればいいという姿勢は通用しなくなって、事業全体を見渡して課題を見つけ動かしていけるエンジニアが、これからの開発を牽引していくと思っています。 ── AIが進化し続ける中で、エンジニアが磨くべき「コアスキル」とは何でしょうか? あさしん: ITバブルの頃、エンジニアの工数が一番レバレッジが効くと言われていたのは、一人分の工数をかけることで、かけた工数をソフトウェアとして何万人というユーザーに展開できる、かけ算的な成長ができるからでした。今後はAIによってプログラミングが民主化されて誰もが主体的に開発できるようになってくる。そういった中で自分たちが発揮できる価値を捉えていく必要があります。アウトプットがソフトウェアである以上、ソフトウェアがわかる人向けの言語化の仕方はエンジニアならではの強みになると思う。 あとはロジックの破綻を整理してあげるとか、ドメイン駆動開発をエンジニアが担ってAIにドメインごとの指示を出していくとか、そういうやり方が主流になっていくでしょう。ドメイン分解能力、抽象能力、言語化能力、そして事業全体を俯瞰できる広い視野。自分のタスクだけ見ていればいいというエンジニアはどんどん淘汰されていって、事業全体から課題を見つけて推進できるエンジニアが成長して牽引できると思っています。 おしん: エンジニアの専門性はPMやデザイナーとも融合してきているし、モバイル・バックエンド・フロントエンドといった境界もなくなりつつある。そこをコアスキルにするのはもったいない。エンジニアが磨くべきは事業理解であり、事業ドメインをソフトウェア設計に落とし込んで、リリース後も安定的に運用できる力こそが本質なんじゃないかと思っています。 おわりに 今回はテックブログとしては新しい取り組みである「エンジニア3人でAI活用の座談会をする」という記事を作成しました。 AIを使える人と使えない人で、仕事の速さも質も変わってきており、何に注力して、何をAIに任せていくか、そして自身の思考をどこに使っていけばいいのかヒントが掴めたように思えます。 AIの使い方に正解はないからこそ、同じように模索しているエンジニアの方とお話してみたいと思っています。この記事が、そのきっかけになれば嬉しいです。 herp.careers 本記事はインタビュー音声をもとに編集・再構成しています。
はじめに はじめまして。株式会社スタメンのエンジニアの鈴木( @16suzu )です。 弊社スタメンでは、組織のエンゲージメントを高めるためのサービスである TUNAG と、チャットサービスの TUNAGチャット を開発・運営しています。 弊社ではドッグフーディングの一環としまして、この TUNAG と TUNAGチャットを全社で業務利用しています。 日々、これらのサービスを運営する中で、CSチーム経由でお客様からお問い合わせをいただきます。 これまでは、 TUNAGチャット上で連絡が来た際に、PdMが手動で GitHub Issue を起票し、エンジニアチームが対応していました。 しかし、このやり方ではPdMが介在してしまうため工数負荷が課題となっておりました。今回、私はこの課題を解決するために、 Google Forms をトリガーに TUNAGチャットの投稿を収集し、Gemini で要約した上で GitHub に Issue を自動起票するワークフロー を Google Apps Script(GAS)で構築しました。本記事ではその設計と実装を紹介します。 ※注意:本記事では検証のため AI Studio の API を使用していますが、実業務で顧客データなど機密情報を扱う場合は、データが学習に利用されない有料プランや Google Cloud Vertex AI の利用を推奨します。 課題 従来のフローには以下の問題点がありました。 TUNAGチャットの投稿内容を手動でコピーして、Issue を作成していた 起票のタイミングが担当者に依存し、対応漏れが発生することがあった ワークフローの全体像 そこで、以下のようなワークフローを構築しました。 CS メンバーが Priority を判断し Google Forms を送信 ↓ GAS がフォーム回答を受信 ↓ TUNAG チャットの API で問い合わせチャンネルの投稿とスレッド情報を取得 ↓ Gemini API で投稿内容を要約・整形 ↓ GitHub API で Issue を自動起票 ↓ Issue をお問い合わせ用の GitHub Projects に入れ Priority や Type を設定する フォームには「Tunag Chat Link」「Priority」「Ticket type」「メールアドレス」の入力項目を設け、起票時に Priority やチケットタイプを設定できるようにしました。 実装 次にコードを示します。 1. Google Formsの送信をトリガーに処理を開始 エントリーポイントとなるファイルです。 Google Forms の投稿をトリガーとし、各モジュールを順番に実行していきます。 // main.gs const TC_CHANNEL_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' const GITHUB_OWNER = "your-org" const GITHUB_REPO = "your-repo" function submitForm ( event ) { console . info ( event . namedValues ) ; const namedValues = event . namedValues ; const tunagChatLink = namedValues [ "Tunag Chat Link" ][ 0 ] ; const priority = namedValues [ "Priority" ][ 0 ] ; const ticketType = namedValues [ "Ticket type" ][ 0 ] ; const chatId = tunagChatLink . split ( "/pl/" )[ 1 ] ; // 問い合わせスレッドのID const email = namedValues [ "メールアドレス" ][ 0 ] ; const chatMessages = getChatThread ( chatId ) ; const image_links = imageLinks ( chatMessages ) ; const title = createIssueTitle ( JSON . stringify ( chatMessages )) ; const body = createIssueBody ( JSON . stringify ( chatMessages ) , tunagChatLink ) ; const body_with_image = body + `\n### 画像 \n ${ image_links . join ( "\n" )} ` ; // GitHub Issueを作成 const issue_url = createGitHubIssue ( GITHUB_OWNER , GITHUB_REPO , title , body_with_image , priority , ticketType ) ; // TUNAGチャットに投稿. 引数は channelId, rootId, issue_url postIssueUrl ( TC_CHANNEL_ID , chatId , issue_url , priority , ticketType , email ) ; } 2. TUNAGチャット から投稿を取得 TUNAGチャット上にお問合せの投稿がされるので、それを取得します。工夫した点として、スレッド内で行われるCS、エンジニアメンバー同士の議論も取得しています。 この議論も含めてGeminiに要約させることで、GitHub Issueに起票される際の文章の信頼度を高めています。 // tunag_chat.gs const CHAT_URL = "https://your-tunag-chat.example.com/" ; // tunag chat の url const BOT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; // botの投稿をgeminiの解析対象から外すのに使う // TUNAGチャット 側の投稿を全て取得する function getChatThread ( chat_id ) { const tunag_chat_api_token = PropertiesService . getScriptProperties () . getProperty ( 'tunag_chat_api_token' ) const url = ` ${ CHAT_URL } /api/v4/posts/ ${ chat_id } /thread?per_page=200` ; const headers = { 'Authorization' : 'Bearer ' + tunag_chat_api_token } ; const options = { 'method' : "GET" , 'headers' : headers , } ; const response = UrlFetchApp . fetch ( url , options ) ; var jsonData = JSON . parse ( response . getContentText ()) ; let res = [] ; for ( const key of jsonData . order ) { let post = jsonData . posts [ key ] // botからの投稿は含めない if ( post . user_id == BOT_ID ) { continue } res . push ( { id : post . id , message : post . message , user_id : post . user_id , file_ids : post . file_ids , created_at : formatedDate ( post . create_at ) , }) ; } return res ; } // TUNAGチャット の元のスレッドに作成したIssueのURLを投稿する function postIssueUrl ( channelId , rootId , issue_url , priority , ticketType , email ) { const issuerName = email . split ( '@' )[ 0 ] ; const message = ` ${ issuerName } が Priority \` ${ priority } \` TicketType \` ${ ticketType } \` でIssueを作成しました。 ${ issue_url } ` const tunag_chat_api_token = PropertiesService . getScriptProperties () . getProperty ( 'tunag_chat_api_token' ) const postUrl = ` ${ CHAT_URL } /api/v4/posts` ; const payload = { channel_id : channelId , message : message , root_id : rootId , // ここで返信先のメッセージIDを指定 } ; const options = { method : 'post' , contentType : 'application/json' , payload : JSON . stringify ( payload ) , headers : { 'Authorization' : `Bearer ${ tunag_chat_api_token } ` } , muteHttpExceptions : true } ; try { const response = UrlFetchApp . fetch ( postUrl , options ) ; const responseCode = response . getResponseCode () ; if ( responseCode === 201 ) { Logger . log ( 'メッセージがスレッドに正常に投稿されました。' ) ; return JSON . parse ( response . getContentText ()) ; } else { Logger . log ( `投稿に失敗しました。ステータスコード: ${ responseCode } ` ) ; Logger . log ( `レスポンス内容: ${ response . getContentText ()} ` ) ; return null ; } } catch ( e ) { Logger . log ( `APIリクエスト中にエラーが発生しました: ${ e . message } ` ) ; return null ; } } // unix timeを日本語の年月日の日付に変換 function formatedDate ( created_at ){ const date = new Date ( created_at ) ; // timestampはミリ秒単位かDate文字列 const year = date . getFullYear () ; const month = String ( date . getMonth () + 1 ) . padStart ( 2 , '0' ) ; // 月は0始まりなので+1 const day = String ( date . getDate ()) . padStart ( 2 , '0' ) ; const hours = String ( date . getHours ()) . padStart ( 2 , '0' ) ; const minutes = String ( date . getMinutes ()) . padStart ( 2 , '0' ) ; return ` ${ year } / ${ month } / ${ day } ${ hours } : ${ minutes } ` ; } /** * ファイルIDを持つチャットメッセージから、画像リンクの配列を生成する関数。 * @param {Array<Object>} chatMessages - 各要素がIDとファイルIDを持つチャットメッセージオブジェクトの配列。 * @returns {Array<string>} 画像へのURLリンクの配列。 */ function imageLinks ( chatMessages ) { return chatMessages . filter ( message => message . file_ids && message . file_ids . length > 0 ) . map ( message => ` ${ CHAT_URL } chat/pl/ ${ message . id } ` ) ; } 3. Gemini で要約 TUNAGチャットから取得したデータを、Gemini API を使ってフォーマットに従って要約します。 prompt に入っているのが Gemini に実際に投げているプロンプト文になります。 // gemini.gs // geminiのモデルを指定 const MODEL_NAME = 'gemini-2.5-flash' ; const GEMINI_API_URL = `https://generativelanguage.googleapis.com/v1beta/models/ ${ MODEL_NAME } :generateContent` ; // gemini にIssueのタイトルを作らせる function createIssueTitle ( chat_messages ) { const prompt = "これはTUNAGというサービスの不具合のお問い合わせのチャットのスレッドです。 これらを要約してGitHub Issueを起票したいのでそのタイトルを考えてください。50文字以内でお願いします。 ただし最初に会社名と会社IDを[#133 会社名]の形式で入れてください。会社名が不明の場合は不要です。 [ここからチャットのスレッドです] " + chat_messages const API_KEY = PropertiesService . getScriptProperties () . getProperty ( 'gemini_api_token' ) const apiUrl = ` ${ GEMINI_API_URL } ?key= ${ API_KEY } ` ; const payload = { contents : [ { parts : [ { text : prompt , } , ] , } , ] , } ; const options = { method : 'post' , contentType : 'application/json' , payload : JSON . stringify ( payload ) , muteHttpExceptions : true , } ; const response = UrlFetchApp . fetch ( apiUrl , options ) ; const responseBody = response . getContentText () ; const data = JSON . parse ( responseBody ) ; const generatedText = data . candidates [ 0 ] . content . parts [ 0 ] . text ; return generatedText ; } // gemini にIssueの本文を作らせる function createIssueBody ( chat_messages , tunag_chat_link ) { const format = "ここから「フォーマット」です。 ### 課題 ### 機能 ### 発生頻度 ### 緊急度と重要度 ### 発生環境 ### 再現手順 ### チャットリンク" ; const prompt = `入力された「チャットメッセージ」に対して、下記の不具合報告書の「フォーマット」の形に整えた上で出力してください。表現は簡潔にまとめてください。 フォーマットのチャットリンク項目に ${ tunag_chat_link } を記入してください。 ここから「チャットメッセージ」です。 ${ chat_messages } ${ format } ` ; const API_KEY = PropertiesService . getScriptProperties () . getProperty ( 'gemini_api_token' ) ; const apiUrl = ` ${ GEMINI_API_URL } ?key= ${ API_KEY } ` ; const payload = { contents : [ { parts : [ { text : prompt , } , ] , } , ] , } ; const options = { method : 'post' , contentType : 'application/json' , payload : JSON . stringify ( payload ) , muteHttpExceptions : true , } ; const response = UrlFetchApp . fetch ( apiUrl , options ) ; const responseBody = response . getContentText () ; const data = JSON . parse ( responseBody ) ; const generatedText = data . candidates [ 0 ] . content . parts [ 0 ] . text ; return generatedText ; } 4. GitHub に Issue を起票 最後に、GitHub に Issue を起票します。起票後に Project への紐付けと、各 Field への値の設定を行います。 // github.gs // GitHub GraphQL API で利用するID const GITHUB_PROJECT_ID = "xxxxxxxxxxxxxxxxxxxx" ; const GITHUB_FIELD_ID_STATUS = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; const GITHUB_OPTION_ID_TODO = "xxxxxxxx" ; const GITHUB_FIELD_ID_PRIORITY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; const GITHUB_OPTION_ID_LOW = "xxxxxxxx" ; const GITHUB_OPTION_ID_MIDDLE = "xxxxxxxx" ; const GITHUB_OPTION_ID_HIGH = "xxxxxxxx" ; const GITHUB_FIELD_ID_TICKET_TYPE = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx" ; const GITHUB_OPTION_ID_BUG = "xxxxxxxx" ; const GITHUB_OPTION_ID_UPDATE = "xxxxxxxx" ; const GITHUB_OPTION_ID_RESEARCH = "xxxxxxxx" ; function createGitHubIssue ( owner = "your-org" , repo = "your-repo" , title = "" , body = "" , priority , ticketType ) { const pat = PropertiesService . getScriptProperties () . getProperty ( 'GITHUB_PAT' ) ; const url = `https://api.github.com/repos/ ${ owner } / ${ repo } /issues` ; const payload = { title : title , body : body } ; const options = { method : 'post' , contentType : 'application/json' , payload : JSON . stringify ( payload ) , headers : { 'Accept' : 'application/vnd.github.v3+json' , 'Authorization' : `Bearer ${ pat } ` , 'X-GitHub-Api-Version' : '2022-11-28' , } , muteHttpExceptions : true } ; try { const response = UrlFetchApp . fetch ( url , options ) ; const result = JSON . parse ( response . getContentText ()) ; // ステータスコードで成功/失敗を判定 if ( response . getResponseCode () === 201 ) { Logger . log ( 'Issueが正常に作成されました: ' + result . html_url ) ; Logger . log ( result . node_id ) ; // Project に追加する const result_add_to_project = addToProject ( GITHUB_PROJECT_ID , result . node_id ) const item_id = result_add_to_project . data . addProjectV2ItemById . item . id ; // Todo をセットする setField ( GITHUB_PROJECT_ID , item_id , // projectにおけるitem id GITHUB_FIELD_ID_STATUS , // fieldId(Status) GITHUB_OPTION_ID_TODO // optionId(Todo) ) // Priority をセットする setPriority ( item_id , priority ) // Ticket type をセットする setTicketType ( item_id , ticketType ) return result . html_url } else { Logger . log ( 'Issue作成に失敗しました: ' + JSON . stringify ( result )) ; } } catch ( e ) { Logger . log ( 'APIリクエスト中にエラーが発生しました: ' + e . message ) ; } } function setPriority ( item_id , priority ) { var p_id = "" ; switch ( priority ) { case 'High' : p_id = GITHUB_OPTION_ID_HIGH ; break; case 'Middle' : p_id = GITHUB_OPTION_ID_MIDDLE ; break; case 'Low' : p_id = GITHUB_OPTION_ID_LOW ; break; default: // 未指定の場合はGitHub IssueのPriorityを設定しない return; } setField ( GITHUB_PROJECT_ID , item_id , GITHUB_FIELD_ID_PRIORITY , p_id ) } function setTicketType ( item_id , ticketType ){ var tt_id = "" ; switch ( ticketType ) { case 'Bug' : tt_id = GITHUB_OPTION_ID_BUG break; case 'Update' : tt_id = GITHUB_OPTION_ID_UPDATE break; case '仕様調査' : tt_id = GITHUB_OPTION_ID_RESEARCH break; default: // 未指定の場合はTicketTypeを設定しない return; } setField ( GITHUB_PROJECT_ID , item_id , GITHUB_FIELD_ID_TICKET_TYPE , tt_id ) } // add to github projects function addToProject ( projectId , issueNodeId ){ const mutation = ` mutation addIssue($projectId: ID!, $issueNodeId: ID!) { addProjectV2ItemById(input: { projectId: $projectId, contentId: $issueNodeId }) { item { id } } } ` ; const variables = { projectId , issueNodeId } ; return callGitHubApi ( mutation , variables ) ; } // Fieldをセットする関数 function setField ( projectId , itemId , fieldId , optionId ) { const mutation = ` mutation updateItemStatus($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { updateProjectV2ItemFieldValue(input: { projectId: $projectId, itemId: $itemId, fieldId: $fieldId, value: { singleSelectOptionId: $optionId } }) { projectV2Item { id } } } ` ; const variables = { projectId , itemId , fieldId , optionId } ; callGitHubApi ( mutation , variables ) ; } // GitHub GraphQL API にリクエストを送信する関数 function callGitHubApi ( query , variables ) { const GITHUB_TOKEN = PropertiesService . getScriptProperties () . getProperty ( 'GITHUB_PAT' ) ; const url = 'https://api.github.com/graphql' ; const headers = { 'Authorization' : `Bearer ${ GITHUB_TOKEN } ` , 'Content-Type' : 'application/json' } ; const payload = JSON . stringify ({ query : query , variables : variables }) ; const options = { 'method' : 'post' , 'headers' : headers , 'payload' : payload } ; try { const response = UrlFetchApp . fetch ( url , options ) ; const data = JSON . parse ( response . getContentText ()) ; if ( data . errors ) { throw new Error ( `GitHub API Error: ${ JSON . stringify ( data . errors )} ` ) ; } return data ; } catch ( e ) { Logger . log ( `API call failed: ${ e . message } ` ) ; throw e ; } } 実際に起票された GitHub Issue こちらが、実際に起票された Issue のサンプルです。 Projects がセットされ、StatusとPriorityも付与されています。 導入後の効果 フォームを送信するだけで Issue が作成されるため、担当者の負担が大幅に軽減されました 起票が自動化されたことで、議論が Issue として確実に残るようになりました Gemini での要約・整形により、Issue として読みやすい形式で残るようになりました おわりに このワークフローは Gemini と相談しながら約2日で作成することができました。 今回苦労したところは GitHub Projects に Issue をセットする部分で GitHub GraphQL を利用する点でした。 GraphQL の利用経験があまりないため、都度調査しながら進めました。 GAS は外部サービスとの連携が容易で、Google Forms のトリガーをそのまま利用できる点が今回の用途に適していました。 Gemini の活用により、単純な転記ではなく「整理された Issue」として起票できる点も大きなメリットです。 同様の課題を抱えるチームの参考になれば幸いです。 herp.careers
はじめに こんにちは、株式会社スタメン 東京プロダクト組織のエンジニアリングマネージャー まっきーです。先日、 EMConf 2026 に参加してきました。EMとして日々悩みながら手を動かしている身として、組織のあり方・マネジメントの考え方・技術と人の接点まで、自分に刺さるセッションが多いカンファレンスでした。 今回の記事は、印象的だったセッションと、参加して得られた気付きを参加レポートとしてまとめたものになります。 冒険する組織のつくりかた 2026.emconf.jp 「軍事的マネジメント」から「冒険的マネジメント」へのパラダイムシフト の話でした。 マネジメントという概念が生まれたのは1900年代、工場の生産ラインの安定化が目的でした。その延長で「兵隊の訓練」がフレームワークになり、目標の達成・統制・危機感の醸成といった発想が根付いていきました。一方で、ここ2〜30年で 会社中心から人生中心のキャリア観 へと世の中が変わり、1980年代型の「危機感を煽る」やり方は人材流動性の高い今では通用しなくなっているという話でした。 そこで提案されていたのが、 半径5mからできる4つのマネジメント です。 ① 目標:SMART と ALIVE の両立 軍事的マネジメントは「目標に対する視野狭窄」を生みがち。一方、冒険できる組織では、 SMART (Specific, Measurable, Achievable, Relevant, Time-bound) ALIVE (Adaptive, Learningful, Interesting, Visionary, Experimental) の両立が重要だという話でした。チームで「ALIVE を達成できているか」を問い直す時間を作ることで、明確な目標設定をすることができそうだと感じました。 組織づくりは、組織に張り巡らされている「目標設定」の網の質を変えていくところから。 最近は具体的な目標を立てる際には従来の"SMART"に加えて"ALIVE"になっているかどうかを意識しています。 #154 SMARTに代わる、目標設定の新法則「ALIVE」 - 安斎 勇樹 https://t.co/EgGPba9qnm #Voicy pic.twitter.com/S78SSI757c — 安斎勇樹 / 最新刊『静かな時間の使い方』予約受付中 (@YukiAnzai) 2024年9月10日 ② 会議:問いの質 「ミーティングで反応が悪いのは、メンバーのせいではなく、ファシリテーターの問いが悪い」 という話を聞き、自分がファシリテーターの時のミーティングは時々こうなっていることに気付きました。対策として、選択肢を絞るような問いにすることで、議論が動きやすくなるとのことでした。これについてはカンファレンス参加後のミーティングから意識するようにしています。 ③ 興味:好奇心と興味の違い 好奇心 :瞬発的な感情 興味 :好奇心が継続し、一定の対象に定着している AI時代には散漫な好奇心より、 深い興味 が重要。 興味 =「レンズ(どんなものの見方で)」×「対象(なにを見るか)」 という表現をされていました。 数年後のビジョンをいきなり立てるより、「いま何が面白いか・興味があるか」を大事にする方という考え方は、目標を立てるのが苦手な自分には少し肩の荷が降りるものでした。 ④ 文化:風土と文化は違う 風土の改善だけでなく、 文化を耕す 。価値基準の集合体が文化であり、皆が集まる場で繰り返し伝え、刷り込み続ける。耕し続ける姿がなければ、マネージャーの「本気」も伝わりにくく、共感も生まれにくいという話は、マネージャーとしての立ち位置を改めて認識させられるものでした。 自律型組織の真実:『甘い自走』を捨てて導いた、EMによる戦略的組織変革 「自走」という言葉が一人歩きし、 マネジメント放棄 に近い状態になってしまうケースについてのセッションでした。 甘い自走の結果として、 生産性低下・士気低下 技術的負債の放置 現場の混乱がそのまま事業の停滞に直結 といった問題が起きます。そして「目的とゴールを浸透させる」「評価制度を見直す」「1on1を増やす」といった 重い仕組みの追加 では、組織が死んでしまうだけ。 重要なのは、 自律 ≠ 自由 自律 = 「境界線(ガードレール)」と「責任」のセット 「ここまではノーブレーキでOK、ここからは止まれ」を決める。最もレバレッジが効くのは「意思決定」に集中する。意思決定ができると、ドミノ倒しのように仕事が進んでいく、という話でした。 この話を聞いて、まず自分自身が仕事に対して責任範囲をきっちりと決めきれていないことに気付きました。そんな状態では、メンバーに対して自律して動いてくださいなどと言えるはずもありません。 メンバー目線で見ても境界線を定めてくれるマネージャーは間違いなく信頼できるので、今後身につけていきたいスキルだと強く感じました。 「ストレッチゾーンに挑戦し続ける」ことって難しくないですか? メンバーの持続的成長を支えるEMの環境設計 在籍歴の長いエンジニアの退職申し出をきっかけに、 ストレッチゾーンのチャレンジが枯渇してしまう 問題にどう向き合うかを扱ったセッションでした。 「今の自分に最適なチャレンジが分からない」という状態に対して、 will / can / must で現状分析する。 ただし、自分の will を正しく言語化できるとは限らないため、must と噛み合わずアクションが生まれないことがある。 そこで、will を一度抽象化(例:レガシーコードを刷新したい → 意思決定がしたい)してから具体化(テストカバレッジ改善など)する また、目標設定に失敗してしまうパターンとして - 経営がうまくいっている企業では、分かりやすいストレッチな目標が減り、インパクトの弱い仕事を選んでしまう -「退屈だが自分の価値が出せる仕事」は魅力的で選びがちだが、野心が削がれ、低い目標設定につながる - 忙しさを理由に「できなかった」を発生させてしまう という話があり、目標を立てるのが苦手な自分にはかなり耳の痛い話でした。 これらへの対策として、 メンバー間の 目標の可視化 裁量範囲の言語化 と権限委譲(「どこまでやっていいか分からない」をなくす) span of control の上限 を意識し、マネージャーがボトルネックにならない仕組み が挙げられていました。 組織・文化・技術の壁に挫折したEMが、アーキテクトとして「構造化思考」を手に、再び保守開発組織の変革に取り組む ピープルマネジメントに注力すればよいと思っていたが、 コード品質の悪さとチームの疲弊 という構造的な問題に直面し、仕様やテストの重要性を説くも場当たり的対応に終わり、撤退を選んだ経験から、EM がアーキテクトとして「構造化思考」を手に再挑戦する話でした。 また、 AI は増幅器である というお話もありました。属人的プロセスやレガシーを放置したまま AI を導入すると、カオスと技術的負債が爆発する。是正すべきは次の3つの「モデルのずれ」です。 How 領域のずれ 実装工程を AI に任せるなら、AI がコードを実装するワークフローをエンジニアリングする。 What/Why 領域のずれ 要求を整合させ、価値あるものを創るために、要求の3階層(ステークホルダ要求・ビジネス要求・ソリューション要求)を意識する。 組織境界とシステム間のずれ 境界を見極めてモジュールとして切り出す。 とにかく思いついたら図にして共有する 変化を起こすには、 組織・チームの目標に据えなければ「余力で進める」からは逃れられない 。EM が本気で取り組まなければ進まない。属人的でその場しのぎの対応に終わらせない。「EMはビジネス・システム・チーム・技術の関係(構造)をデザインせよ」は常に心に留めておきたいと思いました。 まとめ EM歴9ヶ月の状態で参加した今回のEMConfでしたが、参加前までは開発組織の抱える課題に対して、EMという立場からどのように立ち向かっていけばよいか分からない状態でした。今回様々な知見を得られたことで、そういったぼんやりとした状態から少し脱することができ、また自身がEMとしてやりきれていない事が浮き彫りになりました。特に、「自律して動いており開発生産性の高い組織」とはどのようなものかについては、セッションごとに共通しているテーマがあり、私自身としては「権限委譲が適切にできていること」「目標がはっきりしていること」だと感じました。 直近でAIによりソフトウェア開発の根本が全く変わってしまったこと、スタメン内部の開発組織の構造も大きく変革が進む中で、いかに組織の課題解決のために時間を使っていけるかが、組織・自分自身ともに勝負だと感じたので、今回のカンファレンスで得られた知見を具体的なアクションに落としこんでいければと思います。 herp.careers
はじめに:3ヶ月目の今、あえて途中経過を書く理由 スタメンのQAエンジニア、にーくらです。スタメン初のQAエンジニアとして入社して3ヶ月。まだ成果が出てきているフェーズではありませんが、ここで立ち上げ期の思考や試行錯誤を後で振り返るためにも残しておきたいと思います。ですので、本記事は成功事例ではなく、現在試行錯誤しながら行なっていることの話になります。 現在スタメンでは東京オフィスで積極採用中で、個人の努力だけでは回らない局面が見え始めていました。プロダクトや開発、意思決定のスピードが上がる一方で、属人化や判断基準のばらつきが課題となりつつありました。そこで必要とされたのが「QA」という役割です。 QAの役割と品質への考え方 ソフトウェア開発において、QA(品質保証)は単に検証活動を行うだけの役割ではありません。しばしばQAは「テストをする人たち」と誤解されがちですが、本質的な役割はもっと広く、深いものだと信じています。 品質とは、決して「テスト工程」だけに宿るものではありません。品質は次のような日々の積み重ねによって形作られます。 日々の判断 プロセスの設計と運用 暗黙の前提や共通理解 言い換えれば、品質は日々の活動や意思決定の中に自然と組み込まれていくべきものと考えています。だからこそ、品質を高めるためには 「仕組み」「プロセス」「文化」 の3つが極めて重要だと考えています。仕組みは作業を支え、プロセスは効率と一貫性を生み、文化はチーム全体の行動と価値観を方向付けます。これらが整って初めて、個々のテストやレビュー以上の品質が組織として実現できるのです。 プロセスを組織全体に浸透させるには、個人の努力だけでは限界があります。自身が品質保証を実際に取り組む中で、 組織としての意志と後押しの重要性 を感じていました。 プロセスを定着させるには、組織として明確な方針を持ち、積極的に後押しする姿勢が不可欠です。トップやマネジメントの支援がなければ、現場の努力だけでは継続が難しいのです。 今回転職活動をする中で、組織としての支援がどれだけ得られるのかを考えていました。その中でスタメンはQAを単なる個人の作業ではなく、組織的な役割として認識していると感じました。スタメンは人と組織で勝つ会社と明確に謳っているので、その組織の姿勢に共感し、「ここなら一緒にやれる」と思えたのです。 入社して見えた現実 入社して感じたのは、個々人が本気でプロダクトに向き合っていることでした。一方で、品質を横断的に見る仕組みや検証活動の整理・体系化はほぼ実行していないと感じました。しかし、これを否定的に捉えるのではなく、むしろ伸び代が大きい状態だと捉えています。これらの課題に取り組むことで、プロダクトの品質向上と効率化を図り、さらに良い成果を生み出せるものと確信しています。 組織に新しい考え方やフレームワークを導入する際、どこかで成功しているからと、そのまま持ち込むだけでは浸透しないと過去の経験から感じています。組織にその意味を説き、やり方を調整したテーラリングが不可欠です。まずは現実に何が起きているかを観察し、開発の流れを理解することを優先しました。組織で何が起きているのか、その際に必要なフレームワークやどこから浸透させて行くか作戦を練っていく必要があります。 入社後いくつか品質を上げるための課題と提案を進めて行きましたが、それを実行するためには関係者が多く、その人たちと合意していかないと進んでいかないもどかしさを感じていました。そのような悩みをCPOと話していく中で、プロダクト品質・プロセスに関する定例ミーティングが立ち上がることになりました。 当初は、分析に必要なデータも十分に揃っておらず、これをやった方がいいと提案から始まり、ミーティングはQAの私が一人で運営していく形。これでは避けたかったただの「他社の成功事例の持ち込み」になっていないか?と考えていました。 「品質文化」という考え方 品質を上げることで、どんないいことがあるか、どういう状態に持って行きたいか改めて伝えたいと思いました。イメージとしては全員参加で品質を上げていく組織、それはQAとしてよく語られる「品質文化」の浸透という話に置き換えられます。しかし、いざ説明しようとするとその内容を的確に説明することができません。ISOの品質マネジメントに置き換えることもできそうだと思いましたが、あえて「品質文化」という言葉を使っているので、別の定義をしなければいけないと考えました。 そこで出会ったのが、西康晴氏の 「嬉しい、強い、すごい組織」 という考え方です。 What is quality culture? Is it something tasty? | PDF 品質文化とは、ソフトウェアの価値、組織能力、エンジニアリング力を上げる取り組みをし、習慣化させていくこと。組織で勝つ文化のスタメンに合いそうだとこの言葉を借りて、スタメンなりの品質文化を言語化し始めています。 次のステップは、開発プロセスの中に品質意識を溶け込ませることです。新しいルールを増やすのではなく、どこでどんな判断が行われているのかを問い直し、必要な仕組みを整えていきます。決して煩雑にすることが目的ではなく、判断を簡単に、属人化せず、その知識を蓄積して新たにステップアップできる方法ができないかと試行錯誤中です。 おわりに 単なる検証者で終わらず、品質という言葉を元に組織全員を束ねて行きたいと思っています。そして私は組織にそれを考える刺激を与えたいと思っています。道半ばではありますが、少しずつ変化が生まれています。これからも「組織で勝つ」ための品質づくりを続けていきたいと考えています。 note.com herp.careers
こんにちは!スタメンで プロダクトデザイナーをしている森田かすみ( @KasumiMorita )です。 先日、12月25日に、エンジニア・プロダクトマネージャー(PdM)・デザイナー合同の社内LT会を開催しました! 今回の記事では、当日の様子や発表内容、そして私たちスタメンのプロダクト部門が大切にしている「学び合う文化」についてご紹介します。 なぜ今、プロダクトLT会なのか 目まぐるしく変化するIT業界において、企業が競争力を維持し続けるためには、常に新しい知識を取り入れ、お互いに学び合う姿勢が不可欠です。 スタメンではそうした文化をより強固なものにするため、2025年7月から有志の現場メンバーの起案でプロダクト部門全体でLT会を始めました。今回はその第2回目となります。 tech.stmn.co.jp 開催の狙いは、 全員で高め合う学びの場づくり です。 仲間が増えると価値観やカルチャーに少しずつばらつきが出てくるのも自然なことではありますが、スタメンのプロダクト組織でも同じような課題感を持っていました。 今こそ、自分たちの組織を育てていく意識をみんなが持ち、 「こういうことをやっていきたいよね」 「こういうことを大切にしたいよね」 という思いを一人一人が大事にし、言葉や行動にしていく重要性を感じています。 特定の職種だけで完結するのではなく、部門全体の知識共有と交流を深め、プロダクト開発における連携を強化することを目的としています。メンバーがそれぞれの知見や想いを発表し、質疑応答を通じて相互理解を深める。 そうすることで、個人のスキルアップはもちろん、組織全体の成長に繋げていきたいと考えています。 そんな想いを元に、私含め、おたけ( @takeruu__ )、きいろ( @yellow_flagger )、ちぇる( @ryuseikarito )の4名で運営しています。 当日のラインナップ 通常業務や他のメインプロジェクトがある中、多くのメンバーが登壇してくれました! キーノートから技術的な知見、組織論、プロダクトの活用まで、バラエティに富んだ素晴らしい内容ばかりでした。 プログラム 【キーノート】リーダーシップ論 CPO 長田 寛司 AIネイティブ時代のプロダクト開発:全員がチームリーダーになる時代 エンジニアリングマネージャー あさしん( @asashin227 ) カイゼンは行動だ。迷ったら、少しだけ良くする プロダクト開発部 フロントエンドエンジニア 伊賀本 衛 「降りてくる」のを待たない。チーム目標のつくり方 プロダクトデザイナー 森田 かすみ ( @KasumiMorita ) テックブログを書くことで得られるもの プラットフォーム部 バックエンドエンジニア 勝間田 亮 自分がユーザーとして使い倒せるプロダクトだけど、TUNAG使ってますか? プロダクトマネージャー 中野 孝夫 Watchy Windows クライアントアーキテクチャ変更記 Watchy事業部 エンジニア おさじゅん TUNAGの開発に携われるのは、実はすごく恵まれていると思う話 エンジニアリングマネージャー まっきー 白熱した受賞結果発表! 今回は、発表内容に対して 「ベストチーム賞」「ベストCTO賞」「ベストCPO賞」 を設けました。 ベストチーム賞は全てのメンバーからの投票が多かったもの、ベストCTO賞・ベストCPO賞はそれぞれのCxO陣から選出されたセッションに贈られます。 それぞれの視点で選出された、栄えある受賞タイトルをご紹介します! 🏆 ベストCTO賞 「Watchyクライアント配布形態変更の概要」 (Watchy事業部 エンジニア おさじゅん) ▼LTの内容紹介 Watchy事業のエンジニアであるおさじゅんさんから、ストアアプリ特有の「低レイヤー制御の制限」や「企業導入の障壁」に対し、自由度の高いデスクトップアプリ化で挑んだ事例が発表されました。インストーラー実装や電子署名、Windowsサービス併用による権限管理といった技術課題を解決し、過去最高受注に繋げた経緯が共有していただきました。 技術的な挑戦と具体的な実装へのアプローチがCTOから高く評価されました。 🏆 ベストCPO賞 「自分がユーザーとして使い倒せるプロダクトだけど、TUNAG使ってますか?」 (プロダクトマネージャー 中野 孝夫) ▼LTの内容紹介 プロダクトマネージャーの中野さんから、自社プロダクト「TUNAG」の開発組織において、全社平均より利用率が低いという課題に対し、ドッグフーディング(自ら使い倒すこと)の重要性をLT内で説かれています。開発者自身が一番のファンとなり、利用を通じてユーザーの痛みを自分ごと化し、違和感を改善のチャンスに変えていこうと呼びかけられました。 自社プロダクトの活用状況をデータで一覧化し、参加しているメンバーたちへ活用を推進する姿勢が評価されました。 🏆 ベストチーム賞 今回はみんなからの投票の結果、同率で2つの発表が選ばれました! 「テックブログを書くことで得られるもの」 (プラットフォーム部バックエンドエンジニア 勝間田 亮) 「自分がユーザーとして使い倒せるプロダクトだけど、TUNAG使ってますか?」 (プロダクトマネージャー 中野 孝夫) 中野さんはCPO賞とのダブル受賞となりました!おめでとうございます🥳🎉 ▼LTの内容紹介 テックブログの運営チームの一人である勝間田さんによる、テックブログ執筆の効用を個人・組織の両面から説いたセッションです。「理解の深化」や「資産化」という個人メリットに加え、「高品質なドキュメント」や「技術・文化アピール」という組織メリットを提示。執筆は一石二鳥以上の価値があるとし、互いに称賛し合う文化と共に、積極的なアウトプットを推奨されました。 🎁 賞品について ベストCTO賞とCPO賞の受賞者には、それぞれCTO・CPOとのスペシャルランチが贈られます。 そしてベストチーム賞には、 セッションの内容を思い出せるような、TUNAGスタンプ が贈呈されます! 自社プロダクトであるTUNAG上で使えるスタンプとして、今回の学びが形に残るのはスタメンならではの賞品を今回は用意しました。 今回の開催における工夫 反省点もありますが、今回はプロダクトLT会の形骸化を防ぎ、前回との違いを明確にするため、新たにテーマ設定を行いました。 また、内容の質を高めるべく、運営主導のオファー制ではなく「プロポーザル(公募)形式」を採用しました。 1.テーマ設定 今回のテーマは 「未来への貢献」 です。 このLT会はボトムアップで始まりましたが、今回はCTOの野口さんとの雑談をきっかけにテーマを決めました。「将来的にどのような貢献につながるのか示された内容だといいですよね」というアドバイスをいただいたことがヒントになっています。 「みんなで組織を作っていく」というこの会の姿勢にぴったりで、メンバー全員が想いやナレッジを共有するのに最適なテーマだと思い、設定しました。 2. プロポーザル形式の導入 デザイナーやバックエンドエンジニアで構成された運営チームが他職種のメンバー全員に対し、個別に内容を詰めて登壇オファーを出すことは困難であるため、半年の振り返りも兼ねてプロポーザルを提出してもらう形式を採用しました。 提出項目には、タイトルと内容に加え、 「セッションを通して伝えたい意思」 を提出してもらいました。単なるナレッジ共有ならブログでも十分ですが、そこに「意思」が乗ることで、LT会ならではのストーリー性のあるプレゼンになるのではないか、と考えました。そんな仮説と期待を込めて、この項目を設けました。 3.チームによる選抜 多様な職能・部署の発表を聞けるよう、各職能チームもしくは部署内でプロポーザルを選考し、誰が代表として登壇するかを自分たちで決めてもらうフローを採用しました。 結果 これらの新しいアプローチを取り入れた結果、前回は特定の技術やツールなどの「How(どうやるか)」が中心だったのに対し、今回は「Why(なぜやるか)」「どうあるべきか」といった 組織文化やマインドセットに関するトピック が増えました。 また、今回は自社プロダクト「TUNAG」を取り扱うLTもいくつか見られ、 プロダクトへの熱量や当事者意識 が強く感じられる会になったと感じています。 半年間の振り返りとこれから 2025年7月に開催した 第1回のプロダクトLT会 をきっかけに、エンジニア向けやプロダクト企画部向けの社内勉強会を毎月1回以上(多い時は2回)のペースで開催することが定着してきました。 「継続的な勉強会の開催」という枠組みからスタートをしている取り組みではありますが、結果として半年で17人の登壇者、4人の勉強会運営者が誕生しました。 継続的に開催する仕組みがこの半年でできてきたことは、組織として大きな一歩として捉えています。 今後は、この仕組みをベースにしつつ、 自発的な学び合い文化 を内側からさらに醸成していくことに努めていきたいと思っています。 誰かから言われて学ぶのではなく、メンバー一人ひとりが主体的にインプットし、ナレッジを共有し、他者と学び合う。そんな組織を目指して、これからも様々な取り組みを行っていきます! 一緒に働く仲間を募集しています! スタメンでは、このように職種の垣根を超えて学び合い、プロダクト開発に熱狂できる仲間を募集しています。 少しでも興味を持っていただいた方は、ぜひ以下の採用ページをご覧ください。カジュアル面談からでも大歓迎です! herp.careers
はじめに こんにちは。スタメンでエンジニアをしております、 mental-space1532 と申します!今回は、昨年10月に配属されてからエンジニア3ヶ月でOSSに貢献した経験についてお伝えできればと思います。 早速ですが、私は元々プロダクト職ではありませんでした。現在新卒2年目ですが、当初はビジネス職として入社し、1年半ほどインサイドセールスとして勤務しておりました。要はエンジニアとしてはスタートラインに立ったばかりの人間です(大学も文系です!)。似た境遇の方々や、OSSへの貢献を検討されている方の参考になれば幸いです。 1. 何をやったのか 内容としては、 半角 @ が入力された場合のみ表示されていたメンション候補を、全角 @ 入力でも表示されるように改善 しました。一般に、チャットアプリでは @ を入力するとメンションの候補が表示されます。しかし、日本語IME環境では全角 @ がデフォルトで入力されることが多く、日本語ユーザーはメンションの度にいちいち入力方式を切り替えることを強いられていました。これを切り替えることなしに実行できるようにするというのが今回のPRの趣旨です。 ちなみに、技術スタックとしてはReact/TypeScriptですので、これを踏まえた上で以降を読んでいただけると幸いです! 2. 動機 ビジネス職時代、こちらのOSSチャットを使っていた際に、毎回入力切り替えを行わなければならず、「めんどくさいなこれ」と思ったことがきっかけです。 ビジネス職の経験がある方なら共感いただけるかと思いますが、セールスやカスタマーサクセスの現場では「お客様への即応性」が何よりも重要です。結果としてメンバー間での情報共有スピードが求められ、伝達漏れを防ぐためにメンションを多用します。個人的な体感ですが、ビジネス職のメンション使用頻度はプロダクト職の5〜10倍ほど多いように感じられます。 特に顕著な例では、毎朝の進捗共有で6〜7名のメンバーにメンションを飛ばす際、毎回「半角切り替え→ @ 入力→日本語切り替え→名前入力」を繰り返す必要がありました。これが地味にストレスだったのです。 当時の私は不便さを「仕様だから」と受け入れていました。エンジニアにチャレンジすることになった10月初週に「どうせやるなら本家のOSSに還元して、世界中のユーザーの体験を改善しよう!」と思い立ち、上司に相談し、社内の業務を行いつつ、AIを活用したコーディングで進めるという条件でOKをもらうことができました。 3. やったこと 初めてOSSにコントリビューションする上で、以下のようなフローで進める想定でした。 課題を言語化する(企画) GitHub CopilotのCoding Agentで自律的に実装させる(実装) 正しく動作するかローカルで検証しつつ変更箇所のコードを解読する(QAその1) 社内でレビューをいただく(QAその2) OSSのリポジトリでPRを作成し、メンテナーの方にレビューを依頼する(メンテナーレビュー) いただいたレビューを受けて対応し、マージ(貢献完了) 自社プロダクトへの適用(統合) 企画(やりたいことの言語化)〜初期実装 当初、そもそもPRってどうやって作るんだ・・・?というレベルだったので、手っ取り早くGithub CopilotのCoding Agentで200文字程度のプロンプトを投げて自律的に実装してもらう方法を取りました。そのプロンプトがこちらになります。 現在の仕様において、メンション付きのメッセージを送るためには半角のアットマーク(以下@)を入力し、自分でメンションしたいユーザーの名前を入力するか、表示される候補の中から選択する形になっている。 この状態を、@だけではなく、@でもできるようにしたい。すなわち、メッセージの編集欄に@を入力した際にもメンション候補が表示されるような状態に変更をしたい。 今見るとかなりシンプルなプロンプトですね・・・🙃 どのような仕様にするか記述していないので、Copilot先生も困ってしまったのではないかと思います。できたものをローカルで検証したところ早速問題が発生しました。全角 @ でメンション候補は出て挿入できるものの、2個目以降のメンション挿入ができない(メンション候補をクリックまではできる)のです。AI(CopilotのAgent Mode)に聞きつつコードをいじっても解決せず、詰まってしまいました。後から考えれば、この時の自分は構文を理解しようとせずに、漫然とAIに「これどうすればいいのですか?」という曖昧な形でプロンプトを投げてしまっていました。流石にこれはまずい、ということになって同僚に助言を求めたところ、半角と全角の文字の幅の違いによって挿入ができなくなっていることが判明し、フロント側の文字列挿入ロジックを修正することで「実装したい機能がとりあえず動く」という状態に持っていくことができました。 社内レビュー この段階で一度社内レビュー実施していただきました。この時、自分は「ちゃんと動くようになったし、これでOSSにPRを作成できる」と無邪気に考えていました。が、当然そんなうまくいくわけもなく、まだまだ問題が山積みになっていることが明らかになりました。具体的には、私がWebアプリの基本的な構造(責務の分離)を理解できておらず、Copilotが行ったバックエンドAPIの改変(バックエンドAPI側のメンションロジックに全角 @ を追加する修正)を見逃してしまっていたのです。結論としては、そもそもフロント側で半角 @ と全角 @ を共に正規表現としていたので、この改変は必要ないものでした。この社内レビューによって、フロントエンドとバックエンドAPIの責務の分離、個々のファイルがどのような仕事を担当しているのかをざっくりと理解することができました。 というわけでバックエンドの修正を元に戻してフロントエンドの修正するべき箇所をある程度理解した上で再度コードを吟味することになりました。ある程度当たりをつけた上で、バックエンドAPIの件のような不必要な改変がないかを確認しました。 社内レビューを経たことでより具体的に各コードについてAIに聞くことができるようになり、結果的によりシンプルに実装する方法を編み出すことができました。これらの工程を踏まえて最初に社内レビューをいただいたときよりもさらにコードの質を高めることができました。ここで再度社内レビューをいただき、OKをもらうことができたので、晴れてOSS本家のリポジトリでのPRを作成にトライしました! OSS本家でのPR作成〜マージ PRの説明文の書き方なども何も分かっていなかったのですが、幸いにも規定のフォーマットが存在したので、AIに自分の拙い英文を校正してもらいながら何とかメンテナーの方からレビューをいただける状態にしました。そして、ついにコメントが返ってきました!曰く、「その共通コンポーネントの変更、本当に必要?」とのことで、共通UIはコマンド入力などにも使われるため、そこにメンション特有のロジックを入れるのは設計として美しくないし、影響範囲が広すぎるという指摘でした。 そこでメンテナーの方から 「UI側をいじるのではなく、データを供給する側が情報を正しく伝えれば良い」 というヒントをいただき、最終的に以下の修正に落ち着きました。 正規表現を修正し、 @ と @ の両方をキャプチャできるようにする 関数に実際にマッチした文字列( matchedPretext )を渡すよう引数を追加する このことにより、さらにシンプルに同じ機能を実装することができました。 また、海外のプロジェクトであるためPRを英語で書く苦労もありましたが、Geminiに添削してもらうことで乗り切ることができました。一見無理難題に思えることも、分解して各個撃破すれば意外といけるものです。まさに「困難は分割せよ」を実感した瞬間でした! 4. 元ビジネス職という点からプロダクトに貢献できること 弊社のようなSaaS企業ではドッグフーディング(自社製品を自ら使うこと)が推奨されますが、作り手側は「使い慣れている」がゆえに、ユーザーの多様な使い方を見落とすことがあります。 特に、職種によって機能の使用頻度やコンテキストは大きく異なります。元ビジネス職のエンジニアは、 プロダクト職とは異なる、現場ならではの切実な使い方を知っている お客様に近い立場からこぼれる「小さな不満」を拾いやすい という2点において、UX向上に大きく寄与できるアドバンテージがあると感じています。 5. まとめ ここまで書いてきましたが、伝えたいことはシンプルです! 一見理解不能なエラーも、試行錯誤を繰り返せば必ず糸口が見える。まずはやってみよう! 「元ビジネス職」という視点は、エンジニアリングにおいて強力な武器になる このエントリが、OSSへのコントリビューションを迷っている方の背中を押すきっかけになれば幸いです。 最後に 株式会社スタメンではエンジニアを絶賛募集しております!弊社には、AI活用・新人から様々なことにチャレンジできる文化が整っています。もし興味を持っていただけるなら、ぜひ一度カジュアルにお話しいたしましょう! herp.careers 最後まで読んでいただきまして、誠にありがとうございました!!!
はじめに はじめまして! 2026年1月から、株式会社スタメンにモバイルエンジニアとしてジョインしました、りあたそ (@riataso_kebin) です! 前職では約2年半、インフラ構築からバックエンド開発まで、サーバーサイド全般のシステム開発・保守を中心とした業務に携わってきました。 今回はサーバーサイドでのキャリアからなぜ、実務未経験のモバイル領域に転身したのか、 なぜ数ある企業の中からスタメンを選んだのかなど、少しでもスタメンのことが気になっている方向けに書かせていただきました! なぜ、サーバーサイドからモバイルへ? これまでの約2年半、サーバーサイドのエンジニアとして「システムの安定稼働」を支えることに心血を注いできました。インフラの構築やDB設計を練り上げる仕事には特有の面白さがありましたが、一方で 「ユーザーが直接触れる部分(フロントエンド)を自分の手で形にしてみたい」 という想いが次第に強くなっていきました。 その大きなきっかけとなったのが、 個人開発 です。 自分が日常で「欲しい」と感じた機能をアプリとして形にしてみた際、指先ひとつでUIが動き、体験が完結するモバイルアプリ特有の「手触り感」に、これまでにないワクワクを覚えました。 「裏側の仕組みを理解している強みを活かしつつ、このフロントエンドの楽しさを仕事として追求したい」。そのピュアな好奇心が、未経験領域であるモバイルへの転身を後押ししてくれました。 「名前を知っている会社」から「働きたい場所」へ 実は、スタメンという会社の存在自体は、学生時代の就職活動中から知っていました。 そんなスタメンを「自分事」として深く意識するようになったのは、学生時代の友人から誘われた、ある勉強会がきっかけでした。 それが、スタメンが運営している 「mobile.stmn」 という勉強会です。 実際に参加してみると、そこには現場の第一線で活躍するエンジニアの方々がいました。お話しさせていただく中で感じたのは、 技術に対する真摯さと、何より「楽しんで開発している」という圧倒的なエネルギー です。 会話のテンポが良く、非常に心地よい「ノリ」の良さがありながらも、プロダクトへの熱い想いがひしひしと伝わってくる。 その雰囲気に触れ、「この人たちと一緒に開発ができたら、きっと楽しいだろうな」と直感的に感じたことを今でも鮮明に覚えています。 「当事者意識」を「称え合う」文化が決め手に 自社プロダクトを持つ企業は数多くありますが、私が最終的にスタメンを選んだ決め手は、プロダクトに対する 「当事者意識の強さ」 と、それを 「称え合う文化」 でした。 個人開発を経験したからこそ、プロダクトを「自分のもの」として育てていく大変さと喜びを実感していました。入社後、メンバーと交流を深める「Welcomeランチ」や、1ヶ月かけて社員の皆さんと写真を撮る「ルーキーズミッション」といった制度を通じて皆さんとお話しする中で、改めて確信したのは、全員が同じ方向を向き、プロダクトの成長を自分事として楽しんでいる空気感です。 特に、成果を全員で喜び合う 「スタカネ」 の文化は、まさに私が求めていた「チームでプロダクトを育てる理想の姿」そのものでした。 ※スタカネとは: 新機能のリリースや目標達成、あるいはメンバーの初仕事の完了など、ポジティブな成果があった際にベルを鳴らし、全員で拍手をして称え合うスタメン独自の文化です。 先日、念願の「スタカネ」を初めて鳴らすことができました!🔔 スタカネ(成果を称賛する文化)の投稿内容 入社2週間のリアル:未経験の壁と「AI」という心強い相棒 ジョインしてからまだ2週間。正直なところ、現在はモバイル開発の圧倒的な情報量とスピード感に食らいつく毎日の連続です。 個人開発とは異なり、多くのエンジニアの手によって磨き上げられてきた実務のコードベース。そこから実装の意図やロジックを正確に読み解く難しさに直面し、自分の現在地を痛感する瞬間も少なくありません。 しかし、そんな「壁」をポジティブに乗り越えていけるのが、スタメンの開発文化の面白いところです。特に驚いたのは、新しい技術やツールを使いこなすことへの貪欲さです。 現在、私はAIを「相棒」としてフル活用しています。スタメンではDevinやCursorといったツールが当たり前のように開発環境に組み込まれており、AIと対話しながらコードを書くことが文化として根付いています。 さらに、先輩社員のアドバイスを通じて、 「Before(現状のコードやエラー)とAfter(実現したい理想の状態)を明確に言語化して連携する」 というプロンプト(指示出し)のコツを学びました。これだけでAIの回答精度が劇的に向上し、複雑なロジックの読み解きや、詰まっていた課題がスルスルと解決していく体験は、まさに目から鱗でした。 「大変なこと」を「面白い課題」に変えてくれる仲間と、それを支える最新のツールがある。入社してわずか2週間ですが、この環境ならどこまでも成長していけるという確信を得ています。 これからの抱負:モバイル領域からプロジェクト開発全体へ 今後の目標は、まずはモバイルエンジニアとして自立し、一日も早くチームの戦力になることです。しかし、単に「モバイルのコードが書ける」だけでは終わりたくありません。 私が持っている約2年半のサーバーサイドの知識---DB設計やインフラ、APIの裏側の仕組みは、 モバイル開発においても必ず強力な武器になると信じています。 「裏側(サーバーサイド)を知っているからこそ、より効率的で堅牢なフロントエンドを構築できる」。そんな、領域を跨いだ視点を持つエンジニアとして、スタメンのプロダクトを技術面から力強く牽引できる存在を目指していきます 最後に:今の環境から一歩踏み出そうとしている方へ 周囲のメンバーが自分のことのように喜び、拍手で迎えてくれるあの温かい雰囲気は、入社して一番感動した瞬間かもしれません。 インフラやバックエンドの知識という「土台」を大切にしながら、モバイルエンジニアとしての「表現力」を磨き、一日も早くプロダクトの成長を力強く牽引できる存在を目指していきます。 もし、この記事を読んで「 スタメンの雰囲気をもう少し詳しく知りたい」「実際にどんな人たちが開発しているのか気になる」 と少しでも感じた方がいれば、 ぜひスタメンが開催している 勉強会(mobile.stmnなど) をチェックしてみてください。 私自身、勉強会で現場のエンジニアと直接話し、その熱量や空気感に触れたことが入社の大きなきっかけとなりました。 選考という形ではなく、まずはカジュアルな場でスタメンの「リアルな楽しさ」を感じていただけたら嬉しいです✨ 「今までのキャリアを武器にしつつ、新しい領域で自分の熱量を形にしたい」 そんな思いを抱いている方にとって、スタメンはきっと面白い挑戦ができる場所だと思います。 これからもたくさんのことを達成して、皆さんと一緒にスタカネを鳴らせるように頑張ります❗️ herp.careers