Slide 1

Slide 1 text

extension DC 2026 Day1 今時のフルスタック主導の iOSアプリ開発 AI に任せて、一人で回す。 2026 / 09 / 18 @nakajijapan © BaseMe Inc.

Slide 2

Slide 2 text

@nakajijapan Daichi Nakajima CTO / Software Engineer 🏢 Pepabo -> Cookpad -> Campfire -> DeNA -> Startup企業s -> KNOWLEDGEWORK -> BaseMe Shari ★113 PhotoSlider ★248

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

今日の話の流れ 1.一人のエンジニアが見ている範囲 2.AIを仮想チームとして置く 3.現状やれていること 4.まだ課題に上がっていること 5.まとめ © BaseMe Inc. 6

Slide 7

Slide 7 text

一人のエンジニアが見ている範囲 © BaseMe Inc. 7

Slide 8

Slide 8 text

モバイルエンジニアを起点に見る範囲 プロダクト開発から技術基盤まで、担当を分けて待てない スタートアップ 少人数 久々の開発 © BaseMe Inc. 8

Slide 9

Slide 9 text

モバイルエンジニアを起点に見る範囲 プロダクト開発から技術基盤まで、担当を分けて待てない Front-End Design Server Side Security Infrastructure Mobile © BaseMe Inc. 9

Slide 10

Slide 10 text

モバイルエンジニアを起点に見る範囲 幅広く対応 Front-End Design Server Side Security PdM Infrastructure Mission Mobile Web版をベースにして、iOSは 別軸でモバイルらしい体験を作る © BaseMe Inc. 10

Slide 11

Slide 11 text

プロダクトを支えるメンバー それぞれ軸を持ちつつも、職域を越境して開発 © BaseMe Inc. 11

Slide 12

Slide 12 text

1 つの機能が、全部の層をまたぐ 二段階認証。iOS・Web・サーバー・インフラを、一人で対応 1 4 iOS パスワードや Apple で アプリ ログインする Web パスワードや Apple で ブラウザ ログインする 6 ログイン完了。 6 桁を入力する 信頼した端末は次回省略 ログイン完了。 6 桁を入力する 2 信頼した端末は次回省略 5 二段階が有効なら、 サーバー 確かめて、 本物の鍵は渡さず API 本物の鍵を渡す 短い合言葉だけ返す 3 インフラ SMS で 6 桁を送る SMS を送る土台と鍵の置き場 守りと計測 同じ機能の中で 入力と電話登録の 機能フラグで段階的に出す。 電話を失っても 有効にした数と失敗を 回数を絞る 古いアプリを締め出さない 控えの番号で戻れる 数える どの層が欠けても成り立たない機能は、全部の層を一人で触って通す。 © BaseMe Inc. 12

Slide 13

Slide 13 text

開発スケジュール 239 ios/ のコミット数 240 188 180 120 60 22 0 4月 5月 6月 足場を置いた月 8.5倍 App Store 設計に時間を消費し、開発を加速度的に上げていく © BaseMe Inc.

Slide 14

Slide 14 text

AIを、仮想チームとして置く © BaseMe Inc. 14

Slide 15

Slide 15 text

⚫︎ ⚫︎ ⚫︎ ⚫︎ ⚫︎ AIを、仮想チームとして置く 一人で全部を抱えず、AIに役割を持たせる。 ただし、AIを曖昧に「任せる」のではない。 各役割に次の4点を持たせる。 入力、完了条件、検証、引き継ぎ 仕様判断や体験の良し悪しは人が決める © BaseMe Inc. 15

Slide 16

Slide 16 text

役割ごとに、4 点を決める 曖昧に「任せる」のではなく、役ごとに決めておく 何を iOS の実装パイプラインでは 入力は何か planner には 1〜4 行の依頼。builder には仕様書と設計 何を出力させるか planner は仕様書と受け入れ基準。builder は実装と自己評価 何で正しさを確かめるか evaluator が、ビルド・テスト・シミュレータで受け入れ基準ごとに pass / fail どこで人に戻すか 同じスプリントで 3 回 fail したら、人に上げる この 4 点が決まったとき、AI は仮想チームとして機能する © BaseMe Inc. 16

Slide 17

Slide 17 text

