<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Yusuke Kita on Medium]]></title>
        <description><![CDATA[Stories by Yusuke Kita on Medium]]></description>
        <link>https://medium.com/@kitasuke?source=rss-4a31a30bb718------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*GJoXWYUgWrk2eVwDb-qCtg.png</url>
            <title>Stories by Yusuke Kita on Medium</title>
            <link>https://medium.com/@kitasuke?source=rss-4a31a30bb718------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 01:03:08 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kitasuke/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Behaviour Tests Don’t Wait for Code Quality]]></title>
            <link>https://medium.com/@kitasuke/behaviour-tests-dont-wait-for-code-quality-d97271a29552?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/d97271a29552</guid>
            <category><![CDATA[software-testing]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Fri, 06 Nov 2026 02:23:10 GMT</pubDate>
            <atom:updated>2026-11-06T02:23:10.848Z</atom:updated>
            <content:encoded><![CDATA[<h3>Abstract</h3><p>Most teams build features in the order PRD → product code → test code. That order looks at product code quality (maintainability, security, performance) first, and leaves whether the feature does what the PRD promised for later.</p><p>Product code has two things to ensure: behaviour and code quality. Behaviour has fewer dependencies, so it can be ensured first.</p><p>This post makes two points:</p><ul><li>Test the PRD, not the code.</li><li>Test behaviour without waiting for code quality.</li></ul><h3>Why</h3><p>Tests written after the code read the code and assert what it does. If the implementation and the PRD disagree, the test judges the misreading as a pass.</p><p>Contradictions and gaps in the PRD can get filled by an engineer’s guess mid-implementation, which leads to rework later. When tests are generated from the PRD, they show up as expected outcomes you can’t write, and they become questions for the PRD owner.</p><h3>What</h3><p>By behaviour tests I mean integration and E2E tests, which check behaviour observable from outside. Integration tests cover every scenario against fake dependencies, and E2E tests cover the critical paths in a real environment.</p><p>PRD → test code → product code describes dependency, not time.</p><pre>                    PRD<br>                     │<br>                     ▼<br>             executable spec<br>             │             │<br>             ▼             ▼<br>         test code    product code ◀── code quality</pre><ul><li>Executable spec: the behaviour the PRD promises (states, events, expected outcomes), written so tests can be generated from it.</li><li>Test code takes its expected outcomes only from the executable spec, never from the implementation.</li><li>Product code depends on both behaviour and code quality.</li></ul><p>Product code has to satisfy both behaviour and code quality, but test code needs only behaviour. That’s why behaviour tests can be written and run without the final product code.</p><p>The final test code runs against the actual product code. But what a test expects is decided by the spec alone, never by reading the product code.</p><h3>How</h3><p>In the AI era code is easy to generate, so even if you write the tests first, you can also generate the product-side implementation that runs them. Generate throwaway code together with the tests from the executable spec, and run them. The throwaway code can go; the tests stay and run as they are against the actual product code, because they encode the PRD, not the throwaway code.</p><p>In practice:</p><ul><li>If the PRD doesn’t give an expected outcome, ask the PRD owner instead of guessing one.</li><li>Assert only observable outcomes, and don’t depend on implementation details.</li><li>Code quality can be ensured at the same time, as long as behaviour tests don’t wait for it.</li></ul><p>BDD’s Given/When/Then and specification by example share the same idea. ESDD, which I’ve been building (<a href="https://medium.com/@kitasuke/the-executable-spec-pipeline-c5267f00f504">The Executable Spec Pipeline</a>), is one way to automate it. It turns the PRD into a checkable UseCase IR, sends contradictions and gaps back to the PRD owner as questions, and generates tests from the approved IR.</p><h3>Summary</h3><ul><li>Test the PRD, not the code. Tests built from the PRD find misread requirements; tests built from the code miss them.</li><li>Test behaviour without waiting for code quality. What tests expect is decided by behaviour alone, so they can run against throwaway code first. Quality is improved from there.</li></ul><p>Now that code is easy to generate, there’s no reason to wait for finished code before checking behaviour. There are two things to ensure, behaviour and code quality, and behaviour has fewer dependencies, so ensure it first.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d97271a29552" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[振る舞いのテストはコード品質を待たない]]></title>
            <link>https://medium.com/@kitasuke/%E6%8C%AF%E3%82%8B%E8%88%9E%E3%81%84%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E3%81%AF%E3%82%B3%E3%83%BC%E3%83%89%E5%93%81%E8%B3%AA%E3%82%92%E5%BE%85%E3%81%9F%E3%81%AA%E3%81%84-5d3166a5a468?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/5d3166a5a468</guid>
            <category><![CDATA[software-testing]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Fri, 06 Nov 2026 02:16:05 GMT</pubDate>
            <atom:updated>2026-11-06T02:16:05.802Z</atom:updated>
            <content:encoded><![CDATA[<h3>概要</h3><p>多くのチームは「PRD → プロダクトコード → テストコード」の順に機能を作ります。 この順序では、プロダクトコードの品質（保守性、セキュリティ、パフォーマンス）が先に見られ、PRD どおりに動くかは後回しになります。</p><p>プロダクトコードで担保すべきことは、振る舞いとコード品質の二つです。 振る舞いのほうが依存が少ないので、先に担保できます。</p><p>この記事の主張は二つです。</p><ul><li>コードではなく PRD をテストする</li><li>コード品質を待たずに振る舞いをテストする</li></ul><h3>なぜコードではなく PRD をテストするのか</h3><p>コードの後に書くテストは、コードを読んで、コードがしていることを assert します。 実装と PRD に読み違いがあっても、テストはその読み違いを成功として判定します。</p><p>PRD の矛盾や定義漏れも、実装中のエンジニアが推測で埋めて後から手戻りが起きることがあります。 PRD からテストを生成すれば、期待結果が書けないところで矛盾や定義漏れが見つかり、PRD オーナーに質問できます。</p><h3>なぜコード品質を待たずに振る舞いをテストできるのか</h3><p>この記事でいう振る舞いテストは、外から観測できる振る舞いを確かめる Integration テストと E2E テストです。 Integration テストは fake の外部依存に対してすべてのシナリオを、E2E テストは実際の環境でクリティカルパスを確かめます。</p><p>PRD → テストコード → プロダクトコードという並びは、時間の順序ではなく依存関係です。</p><pre>                    PRD<br>                     │<br>                     ▼<br>              実行可能な仕様<br>             │             │<br>             ▼             ▼<br>       テストコード   プロダクトコード ◀── コード品質</pre><ul><li>実行可能な仕様：PRD が約束する振る舞い（状態、イベント、期待結果）を、テストを生成できる形で書いたものです。</li><li>テストコードの期待結果は「実行可能な仕様」だけに依存し、実装からは取りません。</li><li>プロダクトコードは振る舞いとコード品質の両方に依存します。</li></ul><p>プロダクトコードは振る舞いとコード品質の両方を満たす必要がありますが、テストコードが必要とするのは振る舞いだけです。 そのため、最終的なプロダクトコードがなくても、振る舞いのテストは書いて実行できます。</p><p>最終的なテストコードは、実際のプロダクトコードに対して実行します。 ただし、テストが何を期待するかは仕様だけで決まり、プロダクトコードを読んで決めることはしません。</p><h3>どうやって実現するのか</h3><p>AI の時代にはコードの生成が容易なので、先にテストを書いたとしてもそれを実行するプロダクト側の実装も生成できます。 「実行可能な仕様」からテストと一緒に使い捨てのコードを生成し、テストを動かします。 使い捨てのコードは捨てても、テストは残り、そのまま実際のプロダクトコードに対して実行します。 テストが表しているのは、使い捨てのコードではなく PRD だからです。</p><p>実践では次の点を守ります。</p><ul><li>PRD に期待結果が書かれていなければ、値を推測せず PRD オーナーに質問する。</li><li>観測できる結果だけを assert し、実装の詳細に依存しない。</li><li>コード品質の担保は振る舞いテストと同時に進めてよいが、振る舞いテストはその完了を待たない。</li></ul><p>BDD の Given/When/Then や Specification by Example も同じ考え方です。 私が作っている ESDD（<a href="https://medium.com/@kitasuke/%E4%BB%95%E6%A7%98%E3%82%92%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%99%E3%82%8B-executable-spec-%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3-f595d33771c1">仕様を実行可能にする Executable Spec パイプライン</a>）は、これを自動化する方法の一つです。 PRD を検査できる UseCase IR に変え、矛盾や定義漏れを PRD オーナーへの質問として返し、承認された IR からテストを生成します。</p><h3>まとめ</h3><ul><li>コードではなく PRD をテストする：PRD から作ったテストは要件の読み違いを見つけ、コードから作ったテストは読み違いを見逃します。</li><li>コード品質を待たずに振る舞いをテストする：テストの期待結果は振る舞いだけで決まるので、使い捨てのコードに対して先に動かせます。品質はそこから向上させます。</li></ul><p>コードの生成が容易になった今、振る舞いを確かめるために完成したコードを待つ理由はありません。 担保すべきことは振る舞いとコード品質の二つで、依存が少ない振る舞いを先に担保します。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5d3166a5a468" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Executable Spec Pipeline]]></title>
            <link>https://medium.com/@kitasuke/the-executable-spec-pipeline-c5267f00f504?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/c5267f00f504</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Thu, 27 Aug 2026 21:22:58 GMT</pubDate>
            <atom:updated>2026-08-27T21:23:48.571Z</atom:updated>
            <content:encoded><![CDATA[<p>I’m building a development pipeline called <strong>Executable Spec</strong>. It is a foundation for decomposing specification verification into two parts: test execution by machines, and approval by humans. This post explains why it ended up in this shape, what it can do, and how it works.</p><h3>Overview</h3><p>Executable Spec converts natural-language specifications (PRDs and similar documents) into an inspectable <strong>intermediate representation</strong> (IR), machine-checks the IR, has a human approve it, and then generates test code from the approved IR.</p><pre>Specification (PRD / PDD / Figma)<br>  ↓  AI transforms<br>UseCase IR (the spec written as a state machine)<br>  ↓  Machine checks (exhaustiveness, determinism, reachability, coverage, ...)<br>Check results = a list of questions about the spec (undefined combinations, ...)<br>  ↓  A human answers; the IR is revised<br>A human approves the IR once every check passes  ← the single human approval point<br>  ↓  AI transforms<br>Scenario definitions → test code → execution</pre><p>The scope is the UI behavior of web and mobile apps: states, transitions, displayed text, input validation. It does not cover UX quality such as layout or visual sensibility. Keeping the appearance and type of input fields out of the IR is a deliberate design decision: that information belongs to the design-tool and component layers.</p><h3>Background</h3><p>With AI writing the code, the cost of generation has dropped rapidly. The bottleneck that remains is review. As long as a human ultimately reads every generated spec and every generated test to judge its correctness, verification doesn’t scale no matter how fast generation gets.</p><p>The other problem was that nothing kept specifications and tests in sync. E2E tests have been in place for a long time, but “which behavior in the spec does this test verify?” lived in human judgment. Whether tests kept up with a spec change was something you found out only when they broke.</p><p>The approach of converting natural-language specs into an inspectable structure, instead of prompting harder with them as-is, is an extension of the ideas in <a href="https://speakerdeck.com/kitasuke/dont-prompt-harder-structure-better">Don’t Prompt Harder, Structure Better</a>.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2Fef5304580c0141eaa2e3c42d4121f14d&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Fkitasuke%2Fdont-prompt-harder-structure-better&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2Fef5304580c0141eaa2e3c42d4121f14d%2Fslide_0.jpg%3F39078982&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href">https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href</a></iframe><h3>Why build it</h3><p>At the root of Executable Spec is one idea: in AI-based development, quality assurance should concentrate on the upstream and downstream ends of the pipeline.</p><p><strong>Upstream, quality assurance means approving the spec.</strong> Once the spec is lowered into an IR, machine checks return undefined state transitions and missing definitions for concurrent operations as a list of questions about the spec. The human’s job narrows to answering those questions and approving an IR that passes every check. There is exactly one approval point in the whole pipeline.</p><p><strong>Downstream, quality assurance means test results.</strong> The pass / fail verdict comes from a machine.</p><p><strong>The transformations in between carry no semantic checks.</strong> Going from IR to scenario definitions, and from scenario definitions to test code, all that’s needed is to check that nothing from upstream was lost and nothing that isn’t upstream was added. Concentrating semantic checking at the single upstream point is what makes the intermediate artifacts machine-reviewable.</p><p>Human review doesn’t disappear. It changes character: from exhaustively reading artifacts end to end, to answering machine-generated questions and deciding, when a test fails, whether to fix the spec or the implementation. Replacing most of spec and test review with machine checks is the point of this pipeline.</p><h3>What it can do</h3><h4>It turns spec holes into questions</h4><p>The IR is a state machine, and the checker demands that every state × event combination be defined. “Cannot happen” must also be declared explicitly, with a reason, so that undefined is distinguished from “considered and impossible” — and every undeclared combination surfaces as a hole in the spec.</p><p>Piloting this on the PRD of one feature, the checker found 11 undefined combinations in a 13-state × 19-event table, and every one of them was a real question worth asking the PRD owner. The kind of hole humans struggle to spot in review (what happens on this input in this particular state is written nowhere) gets enumerated mechanically, without omissions.</p><p>The same mechanism works for form inputs. It checks exhaustiveness over input fields × {empty, boundary value} and returns the combinations the spec doesn’t answer as questions.</p><h4>It verifies the app works as expected, with or without an implementation</h4><p>The pipeline is used in two phases, and what a generated test passing or failing means changes with the phase.</p><p><strong>Phase 1 (the implementation already exists)</strong>: generated tests are expected to pass. A failure is the detection of a gap between spec and implementation, and a human decides which side to fix. In fact, the very first test generated by this pipeline failed on its first run. The cause was a product-side change to displayed text that the test’s expectations hadn’t followed. The spec in the IR was judged correct, the test was fixed, and it passed. It became the first working example of “failure = drift detected, and a human decides which side to fix.”</p><p><strong>Phase 2 (the implementation doesn’t exist yet)</strong>: frontend code and integration tests are generated together from the spec. The acceptance criterion is that the tests fail on the old build, which lacks the feature, and pass on the generated build — proof that the tests actually verify the feature. Not using E2E as the verification layer in Phase 2 is deliberate. E2E presupposes that every implementation detail — environment, test data, the full journey between screens — already exists, which is far too costly in a phase that generates the implementation itself. Integration tests against a fake server have no environment-induced flakiness, and because frontend and tests are generated together, the mapping to screen elements is closed inside the generation step.</p><p>In both phases the upstream, IR checking and approval, is the same. With or without an implementation, the same entry point verifies that the app behaves as the approved spec says.</p><h4>It can be edited by diff</h4><p>Each element in the IR keeps a stable ID, and ordering follows fixed rules. A behavior change is therefore written as a meaningful diff — this combination’s outcome changed, this transition was added — rather than by regenerating the whole IR.</p><p>This matters operationally. Updating the IR diff in the same PR as the feature change is the only mechanism that keeps spec and tests from drifting apart. If full regeneration were the only tool, every small behavior change would put the entire approved IR back under review, and that practice wouldn’t survive.</p><h3>How it works</h3><h4>What goes into the IR</h4><p>A UseCase IR is a state machine written in YAML. Alongside states, events, and transitions, it carries cross-cutting rules (tables of validation conditions and results), declared input axes, and representative scenarios (critical paths). Here is a minimal skeleton:</p><pre>usecase: UC-EXAMPLE-001<br>name: Order and shipping<br>initial: IDLE<br><br>states:<br>  - id: IDLE<br>  - id: ORDERED<br>    label: Ordered<br>  - id: SHIPPED<br>    label: Shipped<br>  - id: CANCELED<br>    label: Canceled<br>    terminal: true<br><br>events:<br>  - id: place_order<br>    label: Place an order<br>    actor: customer<br>  - id: ship<br>    label: Ship<br>    actor: staff<br>  - id: cancel<br>    label: Cancel<br>    actor: customer<br><br>transitions:<br>  - from: IDLE<br>    on: place_order<br>    to: ORDERED<br>    outcome:<br>      - surface: WEB<br>        ui: Order history shows a new row with status &quot;Ordered&quot;<br><br>  - from: ORDERED<br>    on: ship<br>    to: SHIPPED<br>    outcome:<br>      - surface: WEB<br>        ui: The order&#39;s status changes to &quot;Shipped&quot;<br><br>  - from: ORDERED<br>    on: cancel<br>    to: CANCELED<br>    outcome:<br>      - surface: WEB<br>        ui: The order&#39;s status changes to &quot;Canceled&quot;<br><br>  # &quot;cannot happen&quot; must also be declared, with a reason<br>  - from: IDLE<br>    on: [ship, cancel]<br>    impossible: true<br>    reason: no order exists to act on</pre><p>Of the 9 combinations across the 3 non-terminal states × 3 events, only 5 are declared here. The checker returns the remaining 4 as questions: should a shipped order be cancelable? what happens if you place another order while one is already placed? Combinations declared impossible are excluded as “considered and ruled out.”</p><p>Two principles govern how the IR is written. First, <strong>tests know nothing about the implementation</strong>: transition outcomes are written only in terms of what a user can observe, such as displayed text and states. Second, semantic checking happens exactly once, on the UseCase IR; downstream artifacts never restate behavior and hold only references to which upstream behavior they target.</p><h4>Machine checks produce the questions</h4><p>The checker verifies exhaustiveness (is every state × event combination defined?), determinism (is the result unique for a given state and input?), reachability (is every state reachable?), concurrency declarations (where multiple events can occur at once, is the consequence defined?), and scenario validity and coverage (do scenarios follow real transitions, and do they cover them?). A check error has the shape “what should happen on this input in this state?”, which is exactly a checklist of questions for the PRD owner.</p><p>The PRD owner answers the questions, not the implementation. Filling holes by reading the code silently promotes whatever the code currently does into the spec, destroying the ability to detect drift. Only when onboarding an existing feature for the first time do the implementation and its tests serve as a source, and even then every code-derived fact is recorded as a delta and surfaced as material for updating the spec.</p><h4>Generating a per-target IR</h4><p>The definition of behavior lives in exactly one place, the UseCase IR. Each deliverable (integration test code, E2E test code, and so on) gets its own IR layered on top of it. A target IR never restates behavior; it holds only the decisions that belong to that target. For E2E tests, it looks like this:</p><pre># target IR for E2E tests (excerpt)<br>suite: E2E-EXAMPLE-001<br>usecase: UC-EXAMPLE-001    # behavior is only a reference to the UseCase IR<br>cases:<br>  - id: TC-1<br>    scenario: SC-001       # points at a scenario in the UseCase IR<br>    priority: high         # a decision specific to this target<br>    environment: staging   # choice of execution environment</pre><p>Test steps are not written here. Scenarios in the UseCase IR are defined as paths through the transitions, so steps are derived mechanically by walking the path. What remains in the target IR are only the decisions that can be made nowhere else: which scenarios to include (all of them for integration tests, critical paths only for E2E), priority, and environment. Because behavior lives in one place, a spec change has exactly one place to fix.</p><h4>Generating tests from an approved IR</h4><p>An approved IR is lowered into the existing scenario-definition and test-helper machinery and becomes E2E test code. Running the generated tests is how consistency with the implementation is verified: not by reading the code, but by whether the tests fail.</p><p>Day to day, the flow is one-directional and always starts from the spec. Behavior changes travel as IR diffs in the same PR as the feature change. If rewriting the IR from the implementation ever becomes routine, that is a sign the loop is no longer a loop.</p><h3>Summary</h3><p>Executable Spec is a pipeline that decomposes specification verification into test execution by machines and approval by humans.</p><ul><li>Quality assurance concentrates on the <strong>upstream (IR approval) and downstream (test results) ends of the pipeline</strong>; the transformations in between need only content-preservation checks</li><li>Human review remains, but shifts from reading artifacts line by line to <strong>answering machine-enumerated questions and deciding, on failure, whether to fix the spec or the implementation</strong></li><li>With an implementation it detects spec-implementation drift; without one it is the entry point for co-generating code and tests — <strong>with or without an implementation</strong>, the same spec verifies that the app works as expected</li><li>Stable IDs and ordering make the IR <strong>editable by diff instead of full regeneration</strong>, which is what makes updating the spec in the same PR as the feature change sustainable</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c5267f00f504" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[仕様を実行可能にする Executable Spec パイプライン]]></title>
            <link>https://medium.com/@kitasuke/%E4%BB%95%E6%A7%98%E3%82%92%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%99%E3%82%8B-executable-spec-%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3-f595d33771c1?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/f595d33771c1</guid>
            <category><![CDATA[frontend-development]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Thu, 27 Aug 2026 21:15:00 GMT</pubDate>
            <atom:updated>2026-08-27T21:29:03.654Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Executable Spec</strong> と呼ぶ開発パイプラインを作っています。 仕様の検証を「機械によるテストの実行」と「人間による承認」に分解するための基盤です。 この記事では、なぜこの形に行き着いたのか、何ができて、どう動くのかを説明します。</p><h3>概要</h3><p>Executable Spec は、PRD などの自然言語の仕様を検査可能な <strong>中間表現</strong> （IR: Intermediate Representation）に変換し、IR を機械検査したうえで人間が承認し、承認された IR からテストコードを生成するパイプラインです。</p><pre>仕様（PRD / PDD / Figma）<br>  ↓  AI が変換<br>UseCase IR（ステートマシンとして記述された仕様）<br>  ↓  機械検査（網羅性、決定性、到達性、カバレッジなど）<br>検査結果 = 仕様への質問リスト（未定義の組み合わせなど）<br>  ↓  人間が回答し、IR を修正<br>検査をすべて通った IR を人間が承認  ← 人間の承認ポイントはここ一箇所<br>  ↓  AI が変換<br>シナリオ定義 → テストコード → 実行</pre><p>対象は Web とモバイルアプリの UI の振る舞い（状態、遷移、表示文言、入力検証）です。 レイアウトや感性に関わる UX の品質は扱いません。 入力フィールドの見た目や種類を IR に持たせないのは意図的な設計で、それらはデザインツールとコンポーネントの層に属する情報だからです。</p><h3>背景</h3><p>AI にコードを書かせる開発では、生成のコストは急速に下がりました。 残ったボトルネックはレビューです。 生成された仕様書もテストコードも、最終的に人間が全て読んで正しさを判断している限り、生成をいくら速くしても検証がスケールしません。</p><p>もうひとつの問題は、仕様とテストの対応を保つ仕組みがないことでした。 E2E テストは以前から運用していますが、「このテストは仕様のどの振る舞いを検証しているのか」は人間の判断に依存していました。 仕様が変わったときにテストが追従しているかどうかは、壊れて初めてわかります。</p><p>自然言語の仕様をそのままプロンプトとして強化するのではなく、検査可能な構造に変換してから扱うという方針は、 <a href="https://speakerdeck.com/kitasuke/dont-prompt-harder-structure-better">Don’t Prompt Harder, Structure Better</a> で話した考え方の延長にあります。</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2Fef5304580c0141eaa2e3c42d4121f14d&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Fkitasuke%2Fdont-prompt-harder-structure-better&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2Fef5304580c0141eaa2e3c42d4121f14d%2Fslide_0.jpg%3F39078982&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href">https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href</a></iframe><h3>なぜ作るのか</h3><p>Executable Spec の根底には、「AI ベースの開発では、パイプラインの上流工程と下流工程の品質保証に最も注力すればよい」という考え方があります。</p><p><strong>上流工程の品質保証は、仕様の承認です。</strong> 仕様を IR に落とすと、機械検査が、未定義の状態遷移や、複数の操作が同時に起きた場合の挙動の定義漏れを、仕様への質問リストとして返します。 人間の仕事は、質問に答えることと、検査をすべて通った IR を承認することに絞られます。 承認ポイントはパイプライン全体で一箇所だけです。</p><p><strong>下流工程の品質保証は、テストの実行結果です。</strong> 成功 / 失敗の判定は機械が行います。</p><p><strong>中間の変換には意味の検査を持ち込みません。</strong> IR からシナリオ定義、シナリオ定義からテストコードへの変換で必要なのは、上流の情報が失われていないこと、上流にない振る舞いが勝手に追加されていないことの検査だけです。 意味の検査を上流の一箇所に集めることで、中間成果物のレビューを機械化できます。</p><p>人間のレビューがなくなるわけではありません。 性質が変わります。 成果物を逐一読む網羅的なレビューから、機械が生成した質問への回答と、テストが失敗したときに仕様と実装のどちらを直すかの判断へ。 仕様とテストのレビューの大部分を機械検査に置き換えることが、このパイプラインの目的です。</p><h3>何ができるのか</h3><h4>仕様の穴を質問として返す</h4><p>IR はステートマシンとして書かれ、検査器は状態×イベントの組み合わせすべてが定義されていることを要求します。 「起こり得ない」も理由つきで明示的に宣言させるため、未定義と「検討済みで不可能」が区別され、定義されていない組み合わせはそのまま仕様の穴として浮かび上がります。</p><p>ある機能の PRD をパイロットとして変換したときは、13 状態 × 19 イベントの表から 11 個の未定義の組み合わせが検出され、そのすべてが PRD オーナーに聞くべき実際の質問でした。 人間がレビューで見つけるのが難しい種類の穴（特定の状態で特定の入力が来たときの挙動が、どこにも書かれていない）を、機械が漏れなく列挙します。</p><p>フォーム入力についても同じ仕組みが働きます。 入力フィールド × {未入力、境界値} という軸の組み合わせに対して網羅性を検査し、仕様が答えていない組み合わせを質問として返します。</p><h4>実装の有無を問わず「期待どおり動くこと」を確かめる</h4><p>パイプラインは 2 つのモードで使い、生成テストの成功 / 失敗の意味がモードで変わります。</p><p><strong>モードA（実装が先に存在する）</strong> ：生成テストは成功が期待値です。 失敗は仕様と実装の乖離の検出であり、どちらを直すかは人間が判断します。 実際、このパイプラインで最初に生成したテストの初回実行は失敗でした。 原因は、プロダクト側の表示文言の変更にテストの期待値が追従していなかったことです。 IR に書かれた仕様が正しいと判断し、テスト側を直して成功になりました。 「失敗 = 乖離の検出、どちらを直すかは人間が決める」という意味づけが機能した最初の例になりました。</p><p><strong>モードB（実装がまだ存在しない）</strong> ：仕様からフロントエンドコードと Integration テストを同時生成します。 対象機能を含まない旧ビルドで失敗し、生成後のビルドで成功することが、テストが本当に対象機能を検証していることの合格条件になります。 モードB の検証層を E2E にしないのは意図的です。 E2E は環境、テストデータ、画面遷移の全体といった実装の詳細がすべて揃っていることを前提とするため、実装と同時に生成するフェーズではコストが大きすぎます。 fake サーバーを使った Integration テストであれば環境に起因する不安定さがなく、フロントエンドとテストを同時に生成するため、画面要素との対応づけも生成の中で完結します。</p><p>どちらのフェーズでも、上流（IR の検査と承認）は共通です。 実装があってもなくても、承認済みの仕様に対してアプリが期待どおり動くことを、同じ入り口から確かめられます。</p><h4>差分で編集できる</h4><p>IR は各要素の ID を変えず、並び順も一定のルールで揃えています。 このため振る舞いの変更は、IR 全体を作り直すのではなく、意味のある差分（この組み合わせの結果が変わった、この遷移が増えた）として書けます。</p><p>これは運用上の大事なポイントです。 機能 PR と同一の PR で IR の差分を更新する運用が、仕様とテストの乖離を防ぐ唯一の仕組みになります。 全て再生成する手段しかないと、小さな振る舞い変更のたびに承認済みの IR 全体が再レビュー対象になり、この運用は維持できません。</p><h3>どう動くのか</h3><h4>IR に何を書くか</h4><p>UseCase IR は YAML で書かれたステートマシンです。 状態、イベント、遷移に加えて、横断的なルール（バリデーションの条件と結果の表）、入力軸の宣言、代表シナリオ（クリティカルパス）を持ちます。 骨格だけの小さな例を示します。</p><pre>usecase: UC-EXAMPLE-001<br>name: 注文と発送<br>initial: IDLE<br><br>states:<br>  - id: IDLE<br>  - id: ORDERED<br>    label: 注文済み<br>  - id: SHIPPED<br>    label: 発送済み<br>  - id: CANCELED<br>    label: キャンセル<br>    terminal: true<br><br>events:<br>  - id: place_order<br>    label: 注文する<br>    actor: customer<br>  - id: ship<br>    label: 発送する<br>    actor: staff<br>  - id: cancel<br>    label: キャンセルする<br>    actor: customer<br><br>transitions:<br>  - from: IDLE<br>    on: place_order<br>    to: ORDERED<br>    outcome:<br>      - surface: WEB<br>        ui: 注文履歴にステータス「注文済み」の行が表示される<br><br>  - from: ORDERED<br>    on: ship<br>    to: SHIPPED<br>    outcome:<br>      - surface: WEB<br>        ui: 該当注文のステータスが「発送済み」になる<br><br>  - from: ORDERED<br>    on: cancel<br>    to: CANCELED<br>    outcome:<br>      - surface: WEB<br>        ui: 該当注文のステータスが「キャンセル」になる<br><br>  # 「起こり得ない」も理由つきで態度表明する<br>  - from: IDLE<br>    on: [ship, cancel]<br>    impossible: true<br>    reason: 注文が存在しないため対象がない</pre><p>この例では、終端状態を除く 3 状態 × 3 イベント = 9 個の組み合わせのうち、5 個しか宣言されていません。 検査器は残りの 4 個を「発送済みの注文はキャンセルできるべきか」「注文済みの状態で再度注文したらどうなるのか」という形の質問として返します。 impossible と宣言した組み合わせは「検討済みで不可能」として質問から除外されます。</p><p>書き方には 2 つの原則があります。 第一に、 <strong>テストは実装について何も知りません</strong> 。 遷移の結果は、表示テキストや状態など、ユーザーから観測できるものだけで書きます。 第二に、意味の検査は UseCase IR に一度だけ行い、下流の成果物は振る舞いを再記述せず、上流のどの振る舞いを対象にするかの参照だけを持ちます。</p><h3>機械検査が質問を作る</h3><p>検査器は、網羅性（すべての状態×イベントの組み合わせが定義済みか）、決定性（同じ状態と入力に対して結果が一意か）、到達性（到達できない状態がないか）、同時発生の宣言（複数のイベントが同時に起こりうる箇所で、その帰結が定義されているか）、シナリオの妥当性とカバレッジ（シナリオが実在する遷移をたどり、遷移を網羅しているか）を検査します。 検査エラーは「この状態でこの入力が来たらどうなるべきか」という形の質問であり、そのまま PRD オーナーへの確認リストになります。</p><p>質問に答えるのは PRD オーナーであって、実装コードではありません。 実装を読んで穴を埋めると、実装の現状が無言で仕様に昇格してしまい、乖離を検出する能力が失われます。 既存機能をこのパイプラインに載せる初回だけは実装とテストをソースに使いますが、その場合もコード由来の事実はすべて差分として記録し、仕様更新の材料として出します。</p><h3>ターゲットごとの IR を生成する</h3><p>振る舞いの定義は UseCase IR の一箇所だけに置き、成果物ごと（Integration テストコード、E2E テストコードなど）には、それぞれ専用の IR を UseCase IR の上に重ねます。 ターゲットの IR は振る舞いを再記述せず、そのターゲットでしか決められないことだけを持ちます。 E2E テストの IR なら、次のような形です。</p><pre># E2E テストのターゲット IR（抜粋）<br>suite: E2E-EXAMPLE-001<br>usecase: UC-EXAMPLE-001    # 振る舞いは UseCase IR への参照だけ<br>cases:<br>  - id: TC-1<br>    scenario: SC-001       # UseCase IR 側のシナリオを指す<br>    priority: high         # このターゲット固有の決定<br>    environment: staging   # 実行環境の選択</pre><p>テスト手順はここには書きません。 UseCase IR のシナリオは遷移のパスとして定義されているため、手順はパスをたどることで機械的に得られます。 ターゲットの IR に残るのは、対象シナリオの選択（Integration テストは全シナリオ、E2E はクリティカルパスだけ、といった絞り込み）や優先度、実行環境のような、そのターゲットでしか決められないことだけです。 振る舞いが一箇所にしかないので、仕様が変わったとき直す場所も一箇所で済みます。</p><h3>承認済み IR からテストを生成する</h3><p>承認された IR は、既存のシナリオ定義とテストヘルパーの仕組みに落とし込まれ、E2E テストコードになります。 生成されたテストの実行が、実装との整合の検証手段です。 コードを読んで整合を確認するのではなく、テストを実行して失敗するかどうかで判定します。</p><p>日々の運用では、常に仕様を起点とした一方向の流れを保ちます。 振る舞いの変更は、機能 PR と同一 PR での IR の差分更新として行います。 実装を読んで IR を書き直すことが日常的に必要になったら、それは仕様起点のループになっていないサインとして扱います。</p><h3>まとめ</h3><p>Executable Spec は、仕様の検証を機械によるテストの実行と人間による承認に分解するパイプラインです。</p><ul><li>品質保証は<strong>パイプラインの上流工程（IR の承認）と下流工程（テストの実行結果）に最も注力</strong>し、中間の変換は内容が保たれていることの検査だけで済ませる</li><li>人間のレビューは残るが、成果物を逐一読む作業から、<strong>機械が列挙した質問への回答と、失敗時に仕様と実装のどちらを直すかの判断</strong>に変わる</li><li>実装があれば仕様と実装の乖離の検出として、実装がなければコードとテストの同時生成の入り口として、<strong>実装の有無を問わず</strong>同じ仕様から「期待どおり動くこと」を確かめられる</li><li>ID と並び順を安定させることで、<strong>全て再生成するのではなく差分で</strong> IR を編集でき、機能変更と同一 PR での仕様更新という運用が成立する</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f595d33771c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From PRD to Code: Why Rules Beat Prompts for Efficient AI Codegen]]></title>
            <link>https://medium.com/@kitasuke/from-prd-to-code-why-rules-beat-prompts-for-efficient-ai-codegen-c03a7dedf712?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/c03a7dedf712</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Fri, 15 May 2026 08:39:35 GMT</pubDate>
            <atom:updated>2026-05-15T08:39:35.352Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><em>AI’s real strength isn’t writing code — it’s inference. And the quality of that inference is determined not by better prompts, but by how much of the gap AI has to guess at can be filled in with rules.</em></blockquote><p>When we work with AI on code generation, we tend to think “if I just write a better prompt, the result will get better.” But in practice, what actually moves output quality the most is how much of the inference gap we can shrink before the model starts working.</p><h3>1. AI’s Real Strength Is Inference, Not Code Generation</h3><p>When AI generates code, it isn’t just reusing existing code as-is — it’s filling in missing information through inference.</p><p>A prompt like “build a user list screen” leaves an enormous amount unspecified. The model has to make decisions on every one of them:</p><ul><li>Which layer owns the database query?</li><li>How are errors propagated — exceptions, result types, something else?</li><li>Does the API response flow straight to the UI, or through a translation layer?</li></ul><p>None of these were in the prompt. The model inferred the answers.</p><p>So the essence of AI code generation is the work of closing the gap between what you said and what code needs to exist. The quality of the output depends on three things: how accurately the AI interprets the input, how wide that gap is, and how well the model fills it in.</p><p>If we restate the problem cleanly:</p><blockquote><em>The quality of AI-generated code = how small you can make the gap the model has to infer across.</em></blockquote><p>So the real question isn’t “should we shrink the gap?” but rather “how do we shrink it, and where does that knowledge live?”</p><h3>2. Two Approaches to Closing the Gap — Bare Prompts vs. Shared Rules</h3><p>There are roughly two directions for shrinking the gap, and how much weight you put on each ends up determining the long-term quality of your output.</p><h3>Approach A: Pure Inference (no shared definitions)</h3><p>You hand the model the PRD and the codebase, and that’s it. Architecture, style, error handling, naming, layering — all of it gets inferred from whatever hints the model can pick up from existing files, plus whatever the engineer happens to type into the prompt.</p><p>“You only need a PRD” sounds lightweight, but that lightness is a surface impression. The knowledge that makes the output good has to come from somewhere — and in Pure Inference, the only place it exists is inside the individual engineer’s prompt.</p><p>In reality, Pure Inference is a prompt-tuning workflow disguised as an automation workflow:</p><ul><li>Know-how accumulates in individual chat histories, not in the org</li><li>“What worked last time” can’t be reused by a teammate</li><li>Reviewers have no objective standard to evaluate the result against</li></ul><h3>Approach B: Rules-Augmented Inference (with shared definitions)</h3><p>In addition to the PRD and the codebase, you hand the model explicit organizational definitions. The model still infers, but it does so within the boundaries of pre-defined rules — and the know-how lives in versioned files in the repo, not inside someone’s head.</p><p>These rules split into two sides: code-side and PRD-side.</p><p>Code-side rules — definitions for how the implementation should be shaped:</p><ul><li>Architecture guidelines (Clean Architecture boundaries, hexagonal layers, etc.)</li><li>Design system / UI style guide (tokens, components, spacing, typography)</li><li>Concurrency and threading policy</li><li>Layering and dependency rules (no API types in the View, translation layers, etc.)</li><li>Error-handling conventions (where to throw, where to recover, how to log)</li><li>Testing conventions (what to mock, where unit vs. integration boundaries sit)</li><li>Naming, file structure, and module-boundary rules</li></ul><p>PRD-side rules — definitions for how the spec should be shaped <em>so AI can actually read it</em>:</p><ul><li>A domain glossary that defines what your org-specific terms mean — what counts as an “active” user, when an order is “complete,” what “verified” means in your domain — so the model isn’t guessing</li><li>A requirement template like EARS, so the model can mechanically detect missing trigger/condition/response clauses</li><li>User personas and journey definitions, so acceptance criteria can be evaluated against a known cast of actors</li><li>Analytics/event naming conventions, so the model can fill in the events a feature should emit without inventing names</li><li>Error, empty-state, and loading-state UX standards, so the model auto-completes the unhappy paths PRDs always forget</li></ul><p>PRD-side investment is the part most teams skip, and it’s also the part that hurts most when it’s missing.</p><p>A PRD is fundamentally a “natural-language document full of gaps,” and AI will fill those gaps with inference whether you’ve told it how to or not. Without PRD-side rules, the AI invents a plausible-but-subtly-wrong interpretation of your domain, and the PM ends up correcting it through prompts on every single feature.</p><h3>Comparing the two approaches</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b829c4f99bd33df36a0a207a19a40671/href">https://medium.com/media/b829c4f99bd33df36a0a207a19a40671/href</a></iframe><h3>3. Why Rules Compound and Prompts Don’t</h3><p>1. Rules accumulate as assets; prompts vanish after use. A prompt you wrote disappears when the session ends. A rule you wrote will improve the next session — for you, and for everyone on the team.</p><p>2. One fix lifts every future generation. When Pure Inference produces a bad output, someone tweaks a prompt, and the next engineer hits the same problem from scratch. When a rule is wrong, you fix it once — and the model will never make that mistake again, for anyone.</p><p>3. The act of writing rules surfaces implicit disagreement. Putting a convention into words forces the team to make decisions it had been quietly deferring. Resolving those raises the baseline quality for both humans and AI. Pure Inference, by contrast, just papers over the disagreement.</p><p>4. Onboarding compounds. New members — human engineers or new AI agents — can read the rules and immediately produce output consistent with the team. In Pure Inference, every newcomer has to re-discover the team’s preferences through trial and error.</p><h3>4. When Pure Inference Is Genuinely the Right Choice</h3><p>Rules-augmented isn’t always the right answer. Pure Inference is the better choice in cases like:</p><ul><li>PoCs — code you’re going to throw away doesn’t need rules</li><li>One-shot scripts and data migrations — code that runs once doesn’t need consistency</li><li>Exploring new territory — sometimes you have to write code first to discover what the rules should be</li><li>Early-stage teams without an existing framework — it can be faster to extract rules from the code after the fact</li></ul><p>A rough guideline: long-lived, multi-author, pattern-repeating code → push toward rules-augmented. Everything else → pure inference is fine.</p><h3>5. The Pattern in the Wild</h3><p>Rules-augmented inference has already been adopted at multiple layers of the stack. A few public writeups:</p><h4>Architecture-driven code generation</h4><p><a href="https://techblog.zozo.com/entry/architecture-codegen-pipeline">スキーマでアーキテクチャを縛る ── 人間とAIを同じルールで動かす</a> <em>(Japanese)</em> — “Constraining architecture with schemas: running humans and AI on the same rules.”</p><p>The team treats their architecture guidelines (layer responsibilities, dependency rules, naming conventions) as direct AI inputs, not just human-facing docs. The result: generated code lands in the right files, in the right layers, with the right names — because the rules told the model “where things go” before it started writing.</p><h4>Test code generation</h4><p><a href="https://speakerdeck.com/kitasuke/dont-prompt-harder-structure-better">Don’t prompt harder, structure better</a></p><p>The same thesis applied to test code. Rather than tuning prompts to “write better tests,” the team defines a structural template (what to mock, what to assert, naming patterns for test cases) and feeds that as a rule set. Test variance collapses; review becomes nearly mechanical.</p><h4>Test plan generation</h4><p><a href="https://tech.newmo.me/entry/2025/12/09/090000">Generating test plans with AI</a> <em>(Japanese)</em></p><p>Pushes rules-augmented inference upstream into the spec layer. Combining the PRD with test-planning rules (coverage axes, edge-case categories, output format) produces a usable test plan instead of a generic checklist. This is the PRD-side half of the frame applied concretely.</p><p>The thread tying these three together: none of them invented a smarter prompt. Each team wrote down what they had been holding implicitly and fed it to the model as an input. That’s the entire pattern.</p><h3>6. The Takeaway — Don’t Tune Prompts, Build the Rule Foundation</h3><ul><li>Inference is the key. Rules are how you shrink the gap it has to cross.</li><li>Rules have two sides — code-side and PRD-side. You only get the full effect when both are in place.</li></ul><p>If there’s one thing to take from this post: stop trying harder on prompts. Build the foundation of rules — on both the spec side and the code side — that AI can infer against, and let the rules themselves do the work.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c03a7dedf712" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PRDからコードへ：効率的なAIコード生成に、プロンプトよりルールが効く理由]]></title>
            <link>https://medium.com/@kitasuke/prd%E3%81%8B%E3%82%89%E3%82%B3%E3%83%BC%E3%83%89%E3%81%B8-%E5%8A%B9%E7%8E%87%E7%9A%84%E3%81%AAai%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90%E3%81%AB-%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88%E3%82%88%E3%82%8A%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%8C%E5%8A%B9%E3%81%8F%E7%90%86%E7%94%B1-d8a7e7edecfa?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/d8a7e7edecfa</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Fri, 15 May 2026 07:26:46 GMT</pubDate>
            <atom:updated>2026-05-15T08:42:16.417Z</atom:updated>
            <content:encoded><![CDATA[<p>AIにコードを書かせるとき、つい「もっと良いプロンプトを書けば結果が良くなる」と考えてしまいがちですが、実際に出力の品質を大きく左右しているのは、AIに推測させなければならない余白をどれだけ減らせるか という点ではないかと思います。</p><h3>1. AIの本当の武器はコード生成ではなく「推論」</h3><p>AIがコードを生成するとき、すでにあるコードをそのまま再利用しているわけではなく、足りない情報を 推論で埋めて います。</p><p>たとえば「ユーザー一覧画面を作って」という指示には書かれていないことが山のようにあり、モデルはそれぞれについて自分で判断しなければなりません。</p><ul><li>DBクエリはどの層が持つのか</li><li>エラーはどう伝搬させるのか ― 例外か、Result型か、それ以外か</li><li>APIレスポンスはそのままUIに流すのか、変換層を挟むのか</li></ul><p>これらはプロンプトに書かれていなかった部分であり、モデルが推論で埋めた箇所です。</p><p>つまりAIによるコード生成の本質は、「あなたが言ったこと」と「存在すべきコード」のあいだに開いた ギャップを埋める 作業にあるといえます。出力の品質は、AIが入力をどれだけ正確に解釈できるか、ギャップがどれだけ広いか、そしてその埋め方がどれだけ的確かによって決まります。</p><p>問題の見え方を整理すると、こうなります。</p><blockquote><em>AI生成コードの品質 ＝ モデルが推論で埋めなければならないギャップをどこまで小さくできるか</em></blockquote><p>したがって本当に考えるべき問いは「ギャップを縮めるべきか」ではなく、「どう縮めるか、そしてその知識をどこに置くか」 であるはずです。</p><h3>2. ギャップを埋める2つのアプローチ ― プロンプト vs. 共有ルール</h3><p>ギャップを縮めるアプローチには大きく2つの方向性があり、どちらにどれだけ比重を置くかが長期的な品質を決めることになります。</p><h3>アプローチA：純粋な推論（共有された定義なし）</h3><p>PRDとコードベースだけをモデルに渡すアプローチです。アーキテクチャ、スタイル、エラーハンドリング、命名、レイヤリングといった事柄はすべて、既存ファイルから読み取れるヒントと、エンジニアがその場で書いたプロンプト次第で、モデルがゼロから推論することになります。</p><p>「PRDだけで済む」ので軽量に見えますが、これはあくまで見かけの話で、良い出力を生む知識は結局どこかから来ているはずです。純粋な推論の場合、それは個々のエンジニアのプロンプトの中にしか存在しません。</p><p>実態としては、純粋な推論は 自動化ワークフローを装ったプロンプトチューニングの作業 だといえます。</p><ul><li>ノウハウは個人のチャット履歴に溜まり、組織には残らない</li><li>「前回うまくいった書き方」を同僚が再利用できない</li><li>レビュアーには結果を評価するための客観的な基準がない</li></ul><h3>アプローチB：ルール付き推論（共有された定義つき）</h3><p>PRDとコードベースに加えて、組織として明示された定義 をAIに渡すアプローチです。モデルは推論を続けますが、その推論はあらかじめ定義されたルールの範囲内で行われるようになり、ノウハウは誰かの頭の中ではなく、リポジトリにバージョン管理されたファイルとして残っていきます。</p><p>このルールは、コード側とPRD側の2つの側面に分けて整理できます。</p><p>コード側のルール ― 実装をどう形作るかの定義：</p><ul><li>アーキテクチャガイドライン（Clean Architectureの境界、ヘキサゴナル構造など）</li><li>デザインシステム / UIスタイルガイド（トークン、コンポーネント、間隔、タイポグラフィ）</li><li>並行性・スレッドポリシー</li><li>レイヤリングと依存ルール（APIの型をViewに漏らさない、Translator層を介する、など）</li><li>エラーハンドリング規約（どこで throw し、どこで recover し、どうログに残すか）</li><li>テスト規約（何をモックするか、unit と integration の境界をどこに置くか）</li><li>命名、ファイル構成、モジュール境界のルール</li></ul><p>PRD側のルール ― AIが読めるように仕様をどう形作るかの定義：</p><ul><li>ドメイン用語集 ― 「アクティブな」ユーザーとは何か、注文が「完了」と見なされるのはいつか、「認証済み」が自社の文脈で何を指すかといった、社内固有の用語をAIに推測させないための定義</li><li>要件テンプレート（EARS記法など）― トリガー・条件・レスポンスの抜け漏れをモデルが機械的に検出できるようにするためのフォーマット</li><li>ユーザーペルソナ・ジャーニーの定義 ― 受け入れ条件を「既知の登場人物」に対して評価できるようにするためのもの</li><li>計測イベント命名規則 ― 機能ごとに飛ばすべきイベントを、AIが命名を発明することなく埋められるようにするためのルール</li><li>エラー / 空状態 / ローディング状態のUX標準 ― PRDが書き忘れがちな異常系を、モデルが自動補完できるようにするためのガイドライン</li></ul><p>このPRD側の整備は、多くのチームがスキップしている部分であり、同時にいちばん致命的な部分でもあります。</p><p>PRDはそもそも「ギャップだらけの自然言語文書」であり、AIはあなたが指示するかどうかに関係なく、そのギャップを推論で埋めにいきます。 PRD側のルールがない状態では、AIは「もっともらしいけれど組織の実態とは微妙にズレた」解釈を発明してしまい、PMはそれを毎回プロンプトで補正し続けることになります。</p><h3>2つのアプローチの比較</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c093bb25df8baa5ac788fc2dcd9e7f86/href">https://medium.com/media/c093bb25df8baa5ac788fc2dcd9e7f86/href</a></iframe><h3>3. なぜルールは積み上がり、プロンプトは積み上がらないのか</h3><p>1. ルールは資産として残り、プロンプトはその場限りで消える。 あなたが書いたプロンプトはセッションが終われば消えてしまいますが、あなたが書いたルールは、次回の自分とチーム全員の次回を必ず改善します。</p><p>2. ひとつの修正が、すべての将来の生成に効く。 純粋な推論で何かがうまくいかなかった場合、誰かがプロンプトを少し直すだけになりがちで、次のエンジニアは同じ問題にゼロからぶつかります。ルールが間違っていたなら一度直すだけでよく、モデルはその間違いを誰に対しても二度と犯しません。</p><p>3. ルールを書くこと自体が、暗黙の不一致をあぶり出す。 規約を言語化するという行為そのものが、チームが先延ばしにしていた意思決定を強制します。それを解決することで、人間とAIの双方の出力水準が底上げされていきます。一方で純粋な推論は、その不一致を見えなくしているだけです。</p><p>4. オンボーディングが積み上がる。 新メンバー（人間も新しいAIエージェントも）はルールを読めば即座にチームと整合した出力を出せるようになります。純粋な推論の場合、新しく入った人がチームの好みを試行錯誤で再発見し直す必要が出てきます。</p><h3>4. 純粋な推論で十分な場面</h3><p>ルール付きが常に正解、というわけではありません。次のような場面では、むしろ純粋な推論のほうが適しています。</p><ul><li>PoC ― 捨てる前提のコードにルールは不要です</li><li>単発スクリプト・データ移行 ― 一度しか走らないコードに一貫性は求められません</li><li>新しい領域での試行 ― ルールを決める前に、まず手を動かして「ルールが何であるべきか」を見つけ出す必要があるフェーズです</li><li>未整備のアーリーステージなチーム ― コードから後付けでルールを抽出するほうが速い場合があります</li></ul><p>線引きの目安としては、長く保守する／複数人で触る／同じパターンが繰り返し出てくる コードはルール付き推論に寄せ、それ以外は純粋な推論でよい、というあたりになります。</p><h3>5. 実例 ― 現場での適用パターン</h3><p>すでに各レイヤーでルール付き推論を適用した事例がいくつか公開されているので、ここで紹介します。</p><h4>アーキテクチャ駆動のコード生成</h4><p><a href="https://techblog.zozo.com/entry/architecture-codegen-pipeline">スキーマでアーキテクチャを縛る ── 人間とAIを同じルールで動かす - ZOZO TECH BLOG</a></p><p>チームのアーキテクチャガイドライン（層の責務、依存ルール、命名規則）を「人間向けのドキュメント」ではなく AIへの直接の入力 として扱う取り組みです。結果として、生成されたコードは「正しいファイルに、正しい層で、正しい命名で」着地するようになります。モデルが書き始める前に「ものはどこに置くか」がルールで伝わっているからです。</p><h4>テストコード生成</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fspeakerdeck.com%2Fplayer%2Fef5304580c0141eaa2e3c42d4121f14d&amp;display_name=Speaker+Deck&amp;url=https%3A%2F%2Fspeakerdeck.com%2Fkitasuke%2Fdont-prompt-harder-structure-better&amp;image=https%3A%2F%2Ffiles.speakerdeck.com%2Fpresentations%2Fef5304580c0141eaa2e3c42d4121f14d%2Fslide_0.jpg%3F39078982&amp;type=text%2Fhtml&amp;schema=speakerdeck" width="710" height="399" frameborder="0" scrolling="no"><a href="https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href">https://medium.com/media/344961c6a8f9f61b238641a49adb8f55/href</a></iframe><p>同じ思想をテストに適用した事例です。「もっと良いテストを書いて」とプロンプトを工夫するのではなく、構造テンプレート（何をモックするか、何をアサートするか、テストケースの命名パターン）をルールセットとしてモデルに与えます。テストのブレが消え、レビューがほぼ機械的に進むようになります。</p><h4>テストプラン生成</h4><p><a href="https://tech.newmo.me/entry/2025/12/09/090000">生成AIの入出力品質は「フォーマット」で決まる ― 中間表現とテンプレートの組み合わせ - newmo 技術ブログ</a></p><p>ルール付き推論を 仕様側の上流 にまで押し上げた事例です。PRDとテストプラン生成ルール（カバレッジ軸、エッジケースのカテゴリ、出力フォーマット）を組み合わせることで、汎用的なチェックリストではなく実用的なテストプランが得られます。これはまさに、PRD側のルールを具体的に適用した例だといえます。</p><p>3つの事例に共通しているのは、いずれも「より賢いプロンプト」を発明しているわけではない という点です。それぞれのチームが、これまで暗黙のうちに知っていたことを書き起こし、ルールとしてモデルへの入力にした結果が、これらの取り組みになっています。</p><h3>6. まとめ ― プロンプトをチューニングするのではなく、ルールの土台を整える</h3><ul><li>推論こそが鍵であり、ルールはそのギャップを縮めるための手段です</li><li>ルールにはコード側とPRD側の2つの側面があり、両方を揃えて初めて効果が出ます</li></ul><p>この記事から1つだけ持ち帰るとしたら、プロンプトを頑張ってチューニングするのをやめ、AIが推論するためのルールという土台を仕様側とコード側の両方で整え、そのルール自体に仕事をさせる ということに尽きるのではないかと思います。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d8a7e7edecfa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ドキュメント化したい内容]]></title>
            <link>https://medium.com/@kitasuke/%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E5%8C%96%E3%81%97%E3%81%9F%E3%81%84%E5%86%85%E5%AE%B9-275147de24bb?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/275147de24bb</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Wed, 06 Dec 2023 00:25:02 GMT</pubDate>
            <atom:updated>2023-12-13T14:36:19.048Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ソフトウェアエンジニアの<a href="https://twitter.com/kitasuke">@kitasuke</a> です。</p><p>今回は必要な情報をドキュメント化することで、チームをスケールする方法を紹介します。</p><h3>目的</h3><p>暗黙値を形式知にして不要な属人化を解消することやチーム力を上げることです。</p><h3>前提</h3><p>不要な属人化とは暗黙な知識を持ってる人だけしか出来ない状態です。権限が必要な属人化は問題ないのでスコープ外です。</p><h3>課題</h3><p>ドキュメントを書く文化が無いと下記のような課題があります</p><h4>依存度が高い</h4><p>作業が完了する時間や条件が特定の知識を持った人に依存していることが多いです。作業や人が増えるにつれてその人への負荷が高くなる一方で持続可能な状態ではないです。</p><h4>知識が失われる</h4><p>特定の知識を持った人が居なくなるとその知識も失われることが多いです。チームが育つにつれて役割の変化やチーム移動などもあり得るので、チームとして知識をストックしていないと効率が悪いです。</p><p><strong>必要な情報</strong></p><p>上記のような課題が発生する知識の内容は下記の通りです</p><ul><li>見たい情報がどこにあるか分からない</li><li>ソースコードを読んでも背景が分からない・理解に時間がかかる</li><li>簡単な作業なのにただやり方が分からない</li></ul><p><strong>不要な情報</strong></p><p>下記の内容はドキュメントとしては不要であったり他のツールで管理したいです</p><ul><li>ソースコードと同等の内容</li><li>タスクや進捗管理に関するもの</li></ul><h3>解決策</h3><p>それらの内容をドキュメントに書きましょう。ドキュメント化が進めば依存も減りますし知識も貯まります。主に下記の3つの情報をドキュメント化したいです。</p><h4>どこに何があるか</h4><p>どこを見れば知りたい内容があるか分かると効率が良いです。アーキテクチャ概要やモジュール構成の概要などがそれに該当します。</p><h4>なぜそうなっているか</h4><p>ドメイン知識やTipsなどがあると良いです。特定の知識や手法の選定理由などがそれに該当します。</p><h4>どうやっているか</h4><p>知識がなくてもできることは手順としてまとまってると誰でもできて良いです。また自動化で代用できると更に良いです。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=275147de24bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[早く小分けにレビューしてコードの出戻りを減らす]]></title>
            <link>https://medium.com/@kitasuke/%E6%97%A9%E3%81%8F%E5%B0%8F%E5%88%86%E3%81%91%E3%81%AB%E3%83%AC%E3%83%93%E3%83%A5%E3%83%BC%E3%81%97%E3%81%A6%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E5%87%BA%E6%88%BB%E3%82%8A%E3%82%92%E6%B8%9B%E3%82%89%E3%81%99-a9eb2e59fd23?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/a9eb2e59fd23</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Sun, 13 Aug 2023 04:48:45 GMT</pubDate>
            <atom:updated>2024-05-17T12:24:50.112Z</atom:updated>
            <content:encoded><![CDATA[<h3>早く小分けにレビューしてコードの手戻りを減らす</h3><p>こんにちは。ソフトウェアエンジニアの<a href="https://twitter.com/kitasuke">@kitasuke</a> です。</p><p>今回はなるべく早いタイミングで小分けにレビューをすることで、コードレビューによるコードの手戻りを可能な限り減らす方法を紹介します。</p><h3>概要</h3><p>コードレビューにおける一般的なボトルネックのひとつは、コードに対するフィードバックと修正のサイクルを回す回数が多いことです。その中でも特にコードの手戻りが発生する場合は何回もコードを大幅に書き直すこともあるので、レビューの時間もかかりますし負荷も上がります。その主な原因は、コードを書く上で必要な機能仕様の理解度が低い、コードをどう書けば良いか設計が詰められていない、良いとされるコードの状態を言語化できていないことのどれかに当てはまることが多いです。 それらの詳細なコードがなくてもレビュー可能な内容はコードレビューよりもっと早い段階でレビューしたり、レビュー対象ごとに小分けにレビューしたり、作業のスコープを小さくすることでボトルネックの解消を図ります。</p><h3>前提</h3><p>課題設定の内容から最適な手法が決まるので、今回のやり方は同じような課題を抱えているチーム以外や課題は同じでもチーム構成などの環境が違う状況でも効果があるかは不明です。</p><h3>レビュー目的</h3><p>レビューの主な目的は、下記の2つの内容が問題なく実装されているかを確認することです。</p><ul><li>コードが仕様通りに実装されているか</li><li>良いコードで実装されているか</li></ul><p>それぞれの詳細は下記のとおりです。</p><p><strong>仕様通りのコード</strong></p><p>実装されたコードが仕様通りの挙動になるかは最も重要な観点です。フロントエンドは表示に関わる内容がほとんどです。またサービスとして全体の統一感を保てるように他の仕様と矛盾点がないかであったり、仕様の実現可能性を確認するのも大事です。</p><p><strong>良いコード</strong></p><p>良いコードとは一般的にメンテナンス性が高いコードと言い換えられます。それは具体的には拡張性が高かったり、バグを生みにくかったり、可読性が高いコードなどに該当します。フロントエンドは表示に関する値のデータフローやデータモデリングの内容がほとんどです。そしてそれらをどうやって読みやすいコードで実装するかもポイントです。</p><h3>課題</h3><p>コードレビューにおける課題は色々あります。その中でもコードレビュー中にコードの手戻りが多く発生して、その過程で無駄になるコードの量が多くなることが一番の課題だと感じてます。そして手戻りは想定以上の時間を費やすことにも繋がります。</p><p>手戻りが多くなるのは、コードを書く時に何をどう実装すれば良いかあやふやな状態のまま詳細実装に進むことが主な原因です。またコードレビュー時にそれらも含めてレビュー対象が多すぎることも関連しています。</p><h3>解決策</h3><p>上記の課題を解決するための方針として、可能な限り早い段階でレビューすること、レビュー対象を小分けにしてレビューすること、作業のスコープを小さくすることを目指しましょう。</p><p><strong>早めのレビュー</strong></p><p>詳細なコードがなくてもレビューできる内容はコードレビュー時より早い段階でレビューすべきです。具体的には仕様の妥当性やヌケモレは大体の場合はコードが不要です。またデータモデリングやデータフローはインターフェイスがあれば十分です。これらは詳細なコードの実装の元となる非常に重要な内容なので、詳細なコードを書く前にレビューした方が手戻りが減り効率が良いです。</p><p>またコードを書く際やレビューする時はペアでリアルタイムにフィードバックを得ながら進めると効率が良いです。詳細なコードの実装はペアプロを行い、コードレビューはペアレビューを行うのが良いです。</p><p><strong>小分けのレビュー</strong></p><p>コードレビューだけで全ての内容をレビューするのは難易度も属人性も高くなります。上記のように詳細なコードがなくてもレビューできる内容は対象ごとにレビューしましょう。具体的には機能仕様レビューやアーキテクチャレビューというように、段階的に行うのが良いです。それらを抽象的にカバーできる手法の <a href="https://www.industrialempathy.com/posts/design-docs-at-google/">Design Docs</a> を導入するのも良いです。</p><p>またコードレビューの中でも、内容ごとにレビューできる仕組みがあれば小分けにレビューできます。例えば文法チェックなどのコーディング規約はツールやCIに任せたり、アーキテクチャなどのプラクティスはドキュメントを用意したり、動作確認などのテストはユニットテスト・UIテストを実行すると良いです。</p><p><strong>小さいスコープ</strong></p><p>早めに小分けにレビューするにはひとつの作業のスコープを小さくすることも大事です。なるべく必要最低限の粒度に収まるように気をつけましょう。</p><h3>効果検証</h3><p>上記の解決策の効果を検証するには、定性的・定量的なデータの両方を見ます。定量的には <a href="https://findy-team.io/">Findy Team+</a> のサイクルタイム分析にあるレビューからアプルーブまでの平均時間が減少しているかを確認するのが手軽な方法だと思います。自社で計測ツールを作成する場合も同じような指標を見れば良いと思います。導入直後は定性的なデータに頼ることが多いと思いますが、長期的には定量的なデータを重視するのが理想です。</p><h3>まとめ</h3><p>今回はコードの手戻りを減らす手法を紹介しました。この方法は開発やレビューの生産性を上げるだけではなく、スケールする上でも大事な観点です。チームメンバー数が増えたりコードベースが古くなってくると上記のような課題が生まれることが多いので、環境の変化に適応してより良い開発環境を作りましょう。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9eb2e59fd23" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UIKitで積極的にSwiftUIを使う]]></title>
            <link>https://medium.com/@kitasuke/uikit%E3%81%A7%E7%A9%8D%E6%A5%B5%E7%9A%84%E3%81%ABswiftui%E3%82%92%E4%BD%BF%E3%81%86-2ee98853179a?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/2ee98853179a</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[uikit]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Wed, 27 Jul 2022 02:15:41 GMT</pubDate>
            <atom:updated>2023-02-01T04:20:30.803Z</atom:updated>
            <content:encoded><![CDATA[<h3>UIKitのUIViewControllerとSwiftUIのViewを組み合わせる</h3><p>こんにちは。ソフトウェアエンジニアの<a href="https://twitter.com/kitasuke">@kitasuke</a>です。</p><p>今回は何かしらの事情でUIKitが必要なケースでも、SwiftUIを積極的に使う方法とその運用について説明します。</p><h3>概要</h3><p>開発効率やメンテナンスコストを考慮して、可能な限りSwiftUIでViewを実装するのが良いと考えます。ただSwiftUIの標準APIではまだ実現できないことがあるので、特別な事情でUIKitが必要となるケースがあります。UIKitとSwiftUIを組み合わせる場合は、 UIViewController や UICollectionViewCell などや画面遷移はUIKitで実装して、 UIView はSwiftUIで実装するのがバランス良いと思います。</p><h3>おさらい</h3><p>SwiftUIをUIKitで使うには、 UIViewRepresentable や UIViewControllerRepresentable が用意されていて一通りの機能が揃っています。今回の記事ではこれの逆バージョンを説明します。</p><ul><li><a href="https://developer.apple.com/documentation/swiftui/uiviewrepresentable">UIViewRepresentable | Apple Developer Documentation</a></li><li><a href="https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable">UIViewControllerRepresentable | Apple Developer Documentation</a></li></ul><h3>必要なこと</h3><p>UIKitでSwiftUIを使うのに必要なことは下記の2点です。</p><ul><li>SwiftUIのViewの表示</li><li>SwiftUIのViewの更新</li></ul><p>基本的にはSwiftUIをUIKitで使う際に必要な UIViewRepresentable の func makeUIView(context: Self.Context) -&gt; Self.UIViewType と func updateUIView(Self.UIViewType, context: Self.Context) と同等のインターフェイスがあればよいです。</p><h4>Viewの表示</h4><p>SwiftUIには UIHostingController が用意されてるので、これを利用してSwiftUIのViewをUIKitにセットして表示します。</p><p><a href="https://developer.apple.com/documentation/swiftui/uihostingcontroller">UIHostingController | Apple Developer Documentation</a></p><p>具体的には2つのケースがあるので下記で説明します。どちらもやることはほぼ同じです。</p><p><strong>UIViewControllerへの対応</strong></p><p>UIViewController の一部にSwiftUIのViewを表示するケースです。 UIViewController に UIViewController を追加してレイアウトの指定をします。</p><pre>let viewController = UIHostingController(rootView: SwiftUIView())<br>viewController.view.translatesAutoresizingMaskIntoConstraints = false<br>addChild(viewController)<br>view.addSubview(viewController.view)<br>viewController.didMove(toParent: self)</pre><pre>// configure constraints for AutoLayout<br>...</pre><p><strong>UIViewへの対応</strong></p><p>UICollectionViewCell の一部にSwiftUIのViewを表示するケースです。 UIView に UIView を追加してレイアウトの指定をします。</p><pre>let viewController = UIHostingController(rootView: SwiftUIView())<br>viewController.view.translatesAutoresizingMaskIntoConstraints = false<br>view.addSubview(viewController.view)</pre><pre>// configure constraints for AutoLayout<br>...</pre><h4>Viewの更新</h4><p>UIKit側で値を更新する場合は、その更新をトリガーにしてSwiftUI側で再描画する必要があります。手軽な方法として下記のように @Published を利用して実現できます。UIKit側で text の値が変わるとそれが @Published 経由でSwiftUIのViewが再描画が実行されます。</p><pre>// UIKit</pre><pre>final class HostingViewState: ObservableObject {<br>    @Published var text: String = &quot;&quot;<br>}</pre><pre>final class ViewController: UIViewController {<br>    private let hostingViewState = HostingViewState()</pre><pre>    private func updateSwiftUIView() {<br>        hostingViewState.text = &quot;text&quot;<br>    }<br>}</pre><pre>// SwiftUI<br>struct HostingView: View {<br>    @ObservedObject var viewState: HostingViewState</pre><pre>    var body: some View {<br>        Text(viewState.text)<br>    }<br>}</pre><h4>Viewの再利用性</h4><p>上記のインターフェイスだと、Viewのイニシャライザーの引数に HostingViewState が必要となりますが、これはUIKitのみに必要な無駄な依存です。このViewをSwiftUIだけで利用したい場合は不要なので、下記のようにラップして定義するのが良いと思います。</p><pre>// for SwiftUI<br>struct SwiftUIView: View {<br>    var text: String</pre><pre>    var body: some View {<br>        Text(text)<br>    }<br>}</pre><pre>// for UIKit<br>struct HostingSwiftUIView: View {<br>    @ObservedObject var state: HostingViewState</pre><pre>    var body: some View {<br>        SwiftUIView(text: state.text)<br>    }<br>}</pre><pre>#if DEBUG<br>struct SwiftUIView_Previews: PreviewProvider {<br>    static var previews: some View {<br>        SwiftUIView(text: &quot;text&quot;)<br>    }<br>}<br>#endif</pre><h3>運用方法</h3><p>SwiftUIとUIKitの使い分け方として、必要最低限の機能のみUIKitで実装するのが良いと思います。例えば古いOSをサポートしているためにSwiftUIの refreshable を使わずににPull to Refreshを実現する場合、 UIScrollView にある refreshControl のみUIKitで実装して、そこで表示するViewはSwiftUIのViewで実装するというのが分かりやすいかと思います。 refreshable のようにいずれは必要としていた機能がSwiftUIに追加されることは多いはずので、このような使い分けにすればUIKitの実装を消す際も楽です。</p><p><a href="https://developer.apple.com/documentation/swiftui/view/refreshable(action:)">refreshable(action:) | Apple Developer Documentation</a></p><p>また画面遷移に関してもUIKitの方が仕組みを導入しやすいので、こちらも同様にUIKitで実装するのが無難です。</p><h3>まとめ</h3><p>SwiftUIは宣言的な記述やデバッグや表示仕様の確認など色々な観点でUIKitと比較して開発効率が良いです。SwiftUIとUIKitを組み合わせると多少コード量が増えたりして面倒ですが、それでもSwiftUIを使うほうが良いと考えます。また将来的にSwiftUIに書き換える時の作業も減るのも地味に良い点だと思います。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2ee98853179a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SwiftUIに適したView用のModel]]></title>
            <link>https://medium.com/@kitasuke/swiftui%E3%81%AB%E9%81%A9%E3%81%97%E3%81%9Fview%E7%94%A8%E3%81%AEmodel-6f9179ae66e0?source=rss-4a31a30bb718------2</link>
            <guid isPermaLink="false">https://medium.com/p/6f9179ae66e0</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Yusuke Kita]]></dc:creator>
            <pubDate>Tue, 19 Jul 2022 02:59:47 GMT</pubDate>
            <atom:updated>2022-07-19T02:59:47.788Z</atom:updated>
            <content:encoded><![CDATA[<p>こんにちは。ソフトウェアエンジニアの<a href="https://twitter.com/kitasuke">@kitasuke</a>です。</p><p>今回はSwiftUIに適したView用のModelについて説明します。まだ色々試行錯誤している途中なので改善点があるかもしれませんが、現時点でまとめた内容を紹介します。</p><h3>概要</h3><p>ViewModelでView用の値を一元管理するケースにおいて、ビジネスロジック用とは別にView用のModelを定義してそれをSwiftUIのViewに渡すのが良いと考えます。そうすることでViewに向けたデータ整形のロジックの分散を防げたり、Viewの再利用性を高めたり、SwiftUI Previewsの書きやすさを上げられます。</p><h3>前提</h3><p>MVVMのアーキテクチャで説明しますが、データフローが分かりやすいReduxやFluxなど他のアーキテクチャにも適用可能です。また一部SwiftUIに特化した定義がありますが、基本的な概念はUIKitにも使えます。</p><p>Modelの名前衝突を防ぐためにAppとAppEntityの2つのモジュールで構成していますが、名前空間さえ分けられれば別の方法でも構いません。</p><h3>アプローチ</h3><p>まず全体像を図を見ながら説明して、その後に実装の詳細をコードを見ながら説明します。</p><h4>全体像</h4><p>全体の処理の流れを下の図で示します。ViewModelがViewに必要な値をModelとして生成して、それをViewにバインディングするというデータフローです。</p><figure><img alt="データフロー" src="https://cdn-images-1.medium.com/max/1024/1*d5YbyhONSRC1n_yhQJYY6Q.png" /></figure><p>次にそれぞれの役割を下記で説明します。</p><h4>各Modelの役割</h4><p>ビジネスロジックとViewの表示では扱う値の性質が異なります。両者で無理に同じModelを使い回すと不要な依存が増えたり処理が煩雑になったりするので、役割ごとに型を分けて使うのが良いと思います。下記で2つのModelの役割を説明します。</p><p><strong>ビジネスロジック用のModel</strong></p><p>ビジネスロジック用のModelは、全般的なビジネスロジックの対応に利用します。必要な情報ソース（サーバーやアプリ内DB等）から取得した値をSwiftの型に変換します。</p><pre>// Product.swift in AppEntity module<br>public struct Product {<br>    public let id: String<br>    public let title: String<br>    public let price: Decimal<br>    public let isSoldOut: Bool<br>    public let isFavorite: Bool<br>}</pre><p><strong>View用のModel</strong></p><p>View用のModelは、色々なViewを表示したり制御するために利用します。基本的に表示に使う値は String 型の値に整形されます。またUIのイベント（タップ等）により表示内容を更新したい値は、 @Published を利用してViewを再描画します。そのためView用のModelは ObservableObject を継承しています。</p><pre>// Product.swift in App module<br>final class Product: ObservableObject {<br>    var id = &quot;&quot;<br>    var title = &quot;&quot;<br>    var price = &quot;&quot;<br>    var isSoldOut = false<br>    @Published var isFavorite = false<br>}</pre><h4>ViewModelの役割</h4><p>ViewModelの役割は、ビジネスロジックから取得した値をView用への変換、Viewの表示内容の更新、UIのイベント処理の対応です。ビジネスロジックとは例えば値をサーバーから取得したりサーバーへ送信する処理を指しますが、具体実装については本題と関係ないので解説を省きます。</p><p>下記がViewModelの実装例です。それぞれの役割ごとにコードを抜粋して説明します。</p><pre>import Combine<br>import AppEntity</pre><pre>final class ProductViewModel: ObservableObject {<br>    // state: loading|loaded<br>    @Published var isLoading = true<br>    @Published var product = Product()</pre><pre>    // internal use only for UI event or business logic<br>    private var _product: AppEntity.Product?</pre><pre>    private var businessLogicClient = BusinessLogicClient()<br>    private var cancellables: [AnyCancellable] = []</pre><pre>    // UI event handler<br>    lazy var favoriteButtonTapHandler: () -&gt; Void = { [weak self] in</pre><pre>        // update with latest value for further internal use<br>        self?._product.isFavorite.toggle()</pre><pre>        // rebuild favorite view with isFavorite once it&#39;s changed<br>        self?.product.isFavorite.toggle()<br>    }</pre><pre>    private func getProduct() {<br>        isLoading = true</pre><pre>        businessLogicClient.getProduct()<br>            .sink(receiveCompletion: { [weak self] completion in<br>                self?.isLoading = false<br>            }, receiveValue: { [weak self] product in</pre><pre>                // get AppEntity product from business logic<br>                self?._product = product</pre><pre>                // transform AppEntity product to App product<br>                self?.product = Product(product: product)<br>            }<br>            .store(in: &amp;cancellables)<br>    }<br>}</pre><p><strong>Modelの変換</strong></p><p>ビジネスロジック用のModelからView用のModelへの変換は、下記のようなインターフェイスを用意します。大体の場合は対となるModelを引数に取れば十分ですが、Viewによっては複数のModelが必要なケースもあります。 String 型への整形などView用の値の生成は全てここで処理します。</p><pre>import AppEntity</pre><pre>final class Product: ObservableObject {<br>    ...</pre><pre>    private static let priceFormatter = PriceFormatter()</pre><pre>    convenience init(product: AppEntity.Product) {<br>        self.init(<br>            id: product.id,<br>            title: product.title,<br>            price: priceFormatter.string(from: product.price) ?? &quot;&quot;,<br>            isSoldOut: product.isSoldOut,<br>            isFavorite: product.isFavorite<br>        )<br>    }<br>}</pre><p><strong>Viewへのバインディング</strong></p><p>ViewModelは状態をStateで管理します。一般的にはStateは Enum を使うのが良いですが、 サンプルコードなので手を抜いているのと @Published を使うことを想定して bool で表現しています。ViewModelはこのStateによって表示する値をViewに渡します。</p><p>今回は isLoading の値でデータ読み込み中もしくはデータ読み込み完了のStateと見なします。データ読み込み完了後には App.Product をViewに渡して表示したいので、 isLoading と product の両方を @Published でViewにバインディングして、その値が変わる度に描画を行うようにしています。</p><pre>final class ProductViewModel: ObservableObject {<br>    ...<br>    private func getProduct() {<br>        isLoading = true</pre><pre>        businessLogicClient.getProduct()<br>            .sink(receiveCompletion: { [weak self] completion in<br>                self?.isLoading = false<br>            }, receiveValue: { [weak self] product in</pre><pre>                // get AppEntity product from business logic<br>                self?._product = product</pre><pre>                // transform AppEntity product to App product<br>                self?.product = Product(product: product)<br>            }<br>            .store(in: &amp;cancellables)<br>    }<br>}</pre><p><strong>UIイベントの対応</strong></p><p>Viewの表示内容を更新するタイミングはデータ読み込み後だけでなく、UIからのイベント後にも一部表示更新が必要なケースがあります。今回はボタンを押したら表示を変えるケースについて対応します。ボタン用のイベントハンドラーの favoriteButtonTapHandler 内で @Published の isFavorite の値を変えているので、その更新された値がViewに通知され適切な表示になるよう再描画されます。</p><pre>final class ProductViewModel: ObservableObject {<br>    ...<br>    lazy var favoriteButtonTapHandler: () -&gt; Void = { [weak self] in</pre><pre>        // update with latest value for further internal use<br>        self?._product.isFavorite.toggle()</pre><pre>        // rebuild favorite view with isFavorite once it&#39;s changed<br>        self?.product.isFavorite.toggle()<br>    }<br>    ...<br>}</pre><h4>Viewの役割</h4><p>Viewの役割は、ViewModelの各Stateで表示するViewの制御と、Viewに必要な値をセットして表示することです。</p><p>それぞれの役割ごとにコードを抜粋して説明します。</p><p><strong>Viewの制御</strong></p><p>ViewはViewModelのStateごとに適切なViewを表示します。今回は isLoading がViewModelのStateです。それが true の時はデータ読み込み中なので LoadingView を表示して、逆に false の時はデータ読み込み完了と見なすので Product 情報を表示する ProductContentView を表示します。</p><pre>struct ProductView: View {<br>    @StateObject var viewModel = ProductViewModel()</pre><pre>    var body: some View {<br>        if viewModel.isLoading {<br>            LoadingView()<br>        } else {<br>            ProductContentView(<br>                product: viewModel.product,<br>                favoriteButtonTapHandler: viewModel.favoriteButtonTapHandler<br>            )<br>        }<br>    }<br>}</pre><p><strong>Viewの表示</strong></p><p>ViewはViewModelからStateごとに表示する値を渡してもらい、それを表示します。Product に関連した値はこのModelに全て内包されているので、Viewに対してはModelを渡します。それと追加でUIからのイベントハンドラーも必要なケースがあります。</p><pre>struct ProductContentView: View {<br>    @ObservedObject var product: Product<br>    var favoriteButtonTapHandler: (() -&gt; Void)?</pre><pre>    var body: some View {<br>        ZStack {<br>            if product.isSoldOut {<br>                SoldOutImageView()<br>            }<br>            VStack {<br>                HStack {<br>                    TitleView(title: product.title)<br>                    FavoriteIconView(<br>                        isFavorite: product.isFavorite,<br>                        tapHandler: favoriteButtonTapHandler<br>                    )<br>                }<br>                PriceView(price: product.price)<br>            }    <br>        }<br>    }<br>}</pre><h3>特徴</h3><p>このアプローチの特徴は、Viewに向けたデータ整形のロジックの分散を防げたり、Viewの再利用性を高めたり、SwiftUI Previewsの書きやすさを上げられることです。下記でそれぞれの詳細を説明します。</p><h4>データ整形</h4><p>数値や日付は表示用に String 型へ整形する必要があります。また複数の状態を組み合わせて文字列を生成するケースもあります。そのような整形や生成をViewやViewModelで行うと、同じような処理が分散されるのでコードの見通しが少し悪くなります。代わりにView用のModel内で行うことで一元管理できて分かりやすいです。</p><h4>Viewの再利用</h4><p>上記の ProductView はStateによって適切なViewを表示するのでViewModelに依存していますが、 ProductContentView は表示する値とイベントハンドラーのみあれば十分です。このViewでもViewModelを引数にしてしまうと不要な依存となり、 ProductContentView を違うViewModel経由からも表示したい場合に再利用し難いです。Viewの引数には可能な限り最小限の粒度の値にすることで、Viewの再利用性が上がります。</p><h4>SwiftUI Previews</h4><p>SwiftUI Previewsで達成したいことは、表示用の値をViewに渡せば期待通りに表示されることの確認だと考えます。したがって、正しくStateを管理出来ているか、またビジネスロジック用のModelがView用のModelに正しく変換されているかは、ViewModelのUnit testで担保すればSwiftUI Previewsで確認する必要はないです。</p><p>また共通化されたView（LoadingView やErrorView等）は定義元以外では確認する必要ないので、基本的には正常系のStateの共通化されてないViewのみ確認できれば良いと考えます。</p><p><strong>View用のModelのインターフェイス</strong></p><p>下記のように Product は初期値を持つ init を定義して、SwiftUI Previewsで確認したい表示に必要な値だけセットできるインターフェイスを用意します。</p><pre>final class Product: ObservableObject {<br>    ...<br>    init(id: String = &quot;&quot;, title: String = &quot;&quot;, price: String = &quot;&quot;, isSoldOut: Bool = false, isFavorite: Bool = false) {<br>        self.id = id<br>        self.title = title<br>        self.price = price<br>        self.isSoldOut = isSoldOut<br>        self.isFavorite = isFavorite<br>    }<br>    ...<br>}</pre><p><strong>SwiftUI Previewsの実装例</strong></p><p>今回は Product に関して、通常時、売り切れ時、お気に入り済みの状態の3つのパターンの表示を確認します。可読性やメンテナンス性を上げるために、これらの用途でそれぞれの状態ごとにモック用のメソッドを定義するのが良いです。ただし今回はサンプルコードなので全て init で代用します。</p><p>下記のように表示用の値をセットするだけで書けます。これがビジネスロジック用のModelであったりViewModelを引数に取るViewだと、整形前のデータやSwiftUI Previewsには関係ない不要な依存となるインスタンス生成などが必要で値のセットだけでも大変です。</p><pre>#if DEBUG<br>struct ProductView_Previews: PreviewProvider {<br>    static var previews: some View {<br>        Group {</pre><pre>            // normal<br>            ProductContentView(<br>                product:<br>                    .init(<br>                        title: &quot;title1&quot;,<br>                        price: &quot;¥1,000&quot;<br>                    ),<br>                favoriteButtonTapHandler: {}<br>            ),</pre><pre>            // sold out<br>            ProductContentView(<br>                product:<br>                    .init(<br>                        title: &quot;title2&quot;,<br>                        price: &quot;¥2,000&quot;,<br>                        isSoldOut: true<br>                    ),<br>                favoriteButtonTapHandler: {}<br>            ),</pre><pre>            // favorite<br>            ProductContentView(<br>                product:<br>                    .init(<br>                        title: &quot;title3&quot;,<br>                        price: &quot;¥3,000&quot;,<br>                        isFavorite: true<br>                    ),<br>                favoriteButtonTapHandler: {}<br>            )<br>        }<br>    }<br>}<br>#endif</pre><h3>注意点</h3><p>いくつか注意する点があるので下記で説明します。</p><h4>値の管理</h4><p>ViewModelでビジネスロジック用とView用の2つのModelを管理する必要があるので、片方だけ更新して片方は忘れるというミスが発生する可能性があります。それを防ぐためにModelの変換についてもUnit testで担保する必要があります。</p><h4>値の比較</h4><p>View用のModelは ObservableObject を継承するためにclassで定義しています。したがって、インスタンスの比較ではなく値の比較をする際に非常に面倒です。ただ個別の値でなくModel自体の比較にはstructで定義しているビジネスロジック用のModel同士を比較すれば良いので、大きな問題ではないと考えます。</p><h3>まとめ</h3><p>一般的にデータフローやアーキテクチャ全般にも共通する考え方として、役割は可能な限り小さく持たせるのが良いです（今回の例では肝心のViewModelが役割を持ちすぎてますが、本題とずれるので無視してください）。今回はその考え方をModelに適用してSwift UIに最適化してみました。またSwiftUI Previewsで書きやすいViewは結果的にViewの再利用性が高くなる気がするので、その点でも今回のアプローチは良いのではないかと考えます。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6f9179ae66e0" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>