AIに渡す仕事を、4層に分ける 4 つの層。上ほど毎回、下ほど動かして確かめる 毎回読む CLAUDE.md rules セッションのたびに 10 節 23 本 / 由来 17 必要なとき読む 自前スキル 借りたスキル 12 52 / ハッシュ固定 作業に合わせて 役割として動く orchestrator が回す 動かして確かめる シミュレータ・テスト planner architect builder evaluator fixer DebugHarness テスト E2E 21 ケース 2,266 本 夜間 ここまでは、たぶん皆さんもやっています。 © BaseMe Inc. 17

Slide 18

Slide 18 text

AIに渡す仕事を、4層に分ける 4 つの層。上ほど毎回、下ほど動かして確かめる 毎回読む CLAUDE.md rules セッションのたびに 10 節 23 本 / 由来 17 必要なとき読む 自前スキル 借りたスキル 12 52 / ハッシュ固定 作業に合わせて 役割として動く orchestrator が回す 動かして確かめる シミュレータ・テスト planner architect builder evaluator fixer DebugHarness テスト E2E 21 ケース 2,266 本 夜間 ここまでは、たぶん皆さんもやっています。 © BaseMe Inc. 18

Slide 19

Slide 19 text

自前スキル 12 本は、開発の流れに沿って置いてある どの段で、何を AI に読ませるか。借り物の 52 本は、この外側 作る ビルドする 動かして見る 遅い・固まる・落ちる PR を出す App Store へ generate-feature xcodebuild-baseme ios-simulator-skill swiftui-perf-investigate pr-create-draft appstore-release 画面を MVVM の型で一括生成 正しいビルドの打ち方。罠の対処つき シミュレータを触る・撮る・読む Instruments で追う手順 PR は必ず draft で。テンプレを埋める 提出から公開まで design-to-swiftui review-ios Figma やスクショから SwiftUI へ iOS の PR を見る観点 api-client pr-review-triage サーバー API の通信コード bot の指摘を鵜呑みにしない markdown-render-parity markdown の描画 3 経路を揃える web-parity-audit Web の機能を移すときの調査表 上の段が、開発の流れ。下が、その段で AI に読ませる自前のスキル。数は 12 本。 どれも、一度踏んだ手順を、次に AI が同じ手順で踏めるように書いたもの。 © BaseMe Inc. 19

Slide 20

Slide 20 text

誰が、いつ読むか 4 つの層。上ほど毎回、下ほど動かして確かめる 毎回読む CLAUDE.md rules セッションのたびに 10 節 23 本 / 由来 17 必要なとき読む 自前スキル 借りたスキル 12 52 / ハッシュ固定 作業に合わせて 役割として動く orchestrator が回す 動かして確かめる シミュレータ・テスト planner architect builder evaluator fixer DebugHarness テスト E2E 21 ケース 2,266 本 夜間 ここまでは、たぶん皆さんもやっています。 © BaseMe Inc. 20

Slide 21

Slide 21 text

iOS の実装は、役割を分けて回す © BaseMe Inc. 21

Slide 22

Slide 22 text

iOS の実装は、役割を分けて回す planner から fixer まで。渡すものと、fail のときに戻る場所を決めてある orchestrator(司令塔) 順番に回し、pass か fail を見て次を決める スプリント。1 機能ずつ、この中を回る 自分 1〜4 行の依頼 planner architect 依頼を仕様書に広げる。 受け入れ基準まで書く 仕様書 builder 仕様書から設計。 iOS とサーバーの API。 コードは書かない evaluator 1 機能を実装。 シミュレータで動かして 自己評価を出す ビルドとテスト。 受け入れ基準ごとに pass か fail 次の機能へ pass 全部 pass したら PR。 merge するのは自分 設計書 fail fixer 3 回 fail 3回 evaluator に戻る 最小の変更で直して、 evaluator に戻す 役では直せない。 人間(自分)に戻す Claude Code のサブエージェント。ios/.claude/agents に置いてある。ここまでは役に任せられる。 © BaseMe Inc. 22

Slide 23

Slide 23 text

iOS Orchestrator © BaseMe Inc.

Slide 24

Slide 24 text

© BaseMe Inc. 仮想プロダクトチーム

Slide 25

Slide 25 text

事前に設計を作る © BaseMe Inc. 25

Slide 26

Slide 26 text

アーキテクチャ 5 つの層。数字は 現在の実数 MVVM View 画面。表示だけ Design System SwiftUI の画面。状態も処理も持たない。 共通部品は右の Design System から取る ViewModel 状態と処理 Analytics ViewModel 48 本。状態は @Observable(49 ファイル)。 画面が何を出すかは、ここで決まる 通信 サーバーとのやりとり Test 確かめる側 BM* の共通部品 15 種。 色・余白・字はトークンから。画面をまたいで揃える PostHog にイベント(定義 34 種)とセッション録画。 Crashlytics に落ちた記録。dSYM は CI から Realtime API の型は swagger から自動生成。手で書かない。 チャットの本文は SSE、カードの進捗は ActionCable(右) SSE でチャットの本文を流す。 WebSocket でカードの進捗を押す Unit と UI で 282 ファイル・2,266 本。#Preview 334 のうち共通部品はスナップショットへ。 VRT は S3 に基準画像、E2E は夜間。AI が書いた差分は、ここを通って初めて信じる © BaseMe Inc. 26

Slide 27

Slide 27 text

モバイル側(iOS) ここは自分で設計しました。既製のライブラリは入れていません。 受け口 URLSessionDataDelegate .lines は使わない チャンクをそのまま受ける 行ごとに溜まると流れて見えない SSEParser テストできる形にする 仕様どおりに組む protocol で切り出す テスト 10 ファイル・1,447 行 つなぎっぱなし ActionCableClient WebSocketを閉じ込める 状態を1列に並べる unchecked Sendable なし 生のバイトで受ける 解析 actor 1本 切れたとき 自動で戻す 指数バックオフ ping を見張る 購読の一覧は捨てない 最大 30 秒 6秒来なかったら切れたとみなす 戻ってきたらつなぎ直す © BaseMe Inc. 27

Slide 28

Slide 28 text

登録した当日の体験を直した、1 か月 登録した当日に、どこまで使ってもらえるか。赤い帯は、つまずいたところ 8月上旬 分析 登録した日に何をした人が、7 日後にまた開いているかを調べた 8月上旬 疑う役が差し戻し 直すべき点を 7 つ挙げて、分析を突き返した 8月上旬 判断 条件つきで採用。登録より先に、Ken と話してもらう案 8月中旬 実装 入力なしで始まる新しい入口を作った。68 コミット 8月末 実験に移す A/B の振り分けが、アプリの画面に届いていなかった。直した 8月末 指標を入れる 効果を測る指標が 1 つも無かった。3 つ足した 9月頭 読み直し 指摘どおりに集計し直しても、差は残った 分析から指標まで、こういうのも自分でやっています。間違えても、ここまでは回ります。 © BaseMe Inc. 28

Slide 29

Slide 29 text

ミスを0にするのではなく、直すまでの俊敏さ 設計を先に行っても、ビルドもテストも通る場所でミスは残る。狙うのはゼロではなく、一人で見つけて直せる俊敏さ 1. 振り分けが、画面に届いていなかった 前 サーバー A / B を決める × 2. 効果を測る指標が、1 つも無かった アプリの画面 結果を見ていない 前 だから、A と B の出し分けが起きない 後 実験 2 指標: 0 個 動かしても、効いたかどうかが読めない PostHog の実験 アプリの画面 振り分けを決める A と B を出し分け 振り分けを PostHog の実験に載せ替えた 実験 1 後 イベントを 指標を組む 3 つ足す 効果が読める 測るためのイベントを 3 つ足して、指標を組んだ 見つけて直すところまで一人で回る。 © BaseMe Inc. 29

Slide 30

Slide 30 text

現状やれていること AIがやりそうなことに遭遇し対策する © BaseMe Inc. 30

Slide 31

Slide 31 text

現状やれていること、4 本 この章が発表の中心 何を 一言で 4-1 体験をつくる 動かして、計測して、初めて分かる 4-2 正しく確かめる 古いアプリを見ない。推測で直す前に測る。どちらも失敗から rule にした 4-3 境界をつなぐ API の型は生成する。約束がなければ、AI の型でも壊れる 4-4 経験を次の AI 入力へ戻す 失敗を rule・skill・CI の関門に戻す AI が速く書くほど、確認・約束・学習の仕組みが重要になる © BaseMe Inc. 31

Slide 32

Slide 32 text

4-1 体験をつくる 何を 一言で 4-1 体験をつくる 動かして、計測して、初めて分かる 4-2 正しく確かめる 古いアプリを見ない。推測で直す前に測る。どちらも失敗から rule にした 4-3 境界をつなぐ API の型は生成する。約束がなければ、AI の型でも壊れる 4-4 経験を次の AI 入力へ戻す 失敗を rule・skill・CI の関門に戻す AI が速く書くほど、確認・約束・学習の仕組みが重要になる © BaseMe Inc. 32

Slide 33

Slide 33 text

AI チャットが、16 秒固まった ユーザーがやったことと、起きたこと 1 2 3 4 Ken と、長くやりとりする 答えの最後に、選択肢のチップが 出る 画面が止まる。16.23 秒 もう 1 往復すると 答えは文字が流れて届く。 1 文字ずつ画面が更新される どれかをタップする 何も押せない。 Instruments では Severe Hang CPU が 100% に張り付く。 そのまま落ちることもある 止まっていた時間 0秒 16.23 秒 この間、指が効かない 起きた構成 SwiftUI AI と一緒に作った画面。 コードを読んでも分からない。動かして、初めて分かった。 @Observable SSE ストリーミング どれも普通。特別なことはしていない コードを読んでも分からなかった。動かして、初めて分かった。 © BaseMe Inc. 33

Slide 34

Slide 34 text

Time Profiler の答え。自分のコードは 0.5% Time Profiler は、時間を細かく刻んで「そのとき動いていた関数」を数える道具。誰が呼んだかは数えない 固まっていた 16 秒の間、メインスレッドは誰の関数を動かしていたか 0.5% 自分が書いた関数 99.5% 自分の関数が直接使った時間の合計。どれも軽い SwiftUI の中の関数 レイアウトの計算に、ほとんどの時間が消えている Time Profiler で分かること Time Profiler で分からないこと 誰が時間を使ったか。 答えは「自分の関数ではなく、SwiftUI のレイアウト計算」。 誰がそれを何度も呼んだか。 自分のどの書き方が引き金なのかは、ここからは読めない。 行き止まり 次の道具 自分のコードを削っても直らない。削る場所が無い 「誰が呼んだか」の積み重ねが見える Hangs へ。次のスライド 自分のコードを削っても、直らない。ここで一度、行き止まりになった。 © BaseMe Inc. 34

Slide 35

Slide 35 text

出てきた名前は、誰の処理か Hangs が出した呼び出しの積み重ね。上が画面を出す土台、下へ行くほど自分の選んだ部品に近づく 呼ばれた順 画面を出す土台 1 回ぶんの画面の変更を、まとめて確定させる CA::Transaction::commit UIKit と SwiftUI の橋 UIHostingView.layoutSubviews SwiftUI の中心 AG::Graph::UpdateStack::update LazyVStack の位置計算 LazySubviewPlacements.placeSubviews 高さのキャッシュを捨てる LazyLayoutViewCache.invalidateSize Apple の中。 自分では触れない 並べ直しの指示が来るたび、SwiftUI 全体のレイアウトが走 る View・状態・レイアウトの依存の網。1 つ変わると、つなが る節点を全部更新 子の位置と高さを決める。Lazy でも、位置は全部の子で計算 する 1 つ変わると、全部捨てる。これが何度も呼ばれていた 自分が選んだ入れ物。 LazyVStack の中 犯人 犯人は自分の関数ではなく、自分が選んだ入れ物だった。 © BaseMe Inc. 35

Slide 36

Slide 36 text

1 文字届くたびに、全部測り直していた 「1 つ変わる」とは、最後の吹き出しが 1 文字ぶん伸びること。入れ物によって、そのあとの反応が違う LazyVStack(前) 起きること(どちらも同じ) チャットの画面。答えは 1 文字ずつ届く 測り直し 1 つ変わると 測り直し 測り直し 測り直し 高さのメモを全部捨てて、 全部測り直す 16 秒 止まる 測り直し 測り直し +1 測り直し 答えの途中の吹き出し… 1 文字ごとに、全部。 長い答えほど回数が増えて、止まる 測り直し ↑ 1 文字ぶん、高さが伸びる VStack(後) これが「1 つ変わる」。 1 つ変わると 最後の吹き出しの高さが、 変わった 1 つだけ測り直す。 ほかはそのまま 1 文字届くたびに変わる。 長い答えなら、何百回も起きる 止まらない 1 文字ごとに、1 個ぶん。 回数が増えても軽い 測り直し 16 秒を消したのは、入れ物を変えた 1 語。 LazyVStack { VStack { Lazy の利点は「見える分だけ作る」こと。数十件のチャットでは効かない。数百を超えるなら List を使う 大量で固定のViewを扱うならLazyVStack 可変高Markdownが更新されるため、実測では VStack の方が滑らか © BaseMe Inc. これが宗派y区数千になってくるとまた別問題 36

Slide 37

Slide 37 text

体験品質の確認方法 UI とアニメーションで、決めていること 3 つ 01 02 03 静止画で判定しない Profiler を取る 計測の道具を疑う 固まった 16 秒、メインスレッドは誰の関数を動かしていたか 指のスワイプ(0.4 秒) × アプリの録画 idb の合成スワイプ 正解(デザイナーの動画) 0.5% 自分の関数 99.5% SwiftUI の中 6ms に固まって届く 止まった後の 1枚 コマごとに要素の位置を追い、2 本を並べる Time Profiler Hangs 誰が時間を使ったか 誰が何度も呼んだか 画面: ほぼ動かない メインスレッド: 暇 6.8 秒で 3 コマ ほぼ待ち状態 矛盾。凍ったのはアプリではなく、道具の嘘 止まった後の 1 枚は、何も証明しない。 録画をコマに分解して、要素の位置を毎コマ追う。 正解の動画も同じ方法で測る。仕様の数字はそこから出る simctl recordVideo → ffmpeg / ffprobe でコマに分解 読んでも分からなかった。疑う前に、測る。 時間の 99.5% は、自分の関数の外だった。 道具を 2 つ重ねて、初めて犯人に届いた Instruments: Time Profiler → Hangs 計測が矛盾したら、まず道具を疑う。 合成スワイプはタッチを 6ms に固めて届ける。 実時間で動くドラッグに替えて、測り直した idb → ホストから実時間のドラッグ / 実機で触る ここは今も、自分で確かめています。 © BaseMe Inc. 37

Slide 38

Slide 38 text

4-2 正しく確かめる 2 本目。例は 2 つ。見ていたアプリが古かった話と、見ていた値が古かった話 何を 一言で 4-1 体験をつくる 動かして、計測して、初めて分かる 4-2 正しく確かめる 古いアプリを見ない。推測で直す前に測る。どちらも失敗から rule にした 4-3 境界をつなぐ API の型は生成する。約束がなければ、AI の型でも壊れる 4-4 経験を次の AI 入力へ戻す 失敗を rule・skill・CI の関門に戻す AI が速く書くほど、確認・約束・学習の仕組みが重要になる © BaseMe Inc. 38

Slide 39

Slide 39 text

古いアプリを見て、実在しない不具合を追った ノートの背景色を直したのに、「まだ白い」と言われ続けた コード 新 シミュレータ ビルド #F9F9FC ✓ 新しい色を書いた BUILD SUCCEEDED × エラーは出ない 旧 #FAFAFA 古い app のまま 入れ直していない 「直した」 「直っていない」 見ていたのは、コードとビルド 見ていたのは、シミュレータの画面 AI 確かめる側 AI は「直した」と言い、確かめる側は「直っていない」と言う。どちらも嘘はついていない。 見ていたものが違った! © BaseMe Inc. 39

Slide 40

Slide 40 text

直したのは、アプリではなく AI が読む手順 アプリのコードは 1 行も変えていない。変えたのは、AI が読む 4 ファイル AI が読む手順 アプリのコード + 消す 変えた のは 0行 変えていない ビルド 入れ直す 測る ここが抜けていた 目ではなくピクセルで + rule + skill 消して、ビルドして、入れ直してから測る。鮮度は 1 点で確か める 色は目で見ない。ピクセルで測る + evaluator + fixer 検証の前に、必ず鮮度の関門を通す 直した後の再検証も、同じ関門を通す 検証 BUILD SUCCEEDED でも、1 ファイルもコンパイルしていないことがある。それも rule に書いた。 © BaseMe Inc. 40

Slide 41

Slide 41 text

もう 1 つ。アイコンを変えたのに、drawer だけ古い 設定でアイコンを変えると、横から出るメニュー(drawer)にも同じアイコンが出る。6 月、「登録できない」と報告が来た 1 設定でアイコンを変える 新 保存 ✓ 2 設定画面は、新しいアイコンに なる 状態更新 ? drawer を開く 旧 表示 × 3 古いアイコンのまま 閉じて、もう一度開く 新 その時だけ、新しいアイコンに なる この時点の仮説 保存ではなく、状態更新から表示までのどこかで止まっている 止まっているのは、この間のどこか 見えていたのは「保存失敗」ではなく、「保存後に表示だけが追いつかない」だった。 © BaseMe Inc. 41

Slide 42

Slide 42 text

3 回推測で外して、1 回の計測で当てた 推測を試すたびに、実機の確認で 6〜22 分。だから、ログを 3 か所に置いて 1 回で測った 推測で直す 1 閉じる処理を消してみた 測ってから直す × 外れ 1 実機で確かめるのに 6〜22 分 2 状態を直接読んでみた 画面の一番上で読んでみた 届いた ログを 1 行入れて、1 回動かす × 外れ 2 実機で確かめるのに 6〜22 分 3 更新は届いているか 再描画は走っているか 走っていない ログを 1 行入れて、1 回動かす × 外れ 実機で確かめるのに 6〜22 分 3 なぜ走らないか 「変化なし」と判定 ログを 1 行入れて、1 回動かす 3 回やって、何も分からなかった 1 回で、原因に届いた 推測 1 回ごとに、実機の確認で時間だけが減っていく id が同じで URL だけ違うと、「変化なし」と判定されていた 推測で直す前に、1 回測る。これがそのまま rule になった。 © BaseMe Inc. 42

Slide 43

Slide 43 text

直したのは、比べ方 同じ人なら変化なし、ではなく、同じ中身なら変化なし、にした 前 id だけ比べる 後 いま表示している 保存された 全部を比べる いま表示している 保存された id 42 id 42 id 42 id 42 name Nakajima name Nakajima name Nakajima name Nakajima url /old.png url /new.png url /old.png url /new.png id だけ見る 42 = 42 → 変化なし id・name・url を全部見る url が違う → 変化あり URL の違いは見ていない id が同じでも、中身が違えば描き直す → drawer は描き直さない → drawer が新しくなる 直したのは 1 行。見つけるのに 3 回外して、測ったら 1 回だった。 © BaseMe Inc. 43

Slide 44

Slide 44 text

4-3 境界をつなぐ 何を 一言で 4-1 体験をつくる 動かして、計測して、初めて分かる 4-2 正しく確かめる 古いアプリを見ない。推測で直す前に測る。どちらも失敗から rule にした 4-3 境界をつなぐ API の型は生成する。約束がなければ、AI の型でも壊れる 4-4 経験を次の AI 入力へ戻す 失敗を rule・skill・CI の関門に戻す AI が速く書くほど、確認・約束・学習の仕組みが重要になる © BaseMe Inc. 44

Slide 45

Slide 45 text

実装のずれは、2 通りで起きた どちらも、手元ではビルドもテストも通る。見た目では分からない ケース 1 サーバーだけが進む サーバー側 iOS の生成物 size を 数字 に直した size は 文字列 のまま 前は文字列だった。仕様を正した ケース 2 生成し直していない。古い 本番に出ていたら ビルド ✓ テスト ✓ 数字を文字列に 入れられず、落ちる AI が、生成したファイルに手で足す 生成したファイル(手で編集しない、と書いてある) 次に生成し直した瞬間 struct Suggestion: Codable + Equatable + var title + = nil 動く 見た目は何も問題ない AI が「便利だから」と足した 手で足した分が消える。 消えたところを使っていると壊れる どちらも、型としては正しい Swift。 コンパイラは文句を言わない。 テストも通る。 だから、人は気づかない。 人が動かした差分も、AI が足した差分も、コンパイラは文句を言わない。 © BaseMe Inc. 45

Slide 46

Slide 46 text

手で書ける場所を、無くした 型の正はサーバーに 1 か所。iOS 側は生成するだけ。手で触ったら CI が止める 型の流れ サーバーのテスト swagger.yaml 生成スクリプト Generated/*.swift 型の正はここ。 レスポンスの形を書く テストから生成。 手で編集しない swagger を読んで Swift を書き出す 先頭に 「手で編集しない」 止める場所 CI(iOS に変更があるたびに走る) 差分なし → 通る もう一度、生成する 手元の生成物とは無関係に、 CI が自分で作り直す 手元の生成物と比べる git diff を取るだけ 人が手で直した 1 文字も、 AI が足した 1 行も、 生成し忘れた 1 ファイルも、 ここで赤になる。 差分あり → 赤にする 人も AI も、生成物を直接編集しない。編集しても CI で赤になる。 © BaseMe Inc. 46

Slide 47

Slide 47 text

4-4 経験を次の AI 入力へ戻す 何を 一言で 4-1 体験をつくる 動かして、計測して、初めて分かる 4-2 正しく確かめる 古いアプリを見ない。推測で直す前に測る。どちらも失敗から rule にした 4-3 境界をつなぐ API の型は生成する。約束がなければ、AI の型でも壊れる 4-4 経験を次の AI 入力へ戻す 失敗を rule・skill・CI の関門に戻す AI が速く書くほど、確認・約束・学習の仕組みが重要になる © BaseMe Inc. 47

Slide 48

Slide 48 text

失敗を直して終わると、次のAIも同じ誤った道を踏む 足場は先に組めない。転んだ場所にしか置けない。そして置いた rule にも穴があった 戻す係がいなかった rule を書いても、3 つの穴があった AI が書く 1 失敗する 先に書けない 4 月に想像で書いた 3 本は、6 月の失敗を 1 つも止めなかった。 足場は、転んだ場所にしか置けない 同じ穴を踏む 2 次のセッションの AI 4〜5 月の 5 本には由来が無い。AI が似た状況で応用できず、 人もあとで消していいか判断できない 人が直す 直して終わり × 3 rule に戻す 「なぜ」が無い 全部読ませると、読まれない CLAUDE.md に書いていた頃、長くなると人間が更新しなくなった。 更新されない記憶は、無いより悪い 戻す係がいない 失敗は直る。でも rule には戻らない。だから次のセッションの AI が、同じ失敗を繰り返す だから決めた 戻す係 書き方 戻す先 ふりかえりの役。提案だけ出し、入れるのは人 由来を書く。触るときだけ読ませる rule・skill・agent・DebugHarness を分ける © BaseMe Inc. 48

Slide 49

Slide 49 text

ruleは、失敗から育てる ふりかえりの役を置いた 6 月から、rule に由来が付くようになった。月ごとの本数 追加された rule の本数 由来なし 6月 失敗の由来つき ふりかえりの役を置いた 16 本 3本 4月 2本 5月 6月 1本 1本 0本 7月 8月 9月 4〜5 月の 5 本は由来なし。想像で書いた 最初の由来つき rule は、古いアプリを見ていた失敗から生まれた 7 月以降は 2 本だけ。録画に入力欄を残さない/段を進める演出の時間を上書きしない © BaseMe Inc. 49

Slide 50

Slide 50 text

ローカルとCIの判定を揃える 戻す先は rule だけではない。CI でしか見つからなかった違反を、ローカルの手順に戻した例 前 ローカル 戻した先。rule ではなく、手順に戻した CI push lint はゆるい設定。 違反があっても通る swiftlint --strict。 ここで初めて落ちる ios/Makefile +5 行 lint-strict のターゲットを新設。既存の lint はそのまま ios/.claude/skills/review-ios/SKILL.md 直して、また push。往復が増える +20 行 build gate の隣に Lint gate。make lint-strict を回し、違反があれば GATE: FAIL 後 ローカル CI push make lint-strict。 CI と同じ判定を手元で。 ここで落ちて、ここで直す 変更なし。 同じ判定なので、通る .github/workflows/ci-ios.yml 変更なし 元から swiftlint lint --strict。SwiftLint 0.57.0 にピン留め 副産物: HomeSpeechBubble.swift が 514 行 → 229 行。HomeBubbleShape と HomeGreetingContent に分割 CI を変えたのではない。ローカルの手順に、CI と同じ 1 行を足した。経験を戻す先は、rule だけではない。 そもそもちゃんとしろ案件 © BaseMe Inc. 50

Slide 51

Slide 51 text

4 本に共通すること 4 本とも、AI が速く書いたものを、あとから何で止めたか AI が 実装 確認 約束 動かして測ってから、信じる サーバーと iOS の約束。型は生成して、手で は書かない 1 体験をつくる 3 境界をつなぐ 動かして計測する。 静止画で判定しない 型は生成する。 手で書ける場所を無くして、 CI で止める 学習 次の AI に、由来ごと渡す 4 経験を次の AI に戻す 失敗の由来を rule に書いて、 次の AI に読ませる 2 正しく確かめる 直す前に測る。 古いアプリを見ない 速く書けるほど、速く壊せる。だから、確認・約束・学習を仕組みにしておく。 © BaseMe Inc. 51

Slide 52

Slide 52 text

まだ、課題に上がっていること ここで初めて、人が持つ課題を置く © BaseMe Inc. 52

Slide 53

Slide 53 text

まだ、課題に上がっていること 4 つ。どれも、AI が回す流れの中で、人が抜けられない場所 01 02 仕様に書いた 0.8 秒 実際に動いた 0.2 秒 動きの仕様を、どこまで言葉にでき るか 体験の良し悪しを、どう測れる形に するか 書いた数字が、そのまま動くとは限らない。 録画で測るまで分からない 静止画は CI が比べる。動きは、まだ人が録 画を見て決めている ✓ 静止画 CI が比べる 人が見る 全体の設定に上書きされた 03 AI 人 動き 04 合否 差し戻し rule を入れる merge AI に渡す判断と、人が持つ判断 を、どう分けるか 合否と差し戻しは AI。rule を入れる、 merge する、は人。全体の決まりは、まだ無 い 仮想チームの成果を、誰が引き受け るか planner architect builder evaluator fixer merge 最後に merge するのは自分。一人だから成 り立っている 自分ひとり 線は、場所ごとに引いている 一度解決して終わりにせず、観測できるようにして、次の判断を少し速くする。ここまでしかできていない。 © BaseMe Inc. 53

Slide 54

Slide 54 text

まだ、課題に上がっていること 4 つ。どれも、AI が回す流れの中で、人が抜けられない場所 01 02 仕様に書いた 0.8 秒 実際に動いた 0.2 秒 動きの仕様を、どこまで言葉にでき るか 体験の良し悪しを、どう測れる形に するか 書いた数字が、そのまま動くとは限らない。 録画で測るまで分からない 静止画は CI が比べる。動きは、まだ人が録 画を見て決めている ✓ 静止画 CI が比べる 人が見る 全体の設定に上書きされた 03 AI 人 動き 04 合否 差し戻し rule を入れる merge AI に渡す判断と、人が持つ判断 を、どう分けるか 合否と差し戻しは AI。rule を入れる、 merge する、は人。全体の決まりは、まだ無 い 仮想チームの成果を、誰が引き受け るか planner architect builder evaluator fixer merge 最後に merge するのは自分。一人だから成 り立っている 自分ひとり 線は、場所ごとに引いている 一度解決して終わりにせず、観測できるようにして、次の判断を少し速くする。ここまでしかできていない。 © BaseMe Inc. 54

Slide 55

Slide 55 text

⚫︎ ⚫︎ ⚫︎ ⚫︎ ⚫︎ ⚫︎ アニメーション: 動きは、仕様を細かく決める必要がある アイテムのアニメーション Lottie Kenのアニメーション モバイル用に最適化した動画を生成 複雑なアニメーション、インタラクション SwiftUI (keyframe / transition) 人間との同時並行が必要 © BaseMe Inc.

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

まとめ © BaseMe Inc. 58

Slide 59

Slide 59 text

⚫︎ ⚫︎ ⚫︎ ⚫︎ まとめ iOS エンジニアの守備範囲は、事業側まで広がる AI は補完ではなく、役割と検証条件を持つ仮想チームとして育てる 最近はAIのマネージメント方が比重が高い 人は、体験の意図、品質基準、最終判断へ AIを仮想チームとして育て、人は体験と判断に集中 © BaseMe Inc.

Slide 60

Slide 60 text

Thanks アニメーションの制御 @nakajijapan © BaseMe Inc.